18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ec.c - ACPI Embedded Controller Driver (v3) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2001-2015 Intel Corporation 68c2ecf20Sopenharmony_ci * Author: 2014, 2015 Lv Zheng <lv.zheng@intel.com> 78c2ecf20Sopenharmony_ci * 2006, 2007 Alexey Starikovskiy <alexey.y.starikovskiy@intel.com> 88c2ecf20Sopenharmony_ci * 2006 Denis Sadykov <denis.m.sadykov@intel.com> 98c2ecf20Sopenharmony_ci * 2004 Luming Yu <luming.yu@intel.com> 108c2ecf20Sopenharmony_ci * 2001, 2002 Andy Grover <andrew.grover@intel.com> 118c2ecf20Sopenharmony_ci * 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> 128c2ecf20Sopenharmony_ci * Copyright (C) 2008 Alexey Starikovskiy <astarikovskiy@suse.de> 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/* Uncomment next line to get verbose printout */ 168c2ecf20Sopenharmony_ci/* #define DEBUG */ 178c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "ACPI: EC: " fmt 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/kernel.h> 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/init.h> 228c2ecf20Sopenharmony_ci#include <linux/types.h> 238c2ecf20Sopenharmony_ci#include <linux/delay.h> 248c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 258c2ecf20Sopenharmony_ci#include <linux/list.h> 268c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 278c2ecf20Sopenharmony_ci#include <linux/slab.h> 288c2ecf20Sopenharmony_ci#include <linux/suspend.h> 298c2ecf20Sopenharmony_ci#include <linux/acpi.h> 308c2ecf20Sopenharmony_ci#include <linux/dmi.h> 318c2ecf20Sopenharmony_ci#include <asm/io.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include "internal.h" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define ACPI_EC_CLASS "embedded_controller" 368c2ecf20Sopenharmony_ci#define ACPI_EC_DEVICE_NAME "Embedded Controller" 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* EC status register */ 398c2ecf20Sopenharmony_ci#define ACPI_EC_FLAG_OBF 0x01 /* Output buffer full */ 408c2ecf20Sopenharmony_ci#define ACPI_EC_FLAG_IBF 0x02 /* Input buffer full */ 418c2ecf20Sopenharmony_ci#define ACPI_EC_FLAG_CMD 0x08 /* Input buffer contains a command */ 428c2ecf20Sopenharmony_ci#define ACPI_EC_FLAG_BURST 0x10 /* burst mode */ 438c2ecf20Sopenharmony_ci#define ACPI_EC_FLAG_SCI 0x20 /* EC-SCI occurred */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* 468c2ecf20Sopenharmony_ci * The SCI_EVT clearing timing is not defined by the ACPI specification. 478c2ecf20Sopenharmony_ci * This leads to lots of practical timing issues for the host EC driver. 488c2ecf20Sopenharmony_ci * The following variations are defined (from the target EC firmware's 498c2ecf20Sopenharmony_ci * perspective): 508c2ecf20Sopenharmony_ci * STATUS: After indicating SCI_EVT edge triggered IRQ to the host, the 518c2ecf20Sopenharmony_ci * target can clear SCI_EVT at any time so long as the host can see 528c2ecf20Sopenharmony_ci * the indication by reading the status register (EC_SC). So the 538c2ecf20Sopenharmony_ci * host should re-check SCI_EVT after the first time the SCI_EVT 548c2ecf20Sopenharmony_ci * indication is seen, which is the same time the query request 558c2ecf20Sopenharmony_ci * (QR_EC) is written to the command register (EC_CMD). SCI_EVT set 568c2ecf20Sopenharmony_ci * at any later time could indicate another event. Normally such 578c2ecf20Sopenharmony_ci * kind of EC firmware has implemented an event queue and will 588c2ecf20Sopenharmony_ci * return 0x00 to indicate "no outstanding event". 598c2ecf20Sopenharmony_ci * QUERY: After seeing the query request (QR_EC) written to the command 608c2ecf20Sopenharmony_ci * register (EC_CMD) by the host and having prepared the responding 618c2ecf20Sopenharmony_ci * event value in the data register (EC_DATA), the target can safely 628c2ecf20Sopenharmony_ci * clear SCI_EVT because the target can confirm that the current 638c2ecf20Sopenharmony_ci * event is being handled by the host. The host then should check 648c2ecf20Sopenharmony_ci * SCI_EVT right after reading the event response from the data 658c2ecf20Sopenharmony_ci * register (EC_DATA). 668c2ecf20Sopenharmony_ci * EVENT: After seeing the event response read from the data register 678c2ecf20Sopenharmony_ci * (EC_DATA) by the host, the target can clear SCI_EVT. As the 688c2ecf20Sopenharmony_ci * target requires time to notice the change in the data register 698c2ecf20Sopenharmony_ci * (EC_DATA), the host may be required to wait additional guarding 708c2ecf20Sopenharmony_ci * time before checking the SCI_EVT again. Such guarding may not be 718c2ecf20Sopenharmony_ci * necessary if the host is notified via another IRQ. 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_ci#define ACPI_EC_EVT_TIMING_STATUS 0x00 748c2ecf20Sopenharmony_ci#define ACPI_EC_EVT_TIMING_QUERY 0x01 758c2ecf20Sopenharmony_ci#define ACPI_EC_EVT_TIMING_EVENT 0x02 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* EC commands */ 788c2ecf20Sopenharmony_cienum ec_command { 798c2ecf20Sopenharmony_ci ACPI_EC_COMMAND_READ = 0x80, 808c2ecf20Sopenharmony_ci ACPI_EC_COMMAND_WRITE = 0x81, 818c2ecf20Sopenharmony_ci ACPI_EC_BURST_ENABLE = 0x82, 828c2ecf20Sopenharmony_ci ACPI_EC_BURST_DISABLE = 0x83, 838c2ecf20Sopenharmony_ci ACPI_EC_COMMAND_QUERY = 0x84, 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define ACPI_EC_DELAY 500 /* Wait 500ms max. during EC ops */ 878c2ecf20Sopenharmony_ci#define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ 888c2ecf20Sopenharmony_ci#define ACPI_EC_UDELAY_POLL 550 /* Wait 1ms for EC transaction polling */ 898c2ecf20Sopenharmony_ci#define ACPI_EC_CLEAR_MAX 100 /* Maximum number of events to query 908c2ecf20Sopenharmony_ci * when trying to clear the EC */ 918c2ecf20Sopenharmony_ci#define ACPI_EC_MAX_QUERIES 16 /* Maximum number of parallel queries */ 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cienum { 948c2ecf20Sopenharmony_ci EC_FLAGS_QUERY_ENABLED, /* Query is enabled */ 958c2ecf20Sopenharmony_ci EC_FLAGS_QUERY_PENDING, /* Query is pending */ 968c2ecf20Sopenharmony_ci EC_FLAGS_QUERY_GUARDING, /* Guard for SCI_EVT check */ 978c2ecf20Sopenharmony_ci EC_FLAGS_EVENT_HANDLER_INSTALLED, /* Event handler installed */ 988c2ecf20Sopenharmony_ci EC_FLAGS_EC_HANDLER_INSTALLED, /* OpReg handler installed */ 998c2ecf20Sopenharmony_ci EC_FLAGS_QUERY_METHODS_INSTALLED, /* _Qxx handlers installed */ 1008c2ecf20Sopenharmony_ci EC_FLAGS_STARTED, /* Driver is started */ 1018c2ecf20Sopenharmony_ci EC_FLAGS_STOPPED, /* Driver is stopped */ 1028c2ecf20Sopenharmony_ci EC_FLAGS_EVENTS_MASKED, /* Events masked */ 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci#define ACPI_EC_COMMAND_POLL 0x01 /* Available for command byte */ 1068c2ecf20Sopenharmony_ci#define ACPI_EC_COMMAND_COMPLETE 0x02 /* Completed last byte */ 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* ec.c is compiled in acpi namespace so this shows up as acpi.ec_delay param */ 1098c2ecf20Sopenharmony_cistatic unsigned int ec_delay __read_mostly = ACPI_EC_DELAY; 1108c2ecf20Sopenharmony_cimodule_param(ec_delay, uint, 0644); 1118c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ec_delay, "Timeout(ms) waited until an EC command completes"); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic unsigned int ec_max_queries __read_mostly = ACPI_EC_MAX_QUERIES; 1148c2ecf20Sopenharmony_cimodule_param(ec_max_queries, uint, 0644); 1158c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ec_max_queries, "Maximum parallel _Qxx evaluations"); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic bool ec_busy_polling __read_mostly; 1188c2ecf20Sopenharmony_cimodule_param(ec_busy_polling, bool, 0644); 1198c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ec_busy_polling, "Use busy polling to advance EC transaction"); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic unsigned int ec_polling_guard __read_mostly = ACPI_EC_UDELAY_POLL; 1228c2ecf20Sopenharmony_cimodule_param(ec_polling_guard, uint, 0644); 1238c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ec_polling_guard, "Guard time(us) between EC accesses in polling modes"); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic unsigned int ec_event_clearing __read_mostly = ACPI_EC_EVT_TIMING_QUERY; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci/* 1288c2ecf20Sopenharmony_ci * If the number of false interrupts per one transaction exceeds 1298c2ecf20Sopenharmony_ci * this threshold, will think there is a GPE storm happened and 1308c2ecf20Sopenharmony_ci * will disable the GPE for normal transaction. 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_cistatic unsigned int ec_storm_threshold __read_mostly = 8; 1338c2ecf20Sopenharmony_cimodule_param(ec_storm_threshold, uint, 0644); 1348c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ec_storm_threshold, "Maxim false GPE numbers not considered as GPE storm"); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic bool ec_freeze_events __read_mostly = false; 1378c2ecf20Sopenharmony_cimodule_param(ec_freeze_events, bool, 0644); 1388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ec_freeze_events, "Disabling event handling during suspend/resume"); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic bool ec_no_wakeup __read_mostly; 1418c2ecf20Sopenharmony_cimodule_param(ec_no_wakeup, bool, 0644); 1428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ec_no_wakeup, "Do not wake up from suspend-to-idle"); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistruct acpi_ec_query_handler { 1458c2ecf20Sopenharmony_ci struct list_head node; 1468c2ecf20Sopenharmony_ci acpi_ec_query_func func; 1478c2ecf20Sopenharmony_ci acpi_handle handle; 1488c2ecf20Sopenharmony_ci void *data; 1498c2ecf20Sopenharmony_ci u8 query_bit; 1508c2ecf20Sopenharmony_ci struct kref kref; 1518c2ecf20Sopenharmony_ci}; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistruct transaction { 1548c2ecf20Sopenharmony_ci const u8 *wdata; 1558c2ecf20Sopenharmony_ci u8 *rdata; 1568c2ecf20Sopenharmony_ci unsigned short irq_count; 1578c2ecf20Sopenharmony_ci u8 command; 1588c2ecf20Sopenharmony_ci u8 wi; 1598c2ecf20Sopenharmony_ci u8 ri; 1608c2ecf20Sopenharmony_ci u8 wlen; 1618c2ecf20Sopenharmony_ci u8 rlen; 1628c2ecf20Sopenharmony_ci u8 flags; 1638c2ecf20Sopenharmony_ci}; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistruct acpi_ec_query { 1668c2ecf20Sopenharmony_ci struct transaction transaction; 1678c2ecf20Sopenharmony_ci struct work_struct work; 1688c2ecf20Sopenharmony_ci struct acpi_ec_query_handler *handler; 1698c2ecf20Sopenharmony_ci struct acpi_ec *ec; 1708c2ecf20Sopenharmony_ci}; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int acpi_ec_query(struct acpi_ec *ec, u8 *data); 1738c2ecf20Sopenharmony_cistatic void advance_transaction(struct acpi_ec *ec); 1748c2ecf20Sopenharmony_cistatic void acpi_ec_event_handler(struct work_struct *work); 1758c2ecf20Sopenharmony_cistatic void acpi_ec_event_processor(struct work_struct *work); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistruct acpi_ec *first_ec; 1788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(first_ec); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic struct acpi_ec *boot_ec; 1818c2ecf20Sopenharmony_cistatic bool boot_ec_is_ecdt = false; 1828c2ecf20Sopenharmony_cistatic struct workqueue_struct *ec_wq; 1838c2ecf20Sopenharmony_cistatic struct workqueue_struct *ec_query_wq; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int EC_FLAGS_CORRECT_ECDT; /* Needs ECDT port address correction */ 1868c2ecf20Sopenharmony_cistatic int EC_FLAGS_TRUST_DSDT_GPE; /* Needs DSDT GPE as correction setting */ 1878c2ecf20Sopenharmony_cistatic int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */ 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- 1908c2ecf20Sopenharmony_ci * Logging/Debugging 1918c2ecf20Sopenharmony_ci * -------------------------------------------------------------------------- */ 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci/* 1948c2ecf20Sopenharmony_ci * Splitters used by the developers to track the boundary of the EC 1958c2ecf20Sopenharmony_ci * handling processes. 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_ci#ifdef DEBUG 1988c2ecf20Sopenharmony_ci#define EC_DBG_SEP " " 1998c2ecf20Sopenharmony_ci#define EC_DBG_DRV "+++++" 2008c2ecf20Sopenharmony_ci#define EC_DBG_STM "=====" 2018c2ecf20Sopenharmony_ci#define EC_DBG_REQ "*****" 2028c2ecf20Sopenharmony_ci#define EC_DBG_EVT "#####" 2038c2ecf20Sopenharmony_ci#else 2048c2ecf20Sopenharmony_ci#define EC_DBG_SEP "" 2058c2ecf20Sopenharmony_ci#define EC_DBG_DRV 2068c2ecf20Sopenharmony_ci#define EC_DBG_STM 2078c2ecf20Sopenharmony_ci#define EC_DBG_REQ 2088c2ecf20Sopenharmony_ci#define EC_DBG_EVT 2098c2ecf20Sopenharmony_ci#endif 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci#define ec_log_raw(fmt, ...) \ 2128c2ecf20Sopenharmony_ci pr_info(fmt "\n", ##__VA_ARGS__) 2138c2ecf20Sopenharmony_ci#define ec_dbg_raw(fmt, ...) \ 2148c2ecf20Sopenharmony_ci pr_debug(fmt "\n", ##__VA_ARGS__) 2158c2ecf20Sopenharmony_ci#define ec_log(filter, fmt, ...) \ 2168c2ecf20Sopenharmony_ci ec_log_raw(filter EC_DBG_SEP fmt EC_DBG_SEP filter, ##__VA_ARGS__) 2178c2ecf20Sopenharmony_ci#define ec_dbg(filter, fmt, ...) \ 2188c2ecf20Sopenharmony_ci ec_dbg_raw(filter EC_DBG_SEP fmt EC_DBG_SEP filter, ##__VA_ARGS__) 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci#define ec_log_drv(fmt, ...) \ 2218c2ecf20Sopenharmony_ci ec_log(EC_DBG_DRV, fmt, ##__VA_ARGS__) 2228c2ecf20Sopenharmony_ci#define ec_dbg_drv(fmt, ...) \ 2238c2ecf20Sopenharmony_ci ec_dbg(EC_DBG_DRV, fmt, ##__VA_ARGS__) 2248c2ecf20Sopenharmony_ci#define ec_dbg_stm(fmt, ...) \ 2258c2ecf20Sopenharmony_ci ec_dbg(EC_DBG_STM, fmt, ##__VA_ARGS__) 2268c2ecf20Sopenharmony_ci#define ec_dbg_req(fmt, ...) \ 2278c2ecf20Sopenharmony_ci ec_dbg(EC_DBG_REQ, fmt, ##__VA_ARGS__) 2288c2ecf20Sopenharmony_ci#define ec_dbg_evt(fmt, ...) \ 2298c2ecf20Sopenharmony_ci ec_dbg(EC_DBG_EVT, fmt, ##__VA_ARGS__) 2308c2ecf20Sopenharmony_ci#define ec_dbg_ref(ec, fmt, ...) \ 2318c2ecf20Sopenharmony_ci ec_dbg_raw("%lu: " fmt, ec->reference_count, ## __VA_ARGS__) 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- 2348c2ecf20Sopenharmony_ci * Device Flags 2358c2ecf20Sopenharmony_ci * -------------------------------------------------------------------------- */ 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic bool acpi_ec_started(struct acpi_ec *ec) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci return test_bit(EC_FLAGS_STARTED, &ec->flags) && 2408c2ecf20Sopenharmony_ci !test_bit(EC_FLAGS_STOPPED, &ec->flags); 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic bool acpi_ec_event_enabled(struct acpi_ec *ec) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci /* 2468c2ecf20Sopenharmony_ci * There is an OSPM early stage logic. During the early stages 2478c2ecf20Sopenharmony_ci * (boot/resume), OSPMs shouldn't enable the event handling, only 2488c2ecf20Sopenharmony_ci * the EC transactions are allowed to be performed. 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_ci if (!test_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags)) 2518c2ecf20Sopenharmony_ci return false; 2528c2ecf20Sopenharmony_ci /* 2538c2ecf20Sopenharmony_ci * However, disabling the event handling is experimental for late 2548c2ecf20Sopenharmony_ci * stage (suspend), and is controlled by the boot parameter of 2558c2ecf20Sopenharmony_ci * "ec_freeze_events": 2568c2ecf20Sopenharmony_ci * 1. true: The EC event handling is disabled before entering 2578c2ecf20Sopenharmony_ci * the noirq stage. 2588c2ecf20Sopenharmony_ci * 2. false: The EC event handling is automatically disabled as 2598c2ecf20Sopenharmony_ci * soon as the EC driver is stopped. 2608c2ecf20Sopenharmony_ci */ 2618c2ecf20Sopenharmony_ci if (ec_freeze_events) 2628c2ecf20Sopenharmony_ci return acpi_ec_started(ec); 2638c2ecf20Sopenharmony_ci else 2648c2ecf20Sopenharmony_ci return test_bit(EC_FLAGS_STARTED, &ec->flags); 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic bool acpi_ec_flushed(struct acpi_ec *ec) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci return ec->reference_count == 1; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- 2738c2ecf20Sopenharmony_ci * EC Registers 2748c2ecf20Sopenharmony_ci * -------------------------------------------------------------------------- */ 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic inline u8 acpi_ec_read_status(struct acpi_ec *ec) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci u8 x = inb(ec->command_addr); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci ec_dbg_raw("EC_SC(R) = 0x%2.2x " 2818c2ecf20Sopenharmony_ci "SCI_EVT=%d BURST=%d CMD=%d IBF=%d OBF=%d", 2828c2ecf20Sopenharmony_ci x, 2838c2ecf20Sopenharmony_ci !!(x & ACPI_EC_FLAG_SCI), 2848c2ecf20Sopenharmony_ci !!(x & ACPI_EC_FLAG_BURST), 2858c2ecf20Sopenharmony_ci !!(x & ACPI_EC_FLAG_CMD), 2868c2ecf20Sopenharmony_ci !!(x & ACPI_EC_FLAG_IBF), 2878c2ecf20Sopenharmony_ci !!(x & ACPI_EC_FLAG_OBF)); 2888c2ecf20Sopenharmony_ci return x; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic inline u8 acpi_ec_read_data(struct acpi_ec *ec) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci u8 x = inb(ec->data_addr); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci ec->timestamp = jiffies; 2968c2ecf20Sopenharmony_ci ec_dbg_raw("EC_DATA(R) = 0x%2.2x", x); 2978c2ecf20Sopenharmony_ci return x; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic inline void acpi_ec_write_cmd(struct acpi_ec *ec, u8 command) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci ec_dbg_raw("EC_SC(W) = 0x%2.2x", command); 3038c2ecf20Sopenharmony_ci outb(command, ec->command_addr); 3048c2ecf20Sopenharmony_ci ec->timestamp = jiffies; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci ec_dbg_raw("EC_DATA(W) = 0x%2.2x", data); 3108c2ecf20Sopenharmony_ci outb(data, ec->data_addr); 3118c2ecf20Sopenharmony_ci ec->timestamp = jiffies; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) 3158c2ecf20Sopenharmony_cistatic const char *acpi_ec_cmd_string(u8 cmd) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci switch (cmd) { 3188c2ecf20Sopenharmony_ci case 0x80: 3198c2ecf20Sopenharmony_ci return "RD_EC"; 3208c2ecf20Sopenharmony_ci case 0x81: 3218c2ecf20Sopenharmony_ci return "WR_EC"; 3228c2ecf20Sopenharmony_ci case 0x82: 3238c2ecf20Sopenharmony_ci return "BE_EC"; 3248c2ecf20Sopenharmony_ci case 0x83: 3258c2ecf20Sopenharmony_ci return "BD_EC"; 3268c2ecf20Sopenharmony_ci case 0x84: 3278c2ecf20Sopenharmony_ci return "QR_EC"; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci return "UNKNOWN"; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci#else 3328c2ecf20Sopenharmony_ci#define acpi_ec_cmd_string(cmd) "UNDEF" 3338c2ecf20Sopenharmony_ci#endif 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- 3368c2ecf20Sopenharmony_ci * GPE Registers 3378c2ecf20Sopenharmony_ci * -------------------------------------------------------------------------- */ 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic inline bool acpi_ec_is_gpe_raised(struct acpi_ec *ec) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci acpi_event_status gpe_status = 0; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci (void)acpi_get_gpe_status(NULL, ec->gpe, &gpe_status); 3448c2ecf20Sopenharmony_ci return (gpe_status & ACPI_EVENT_FLAG_STATUS_SET) ? true : false; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic inline void acpi_ec_enable_gpe(struct acpi_ec *ec, bool open) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci if (open) 3508c2ecf20Sopenharmony_ci acpi_enable_gpe(NULL, ec->gpe); 3518c2ecf20Sopenharmony_ci else { 3528c2ecf20Sopenharmony_ci BUG_ON(ec->reference_count < 1); 3538c2ecf20Sopenharmony_ci acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_ENABLE); 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci if (acpi_ec_is_gpe_raised(ec)) { 3568c2ecf20Sopenharmony_ci /* 3578c2ecf20Sopenharmony_ci * On some platforms, EN=1 writes cannot trigger GPE. So 3588c2ecf20Sopenharmony_ci * software need to manually trigger a pseudo GPE event on 3598c2ecf20Sopenharmony_ci * EN=1 writes. 3608c2ecf20Sopenharmony_ci */ 3618c2ecf20Sopenharmony_ci ec_dbg_raw("Polling quirk"); 3628c2ecf20Sopenharmony_ci advance_transaction(ec); 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic inline void acpi_ec_disable_gpe(struct acpi_ec *ec, bool close) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci if (close) 3698c2ecf20Sopenharmony_ci acpi_disable_gpe(NULL, ec->gpe); 3708c2ecf20Sopenharmony_ci else { 3718c2ecf20Sopenharmony_ci BUG_ON(ec->reference_count < 1); 3728c2ecf20Sopenharmony_ci acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE); 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic inline void acpi_ec_clear_gpe(struct acpi_ec *ec) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci /* 3798c2ecf20Sopenharmony_ci * GPE STS is a W1C register, which means: 3808c2ecf20Sopenharmony_ci * 1. Software can clear it without worrying about clearing other 3818c2ecf20Sopenharmony_ci * GPEs' STS bits when the hardware sets them in parallel. 3828c2ecf20Sopenharmony_ci * 2. As long as software can ensure only clearing it when it is 3838c2ecf20Sopenharmony_ci * set, hardware won't set it in parallel. 3848c2ecf20Sopenharmony_ci * So software can clear GPE in any contexts. 3858c2ecf20Sopenharmony_ci * Warning: do not move the check into advance_transaction() as the 3868c2ecf20Sopenharmony_ci * EC commands will be sent without GPE raised. 3878c2ecf20Sopenharmony_ci */ 3888c2ecf20Sopenharmony_ci if (!acpi_ec_is_gpe_raised(ec)) 3898c2ecf20Sopenharmony_ci return; 3908c2ecf20Sopenharmony_ci acpi_clear_gpe(NULL, ec->gpe); 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- 3948c2ecf20Sopenharmony_ci * Transaction Management 3958c2ecf20Sopenharmony_ci * -------------------------------------------------------------------------- */ 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic void acpi_ec_submit_request(struct acpi_ec *ec) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci ec->reference_count++; 4008c2ecf20Sopenharmony_ci if (test_bit(EC_FLAGS_EVENT_HANDLER_INSTALLED, &ec->flags) && 4018c2ecf20Sopenharmony_ci ec->gpe >= 0 && ec->reference_count == 1) 4028c2ecf20Sopenharmony_ci acpi_ec_enable_gpe(ec, true); 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic void acpi_ec_complete_request(struct acpi_ec *ec) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci bool flushed = false; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci ec->reference_count--; 4108c2ecf20Sopenharmony_ci if (test_bit(EC_FLAGS_EVENT_HANDLER_INSTALLED, &ec->flags) && 4118c2ecf20Sopenharmony_ci ec->gpe >= 0 && ec->reference_count == 0) 4128c2ecf20Sopenharmony_ci acpi_ec_disable_gpe(ec, true); 4138c2ecf20Sopenharmony_ci flushed = acpi_ec_flushed(ec); 4148c2ecf20Sopenharmony_ci if (flushed) 4158c2ecf20Sopenharmony_ci wake_up(&ec->wait); 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic void acpi_ec_mask_events(struct acpi_ec *ec) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci if (!test_bit(EC_FLAGS_EVENTS_MASKED, &ec->flags)) { 4218c2ecf20Sopenharmony_ci if (ec->gpe >= 0) 4228c2ecf20Sopenharmony_ci acpi_ec_disable_gpe(ec, false); 4238c2ecf20Sopenharmony_ci else 4248c2ecf20Sopenharmony_ci disable_irq_nosync(ec->irq); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci ec_dbg_drv("Polling enabled"); 4278c2ecf20Sopenharmony_ci set_bit(EC_FLAGS_EVENTS_MASKED, &ec->flags); 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic void acpi_ec_unmask_events(struct acpi_ec *ec) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci if (test_bit(EC_FLAGS_EVENTS_MASKED, &ec->flags)) { 4348c2ecf20Sopenharmony_ci clear_bit(EC_FLAGS_EVENTS_MASKED, &ec->flags); 4358c2ecf20Sopenharmony_ci if (ec->gpe >= 0) 4368c2ecf20Sopenharmony_ci acpi_ec_enable_gpe(ec, false); 4378c2ecf20Sopenharmony_ci else 4388c2ecf20Sopenharmony_ci enable_irq(ec->irq); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci ec_dbg_drv("Polling disabled"); 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci/* 4458c2ecf20Sopenharmony_ci * acpi_ec_submit_flushable_request() - Increase the reference count unless 4468c2ecf20Sopenharmony_ci * the flush operation is not in 4478c2ecf20Sopenharmony_ci * progress 4488c2ecf20Sopenharmony_ci * @ec: the EC device 4498c2ecf20Sopenharmony_ci * 4508c2ecf20Sopenharmony_ci * This function must be used before taking a new action that should hold 4518c2ecf20Sopenharmony_ci * the reference count. If this function returns false, then the action 4528c2ecf20Sopenharmony_ci * must be discarded or it will prevent the flush operation from being 4538c2ecf20Sopenharmony_ci * completed. 4548c2ecf20Sopenharmony_ci */ 4558c2ecf20Sopenharmony_cistatic bool acpi_ec_submit_flushable_request(struct acpi_ec *ec) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci if (!acpi_ec_started(ec)) 4588c2ecf20Sopenharmony_ci return false; 4598c2ecf20Sopenharmony_ci acpi_ec_submit_request(ec); 4608c2ecf20Sopenharmony_ci return true; 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic void acpi_ec_submit_query(struct acpi_ec *ec) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci acpi_ec_mask_events(ec); 4668c2ecf20Sopenharmony_ci if (!acpi_ec_event_enabled(ec)) 4678c2ecf20Sopenharmony_ci return; 4688c2ecf20Sopenharmony_ci if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) { 4698c2ecf20Sopenharmony_ci ec_dbg_evt("Command(%s) submitted/blocked", 4708c2ecf20Sopenharmony_ci acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY)); 4718c2ecf20Sopenharmony_ci ec->nr_pending_queries++; 4728c2ecf20Sopenharmony_ci ec->events_in_progress++; 4738c2ecf20Sopenharmony_ci queue_work(ec_wq, &ec->work); 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic void acpi_ec_complete_query(struct acpi_ec *ec) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci if (test_and_clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) 4808c2ecf20Sopenharmony_ci ec_dbg_evt("Command(%s) unblocked", 4818c2ecf20Sopenharmony_ci acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY)); 4828c2ecf20Sopenharmony_ci acpi_ec_unmask_events(ec); 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic inline void __acpi_ec_enable_event(struct acpi_ec *ec) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci if (!test_and_set_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags)) 4888c2ecf20Sopenharmony_ci ec_log_drv("event unblocked"); 4898c2ecf20Sopenharmony_ci /* 4908c2ecf20Sopenharmony_ci * Unconditionally invoke this once after enabling the event 4918c2ecf20Sopenharmony_ci * handling mechanism to detect the pending events. 4928c2ecf20Sopenharmony_ci */ 4938c2ecf20Sopenharmony_ci advance_transaction(ec); 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic inline void __acpi_ec_disable_event(struct acpi_ec *ec) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci if (test_and_clear_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags)) 4998c2ecf20Sopenharmony_ci ec_log_drv("event blocked"); 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci/* 5038c2ecf20Sopenharmony_ci * Process _Q events that might have accumulated in the EC. 5048c2ecf20Sopenharmony_ci * Run with locked ec mutex. 5058c2ecf20Sopenharmony_ci */ 5068c2ecf20Sopenharmony_cistatic void acpi_ec_clear(struct acpi_ec *ec) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci int i, status; 5098c2ecf20Sopenharmony_ci u8 value = 0; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci for (i = 0; i < ACPI_EC_CLEAR_MAX; i++) { 5128c2ecf20Sopenharmony_ci status = acpi_ec_query(ec, &value); 5138c2ecf20Sopenharmony_ci if (status || !value) 5148c2ecf20Sopenharmony_ci break; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci if (unlikely(i == ACPI_EC_CLEAR_MAX)) 5178c2ecf20Sopenharmony_ci pr_warn("Warning: Maximum of %d stale EC events cleared\n", i); 5188c2ecf20Sopenharmony_ci else 5198c2ecf20Sopenharmony_ci pr_info("%d stale EC events cleared\n", i); 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic void acpi_ec_enable_event(struct acpi_ec *ec) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci unsigned long flags; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci spin_lock_irqsave(&ec->lock, flags); 5278c2ecf20Sopenharmony_ci if (acpi_ec_started(ec)) 5288c2ecf20Sopenharmony_ci __acpi_ec_enable_event(ec); 5298c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ec->lock, flags); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci /* Drain additional events if hardware requires that */ 5328c2ecf20Sopenharmony_ci if (EC_FLAGS_CLEAR_ON_RESUME) 5338c2ecf20Sopenharmony_ci acpi_ec_clear(ec); 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 5378c2ecf20Sopenharmony_cistatic void __acpi_ec_flush_work(void) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci flush_workqueue(ec_wq); /* flush ec->work */ 5408c2ecf20Sopenharmony_ci flush_workqueue(ec_query_wq); /* flush queries */ 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic void acpi_ec_disable_event(struct acpi_ec *ec) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci unsigned long flags; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci spin_lock_irqsave(&ec->lock, flags); 5488c2ecf20Sopenharmony_ci __acpi_ec_disable_event(ec); 5498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ec->lock, flags); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* 5528c2ecf20Sopenharmony_ci * When ec_freeze_events is true, we need to flush events in 5538c2ecf20Sopenharmony_ci * the proper position before entering the noirq stage. 5548c2ecf20Sopenharmony_ci */ 5558c2ecf20Sopenharmony_ci __acpi_ec_flush_work(); 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_civoid acpi_ec_flush_work(void) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci /* Without ec_wq there is nothing to flush. */ 5618c2ecf20Sopenharmony_ci if (!ec_wq) 5628c2ecf20Sopenharmony_ci return; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci __acpi_ec_flush_work(); 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cistatic bool acpi_ec_guard_event(struct acpi_ec *ec) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci bool guarded = true; 5718c2ecf20Sopenharmony_ci unsigned long flags; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci spin_lock_irqsave(&ec->lock, flags); 5748c2ecf20Sopenharmony_ci /* 5758c2ecf20Sopenharmony_ci * If firmware SCI_EVT clearing timing is "event", we actually 5768c2ecf20Sopenharmony_ci * don't know when the SCI_EVT will be cleared by firmware after 5778c2ecf20Sopenharmony_ci * evaluating _Qxx, so we need to re-check SCI_EVT after waiting an 5788c2ecf20Sopenharmony_ci * acceptable period. 5798c2ecf20Sopenharmony_ci * 5808c2ecf20Sopenharmony_ci * The guarding period begins when EC_FLAGS_QUERY_PENDING is 5818c2ecf20Sopenharmony_ci * flagged, which means SCI_EVT check has just been performed. 5828c2ecf20Sopenharmony_ci * But if the current transaction is ACPI_EC_COMMAND_QUERY, the 5838c2ecf20Sopenharmony_ci * guarding should have already been performed (via 5848c2ecf20Sopenharmony_ci * EC_FLAGS_QUERY_GUARDING) and should not be applied so that the 5858c2ecf20Sopenharmony_ci * ACPI_EC_COMMAND_QUERY transaction can be transitioned into 5868c2ecf20Sopenharmony_ci * ACPI_EC_COMMAND_POLL state immediately. 5878c2ecf20Sopenharmony_ci */ 5888c2ecf20Sopenharmony_ci if (ec_event_clearing == ACPI_EC_EVT_TIMING_STATUS || 5898c2ecf20Sopenharmony_ci ec_event_clearing == ACPI_EC_EVT_TIMING_QUERY || 5908c2ecf20Sopenharmony_ci !test_bit(EC_FLAGS_QUERY_PENDING, &ec->flags) || 5918c2ecf20Sopenharmony_ci (ec->curr && ec->curr->command == ACPI_EC_COMMAND_QUERY)) 5928c2ecf20Sopenharmony_ci guarded = false; 5938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ec->lock, flags); 5948c2ecf20Sopenharmony_ci return guarded; 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistatic int ec_transaction_polled(struct acpi_ec *ec) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci unsigned long flags; 6008c2ecf20Sopenharmony_ci int ret = 0; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci spin_lock_irqsave(&ec->lock, flags); 6038c2ecf20Sopenharmony_ci if (ec->curr && (ec->curr->flags & ACPI_EC_COMMAND_POLL)) 6048c2ecf20Sopenharmony_ci ret = 1; 6058c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ec->lock, flags); 6068c2ecf20Sopenharmony_ci return ret; 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_cistatic int ec_transaction_completed(struct acpi_ec *ec) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci unsigned long flags; 6128c2ecf20Sopenharmony_ci int ret = 0; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci spin_lock_irqsave(&ec->lock, flags); 6158c2ecf20Sopenharmony_ci if (ec->curr && (ec->curr->flags & ACPI_EC_COMMAND_COMPLETE)) 6168c2ecf20Sopenharmony_ci ret = 1; 6178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ec->lock, flags); 6188c2ecf20Sopenharmony_ci return ret; 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_cistatic inline void ec_transaction_transition(struct acpi_ec *ec, unsigned long flag) 6228c2ecf20Sopenharmony_ci{ 6238c2ecf20Sopenharmony_ci ec->curr->flags |= flag; 6248c2ecf20Sopenharmony_ci if (ec->curr->command == ACPI_EC_COMMAND_QUERY) { 6258c2ecf20Sopenharmony_ci if (ec_event_clearing == ACPI_EC_EVT_TIMING_STATUS && 6268c2ecf20Sopenharmony_ci flag == ACPI_EC_COMMAND_POLL) 6278c2ecf20Sopenharmony_ci acpi_ec_complete_query(ec); 6288c2ecf20Sopenharmony_ci if (ec_event_clearing == ACPI_EC_EVT_TIMING_QUERY && 6298c2ecf20Sopenharmony_ci flag == ACPI_EC_COMMAND_COMPLETE) 6308c2ecf20Sopenharmony_ci acpi_ec_complete_query(ec); 6318c2ecf20Sopenharmony_ci if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT && 6328c2ecf20Sopenharmony_ci flag == ACPI_EC_COMMAND_COMPLETE) 6338c2ecf20Sopenharmony_ci set_bit(EC_FLAGS_QUERY_GUARDING, &ec->flags); 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci} 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_cistatic void advance_transaction(struct acpi_ec *ec) 6388c2ecf20Sopenharmony_ci{ 6398c2ecf20Sopenharmony_ci struct transaction *t; 6408c2ecf20Sopenharmony_ci u8 status; 6418c2ecf20Sopenharmony_ci bool wakeup = false; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci ec_dbg_stm("%s (%d)", in_interrupt() ? "IRQ" : "TASK", 6448c2ecf20Sopenharmony_ci smp_processor_id()); 6458c2ecf20Sopenharmony_ci /* 6468c2ecf20Sopenharmony_ci * By always clearing STS before handling all indications, we can 6478c2ecf20Sopenharmony_ci * ensure a hardware STS 0->1 change after this clearing can always 6488c2ecf20Sopenharmony_ci * trigger a GPE interrupt. 6498c2ecf20Sopenharmony_ci */ 6508c2ecf20Sopenharmony_ci if (ec->gpe >= 0) 6518c2ecf20Sopenharmony_ci acpi_ec_clear_gpe(ec); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci status = acpi_ec_read_status(ec); 6548c2ecf20Sopenharmony_ci t = ec->curr; 6558c2ecf20Sopenharmony_ci /* 6568c2ecf20Sopenharmony_ci * Another IRQ or a guarded polling mode advancement is detected, 6578c2ecf20Sopenharmony_ci * the next QR_EC submission is then allowed. 6588c2ecf20Sopenharmony_ci */ 6598c2ecf20Sopenharmony_ci if (!t || !(t->flags & ACPI_EC_COMMAND_POLL)) { 6608c2ecf20Sopenharmony_ci if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT && 6618c2ecf20Sopenharmony_ci (!ec->nr_pending_queries || 6628c2ecf20Sopenharmony_ci test_bit(EC_FLAGS_QUERY_GUARDING, &ec->flags))) { 6638c2ecf20Sopenharmony_ci clear_bit(EC_FLAGS_QUERY_GUARDING, &ec->flags); 6648c2ecf20Sopenharmony_ci acpi_ec_complete_query(ec); 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci if (!t) 6688c2ecf20Sopenharmony_ci goto err; 6698c2ecf20Sopenharmony_ci if (t->flags & ACPI_EC_COMMAND_POLL) { 6708c2ecf20Sopenharmony_ci if (t->wlen > t->wi) { 6718c2ecf20Sopenharmony_ci if ((status & ACPI_EC_FLAG_IBF) == 0) 6728c2ecf20Sopenharmony_ci acpi_ec_write_data(ec, t->wdata[t->wi++]); 6738c2ecf20Sopenharmony_ci else 6748c2ecf20Sopenharmony_ci goto err; 6758c2ecf20Sopenharmony_ci } else if (t->rlen > t->ri) { 6768c2ecf20Sopenharmony_ci if ((status & ACPI_EC_FLAG_OBF) == 1) { 6778c2ecf20Sopenharmony_ci t->rdata[t->ri++] = acpi_ec_read_data(ec); 6788c2ecf20Sopenharmony_ci if (t->rlen == t->ri) { 6798c2ecf20Sopenharmony_ci ec_transaction_transition(ec, ACPI_EC_COMMAND_COMPLETE); 6808c2ecf20Sopenharmony_ci if (t->command == ACPI_EC_COMMAND_QUERY) 6818c2ecf20Sopenharmony_ci ec_dbg_evt("Command(%s) completed by hardware", 6828c2ecf20Sopenharmony_ci acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY)); 6838c2ecf20Sopenharmony_ci wakeup = true; 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci } else 6868c2ecf20Sopenharmony_ci goto err; 6878c2ecf20Sopenharmony_ci } else if (t->wlen == t->wi && 6888c2ecf20Sopenharmony_ci (status & ACPI_EC_FLAG_IBF) == 0) { 6898c2ecf20Sopenharmony_ci ec_transaction_transition(ec, ACPI_EC_COMMAND_COMPLETE); 6908c2ecf20Sopenharmony_ci wakeup = true; 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci goto out; 6938c2ecf20Sopenharmony_ci } else if (!(status & ACPI_EC_FLAG_IBF)) { 6948c2ecf20Sopenharmony_ci acpi_ec_write_cmd(ec, t->command); 6958c2ecf20Sopenharmony_ci ec_transaction_transition(ec, ACPI_EC_COMMAND_POLL); 6968c2ecf20Sopenharmony_ci goto out; 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_cierr: 6998c2ecf20Sopenharmony_ci /* 7008c2ecf20Sopenharmony_ci * If SCI bit is set, then don't think it's a false IRQ 7018c2ecf20Sopenharmony_ci * otherwise will take a not handled IRQ as a false one. 7028c2ecf20Sopenharmony_ci */ 7038c2ecf20Sopenharmony_ci if (!(status & ACPI_EC_FLAG_SCI)) { 7048c2ecf20Sopenharmony_ci if (in_interrupt() && t) { 7058c2ecf20Sopenharmony_ci if (t->irq_count < ec_storm_threshold) 7068c2ecf20Sopenharmony_ci ++t->irq_count; 7078c2ecf20Sopenharmony_ci /* Allow triggering on 0 threshold */ 7088c2ecf20Sopenharmony_ci if (t->irq_count == ec_storm_threshold) 7098c2ecf20Sopenharmony_ci acpi_ec_mask_events(ec); 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ciout: 7138c2ecf20Sopenharmony_ci if (status & ACPI_EC_FLAG_SCI) 7148c2ecf20Sopenharmony_ci acpi_ec_submit_query(ec); 7158c2ecf20Sopenharmony_ci if (wakeup && in_interrupt()) 7168c2ecf20Sopenharmony_ci wake_up(&ec->wait); 7178c2ecf20Sopenharmony_ci} 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_cistatic void start_transaction(struct acpi_ec *ec) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci ec->curr->irq_count = ec->curr->wi = ec->curr->ri = 0; 7228c2ecf20Sopenharmony_ci ec->curr->flags = 0; 7238c2ecf20Sopenharmony_ci} 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_cistatic int ec_guard(struct acpi_ec *ec) 7268c2ecf20Sopenharmony_ci{ 7278c2ecf20Sopenharmony_ci unsigned long guard = usecs_to_jiffies(ec->polling_guard); 7288c2ecf20Sopenharmony_ci unsigned long timeout = ec->timestamp + guard; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci /* Ensure guarding period before polling EC status */ 7318c2ecf20Sopenharmony_ci do { 7328c2ecf20Sopenharmony_ci if (ec->busy_polling) { 7338c2ecf20Sopenharmony_ci /* Perform busy polling */ 7348c2ecf20Sopenharmony_ci if (ec_transaction_completed(ec)) 7358c2ecf20Sopenharmony_ci return 0; 7368c2ecf20Sopenharmony_ci udelay(jiffies_to_usecs(guard)); 7378c2ecf20Sopenharmony_ci } else { 7388c2ecf20Sopenharmony_ci /* 7398c2ecf20Sopenharmony_ci * Perform wait polling 7408c2ecf20Sopenharmony_ci * 1. Wait the transaction to be completed by the 7418c2ecf20Sopenharmony_ci * GPE handler after the transaction enters 7428c2ecf20Sopenharmony_ci * ACPI_EC_COMMAND_POLL state. 7438c2ecf20Sopenharmony_ci * 2. A special guarding logic is also required 7448c2ecf20Sopenharmony_ci * for event clearing mode "event" before the 7458c2ecf20Sopenharmony_ci * transaction enters ACPI_EC_COMMAND_POLL 7468c2ecf20Sopenharmony_ci * state. 7478c2ecf20Sopenharmony_ci */ 7488c2ecf20Sopenharmony_ci if (!ec_transaction_polled(ec) && 7498c2ecf20Sopenharmony_ci !acpi_ec_guard_event(ec)) 7508c2ecf20Sopenharmony_ci break; 7518c2ecf20Sopenharmony_ci if (wait_event_timeout(ec->wait, 7528c2ecf20Sopenharmony_ci ec_transaction_completed(ec), 7538c2ecf20Sopenharmony_ci guard)) 7548c2ecf20Sopenharmony_ci return 0; 7558c2ecf20Sopenharmony_ci } 7568c2ecf20Sopenharmony_ci } while (time_before(jiffies, timeout)); 7578c2ecf20Sopenharmony_ci return -ETIME; 7588c2ecf20Sopenharmony_ci} 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_cistatic int ec_poll(struct acpi_ec *ec) 7618c2ecf20Sopenharmony_ci{ 7628c2ecf20Sopenharmony_ci unsigned long flags; 7638c2ecf20Sopenharmony_ci int repeat = 5; /* number of command restarts */ 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci while (repeat--) { 7668c2ecf20Sopenharmony_ci unsigned long delay = jiffies + 7678c2ecf20Sopenharmony_ci msecs_to_jiffies(ec_delay); 7688c2ecf20Sopenharmony_ci do { 7698c2ecf20Sopenharmony_ci if (!ec_guard(ec)) 7708c2ecf20Sopenharmony_ci return 0; 7718c2ecf20Sopenharmony_ci spin_lock_irqsave(&ec->lock, flags); 7728c2ecf20Sopenharmony_ci advance_transaction(ec); 7738c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ec->lock, flags); 7748c2ecf20Sopenharmony_ci } while (time_before(jiffies, delay)); 7758c2ecf20Sopenharmony_ci pr_debug("controller reset, restart transaction\n"); 7768c2ecf20Sopenharmony_ci spin_lock_irqsave(&ec->lock, flags); 7778c2ecf20Sopenharmony_ci start_transaction(ec); 7788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ec->lock, flags); 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci return -ETIME; 7818c2ecf20Sopenharmony_ci} 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_cistatic int acpi_ec_transaction_unlocked(struct acpi_ec *ec, 7848c2ecf20Sopenharmony_ci struct transaction *t) 7858c2ecf20Sopenharmony_ci{ 7868c2ecf20Sopenharmony_ci unsigned long tmp; 7878c2ecf20Sopenharmony_ci int ret = 0; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci /* start transaction */ 7908c2ecf20Sopenharmony_ci spin_lock_irqsave(&ec->lock, tmp); 7918c2ecf20Sopenharmony_ci /* Enable GPE for command processing (IBF=0/OBF=1) */ 7928c2ecf20Sopenharmony_ci if (!acpi_ec_submit_flushable_request(ec)) { 7938c2ecf20Sopenharmony_ci ret = -EINVAL; 7948c2ecf20Sopenharmony_ci goto unlock; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci ec_dbg_ref(ec, "Increase command"); 7978c2ecf20Sopenharmony_ci /* following two actions should be kept atomic */ 7988c2ecf20Sopenharmony_ci ec->curr = t; 7998c2ecf20Sopenharmony_ci ec_dbg_req("Command(%s) started", acpi_ec_cmd_string(t->command)); 8008c2ecf20Sopenharmony_ci start_transaction(ec); 8018c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ec->lock, tmp); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci ret = ec_poll(ec); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci spin_lock_irqsave(&ec->lock, tmp); 8068c2ecf20Sopenharmony_ci if (t->irq_count == ec_storm_threshold) 8078c2ecf20Sopenharmony_ci acpi_ec_unmask_events(ec); 8088c2ecf20Sopenharmony_ci ec_dbg_req("Command(%s) stopped", acpi_ec_cmd_string(t->command)); 8098c2ecf20Sopenharmony_ci ec->curr = NULL; 8108c2ecf20Sopenharmony_ci /* Disable GPE for command processing (IBF=0/OBF=1) */ 8118c2ecf20Sopenharmony_ci acpi_ec_complete_request(ec); 8128c2ecf20Sopenharmony_ci ec_dbg_ref(ec, "Decrease command"); 8138c2ecf20Sopenharmony_ciunlock: 8148c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ec->lock, tmp); 8158c2ecf20Sopenharmony_ci return ret; 8168c2ecf20Sopenharmony_ci} 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_cistatic int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) 8198c2ecf20Sopenharmony_ci{ 8208c2ecf20Sopenharmony_ci int status; 8218c2ecf20Sopenharmony_ci u32 glk; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci if (!ec || (!t) || (t->wlen && !t->wdata) || (t->rlen && !t->rdata)) 8248c2ecf20Sopenharmony_ci return -EINVAL; 8258c2ecf20Sopenharmony_ci if (t->rdata) 8268c2ecf20Sopenharmony_ci memset(t->rdata, 0, t->rlen); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci mutex_lock(&ec->mutex); 8298c2ecf20Sopenharmony_ci if (ec->global_lock) { 8308c2ecf20Sopenharmony_ci status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); 8318c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 8328c2ecf20Sopenharmony_ci status = -ENODEV; 8338c2ecf20Sopenharmony_ci goto unlock; 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci status = acpi_ec_transaction_unlocked(ec, t); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci if (ec->global_lock) 8408c2ecf20Sopenharmony_ci acpi_release_global_lock(glk); 8418c2ecf20Sopenharmony_ciunlock: 8428c2ecf20Sopenharmony_ci mutex_unlock(&ec->mutex); 8438c2ecf20Sopenharmony_ci return status; 8448c2ecf20Sopenharmony_ci} 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_cistatic int acpi_ec_burst_enable(struct acpi_ec *ec) 8478c2ecf20Sopenharmony_ci{ 8488c2ecf20Sopenharmony_ci u8 d; 8498c2ecf20Sopenharmony_ci struct transaction t = {.command = ACPI_EC_BURST_ENABLE, 8508c2ecf20Sopenharmony_ci .wdata = NULL, .rdata = &d, 8518c2ecf20Sopenharmony_ci .wlen = 0, .rlen = 1}; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci return acpi_ec_transaction(ec, &t); 8548c2ecf20Sopenharmony_ci} 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_cistatic int acpi_ec_burst_disable(struct acpi_ec *ec) 8578c2ecf20Sopenharmony_ci{ 8588c2ecf20Sopenharmony_ci struct transaction t = {.command = ACPI_EC_BURST_DISABLE, 8598c2ecf20Sopenharmony_ci .wdata = NULL, .rdata = NULL, 8608c2ecf20Sopenharmony_ci .wlen = 0, .rlen = 0}; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci return (acpi_ec_read_status(ec) & ACPI_EC_FLAG_BURST) ? 8638c2ecf20Sopenharmony_ci acpi_ec_transaction(ec, &t) : 0; 8648c2ecf20Sopenharmony_ci} 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_cistatic int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 *data) 8678c2ecf20Sopenharmony_ci{ 8688c2ecf20Sopenharmony_ci int result; 8698c2ecf20Sopenharmony_ci u8 d; 8708c2ecf20Sopenharmony_ci struct transaction t = {.command = ACPI_EC_COMMAND_READ, 8718c2ecf20Sopenharmony_ci .wdata = &address, .rdata = &d, 8728c2ecf20Sopenharmony_ci .wlen = 1, .rlen = 1}; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci result = acpi_ec_transaction(ec, &t); 8758c2ecf20Sopenharmony_ci *data = d; 8768c2ecf20Sopenharmony_ci return result; 8778c2ecf20Sopenharmony_ci} 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_cistatic int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data) 8808c2ecf20Sopenharmony_ci{ 8818c2ecf20Sopenharmony_ci u8 wdata[2] = { address, data }; 8828c2ecf20Sopenharmony_ci struct transaction t = {.command = ACPI_EC_COMMAND_WRITE, 8838c2ecf20Sopenharmony_ci .wdata = wdata, .rdata = NULL, 8848c2ecf20Sopenharmony_ci .wlen = 2, .rlen = 0}; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci return acpi_ec_transaction(ec, &t); 8878c2ecf20Sopenharmony_ci} 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ciint ec_read(u8 addr, u8 *val) 8908c2ecf20Sopenharmony_ci{ 8918c2ecf20Sopenharmony_ci int err; 8928c2ecf20Sopenharmony_ci u8 temp_data; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci if (!first_ec) 8958c2ecf20Sopenharmony_ci return -ENODEV; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci err = acpi_ec_read(first_ec, addr, &temp_data); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci if (!err) { 9008c2ecf20Sopenharmony_ci *val = temp_data; 9018c2ecf20Sopenharmony_ci return 0; 9028c2ecf20Sopenharmony_ci } 9038c2ecf20Sopenharmony_ci return err; 9048c2ecf20Sopenharmony_ci} 9058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ec_read); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ciint ec_write(u8 addr, u8 val) 9088c2ecf20Sopenharmony_ci{ 9098c2ecf20Sopenharmony_ci int err; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci if (!first_ec) 9128c2ecf20Sopenharmony_ci return -ENODEV; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci err = acpi_ec_write(first_ec, addr, val); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci return err; 9178c2ecf20Sopenharmony_ci} 9188c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ec_write); 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ciint ec_transaction(u8 command, 9218c2ecf20Sopenharmony_ci const u8 *wdata, unsigned wdata_len, 9228c2ecf20Sopenharmony_ci u8 *rdata, unsigned rdata_len) 9238c2ecf20Sopenharmony_ci{ 9248c2ecf20Sopenharmony_ci struct transaction t = {.command = command, 9258c2ecf20Sopenharmony_ci .wdata = wdata, .rdata = rdata, 9268c2ecf20Sopenharmony_ci .wlen = wdata_len, .rlen = rdata_len}; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci if (!first_ec) 9298c2ecf20Sopenharmony_ci return -ENODEV; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci return acpi_ec_transaction(first_ec, &t); 9328c2ecf20Sopenharmony_ci} 9338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ec_transaction); 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci/* Get the handle to the EC device */ 9368c2ecf20Sopenharmony_ciacpi_handle ec_get_handle(void) 9378c2ecf20Sopenharmony_ci{ 9388c2ecf20Sopenharmony_ci if (!first_ec) 9398c2ecf20Sopenharmony_ci return NULL; 9408c2ecf20Sopenharmony_ci return first_ec->handle; 9418c2ecf20Sopenharmony_ci} 9428c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ec_get_handle); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_cistatic void acpi_ec_start(struct acpi_ec *ec, bool resuming) 9458c2ecf20Sopenharmony_ci{ 9468c2ecf20Sopenharmony_ci unsigned long flags; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci spin_lock_irqsave(&ec->lock, flags); 9498c2ecf20Sopenharmony_ci if (!test_and_set_bit(EC_FLAGS_STARTED, &ec->flags)) { 9508c2ecf20Sopenharmony_ci ec_dbg_drv("Starting EC"); 9518c2ecf20Sopenharmony_ci /* Enable GPE for event processing (SCI_EVT=1) */ 9528c2ecf20Sopenharmony_ci if (!resuming) { 9538c2ecf20Sopenharmony_ci acpi_ec_submit_request(ec); 9548c2ecf20Sopenharmony_ci ec_dbg_ref(ec, "Increase driver"); 9558c2ecf20Sopenharmony_ci } 9568c2ecf20Sopenharmony_ci ec_log_drv("EC started"); 9578c2ecf20Sopenharmony_ci } 9588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ec->lock, flags); 9598c2ecf20Sopenharmony_ci} 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_cistatic bool acpi_ec_stopped(struct acpi_ec *ec) 9628c2ecf20Sopenharmony_ci{ 9638c2ecf20Sopenharmony_ci unsigned long flags; 9648c2ecf20Sopenharmony_ci bool flushed; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci spin_lock_irqsave(&ec->lock, flags); 9678c2ecf20Sopenharmony_ci flushed = acpi_ec_flushed(ec); 9688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ec->lock, flags); 9698c2ecf20Sopenharmony_ci return flushed; 9708c2ecf20Sopenharmony_ci} 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_cistatic void acpi_ec_stop(struct acpi_ec *ec, bool suspending) 9738c2ecf20Sopenharmony_ci{ 9748c2ecf20Sopenharmony_ci unsigned long flags; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci spin_lock_irqsave(&ec->lock, flags); 9778c2ecf20Sopenharmony_ci if (acpi_ec_started(ec)) { 9788c2ecf20Sopenharmony_ci ec_dbg_drv("Stopping EC"); 9798c2ecf20Sopenharmony_ci set_bit(EC_FLAGS_STOPPED, &ec->flags); 9808c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ec->lock, flags); 9818c2ecf20Sopenharmony_ci wait_event(ec->wait, acpi_ec_stopped(ec)); 9828c2ecf20Sopenharmony_ci spin_lock_irqsave(&ec->lock, flags); 9838c2ecf20Sopenharmony_ci /* Disable GPE for event processing (SCI_EVT=1) */ 9848c2ecf20Sopenharmony_ci if (!suspending) { 9858c2ecf20Sopenharmony_ci acpi_ec_complete_request(ec); 9868c2ecf20Sopenharmony_ci ec_dbg_ref(ec, "Decrease driver"); 9878c2ecf20Sopenharmony_ci } else if (!ec_freeze_events) 9888c2ecf20Sopenharmony_ci __acpi_ec_disable_event(ec); 9898c2ecf20Sopenharmony_ci clear_bit(EC_FLAGS_STARTED, &ec->flags); 9908c2ecf20Sopenharmony_ci clear_bit(EC_FLAGS_STOPPED, &ec->flags); 9918c2ecf20Sopenharmony_ci ec_log_drv("EC stopped"); 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ec->lock, flags); 9948c2ecf20Sopenharmony_ci} 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_cistatic void acpi_ec_enter_noirq(struct acpi_ec *ec) 9978c2ecf20Sopenharmony_ci{ 9988c2ecf20Sopenharmony_ci unsigned long flags; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci spin_lock_irqsave(&ec->lock, flags); 10018c2ecf20Sopenharmony_ci ec->busy_polling = true; 10028c2ecf20Sopenharmony_ci ec->polling_guard = 0; 10038c2ecf20Sopenharmony_ci ec_log_drv("interrupt blocked"); 10048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ec->lock, flags); 10058c2ecf20Sopenharmony_ci} 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_cistatic void acpi_ec_leave_noirq(struct acpi_ec *ec) 10088c2ecf20Sopenharmony_ci{ 10098c2ecf20Sopenharmony_ci unsigned long flags; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci spin_lock_irqsave(&ec->lock, flags); 10128c2ecf20Sopenharmony_ci ec->busy_polling = ec_busy_polling; 10138c2ecf20Sopenharmony_ci ec->polling_guard = ec_polling_guard; 10148c2ecf20Sopenharmony_ci ec_log_drv("interrupt unblocked"); 10158c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ec->lock, flags); 10168c2ecf20Sopenharmony_ci} 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_civoid acpi_ec_block_transactions(void) 10198c2ecf20Sopenharmony_ci{ 10208c2ecf20Sopenharmony_ci struct acpi_ec *ec = first_ec; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci if (!ec) 10238c2ecf20Sopenharmony_ci return; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci mutex_lock(&ec->mutex); 10268c2ecf20Sopenharmony_ci /* Prevent transactions from being carried out */ 10278c2ecf20Sopenharmony_ci acpi_ec_stop(ec, true); 10288c2ecf20Sopenharmony_ci mutex_unlock(&ec->mutex); 10298c2ecf20Sopenharmony_ci} 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_civoid acpi_ec_unblock_transactions(void) 10328c2ecf20Sopenharmony_ci{ 10338c2ecf20Sopenharmony_ci /* 10348c2ecf20Sopenharmony_ci * Allow transactions to happen again (this function is called from 10358c2ecf20Sopenharmony_ci * atomic context during wakeup, so we don't need to acquire the mutex). 10368c2ecf20Sopenharmony_ci */ 10378c2ecf20Sopenharmony_ci if (first_ec) 10388c2ecf20Sopenharmony_ci acpi_ec_start(first_ec, true); 10398c2ecf20Sopenharmony_ci} 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- 10428c2ecf20Sopenharmony_ci Event Management 10438c2ecf20Sopenharmony_ci -------------------------------------------------------------------------- */ 10448c2ecf20Sopenharmony_cistatic struct acpi_ec_query_handler * 10458c2ecf20Sopenharmony_ciacpi_ec_get_query_handler_by_value(struct acpi_ec *ec, u8 value) 10468c2ecf20Sopenharmony_ci{ 10478c2ecf20Sopenharmony_ci struct acpi_ec_query_handler *handler; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci mutex_lock(&ec->mutex); 10508c2ecf20Sopenharmony_ci list_for_each_entry(handler, &ec->list, node) { 10518c2ecf20Sopenharmony_ci if (value == handler->query_bit) { 10528c2ecf20Sopenharmony_ci kref_get(&handler->kref); 10538c2ecf20Sopenharmony_ci mutex_unlock(&ec->mutex); 10548c2ecf20Sopenharmony_ci return handler; 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci } 10578c2ecf20Sopenharmony_ci mutex_unlock(&ec->mutex); 10588c2ecf20Sopenharmony_ci return NULL; 10598c2ecf20Sopenharmony_ci} 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_cistatic void acpi_ec_query_handler_release(struct kref *kref) 10628c2ecf20Sopenharmony_ci{ 10638c2ecf20Sopenharmony_ci struct acpi_ec_query_handler *handler = 10648c2ecf20Sopenharmony_ci container_of(kref, struct acpi_ec_query_handler, kref); 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci kfree(handler); 10678c2ecf20Sopenharmony_ci} 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_cistatic void acpi_ec_put_query_handler(struct acpi_ec_query_handler *handler) 10708c2ecf20Sopenharmony_ci{ 10718c2ecf20Sopenharmony_ci kref_put(&handler->kref, acpi_ec_query_handler_release); 10728c2ecf20Sopenharmony_ci} 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ciint acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, 10758c2ecf20Sopenharmony_ci acpi_handle handle, acpi_ec_query_func func, 10768c2ecf20Sopenharmony_ci void *data) 10778c2ecf20Sopenharmony_ci{ 10788c2ecf20Sopenharmony_ci struct acpi_ec_query_handler *handler = 10798c2ecf20Sopenharmony_ci kzalloc(sizeof(struct acpi_ec_query_handler), GFP_KERNEL); 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci if (!handler) 10828c2ecf20Sopenharmony_ci return -ENOMEM; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci handler->query_bit = query_bit; 10858c2ecf20Sopenharmony_ci handler->handle = handle; 10868c2ecf20Sopenharmony_ci handler->func = func; 10878c2ecf20Sopenharmony_ci handler->data = data; 10888c2ecf20Sopenharmony_ci mutex_lock(&ec->mutex); 10898c2ecf20Sopenharmony_ci kref_init(&handler->kref); 10908c2ecf20Sopenharmony_ci list_add(&handler->node, &ec->list); 10918c2ecf20Sopenharmony_ci mutex_unlock(&ec->mutex); 10928c2ecf20Sopenharmony_ci return 0; 10938c2ecf20Sopenharmony_ci} 10948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_ec_add_query_handler); 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_cistatic void acpi_ec_remove_query_handlers(struct acpi_ec *ec, 10978c2ecf20Sopenharmony_ci bool remove_all, u8 query_bit) 10988c2ecf20Sopenharmony_ci{ 10998c2ecf20Sopenharmony_ci struct acpi_ec_query_handler *handler, *tmp; 11008c2ecf20Sopenharmony_ci LIST_HEAD(free_list); 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci mutex_lock(&ec->mutex); 11038c2ecf20Sopenharmony_ci list_for_each_entry_safe(handler, tmp, &ec->list, node) { 11048c2ecf20Sopenharmony_ci if (remove_all || query_bit == handler->query_bit) { 11058c2ecf20Sopenharmony_ci list_del_init(&handler->node); 11068c2ecf20Sopenharmony_ci list_add(&handler->node, &free_list); 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci } 11098c2ecf20Sopenharmony_ci mutex_unlock(&ec->mutex); 11108c2ecf20Sopenharmony_ci list_for_each_entry_safe(handler, tmp, &free_list, node) 11118c2ecf20Sopenharmony_ci acpi_ec_put_query_handler(handler); 11128c2ecf20Sopenharmony_ci} 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_civoid acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit) 11158c2ecf20Sopenharmony_ci{ 11168c2ecf20Sopenharmony_ci acpi_ec_remove_query_handlers(ec, false, query_bit); 11178c2ecf20Sopenharmony_ci flush_workqueue(ec_query_wq); 11188c2ecf20Sopenharmony_ci} 11198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler); 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_cistatic struct acpi_ec_query *acpi_ec_create_query(struct acpi_ec *ec, u8 *pval) 11228c2ecf20Sopenharmony_ci{ 11238c2ecf20Sopenharmony_ci struct acpi_ec_query *q; 11248c2ecf20Sopenharmony_ci struct transaction *t; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci q = kzalloc(sizeof (struct acpi_ec_query), GFP_KERNEL); 11278c2ecf20Sopenharmony_ci if (!q) 11288c2ecf20Sopenharmony_ci return NULL; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci INIT_WORK(&q->work, acpi_ec_event_processor); 11318c2ecf20Sopenharmony_ci t = &q->transaction; 11328c2ecf20Sopenharmony_ci t->command = ACPI_EC_COMMAND_QUERY; 11338c2ecf20Sopenharmony_ci t->rdata = pval; 11348c2ecf20Sopenharmony_ci t->rlen = 1; 11358c2ecf20Sopenharmony_ci q->ec = ec; 11368c2ecf20Sopenharmony_ci return q; 11378c2ecf20Sopenharmony_ci} 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_cistatic void acpi_ec_delete_query(struct acpi_ec_query *q) 11408c2ecf20Sopenharmony_ci{ 11418c2ecf20Sopenharmony_ci if (q) { 11428c2ecf20Sopenharmony_ci if (q->handler) 11438c2ecf20Sopenharmony_ci acpi_ec_put_query_handler(q->handler); 11448c2ecf20Sopenharmony_ci kfree(q); 11458c2ecf20Sopenharmony_ci } 11468c2ecf20Sopenharmony_ci} 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_cistatic void acpi_ec_event_processor(struct work_struct *work) 11498c2ecf20Sopenharmony_ci{ 11508c2ecf20Sopenharmony_ci struct acpi_ec_query *q = container_of(work, struct acpi_ec_query, work); 11518c2ecf20Sopenharmony_ci struct acpi_ec_query_handler *handler = q->handler; 11528c2ecf20Sopenharmony_ci struct acpi_ec *ec = q->ec; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci ec_dbg_evt("Query(0x%02x) started", handler->query_bit); 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci if (handler->func) 11578c2ecf20Sopenharmony_ci handler->func(handler->data); 11588c2ecf20Sopenharmony_ci else if (handler->handle) 11598c2ecf20Sopenharmony_ci acpi_evaluate_object(handler->handle, NULL, NULL, NULL); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci ec_dbg_evt("Query(0x%02x) stopped", handler->query_bit); 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci spin_lock_irq(&ec->lock); 11648c2ecf20Sopenharmony_ci ec->queries_in_progress--; 11658c2ecf20Sopenharmony_ci spin_unlock_irq(&ec->lock); 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci acpi_ec_delete_query(q); 11688c2ecf20Sopenharmony_ci} 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_cistatic int acpi_ec_query(struct acpi_ec *ec, u8 *data) 11718c2ecf20Sopenharmony_ci{ 11728c2ecf20Sopenharmony_ci u8 value = 0; 11738c2ecf20Sopenharmony_ci int result; 11748c2ecf20Sopenharmony_ci struct acpi_ec_query *q; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci q = acpi_ec_create_query(ec, &value); 11778c2ecf20Sopenharmony_ci if (!q) 11788c2ecf20Sopenharmony_ci return -ENOMEM; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci /* 11818c2ecf20Sopenharmony_ci * Query the EC to find out which _Qxx method we need to evaluate. 11828c2ecf20Sopenharmony_ci * Note that successful completion of the query causes the ACPI_EC_SCI 11838c2ecf20Sopenharmony_ci * bit to be cleared (and thus clearing the interrupt source). 11848c2ecf20Sopenharmony_ci */ 11858c2ecf20Sopenharmony_ci result = acpi_ec_transaction(ec, &q->transaction); 11868c2ecf20Sopenharmony_ci if (!value) 11878c2ecf20Sopenharmony_ci result = -ENODATA; 11888c2ecf20Sopenharmony_ci if (result) 11898c2ecf20Sopenharmony_ci goto err_exit; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci q->handler = acpi_ec_get_query_handler_by_value(ec, value); 11928c2ecf20Sopenharmony_ci if (!q->handler) { 11938c2ecf20Sopenharmony_ci result = -ENODATA; 11948c2ecf20Sopenharmony_ci goto err_exit; 11958c2ecf20Sopenharmony_ci } 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci /* 11988c2ecf20Sopenharmony_ci * It is reported that _Qxx are evaluated in a parallel way on Windows: 11998c2ecf20Sopenharmony_ci * https://bugzilla.kernel.org/show_bug.cgi?id=94411 12008c2ecf20Sopenharmony_ci * 12018c2ecf20Sopenharmony_ci * Put this log entry before queue_work() to make it appear in the log 12028c2ecf20Sopenharmony_ci * before any other messages emitted during workqueue handling. 12038c2ecf20Sopenharmony_ci */ 12048c2ecf20Sopenharmony_ci ec_dbg_evt("Query(0x%02x) scheduled", value); 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci spin_lock_irq(&ec->lock); 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci ec->queries_in_progress++; 12098c2ecf20Sopenharmony_ci queue_work(ec_query_wq, &q->work); 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci spin_unlock_irq(&ec->lock); 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_cierr_exit: 12148c2ecf20Sopenharmony_ci if (result) 12158c2ecf20Sopenharmony_ci acpi_ec_delete_query(q); 12168c2ecf20Sopenharmony_ci if (data) 12178c2ecf20Sopenharmony_ci *data = value; 12188c2ecf20Sopenharmony_ci return result; 12198c2ecf20Sopenharmony_ci} 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_cistatic void acpi_ec_check_event(struct acpi_ec *ec) 12228c2ecf20Sopenharmony_ci{ 12238c2ecf20Sopenharmony_ci unsigned long flags; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT) { 12268c2ecf20Sopenharmony_ci if (ec_guard(ec)) { 12278c2ecf20Sopenharmony_ci spin_lock_irqsave(&ec->lock, flags); 12288c2ecf20Sopenharmony_ci /* 12298c2ecf20Sopenharmony_ci * Take care of the SCI_EVT unless no one else is 12308c2ecf20Sopenharmony_ci * taking care of it. 12318c2ecf20Sopenharmony_ci */ 12328c2ecf20Sopenharmony_ci if (!ec->curr) 12338c2ecf20Sopenharmony_ci advance_transaction(ec); 12348c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ec->lock, flags); 12358c2ecf20Sopenharmony_ci } 12368c2ecf20Sopenharmony_ci } 12378c2ecf20Sopenharmony_ci} 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_cistatic void acpi_ec_event_handler(struct work_struct *work) 12408c2ecf20Sopenharmony_ci{ 12418c2ecf20Sopenharmony_ci unsigned long flags; 12428c2ecf20Sopenharmony_ci struct acpi_ec *ec = container_of(work, struct acpi_ec, work); 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci ec_dbg_evt("Event started"); 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci spin_lock_irqsave(&ec->lock, flags); 12478c2ecf20Sopenharmony_ci while (ec->nr_pending_queries) { 12488c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ec->lock, flags); 12498c2ecf20Sopenharmony_ci (void)acpi_ec_query(ec, NULL); 12508c2ecf20Sopenharmony_ci spin_lock_irqsave(&ec->lock, flags); 12518c2ecf20Sopenharmony_ci ec->nr_pending_queries--; 12528c2ecf20Sopenharmony_ci /* 12538c2ecf20Sopenharmony_ci * Before exit, make sure that this work item can be 12548c2ecf20Sopenharmony_ci * scheduled again. There might be QR_EC failures, leaving 12558c2ecf20Sopenharmony_ci * EC_FLAGS_QUERY_PENDING uncleared and preventing this work 12568c2ecf20Sopenharmony_ci * item from being scheduled again. 12578c2ecf20Sopenharmony_ci */ 12588c2ecf20Sopenharmony_ci if (!ec->nr_pending_queries) { 12598c2ecf20Sopenharmony_ci if (ec_event_clearing == ACPI_EC_EVT_TIMING_STATUS || 12608c2ecf20Sopenharmony_ci ec_event_clearing == ACPI_EC_EVT_TIMING_QUERY) 12618c2ecf20Sopenharmony_ci acpi_ec_complete_query(ec); 12628c2ecf20Sopenharmony_ci } 12638c2ecf20Sopenharmony_ci } 12648c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ec->lock, flags); 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci ec_dbg_evt("Event stopped"); 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci acpi_ec_check_event(ec); 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci spin_lock_irqsave(&ec->lock, flags); 12718c2ecf20Sopenharmony_ci ec->events_in_progress--; 12728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ec->lock, flags); 12738c2ecf20Sopenharmony_ci} 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_cistatic void acpi_ec_handle_interrupt(struct acpi_ec *ec) 12768c2ecf20Sopenharmony_ci{ 12778c2ecf20Sopenharmony_ci unsigned long flags; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci spin_lock_irqsave(&ec->lock, flags); 12808c2ecf20Sopenharmony_ci advance_transaction(ec); 12818c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ec->lock, flags); 12828c2ecf20Sopenharmony_ci} 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_cistatic u32 acpi_ec_gpe_handler(acpi_handle gpe_device, 12858c2ecf20Sopenharmony_ci u32 gpe_number, void *data) 12868c2ecf20Sopenharmony_ci{ 12878c2ecf20Sopenharmony_ci acpi_ec_handle_interrupt(data); 12888c2ecf20Sopenharmony_ci return ACPI_INTERRUPT_HANDLED; 12898c2ecf20Sopenharmony_ci} 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_cistatic irqreturn_t acpi_ec_irq_handler(int irq, void *data) 12928c2ecf20Sopenharmony_ci{ 12938c2ecf20Sopenharmony_ci acpi_ec_handle_interrupt(data); 12948c2ecf20Sopenharmony_ci return IRQ_HANDLED; 12958c2ecf20Sopenharmony_ci} 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- 12988c2ecf20Sopenharmony_ci * Address Space Management 12998c2ecf20Sopenharmony_ci * -------------------------------------------------------------------------- */ 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_cistatic acpi_status 13028c2ecf20Sopenharmony_ciacpi_ec_space_handler(u32 function, acpi_physical_address address, 13038c2ecf20Sopenharmony_ci u32 bits, u64 *value64, 13048c2ecf20Sopenharmony_ci void *handler_context, void *region_context) 13058c2ecf20Sopenharmony_ci{ 13068c2ecf20Sopenharmony_ci struct acpi_ec *ec = handler_context; 13078c2ecf20Sopenharmony_ci int result = 0, i, bytes = bits / 8; 13088c2ecf20Sopenharmony_ci u8 *value = (u8 *)value64; 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci if ((address > 0xFF) || !value || !handler_context) 13118c2ecf20Sopenharmony_ci return AE_BAD_PARAMETER; 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci if (function != ACPI_READ && function != ACPI_WRITE) 13148c2ecf20Sopenharmony_ci return AE_BAD_PARAMETER; 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci if (ec->busy_polling || bits > 8) 13178c2ecf20Sopenharmony_ci acpi_ec_burst_enable(ec); 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci for (i = 0; i < bytes; ++i, ++address, ++value) 13208c2ecf20Sopenharmony_ci result = (function == ACPI_READ) ? 13218c2ecf20Sopenharmony_ci acpi_ec_read(ec, address, value) : 13228c2ecf20Sopenharmony_ci acpi_ec_write(ec, address, *value); 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci if (ec->busy_polling || bits > 8) 13258c2ecf20Sopenharmony_ci acpi_ec_burst_disable(ec); 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci switch (result) { 13288c2ecf20Sopenharmony_ci case -EINVAL: 13298c2ecf20Sopenharmony_ci return AE_BAD_PARAMETER; 13308c2ecf20Sopenharmony_ci case -ENODEV: 13318c2ecf20Sopenharmony_ci return AE_NOT_FOUND; 13328c2ecf20Sopenharmony_ci case -ETIME: 13338c2ecf20Sopenharmony_ci return AE_TIME; 13348c2ecf20Sopenharmony_ci default: 13358c2ecf20Sopenharmony_ci return AE_OK; 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci} 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- 13408c2ecf20Sopenharmony_ci * Driver Interface 13418c2ecf20Sopenharmony_ci * -------------------------------------------------------------------------- */ 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_cistatic acpi_status 13448c2ecf20Sopenharmony_ciec_parse_io_ports(struct acpi_resource *resource, void *context); 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_cistatic void acpi_ec_free(struct acpi_ec *ec) 13478c2ecf20Sopenharmony_ci{ 13488c2ecf20Sopenharmony_ci if (first_ec == ec) 13498c2ecf20Sopenharmony_ci first_ec = NULL; 13508c2ecf20Sopenharmony_ci if (boot_ec == ec) 13518c2ecf20Sopenharmony_ci boot_ec = NULL; 13528c2ecf20Sopenharmony_ci kfree(ec); 13538c2ecf20Sopenharmony_ci} 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_cistatic struct acpi_ec *acpi_ec_alloc(void) 13568c2ecf20Sopenharmony_ci{ 13578c2ecf20Sopenharmony_ci struct acpi_ec *ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci if (!ec) 13608c2ecf20Sopenharmony_ci return NULL; 13618c2ecf20Sopenharmony_ci mutex_init(&ec->mutex); 13628c2ecf20Sopenharmony_ci init_waitqueue_head(&ec->wait); 13638c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ec->list); 13648c2ecf20Sopenharmony_ci spin_lock_init(&ec->lock); 13658c2ecf20Sopenharmony_ci INIT_WORK(&ec->work, acpi_ec_event_handler); 13668c2ecf20Sopenharmony_ci ec->timestamp = jiffies; 13678c2ecf20Sopenharmony_ci ec->busy_polling = true; 13688c2ecf20Sopenharmony_ci ec->polling_guard = 0; 13698c2ecf20Sopenharmony_ci ec->gpe = -1; 13708c2ecf20Sopenharmony_ci ec->irq = -1; 13718c2ecf20Sopenharmony_ci return ec; 13728c2ecf20Sopenharmony_ci} 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_cistatic acpi_status 13758c2ecf20Sopenharmony_ciacpi_ec_register_query_methods(acpi_handle handle, u32 level, 13768c2ecf20Sopenharmony_ci void *context, void **return_value) 13778c2ecf20Sopenharmony_ci{ 13788c2ecf20Sopenharmony_ci char node_name[5]; 13798c2ecf20Sopenharmony_ci struct acpi_buffer buffer = { sizeof(node_name), node_name }; 13808c2ecf20Sopenharmony_ci struct acpi_ec *ec = context; 13818c2ecf20Sopenharmony_ci int value = 0; 13828c2ecf20Sopenharmony_ci acpi_status status; 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci status = acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer); 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci if (ACPI_SUCCESS(status) && sscanf(node_name, "_Q%x", &value) == 1) 13878c2ecf20Sopenharmony_ci acpi_ec_add_query_handler(ec, value, handle, NULL, NULL); 13888c2ecf20Sopenharmony_ci return AE_OK; 13898c2ecf20Sopenharmony_ci} 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_cistatic acpi_status 13928c2ecf20Sopenharmony_ciec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) 13938c2ecf20Sopenharmony_ci{ 13948c2ecf20Sopenharmony_ci acpi_status status; 13958c2ecf20Sopenharmony_ci unsigned long long tmp = 0; 13968c2ecf20Sopenharmony_ci struct acpi_ec *ec = context; 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci /* clear addr values, ec_parse_io_ports depend on it */ 13998c2ecf20Sopenharmony_ci ec->command_addr = ec->data_addr = 0; 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci status = acpi_walk_resources(handle, METHOD_NAME__CRS, 14028c2ecf20Sopenharmony_ci ec_parse_io_ports, ec); 14038c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 14048c2ecf20Sopenharmony_ci return status; 14058c2ecf20Sopenharmony_ci if (ec->data_addr == 0 || ec->command_addr == 0) 14068c2ecf20Sopenharmony_ci return AE_OK; 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci /* Get GPE bit assignment (EC events). */ 14098c2ecf20Sopenharmony_ci /* TODO: Add support for _GPE returning a package */ 14108c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp); 14118c2ecf20Sopenharmony_ci if (ACPI_SUCCESS(status)) 14128c2ecf20Sopenharmony_ci ec->gpe = tmp; 14138c2ecf20Sopenharmony_ci /* 14148c2ecf20Sopenharmony_ci * Errors are non-fatal, allowing for ACPI Reduced Hardware 14158c2ecf20Sopenharmony_ci * platforms which use GpioInt instead of GPE. 14168c2ecf20Sopenharmony_ci */ 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci /* Use the global lock for all EC transactions? */ 14198c2ecf20Sopenharmony_ci tmp = 0; 14208c2ecf20Sopenharmony_ci acpi_evaluate_integer(handle, "_GLK", NULL, &tmp); 14218c2ecf20Sopenharmony_ci ec->global_lock = tmp; 14228c2ecf20Sopenharmony_ci ec->handle = handle; 14238c2ecf20Sopenharmony_ci return AE_CTRL_TERMINATE; 14248c2ecf20Sopenharmony_ci} 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_cistatic bool install_gpe_event_handler(struct acpi_ec *ec) 14278c2ecf20Sopenharmony_ci{ 14288c2ecf20Sopenharmony_ci acpi_status status; 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci status = acpi_install_gpe_raw_handler(NULL, ec->gpe, 14318c2ecf20Sopenharmony_ci ACPI_GPE_EDGE_TRIGGERED, 14328c2ecf20Sopenharmony_ci &acpi_ec_gpe_handler, ec); 14338c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 14348c2ecf20Sopenharmony_ci return false; 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci if (test_bit(EC_FLAGS_STARTED, &ec->flags) && ec->reference_count >= 1) 14378c2ecf20Sopenharmony_ci acpi_ec_enable_gpe(ec, true); 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci return true; 14408c2ecf20Sopenharmony_ci} 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_cistatic bool install_gpio_irq_event_handler(struct acpi_ec *ec) 14438c2ecf20Sopenharmony_ci{ 14448c2ecf20Sopenharmony_ci return request_irq(ec->irq, acpi_ec_irq_handler, IRQF_SHARED, 14458c2ecf20Sopenharmony_ci "ACPI EC", ec) >= 0; 14468c2ecf20Sopenharmony_ci} 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci/** 14498c2ecf20Sopenharmony_ci * ec_install_handlers - Install service callbacks and register query methods. 14508c2ecf20Sopenharmony_ci * @ec: Target EC. 14518c2ecf20Sopenharmony_ci * @device: ACPI device object corresponding to @ec. 14528c2ecf20Sopenharmony_ci * 14538c2ecf20Sopenharmony_ci * Install a handler for the EC address space type unless it has been installed 14548c2ecf20Sopenharmony_ci * already. If @device is not NULL, also look for EC query methods in the 14558c2ecf20Sopenharmony_ci * namespace and register them, and install an event (either GPE or GPIO IRQ) 14568c2ecf20Sopenharmony_ci * handler for the EC, if possible. 14578c2ecf20Sopenharmony_ci * 14588c2ecf20Sopenharmony_ci * Return: 14598c2ecf20Sopenharmony_ci * -ENODEV if the address space handler cannot be installed, which means 14608c2ecf20Sopenharmony_ci * "unable to handle transactions", 14618c2ecf20Sopenharmony_ci * -EPROBE_DEFER if GPIO IRQ acquisition needs to be deferred, 14628c2ecf20Sopenharmony_ci * or 0 (success) otherwise. 14638c2ecf20Sopenharmony_ci */ 14648c2ecf20Sopenharmony_cistatic int ec_install_handlers(struct acpi_ec *ec, struct acpi_device *device) 14658c2ecf20Sopenharmony_ci{ 14668c2ecf20Sopenharmony_ci acpi_status status; 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci acpi_ec_start(ec, false); 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci if (!test_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags)) { 14718c2ecf20Sopenharmony_ci acpi_ec_enter_noirq(ec); 14728c2ecf20Sopenharmony_ci status = acpi_install_address_space_handler(ec->handle, 14738c2ecf20Sopenharmony_ci ACPI_ADR_SPACE_EC, 14748c2ecf20Sopenharmony_ci &acpi_ec_space_handler, 14758c2ecf20Sopenharmony_ci NULL, ec); 14768c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 14778c2ecf20Sopenharmony_ci acpi_ec_stop(ec, false); 14788c2ecf20Sopenharmony_ci return -ENODEV; 14798c2ecf20Sopenharmony_ci } 14808c2ecf20Sopenharmony_ci set_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags); 14818c2ecf20Sopenharmony_ci } 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci if (!device) 14848c2ecf20Sopenharmony_ci return 0; 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci if (ec->gpe < 0) { 14878c2ecf20Sopenharmony_ci /* ACPI reduced hardware platforms use a GpioInt from _CRS. */ 14888c2ecf20Sopenharmony_ci int irq = acpi_dev_gpio_irq_get(device, 0); 14898c2ecf20Sopenharmony_ci /* 14908c2ecf20Sopenharmony_ci * Bail out right away for deferred probing or complete the 14918c2ecf20Sopenharmony_ci * initialization regardless of any other errors. 14928c2ecf20Sopenharmony_ci */ 14938c2ecf20Sopenharmony_ci if (irq == -EPROBE_DEFER) 14948c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 14958c2ecf20Sopenharmony_ci else if (irq >= 0) 14968c2ecf20Sopenharmony_ci ec->irq = irq; 14978c2ecf20Sopenharmony_ci } 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci if (!test_bit(EC_FLAGS_QUERY_METHODS_INSTALLED, &ec->flags)) { 15008c2ecf20Sopenharmony_ci /* Find and register all query methods */ 15018c2ecf20Sopenharmony_ci acpi_walk_namespace(ACPI_TYPE_METHOD, ec->handle, 1, 15028c2ecf20Sopenharmony_ci acpi_ec_register_query_methods, 15038c2ecf20Sopenharmony_ci NULL, ec, NULL); 15048c2ecf20Sopenharmony_ci set_bit(EC_FLAGS_QUERY_METHODS_INSTALLED, &ec->flags); 15058c2ecf20Sopenharmony_ci } 15068c2ecf20Sopenharmony_ci if (!test_bit(EC_FLAGS_EVENT_HANDLER_INSTALLED, &ec->flags)) { 15078c2ecf20Sopenharmony_ci bool ready = false; 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci if (ec->gpe >= 0) 15108c2ecf20Sopenharmony_ci ready = install_gpe_event_handler(ec); 15118c2ecf20Sopenharmony_ci else if (ec->irq >= 0) 15128c2ecf20Sopenharmony_ci ready = install_gpio_irq_event_handler(ec); 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci if (ready) { 15158c2ecf20Sopenharmony_ci set_bit(EC_FLAGS_EVENT_HANDLER_INSTALLED, &ec->flags); 15168c2ecf20Sopenharmony_ci acpi_ec_leave_noirq(ec); 15178c2ecf20Sopenharmony_ci } 15188c2ecf20Sopenharmony_ci /* 15198c2ecf20Sopenharmony_ci * Failures to install an event handler are not fatal, because 15208c2ecf20Sopenharmony_ci * the EC can be polled for events. 15218c2ecf20Sopenharmony_ci */ 15228c2ecf20Sopenharmony_ci } 15238c2ecf20Sopenharmony_ci /* EC is fully operational, allow queries */ 15248c2ecf20Sopenharmony_ci acpi_ec_enable_event(ec); 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci return 0; 15278c2ecf20Sopenharmony_ci} 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_cistatic void ec_remove_handlers(struct acpi_ec *ec) 15308c2ecf20Sopenharmony_ci{ 15318c2ecf20Sopenharmony_ci if (test_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags)) { 15328c2ecf20Sopenharmony_ci if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle, 15338c2ecf20Sopenharmony_ci ACPI_ADR_SPACE_EC, &acpi_ec_space_handler))) 15348c2ecf20Sopenharmony_ci pr_err("failed to remove space handler\n"); 15358c2ecf20Sopenharmony_ci clear_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags); 15368c2ecf20Sopenharmony_ci } 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci /* 15398c2ecf20Sopenharmony_ci * Stops handling the EC transactions after removing the operation 15408c2ecf20Sopenharmony_ci * region handler. This is required because _REG(DISCONNECT) 15418c2ecf20Sopenharmony_ci * invoked during the removal can result in new EC transactions. 15428c2ecf20Sopenharmony_ci * 15438c2ecf20Sopenharmony_ci * Flushes the EC requests and thus disables the GPE before 15448c2ecf20Sopenharmony_ci * removing the GPE handler. This is required by the current ACPICA 15458c2ecf20Sopenharmony_ci * GPE core. ACPICA GPE core will automatically disable a GPE when 15468c2ecf20Sopenharmony_ci * it is indicated but there is no way to handle it. So the drivers 15478c2ecf20Sopenharmony_ci * must disable the GPEs prior to removing the GPE handlers. 15488c2ecf20Sopenharmony_ci */ 15498c2ecf20Sopenharmony_ci acpi_ec_stop(ec, false); 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci if (test_bit(EC_FLAGS_EVENT_HANDLER_INSTALLED, &ec->flags)) { 15528c2ecf20Sopenharmony_ci if (ec->gpe >= 0 && 15538c2ecf20Sopenharmony_ci ACPI_FAILURE(acpi_remove_gpe_handler(NULL, ec->gpe, 15548c2ecf20Sopenharmony_ci &acpi_ec_gpe_handler))) 15558c2ecf20Sopenharmony_ci pr_err("failed to remove gpe handler\n"); 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci if (ec->irq >= 0) 15588c2ecf20Sopenharmony_ci free_irq(ec->irq, ec); 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci clear_bit(EC_FLAGS_EVENT_HANDLER_INSTALLED, &ec->flags); 15618c2ecf20Sopenharmony_ci } 15628c2ecf20Sopenharmony_ci if (test_bit(EC_FLAGS_QUERY_METHODS_INSTALLED, &ec->flags)) { 15638c2ecf20Sopenharmony_ci acpi_ec_remove_query_handlers(ec, true, 0); 15648c2ecf20Sopenharmony_ci clear_bit(EC_FLAGS_QUERY_METHODS_INSTALLED, &ec->flags); 15658c2ecf20Sopenharmony_ci } 15668c2ecf20Sopenharmony_ci} 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_cistatic int acpi_ec_setup(struct acpi_ec *ec, struct acpi_device *device) 15698c2ecf20Sopenharmony_ci{ 15708c2ecf20Sopenharmony_ci int ret; 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci ret = ec_install_handlers(ec, device); 15738c2ecf20Sopenharmony_ci if (ret) 15748c2ecf20Sopenharmony_ci return ret; 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci /* First EC capable of handling transactions */ 15778c2ecf20Sopenharmony_ci if (!first_ec) 15788c2ecf20Sopenharmony_ci first_ec = ec; 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci pr_info("EC_CMD/EC_SC=0x%lx, EC_DATA=0x%lx\n", ec->command_addr, 15818c2ecf20Sopenharmony_ci ec->data_addr); 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci if (test_bit(EC_FLAGS_EVENT_HANDLER_INSTALLED, &ec->flags)) { 15848c2ecf20Sopenharmony_ci if (ec->gpe >= 0) 15858c2ecf20Sopenharmony_ci pr_info("GPE=0x%x\n", ec->gpe); 15868c2ecf20Sopenharmony_ci else 15878c2ecf20Sopenharmony_ci pr_info("IRQ=%d\n", ec->irq); 15888c2ecf20Sopenharmony_ci } 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci return ret; 15918c2ecf20Sopenharmony_ci} 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_cistatic int acpi_ec_add(struct acpi_device *device) 15948c2ecf20Sopenharmony_ci{ 15958c2ecf20Sopenharmony_ci struct acpi_ec *ec; 15968c2ecf20Sopenharmony_ci int ret; 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); 15998c2ecf20Sopenharmony_ci strcpy(acpi_device_class(device), ACPI_EC_CLASS); 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci if (boot_ec && (boot_ec->handle == device->handle || 16028c2ecf20Sopenharmony_ci !strcmp(acpi_device_hid(device), ACPI_ECDT_HID))) { 16038c2ecf20Sopenharmony_ci /* Fast path: this device corresponds to the boot EC. */ 16048c2ecf20Sopenharmony_ci ec = boot_ec; 16058c2ecf20Sopenharmony_ci } else { 16068c2ecf20Sopenharmony_ci acpi_status status; 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci ec = acpi_ec_alloc(); 16098c2ecf20Sopenharmony_ci if (!ec) 16108c2ecf20Sopenharmony_ci return -ENOMEM; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci status = ec_parse_device(device->handle, 0, ec, NULL); 16138c2ecf20Sopenharmony_ci if (status != AE_CTRL_TERMINATE) { 16148c2ecf20Sopenharmony_ci ret = -EINVAL; 16158c2ecf20Sopenharmony_ci goto err; 16168c2ecf20Sopenharmony_ci } 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci if (boot_ec && ec->command_addr == boot_ec->command_addr && 16198c2ecf20Sopenharmony_ci ec->data_addr == boot_ec->data_addr && 16208c2ecf20Sopenharmony_ci !EC_FLAGS_TRUST_DSDT_GPE) { 16218c2ecf20Sopenharmony_ci /* 16228c2ecf20Sopenharmony_ci * Trust PNP0C09 namespace location rather than 16238c2ecf20Sopenharmony_ci * ECDT ID. But trust ECDT GPE rather than _GPE 16248c2ecf20Sopenharmony_ci * because of ASUS quirks, so do not change 16258c2ecf20Sopenharmony_ci * boot_ec->gpe to ec->gpe. 16268c2ecf20Sopenharmony_ci */ 16278c2ecf20Sopenharmony_ci boot_ec->handle = ec->handle; 16288c2ecf20Sopenharmony_ci acpi_handle_debug(ec->handle, "duplicated.\n"); 16298c2ecf20Sopenharmony_ci acpi_ec_free(ec); 16308c2ecf20Sopenharmony_ci ec = boot_ec; 16318c2ecf20Sopenharmony_ci } 16328c2ecf20Sopenharmony_ci } 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci ret = acpi_ec_setup(ec, device); 16358c2ecf20Sopenharmony_ci if (ret) 16368c2ecf20Sopenharmony_ci goto err; 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci if (ec == boot_ec) 16398c2ecf20Sopenharmony_ci acpi_handle_info(boot_ec->handle, 16408c2ecf20Sopenharmony_ci "Boot %s EC initialization complete\n", 16418c2ecf20Sopenharmony_ci boot_ec_is_ecdt ? "ECDT" : "DSDT"); 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci acpi_handle_info(ec->handle, 16448c2ecf20Sopenharmony_ci "EC: Used to handle transactions and events\n"); 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci device->driver_data = ec; 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci ret = !!request_region(ec->data_addr, 1, "EC data"); 16498c2ecf20Sopenharmony_ci WARN(!ret, "Could not request EC data io port 0x%lx", ec->data_addr); 16508c2ecf20Sopenharmony_ci ret = !!request_region(ec->command_addr, 1, "EC cmd"); 16518c2ecf20Sopenharmony_ci WARN(!ret, "Could not request EC cmd io port 0x%lx", ec->command_addr); 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci /* Reprobe devices depending on the EC */ 16548c2ecf20Sopenharmony_ci acpi_walk_dep_device_list(ec->handle); 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci acpi_handle_debug(ec->handle, "enumerated.\n"); 16578c2ecf20Sopenharmony_ci return 0; 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_cierr: 16608c2ecf20Sopenharmony_ci if (ec != boot_ec) 16618c2ecf20Sopenharmony_ci acpi_ec_free(ec); 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci return ret; 16648c2ecf20Sopenharmony_ci} 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_cistatic int acpi_ec_remove(struct acpi_device *device) 16678c2ecf20Sopenharmony_ci{ 16688c2ecf20Sopenharmony_ci struct acpi_ec *ec; 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci if (!device) 16718c2ecf20Sopenharmony_ci return -EINVAL; 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci ec = acpi_driver_data(device); 16748c2ecf20Sopenharmony_ci release_region(ec->data_addr, 1); 16758c2ecf20Sopenharmony_ci release_region(ec->command_addr, 1); 16768c2ecf20Sopenharmony_ci device->driver_data = NULL; 16778c2ecf20Sopenharmony_ci if (ec != boot_ec) { 16788c2ecf20Sopenharmony_ci ec_remove_handlers(ec); 16798c2ecf20Sopenharmony_ci acpi_ec_free(ec); 16808c2ecf20Sopenharmony_ci } 16818c2ecf20Sopenharmony_ci return 0; 16828c2ecf20Sopenharmony_ci} 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_cistatic acpi_status 16858c2ecf20Sopenharmony_ciec_parse_io_ports(struct acpi_resource *resource, void *context) 16868c2ecf20Sopenharmony_ci{ 16878c2ecf20Sopenharmony_ci struct acpi_ec *ec = context; 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci if (resource->type != ACPI_RESOURCE_TYPE_IO) 16908c2ecf20Sopenharmony_ci return AE_OK; 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_ci /* 16938c2ecf20Sopenharmony_ci * The first address region returned is the data port, and 16948c2ecf20Sopenharmony_ci * the second address region returned is the status/command 16958c2ecf20Sopenharmony_ci * port. 16968c2ecf20Sopenharmony_ci */ 16978c2ecf20Sopenharmony_ci if (ec->data_addr == 0) 16988c2ecf20Sopenharmony_ci ec->data_addr = resource->data.io.minimum; 16998c2ecf20Sopenharmony_ci else if (ec->command_addr == 0) 17008c2ecf20Sopenharmony_ci ec->command_addr = resource->data.io.minimum; 17018c2ecf20Sopenharmony_ci else 17028c2ecf20Sopenharmony_ci return AE_CTRL_TERMINATE; 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci return AE_OK; 17058c2ecf20Sopenharmony_ci} 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_cistatic const struct acpi_device_id ec_device_ids[] = { 17088c2ecf20Sopenharmony_ci {"PNP0C09", 0}, 17098c2ecf20Sopenharmony_ci {ACPI_ECDT_HID, 0}, 17108c2ecf20Sopenharmony_ci {"", 0}, 17118c2ecf20Sopenharmony_ci}; 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci/* 17148c2ecf20Sopenharmony_ci * This function is not Windows-compatible as Windows never enumerates the 17158c2ecf20Sopenharmony_ci * namespace EC before the main ACPI device enumeration process. It is 17168c2ecf20Sopenharmony_ci * retained for historical reason and will be deprecated in the future. 17178c2ecf20Sopenharmony_ci */ 17188c2ecf20Sopenharmony_civoid __init acpi_ec_dsdt_probe(void) 17198c2ecf20Sopenharmony_ci{ 17208c2ecf20Sopenharmony_ci struct acpi_ec *ec; 17218c2ecf20Sopenharmony_ci acpi_status status; 17228c2ecf20Sopenharmony_ci int ret; 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ci /* 17258c2ecf20Sopenharmony_ci * If a platform has ECDT, there is no need to proceed as the 17268c2ecf20Sopenharmony_ci * following probe is not a part of the ACPI device enumeration, 17278c2ecf20Sopenharmony_ci * executing _STA is not safe, and thus this probe may risk of 17288c2ecf20Sopenharmony_ci * picking up an invalid EC device. 17298c2ecf20Sopenharmony_ci */ 17308c2ecf20Sopenharmony_ci if (boot_ec) 17318c2ecf20Sopenharmony_ci return; 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci ec = acpi_ec_alloc(); 17348c2ecf20Sopenharmony_ci if (!ec) 17358c2ecf20Sopenharmony_ci return; 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci /* 17388c2ecf20Sopenharmony_ci * At this point, the namespace is initialized, so start to find 17398c2ecf20Sopenharmony_ci * the namespace objects. 17408c2ecf20Sopenharmony_ci */ 17418c2ecf20Sopenharmony_ci status = acpi_get_devices(ec_device_ids[0].id, ec_parse_device, ec, NULL); 17428c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status) || !ec->handle) { 17438c2ecf20Sopenharmony_ci acpi_ec_free(ec); 17448c2ecf20Sopenharmony_ci return; 17458c2ecf20Sopenharmony_ci } 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci /* 17488c2ecf20Sopenharmony_ci * When the DSDT EC is available, always re-configure boot EC to 17498c2ecf20Sopenharmony_ci * have _REG evaluated. _REG can only be evaluated after the 17508c2ecf20Sopenharmony_ci * namespace initialization. 17518c2ecf20Sopenharmony_ci * At this point, the GPE is not fully initialized, so do not to 17528c2ecf20Sopenharmony_ci * handle the events. 17538c2ecf20Sopenharmony_ci */ 17548c2ecf20Sopenharmony_ci ret = acpi_ec_setup(ec, NULL); 17558c2ecf20Sopenharmony_ci if (ret) { 17568c2ecf20Sopenharmony_ci acpi_ec_free(ec); 17578c2ecf20Sopenharmony_ci return; 17588c2ecf20Sopenharmony_ci } 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci boot_ec = ec; 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci acpi_handle_info(ec->handle, 17638c2ecf20Sopenharmony_ci "Boot DSDT EC used to handle transactions\n"); 17648c2ecf20Sopenharmony_ci} 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_ci/* 17678c2ecf20Sopenharmony_ci * acpi_ec_ecdt_start - Finalize the boot ECDT EC initialization. 17688c2ecf20Sopenharmony_ci * 17698c2ecf20Sopenharmony_ci * First, look for an ACPI handle for the boot ECDT EC if acpi_ec_add() has not 17708c2ecf20Sopenharmony_ci * found a matching object in the namespace. 17718c2ecf20Sopenharmony_ci * 17728c2ecf20Sopenharmony_ci * Next, in case the DSDT EC is not functioning, it is still necessary to 17738c2ecf20Sopenharmony_ci * provide a functional ECDT EC to handle events, so add an extra device object 17748c2ecf20Sopenharmony_ci * to represent it (see https://bugzilla.kernel.org/show_bug.cgi?id=115021). 17758c2ecf20Sopenharmony_ci * 17768c2ecf20Sopenharmony_ci * This is useful on platforms with valid ECDT and invalid DSDT EC settings, 17778c2ecf20Sopenharmony_ci * like ASUS X550ZE (see https://bugzilla.kernel.org/show_bug.cgi?id=196847). 17788c2ecf20Sopenharmony_ci */ 17798c2ecf20Sopenharmony_cistatic void __init acpi_ec_ecdt_start(void) 17808c2ecf20Sopenharmony_ci{ 17818c2ecf20Sopenharmony_ci struct acpi_table_ecdt *ecdt_ptr; 17828c2ecf20Sopenharmony_ci acpi_handle handle; 17838c2ecf20Sopenharmony_ci acpi_status status; 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci /* Bail out if a matching EC has been found in the namespace. */ 17868c2ecf20Sopenharmony_ci if (!boot_ec || boot_ec->handle != ACPI_ROOT_OBJECT) 17878c2ecf20Sopenharmony_ci return; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci /* Look up the object pointed to from the ECDT in the namespace. */ 17908c2ecf20Sopenharmony_ci status = acpi_get_table(ACPI_SIG_ECDT, 1, 17918c2ecf20Sopenharmony_ci (struct acpi_table_header **)&ecdt_ptr); 17928c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 17938c2ecf20Sopenharmony_ci return; 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci status = acpi_get_handle(NULL, ecdt_ptr->id, &handle); 17968c2ecf20Sopenharmony_ci if (ACPI_SUCCESS(status)) { 17978c2ecf20Sopenharmony_ci boot_ec->handle = handle; 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci /* Add a special ACPI device object to represent the boot EC. */ 18008c2ecf20Sopenharmony_ci acpi_bus_register_early_device(ACPI_BUS_TYPE_ECDT_EC); 18018c2ecf20Sopenharmony_ci } 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci acpi_put_table((struct acpi_table_header *)ecdt_ptr); 18048c2ecf20Sopenharmony_ci} 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci/* 18078c2ecf20Sopenharmony_ci * On some hardware it is necessary to clear events accumulated by the EC during 18088c2ecf20Sopenharmony_ci * sleep. These ECs stop reporting GPEs until they are manually polled, if too 18098c2ecf20Sopenharmony_ci * many events are accumulated. (e.g. Samsung Series 5/9 notebooks) 18108c2ecf20Sopenharmony_ci * 18118c2ecf20Sopenharmony_ci * https://bugzilla.kernel.org/show_bug.cgi?id=44161 18128c2ecf20Sopenharmony_ci * 18138c2ecf20Sopenharmony_ci * Ideally, the EC should also be instructed NOT to accumulate events during 18148c2ecf20Sopenharmony_ci * sleep (which Windows seems to do somehow), but the interface to control this 18158c2ecf20Sopenharmony_ci * behaviour is not known at this time. 18168c2ecf20Sopenharmony_ci * 18178c2ecf20Sopenharmony_ci * Models known to be affected are Samsung 530Uxx/535Uxx/540Uxx/550Pxx/900Xxx, 18188c2ecf20Sopenharmony_ci * however it is very likely that other Samsung models are affected. 18198c2ecf20Sopenharmony_ci * 18208c2ecf20Sopenharmony_ci * On systems which don't accumulate _Q events during sleep, this extra check 18218c2ecf20Sopenharmony_ci * should be harmless. 18228c2ecf20Sopenharmony_ci */ 18238c2ecf20Sopenharmony_cistatic int ec_clear_on_resume(const struct dmi_system_id *id) 18248c2ecf20Sopenharmony_ci{ 18258c2ecf20Sopenharmony_ci pr_debug("Detected system needing EC poll on resume.\n"); 18268c2ecf20Sopenharmony_ci EC_FLAGS_CLEAR_ON_RESUME = 1; 18278c2ecf20Sopenharmony_ci ec_event_clearing = ACPI_EC_EVT_TIMING_STATUS; 18288c2ecf20Sopenharmony_ci return 0; 18298c2ecf20Sopenharmony_ci} 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci/* 18328c2ecf20Sopenharmony_ci * Some ECDTs contain wrong register addresses. 18338c2ecf20Sopenharmony_ci * MSI MS-171F 18348c2ecf20Sopenharmony_ci * https://bugzilla.kernel.org/show_bug.cgi?id=12461 18358c2ecf20Sopenharmony_ci */ 18368c2ecf20Sopenharmony_cistatic int ec_correct_ecdt(const struct dmi_system_id *id) 18378c2ecf20Sopenharmony_ci{ 18388c2ecf20Sopenharmony_ci pr_debug("Detected system needing ECDT address correction.\n"); 18398c2ecf20Sopenharmony_ci EC_FLAGS_CORRECT_ECDT = 1; 18408c2ecf20Sopenharmony_ci return 0; 18418c2ecf20Sopenharmony_ci} 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci/* 18448c2ecf20Sopenharmony_ci * Some ECDTs contain wrong GPE setting, but they share the same port addresses 18458c2ecf20Sopenharmony_ci * with DSDT EC, don't duplicate the DSDT EC with ECDT EC in this case. 18468c2ecf20Sopenharmony_ci * https://bugzilla.kernel.org/show_bug.cgi?id=209989 18478c2ecf20Sopenharmony_ci */ 18488c2ecf20Sopenharmony_cistatic int ec_honor_dsdt_gpe(const struct dmi_system_id *id) 18498c2ecf20Sopenharmony_ci{ 18508c2ecf20Sopenharmony_ci pr_debug("Detected system needing DSDT GPE setting.\n"); 18518c2ecf20Sopenharmony_ci EC_FLAGS_TRUST_DSDT_GPE = 1; 18528c2ecf20Sopenharmony_ci return 0; 18538c2ecf20Sopenharmony_ci} 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_cistatic const struct dmi_system_id ec_dmi_table[] __initconst = { 18568c2ecf20Sopenharmony_ci { 18578c2ecf20Sopenharmony_ci ec_correct_ecdt, "MSI MS-171F", { 18588c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star"), 18598c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "MS-171F"),}, NULL}, 18608c2ecf20Sopenharmony_ci { 18618c2ecf20Sopenharmony_ci /* https://bugzilla.kernel.org/show_bug.cgi?id=209989 */ 18628c2ecf20Sopenharmony_ci ec_honor_dsdt_gpe, "HP Pavilion Gaming Laptop 15-cx0xxx", { 18638c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "HP"), 18648c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion Gaming Laptop 15-cx0xxx"),}, NULL}, 18658c2ecf20Sopenharmony_ci { 18668c2ecf20Sopenharmony_ci ec_clear_on_resume, "Samsung hardware", { 18678c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD.")}, NULL}, 18688c2ecf20Sopenharmony_ci {}, 18698c2ecf20Sopenharmony_ci}; 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_civoid __init acpi_ec_ecdt_probe(void) 18728c2ecf20Sopenharmony_ci{ 18738c2ecf20Sopenharmony_ci struct acpi_table_ecdt *ecdt_ptr; 18748c2ecf20Sopenharmony_ci struct acpi_ec *ec; 18758c2ecf20Sopenharmony_ci acpi_status status; 18768c2ecf20Sopenharmony_ci int ret; 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci /* Generate a boot ec context. */ 18798c2ecf20Sopenharmony_ci dmi_check_system(ec_dmi_table); 18808c2ecf20Sopenharmony_ci status = acpi_get_table(ACPI_SIG_ECDT, 1, 18818c2ecf20Sopenharmony_ci (struct acpi_table_header **)&ecdt_ptr); 18828c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 18838c2ecf20Sopenharmony_ci return; 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci if (!ecdt_ptr->control.address || !ecdt_ptr->data.address) { 18868c2ecf20Sopenharmony_ci /* 18878c2ecf20Sopenharmony_ci * Asus X50GL: 18888c2ecf20Sopenharmony_ci * https://bugzilla.kernel.org/show_bug.cgi?id=11880 18898c2ecf20Sopenharmony_ci */ 18908c2ecf20Sopenharmony_ci goto out; 18918c2ecf20Sopenharmony_ci } 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci ec = acpi_ec_alloc(); 18948c2ecf20Sopenharmony_ci if (!ec) 18958c2ecf20Sopenharmony_ci goto out; 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci if (EC_FLAGS_CORRECT_ECDT) { 18988c2ecf20Sopenharmony_ci ec->command_addr = ecdt_ptr->data.address; 18998c2ecf20Sopenharmony_ci ec->data_addr = ecdt_ptr->control.address; 19008c2ecf20Sopenharmony_ci } else { 19018c2ecf20Sopenharmony_ci ec->command_addr = ecdt_ptr->control.address; 19028c2ecf20Sopenharmony_ci ec->data_addr = ecdt_ptr->data.address; 19038c2ecf20Sopenharmony_ci } 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci /* 19068c2ecf20Sopenharmony_ci * Ignore the GPE value on Reduced Hardware platforms. 19078c2ecf20Sopenharmony_ci * Some products have this set to an erroneous value. 19088c2ecf20Sopenharmony_ci */ 19098c2ecf20Sopenharmony_ci if (!acpi_gbl_reduced_hardware) 19108c2ecf20Sopenharmony_ci ec->gpe = ecdt_ptr->gpe; 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci ec->handle = ACPI_ROOT_OBJECT; 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ci /* 19158c2ecf20Sopenharmony_ci * At this point, the namespace is not initialized, so do not find 19168c2ecf20Sopenharmony_ci * the namespace objects, or handle the events. 19178c2ecf20Sopenharmony_ci */ 19188c2ecf20Sopenharmony_ci ret = acpi_ec_setup(ec, NULL); 19198c2ecf20Sopenharmony_ci if (ret) { 19208c2ecf20Sopenharmony_ci acpi_ec_free(ec); 19218c2ecf20Sopenharmony_ci goto out; 19228c2ecf20Sopenharmony_ci } 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci boot_ec = ec; 19258c2ecf20Sopenharmony_ci boot_ec_is_ecdt = true; 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci pr_info("Boot ECDT EC used to handle transactions\n"); 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ciout: 19308c2ecf20Sopenharmony_ci acpi_put_table((struct acpi_table_header *)ecdt_ptr); 19318c2ecf20Sopenharmony_ci} 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 19348c2ecf20Sopenharmony_cistatic int acpi_ec_suspend(struct device *dev) 19358c2ecf20Sopenharmony_ci{ 19368c2ecf20Sopenharmony_ci struct acpi_ec *ec = 19378c2ecf20Sopenharmony_ci acpi_driver_data(to_acpi_device(dev)); 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci if (!pm_suspend_no_platform() && ec_freeze_events) 19408c2ecf20Sopenharmony_ci acpi_ec_disable_event(ec); 19418c2ecf20Sopenharmony_ci return 0; 19428c2ecf20Sopenharmony_ci} 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_cistatic int acpi_ec_suspend_noirq(struct device *dev) 19458c2ecf20Sopenharmony_ci{ 19468c2ecf20Sopenharmony_ci struct acpi_ec *ec = acpi_driver_data(to_acpi_device(dev)); 19478c2ecf20Sopenharmony_ci 19488c2ecf20Sopenharmony_ci /* 19498c2ecf20Sopenharmony_ci * The SCI handler doesn't run at this point, so the GPE can be 19508c2ecf20Sopenharmony_ci * masked at the low level without side effects. 19518c2ecf20Sopenharmony_ci */ 19528c2ecf20Sopenharmony_ci if (ec_no_wakeup && test_bit(EC_FLAGS_STARTED, &ec->flags) && 19538c2ecf20Sopenharmony_ci ec->gpe >= 0 && ec->reference_count >= 1) 19548c2ecf20Sopenharmony_ci acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE); 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci acpi_ec_enter_noirq(ec); 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci return 0; 19598c2ecf20Sopenharmony_ci} 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_cistatic int acpi_ec_resume_noirq(struct device *dev) 19628c2ecf20Sopenharmony_ci{ 19638c2ecf20Sopenharmony_ci struct acpi_ec *ec = acpi_driver_data(to_acpi_device(dev)); 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci acpi_ec_leave_noirq(ec); 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci if (ec_no_wakeup && test_bit(EC_FLAGS_STARTED, &ec->flags) && 19688c2ecf20Sopenharmony_ci ec->gpe >= 0 && ec->reference_count >= 1) 19698c2ecf20Sopenharmony_ci acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_ENABLE); 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci return 0; 19728c2ecf20Sopenharmony_ci} 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_cistatic int acpi_ec_resume(struct device *dev) 19758c2ecf20Sopenharmony_ci{ 19768c2ecf20Sopenharmony_ci struct acpi_ec *ec = 19778c2ecf20Sopenharmony_ci acpi_driver_data(to_acpi_device(dev)); 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci acpi_ec_enable_event(ec); 19808c2ecf20Sopenharmony_ci return 0; 19818c2ecf20Sopenharmony_ci} 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_civoid acpi_ec_mark_gpe_for_wake(void) 19848c2ecf20Sopenharmony_ci{ 19858c2ecf20Sopenharmony_ci if (first_ec && !ec_no_wakeup) 19868c2ecf20Sopenharmony_ci acpi_mark_gpe_for_wake(NULL, first_ec->gpe); 19878c2ecf20Sopenharmony_ci} 19888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_ec_mark_gpe_for_wake); 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_civoid acpi_ec_set_gpe_wake_mask(u8 action) 19918c2ecf20Sopenharmony_ci{ 19928c2ecf20Sopenharmony_ci if (pm_suspend_no_platform() && first_ec && !ec_no_wakeup) 19938c2ecf20Sopenharmony_ci acpi_set_gpe_wake_mask(NULL, first_ec->gpe, action); 19948c2ecf20Sopenharmony_ci} 19958c2ecf20Sopenharmony_ci 19968c2ecf20Sopenharmony_cibool acpi_ec_dispatch_gpe(void) 19978c2ecf20Sopenharmony_ci{ 19988c2ecf20Sopenharmony_ci bool work_in_progress; 19998c2ecf20Sopenharmony_ci u32 ret; 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci if (!first_ec) 20028c2ecf20Sopenharmony_ci return acpi_any_gpe_status_set(U32_MAX); 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci /* 20058c2ecf20Sopenharmony_ci * Report wakeup if the status bit is set for any enabled GPE other 20068c2ecf20Sopenharmony_ci * than the EC one. 20078c2ecf20Sopenharmony_ci */ 20088c2ecf20Sopenharmony_ci if (acpi_any_gpe_status_set(first_ec->gpe)) 20098c2ecf20Sopenharmony_ci return true; 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ci /* 20128c2ecf20Sopenharmony_ci * Dispatch the EC GPE in-band, but do not report wakeup in any case 20138c2ecf20Sopenharmony_ci * to allow the caller to process events properly after that. 20148c2ecf20Sopenharmony_ci */ 20158c2ecf20Sopenharmony_ci ret = acpi_dispatch_gpe(NULL, first_ec->gpe); 20168c2ecf20Sopenharmony_ci if (ret == ACPI_INTERRUPT_HANDLED) 20178c2ecf20Sopenharmony_ci pm_pr_dbg("ACPI EC GPE dispatched\n"); 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci /* Drain EC work. */ 20208c2ecf20Sopenharmony_ci do { 20218c2ecf20Sopenharmony_ci acpi_ec_flush_work(); 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci pm_pr_dbg("ACPI EC work flushed\n"); 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_ci spin_lock_irq(&first_ec->lock); 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci work_in_progress = first_ec->events_in_progress + 20288c2ecf20Sopenharmony_ci first_ec->queries_in_progress > 0; 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_ci spin_unlock_irq(&first_ec->lock); 20318c2ecf20Sopenharmony_ci } while (work_in_progress && !pm_wakeup_pending()); 20328c2ecf20Sopenharmony_ci 20338c2ecf20Sopenharmony_ci return false; 20348c2ecf20Sopenharmony_ci} 20358c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_cistatic const struct dev_pm_ops acpi_ec_pm = { 20388c2ecf20Sopenharmony_ci SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend_noirq, acpi_ec_resume_noirq) 20398c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend, acpi_ec_resume) 20408c2ecf20Sopenharmony_ci}; 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_cistatic int param_set_event_clearing(const char *val, 20438c2ecf20Sopenharmony_ci const struct kernel_param *kp) 20448c2ecf20Sopenharmony_ci{ 20458c2ecf20Sopenharmony_ci int result = 0; 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci if (!strncmp(val, "status", sizeof("status") - 1)) { 20488c2ecf20Sopenharmony_ci ec_event_clearing = ACPI_EC_EVT_TIMING_STATUS; 20498c2ecf20Sopenharmony_ci pr_info("Assuming SCI_EVT clearing on EC_SC accesses\n"); 20508c2ecf20Sopenharmony_ci } else if (!strncmp(val, "query", sizeof("query") - 1)) { 20518c2ecf20Sopenharmony_ci ec_event_clearing = ACPI_EC_EVT_TIMING_QUERY; 20528c2ecf20Sopenharmony_ci pr_info("Assuming SCI_EVT clearing on QR_EC writes\n"); 20538c2ecf20Sopenharmony_ci } else if (!strncmp(val, "event", sizeof("event") - 1)) { 20548c2ecf20Sopenharmony_ci ec_event_clearing = ACPI_EC_EVT_TIMING_EVENT; 20558c2ecf20Sopenharmony_ci pr_info("Assuming SCI_EVT clearing on event reads\n"); 20568c2ecf20Sopenharmony_ci } else 20578c2ecf20Sopenharmony_ci result = -EINVAL; 20588c2ecf20Sopenharmony_ci return result; 20598c2ecf20Sopenharmony_ci} 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_cistatic int param_get_event_clearing(char *buffer, 20628c2ecf20Sopenharmony_ci const struct kernel_param *kp) 20638c2ecf20Sopenharmony_ci{ 20648c2ecf20Sopenharmony_ci switch (ec_event_clearing) { 20658c2ecf20Sopenharmony_ci case ACPI_EC_EVT_TIMING_STATUS: 20668c2ecf20Sopenharmony_ci return sprintf(buffer, "status\n"); 20678c2ecf20Sopenharmony_ci case ACPI_EC_EVT_TIMING_QUERY: 20688c2ecf20Sopenharmony_ci return sprintf(buffer, "query\n"); 20698c2ecf20Sopenharmony_ci case ACPI_EC_EVT_TIMING_EVENT: 20708c2ecf20Sopenharmony_ci return sprintf(buffer, "event\n"); 20718c2ecf20Sopenharmony_ci default: 20728c2ecf20Sopenharmony_ci return sprintf(buffer, "invalid\n"); 20738c2ecf20Sopenharmony_ci } 20748c2ecf20Sopenharmony_ci return 0; 20758c2ecf20Sopenharmony_ci} 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_cimodule_param_call(ec_event_clearing, param_set_event_clearing, param_get_event_clearing, 20788c2ecf20Sopenharmony_ci NULL, 0644); 20798c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ec_event_clearing, "Assumed SCI_EVT clearing timing"); 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_cistatic struct acpi_driver acpi_ec_driver = { 20828c2ecf20Sopenharmony_ci .name = "ec", 20838c2ecf20Sopenharmony_ci .class = ACPI_EC_CLASS, 20848c2ecf20Sopenharmony_ci .ids = ec_device_ids, 20858c2ecf20Sopenharmony_ci .ops = { 20868c2ecf20Sopenharmony_ci .add = acpi_ec_add, 20878c2ecf20Sopenharmony_ci .remove = acpi_ec_remove, 20888c2ecf20Sopenharmony_ci }, 20898c2ecf20Sopenharmony_ci .drv.pm = &acpi_ec_pm, 20908c2ecf20Sopenharmony_ci}; 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_cistatic void acpi_ec_destroy_workqueues(void) 20938c2ecf20Sopenharmony_ci{ 20948c2ecf20Sopenharmony_ci if (ec_wq) { 20958c2ecf20Sopenharmony_ci destroy_workqueue(ec_wq); 20968c2ecf20Sopenharmony_ci ec_wq = NULL; 20978c2ecf20Sopenharmony_ci } 20988c2ecf20Sopenharmony_ci if (ec_query_wq) { 20998c2ecf20Sopenharmony_ci destroy_workqueue(ec_query_wq); 21008c2ecf20Sopenharmony_ci ec_query_wq = NULL; 21018c2ecf20Sopenharmony_ci } 21028c2ecf20Sopenharmony_ci} 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_cistatic int acpi_ec_init_workqueues(void) 21058c2ecf20Sopenharmony_ci{ 21068c2ecf20Sopenharmony_ci if (!ec_wq) 21078c2ecf20Sopenharmony_ci ec_wq = alloc_ordered_workqueue("kec", 0); 21088c2ecf20Sopenharmony_ci 21098c2ecf20Sopenharmony_ci if (!ec_query_wq) 21108c2ecf20Sopenharmony_ci ec_query_wq = alloc_workqueue("kec_query", 0, ec_max_queries); 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_ci if (!ec_wq || !ec_query_wq) { 21138c2ecf20Sopenharmony_ci acpi_ec_destroy_workqueues(); 21148c2ecf20Sopenharmony_ci return -ENODEV; 21158c2ecf20Sopenharmony_ci } 21168c2ecf20Sopenharmony_ci return 0; 21178c2ecf20Sopenharmony_ci} 21188c2ecf20Sopenharmony_ci 21198c2ecf20Sopenharmony_cistatic const struct dmi_system_id acpi_ec_no_wakeup[] = { 21208c2ecf20Sopenharmony_ci { 21218c2ecf20Sopenharmony_ci .ident = "Thinkpad X1 Carbon 6th", 21228c2ecf20Sopenharmony_ci .matches = { 21238c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 21248c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_FAMILY, "Thinkpad X1 Carbon 6th"), 21258c2ecf20Sopenharmony_ci }, 21268c2ecf20Sopenharmony_ci }, 21278c2ecf20Sopenharmony_ci { 21288c2ecf20Sopenharmony_ci .ident = "ThinkPad X1 Yoga 3rd", 21298c2ecf20Sopenharmony_ci .matches = { 21308c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 21318c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_FAMILY, "ThinkPad X1 Yoga 3rd"), 21328c2ecf20Sopenharmony_ci }, 21338c2ecf20Sopenharmony_ci }, 21348c2ecf20Sopenharmony_ci { }, 21358c2ecf20Sopenharmony_ci}; 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_civoid __init acpi_ec_init(void) 21388c2ecf20Sopenharmony_ci{ 21398c2ecf20Sopenharmony_ci int result; 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_ci result = acpi_ec_init_workqueues(); 21428c2ecf20Sopenharmony_ci if (result) 21438c2ecf20Sopenharmony_ci return; 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci /* 21468c2ecf20Sopenharmony_ci * Disable EC wakeup on following systems to prevent periodic 21478c2ecf20Sopenharmony_ci * wakeup from EC GPE. 21488c2ecf20Sopenharmony_ci */ 21498c2ecf20Sopenharmony_ci if (dmi_check_system(acpi_ec_no_wakeup)) { 21508c2ecf20Sopenharmony_ci ec_no_wakeup = true; 21518c2ecf20Sopenharmony_ci pr_debug("Disabling EC wakeup on suspend-to-idle\n"); 21528c2ecf20Sopenharmony_ci } 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_ci /* Driver must be registered after acpi_ec_init_workqueues(). */ 21558c2ecf20Sopenharmony_ci acpi_bus_register_driver(&acpi_ec_driver); 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci acpi_ec_ecdt_start(); 21588c2ecf20Sopenharmony_ci} 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci/* EC driver currently not unloadable */ 21618c2ecf20Sopenharmony_ci#if 0 21628c2ecf20Sopenharmony_cistatic void __exit acpi_ec_exit(void) 21638c2ecf20Sopenharmony_ci{ 21648c2ecf20Sopenharmony_ci 21658c2ecf20Sopenharmony_ci acpi_bus_unregister_driver(&acpi_ec_driver); 21668c2ecf20Sopenharmony_ci acpi_ec_destroy_workqueues(); 21678c2ecf20Sopenharmony_ci} 21688c2ecf20Sopenharmony_ci#endif /* 0 */ 2169