mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 06:31:49 +00:00
platform-drivers-x86 for 4.5-1
Add intel punit and telemetry driver for APL SoCs. Add intel-hid driver for various laptop hotkey support. Add asus-wireless radio control driver. Keyboard backlight support/improvements for ThinkPads, Vaio, and Toshiba. Several hotkey related fixes and improvements for dell and toshiba. Fix oops on dual GPU Macs in apple-gmux. A few new device IDs and quirks. Various minor config related build issues and cleanups. surface pro 4: - fix compare_const_fl.cocci warnings - Add support for Surface Pro 4 Buttons platform/x86: - Add Intel Telemetry Debugfs interfaces - Add Intel telemetry platform device - Add Intel telemetry platform driver - Add Intel Telemetry Core Driver - add NULL check for input parameters - add Intel P-Unit mailbox IPC driver - update acpi resource structure for Punit thinkpad_acpi: - Add support for keyboard backlight dell-wmi: - Process only one event on devices with interface version 0 - Check if Dell WMI descriptor structure is valid - Improve unknown hotkey handling - Use a C99-style array for bios_to_linux_keycode tc1100-wmi: - fix build warning when CONFIG_PM not enabled asus-wireless: - Add ACPI HID ATK4001 - Add Asus Wireless Radio Control driver asus-wmi: - drop to_platform_driver macro intel-hid: - new hid event driver for hotkeys sony-laptop: - Keyboard backlight control for some Vaio Fit models ideapad-laptop: - Add Lenovo ideapad Y700-17ISK to no_hw_rfkill dmi list apple-gmux: - Assign apple_gmux_data before registering toshiba_acpi: - Add rfkill dependency to ACPI_TOSHIBA entry - Fix keyboard backlight sysfs entries not being updated - Add WWAN RFKill support - Add support for WWAN devices - Fix blank screen at boot if transflective backlight is supported - Propagate the hotkey value via genetlink toshiba_bluetooth: - Add missing newline in toshiba_bluetooth_present function -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJWnuSYAAoJEKbMaAwKp364xckH/A/INtwr1LzmSw8fsEIe6IGQ MKEzCHZOUdSPcZSHcTW/fE/J8kt1j4YdR2kkxNbivtxST5O81E6KfAmo37TRrTNx bunVt7Swj7FcPsm6t0m3PQkbWkOy3UMUncRaZjeofzhRl2m+bHEycEIJsHs7OU+e 7UPjbWZjRz6/LahcryKDH/lRhweMEsgZSFzjNlby8Wzz4LRCu8HLwNGUQa0vQfNI 2UW+s7g2ZkFXx9fbmWu9twkyP7LwNoaZ3IDGsEffIYjmXeZKe+yTPy/TueYYzpon aclJSjhxLNnfWUaO0eUt/H4T5VyUag3v3mp6PVSwm2TA3Cpuh1SDYrJcurls6Mw= =b4Wa -----END PGP SIGNATURE----- Merge tag 'platform-drivers-x86-v4.5-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86 Pull x86 platform driver updates from Darren Hart: "Add intel punit and telemetry driver for APL SoCs. Add intel-hid driver for various laptop hotkey support. Add asus-wireless radio control driver. Keyboard backlight support/improvements for ThinkPads, Vaio, and Toshiba. Several hotkey related fixes and improvements for dell and toshiba. Fix oops on dual GPU Macs in apple-gmux. A few new device IDs and quirks. Various minor config related build issues and cleanups. surface pro 4: - fix compare_const_fl.cocci warnings - Add support for Surface Pro 4 Buttons platform/x86: - Add Intel Telemetry Debugfs interfaces - Add Intel telemetry platform device - Add Intel telemetry platform driver - Add Intel Telemetry Core Driver - add NULL check for input parameters - add Intel P-Unit mailbox IPC driver - update acpi resource structure for Punit thinkpad_acpi: - Add support for keyboard backlight dell-wmi: - Process only one event on devices with interface version 0 - Check if Dell WMI descriptor structure is valid - Improve unknown hotkey handling - Use a C99-style array for bios_to_linux_keycode tc1100-wmi: - fix build warning when CONFIG_PM not enabled asus-wireless: - Add ACPI HID ATK4001 - Add Asus Wireless Radio Control driver asus-wmi: - drop to_platform_driver macro intel-hid: - new hid event driver for hotkeys sony-laptop: - Keyboard backlight control for some Vaio Fit models ideapad-laptop: - Add Lenovo ideapad Y700-17ISK to no_hw_rfkill dmi list apple-gmux: - Assign apple_gmux_data before registering toshiba_acpi: - Add rfkill dependency to ACPI_TOSHIBA entry - Fix keyboard backlight sysfs entries not being updated - Add WWAN RFKill support - Add support for WWAN devices - Fix blank screen at boot if transflective backlight is supported - Propagate the hotkey value via genetlink toshiba_bluetooth: - Add missing newline in toshiba_bluetooth_present function" * tag 'platform-drivers-x86-v4.5-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86: (29 commits) surface pro 4: fix compare_const_fl.cocci warnings surface pro 4: Add support for Surface Pro 4 Buttons platform:x86: Add Intel Telemetry Debugfs interfaces platform:x86: Add Intel telemetry platform device platform:x86: Add Intel telemetry platform driver platform/x86: Add Intel Telemetry Core Driver intel_punit_ipc: add NULL check for input parameters thinkpad_acpi: Add support for keyboard backlight dell-wmi: Process only one event on devices with interface version 0 dell-wmi: Check if Dell WMI descriptor structure is valid tc1100-wmi: fix build warning when CONFIG_PM not enabled asus-wireless: Add ACPI HID ATK4001 platform/x86: Add Asus Wireless Radio Control driver asus-wmi: drop to_platform_driver macro intel-hid: new hid event driver for hotkeys Keyboard backlight control for some Vaio Fit models platform/x86: Add rfkill dependency to ACPI_TOSHIBA entry platform:x86: add Intel P-Unit mailbox IPC driver intel_pmc_ipc: update acpi resource structure for Punit ideapad-laptop: Add Lenovo ideapad Y700-17ISK to no_hw_rfkill dmi list ...
This commit is contained in:
commit
2b4015e9fb
25
MAINTAINERS
25
MAINTAINERS
@ -1806,6 +1806,12 @@ S: Maintained
|
||||
F: drivers/platform/x86/asus*.c
|
||||
F: drivers/platform/x86/eeepc*.c
|
||||
|
||||
ASUS WIRELESS RADIO CONTROL DRIVER
|
||||
M: João Paulo Rechi Vita <jprvita@gmail.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/asus-wireless.c
|
||||
|
||||
ASYNCHRONOUS TRANSFERS/TRANSFORMS (IOAT) API
|
||||
R: Dan Williams <dan.j.williams@intel.com>
|
||||
W: http://sourceforge.net/projects/xscaleiop
|
||||
@ -5533,6 +5539,12 @@ T: git git://git.code.sf.net/p/intel-sas/isci
|
||||
S: Supported
|
||||
F: drivers/scsi/isci/
|
||||
|
||||
INTEL HID EVENT DRIVER
|
||||
M: Alex Hung <alex.hung@canonical.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/intel-hid.c
|
||||
|
||||
INTEL IDLE DRIVER
|
||||
M: Len Brown <lenb@kernel.org>
|
||||
L: linux-pm@vger.kernel.org
|
||||
@ -5713,12 +5725,23 @@ F: drivers/dma/mic_x100_dma.c
|
||||
F: drivers/dma/mic_x100_dma.h
|
||||
F Documentation/mic/
|
||||
|
||||
INTEL PMC IPC DRIVER
|
||||
INTEL PMC/P-Unit IPC DRIVER
|
||||
M: Zha Qipeng<qipeng.zha@intel.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/intel_pmc_ipc.c
|
||||
F: drivers/platform/x86/intel_punit_ipc.c
|
||||
F: arch/x86/include/asm/intel_pmc_ipc.h
|
||||
F: arch/x86/include/asm/intel_punit_ipc.h
|
||||
|
||||
INTEL TELEMETRY DRIVER
|
||||
M: Souvik Kumar Chakravarty <souvik.k.chakravarty@intel.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/intel_telemetry_core.c
|
||||
F: arch/x86/include/asm/intel_telemetry.h
|
||||
F: drivers/platform/x86/intel_telemetry_pltdrv.c
|
||||
F: drivers/platform/x86/intel_telemetry_debugfs.c
|
||||
|
||||
IOC3 ETHERNET DRIVER
|
||||
M: Ralf Baechle <ralf@linux-mips.org>
|
||||
|
101
arch/x86/include/asm/intel_punit_ipc.h
Normal file
101
arch/x86/include/asm/intel_punit_ipc.h
Normal file
@ -0,0 +1,101 @@
|
||||
#ifndef _ASM_X86_INTEL_PUNIT_IPC_H_
|
||||
#define _ASM_X86_INTEL_PUNIT_IPC_H_
|
||||
|
||||
/*
|
||||
* Three types of 8bit P-Unit IPC commands are supported,
|
||||
* bit[7:6]: [00]: BIOS; [01]: GTD; [10]: ISPD.
|
||||
*/
|
||||
typedef enum {
|
||||
BIOS_IPC = 0,
|
||||
GTDRIVER_IPC,
|
||||
ISPDRIVER_IPC,
|
||||
RESERVED_IPC,
|
||||
} IPC_TYPE;
|
||||
|
||||
#define IPC_TYPE_OFFSET 6
|
||||
#define IPC_PUNIT_BIOS_CMD_BASE (BIOS_IPC << IPC_TYPE_OFFSET)
|
||||
#define IPC_PUNIT_GTD_CMD_BASE (GTDDRIVER_IPC << IPC_TYPE_OFFSET)
|
||||
#define IPC_PUNIT_ISPD_CMD_BASE (ISPDRIVER_IPC << IPC_TYPE_OFFSET)
|
||||
#define IPC_PUNIT_CMD_TYPE_MASK (RESERVED_IPC << IPC_TYPE_OFFSET)
|
||||
|
||||
/* BIOS => Pcode commands */
|
||||
#define IPC_PUNIT_BIOS_ZERO (IPC_PUNIT_BIOS_CMD_BASE | 0x00)
|
||||
#define IPC_PUNIT_BIOS_VR_INTERFACE (IPC_PUNIT_BIOS_CMD_BASE | 0x01)
|
||||
#define IPC_PUNIT_BIOS_READ_PCS (IPC_PUNIT_BIOS_CMD_BASE | 0x02)
|
||||
#define IPC_PUNIT_BIOS_WRITE_PCS (IPC_PUNIT_BIOS_CMD_BASE | 0x03)
|
||||
#define IPC_PUNIT_BIOS_READ_PCU_CONFIG (IPC_PUNIT_BIOS_CMD_BASE | 0x04)
|
||||
#define IPC_PUNIT_BIOS_WRITE_PCU_CONFIG (IPC_PUNIT_BIOS_CMD_BASE | 0x05)
|
||||
#define IPC_PUNIT_BIOS_READ_PL1_SETTING (IPC_PUNIT_BIOS_CMD_BASE | 0x06)
|
||||
#define IPC_PUNIT_BIOS_WRITE_PL1_SETTING (IPC_PUNIT_BIOS_CMD_BASE | 0x07)
|
||||
#define IPC_PUNIT_BIOS_TRIGGER_VDD_RAM (IPC_PUNIT_BIOS_CMD_BASE | 0x08)
|
||||
#define IPC_PUNIT_BIOS_READ_TELE_INFO (IPC_PUNIT_BIOS_CMD_BASE | 0x09)
|
||||
#define IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL (IPC_PUNIT_BIOS_CMD_BASE | 0x0a)
|
||||
#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL (IPC_PUNIT_BIOS_CMD_BASE | 0x0b)
|
||||
#define IPC_PUNIT_BIOS_READ_TELE_EVENT_CTRL (IPC_PUNIT_BIOS_CMD_BASE | 0x0c)
|
||||
#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT_CTRL (IPC_PUNIT_BIOS_CMD_BASE | 0x0d)
|
||||
#define IPC_PUNIT_BIOS_READ_TELE_TRACE (IPC_PUNIT_BIOS_CMD_BASE | 0x0e)
|
||||
#define IPC_PUNIT_BIOS_WRITE_TELE_TRACE (IPC_PUNIT_BIOS_CMD_BASE | 0x0f)
|
||||
#define IPC_PUNIT_BIOS_READ_TELE_EVENT (IPC_PUNIT_BIOS_CMD_BASE | 0x10)
|
||||
#define IPC_PUNIT_BIOS_WRITE_TELE_EVENT (IPC_PUNIT_BIOS_CMD_BASE | 0x11)
|
||||
#define IPC_PUNIT_BIOS_READ_MODULE_TEMP (IPC_PUNIT_BIOS_CMD_BASE | 0x12)
|
||||
#define IPC_PUNIT_BIOS_RESERVED (IPC_PUNIT_BIOS_CMD_BASE | 0x13)
|
||||
#define IPC_PUNIT_BIOS_READ_VOLTAGE_OVER (IPC_PUNIT_BIOS_CMD_BASE | 0x14)
|
||||
#define IPC_PUNIT_BIOS_WRITE_VOLTAGE_OVER (IPC_PUNIT_BIOS_CMD_BASE | 0x15)
|
||||
#define IPC_PUNIT_BIOS_READ_RATIO_OVER (IPC_PUNIT_BIOS_CMD_BASE | 0x16)
|
||||
#define IPC_PUNIT_BIOS_WRITE_RATIO_OVER (IPC_PUNIT_BIOS_CMD_BASE | 0x17)
|
||||
#define IPC_PUNIT_BIOS_READ_VF_GL_CTRL (IPC_PUNIT_BIOS_CMD_BASE | 0x18)
|
||||
#define IPC_PUNIT_BIOS_WRITE_VF_GL_CTRL (IPC_PUNIT_BIOS_CMD_BASE | 0x19)
|
||||
#define IPC_PUNIT_BIOS_READ_FM_SOC_TEMP_THRESH (IPC_PUNIT_BIOS_CMD_BASE | 0x1a)
|
||||
#define IPC_PUNIT_BIOS_WRITE_FM_SOC_TEMP_THRESH (IPC_PUNIT_BIOS_CMD_BASE | 0x1b)
|
||||
|
||||
/* GT Driver => Pcode commands */
|
||||
#define IPC_PUNIT_GTD_ZERO (IPC_PUNIT_GTD_CMD_BASE | 0x00)
|
||||
#define IPC_PUNIT_GTD_CONFIG (IPC_PUNIT_GTD_CMD_BASE | 0x01)
|
||||
#define IPC_PUNIT_GTD_READ_ICCP_LIC_CDYN_SCAL (IPC_PUNIT_GTD_CMD_BASE | 0x02)
|
||||
#define IPC_PUNIT_GTD_WRITE_ICCP_LIC_CDYN_SCAL (IPC_PUNIT_GTD_CMD_BASE | 0x03)
|
||||
#define IPC_PUNIT_GTD_GET_WM_VAL (IPC_PUNIT_GTD_CMD_BASE | 0x06)
|
||||
#define IPC_PUNIT_GTD_WRITE_CONFIG_WISHREQ (IPC_PUNIT_GTD_CMD_BASE | 0x07)
|
||||
#define IPC_PUNIT_GTD_READ_REQ_DUTY_CYCLE (IPC_PUNIT_GTD_CMD_BASE | 0x16)
|
||||
#define IPC_PUNIT_GTD_DIS_VOL_FREQ_CHG_REQUEST (IPC_PUNIT_GTD_CMD_BASE | 0x17)
|
||||
#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_CTRL (IPC_PUNIT_GTD_CMD_BASE | 0x1a)
|
||||
#define IPC_PUNIT_GTD_DYNA_DUTY_CYCLE_TUNING (IPC_PUNIT_GTD_CMD_BASE | 0x1c)
|
||||
|
||||
/* ISP Driver => Pcode commands */
|
||||
#define IPC_PUNIT_ISPD_ZERO (IPC_PUNIT_ISPD_CMD_BASE | 0x00)
|
||||
#define IPC_PUNIT_ISPD_CONFIG (IPC_PUNIT_ISPD_CMD_BASE | 0x01)
|
||||
#define IPC_PUNIT_ISPD_GET_ISP_LTR_VAL (IPC_PUNIT_ISPD_CMD_BASE | 0x02)
|
||||
#define IPC_PUNIT_ISPD_ACCESS_IU_FREQ_BOUNDS (IPC_PUNIT_ISPD_CMD_BASE | 0x03)
|
||||
#define IPC_PUNIT_ISPD_READ_CDYN_LEVEL (IPC_PUNIT_ISPD_CMD_BASE | 0x04)
|
||||
#define IPC_PUNIT_ISPD_WRITE_CDYN_LEVEL (IPC_PUNIT_ISPD_CMD_BASE | 0x05)
|
||||
|
||||
/* Error codes */
|
||||
#define IPC_PUNIT_ERR_SUCCESS 0
|
||||
#define IPC_PUNIT_ERR_INVALID_CMD 1
|
||||
#define IPC_PUNIT_ERR_INVALID_PARAMETER 2
|
||||
#define IPC_PUNIT_ERR_CMD_TIMEOUT 3
|
||||
#define IPC_PUNIT_ERR_CMD_LOCKED 4
|
||||
#define IPC_PUNIT_ERR_INVALID_VR_ID 5
|
||||
#define IPC_PUNIT_ERR_VR_ERR 6
|
||||
|
||||
#if IS_ENABLED(CONFIG_INTEL_PUNIT_IPC)
|
||||
|
||||
int intel_punit_ipc_simple_command(int cmd, int para1, int para2);
|
||||
int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32 *out);
|
||||
|
||||
#else
|
||||
|
||||
static inline int intel_punit_ipc_simple_command(int cmd,
|
||||
int para1, int para2)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2,
|
||||
u32 *in, u32 *out)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_INTEL_PUNIT_IPC */
|
||||
|
||||
#endif
|
147
arch/x86/include/asm/intel_telemetry.h
Normal file
147
arch/x86/include/asm/intel_telemetry.h
Normal file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Intel SOC Telemetry Driver Header File
|
||||
* Copyright (C) 2015, Intel Corporation.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
#ifndef INTEL_TELEMETRY_H
|
||||
#define INTEL_TELEMETRY_H
|
||||
|
||||
#define TELEM_MAX_EVENTS_SRAM 28
|
||||
#define TELEM_MAX_OS_ALLOCATED_EVENTS 20
|
||||
|
||||
enum telemetry_unit {
|
||||
TELEM_PSS = 0,
|
||||
TELEM_IOSS,
|
||||
TELEM_UNIT_NONE
|
||||
};
|
||||
|
||||
struct telemetry_evtlog {
|
||||
u32 telem_evtid;
|
||||
u64 telem_evtlog;
|
||||
};
|
||||
|
||||
struct telemetry_evtconfig {
|
||||
/* Array of Event-IDs to Enable */
|
||||
u32 *evtmap;
|
||||
|
||||
/* Number of Events (<29) in evtmap */
|
||||
u8 num_evts;
|
||||
|
||||
/* Sampling period */
|
||||
u8 period;
|
||||
};
|
||||
|
||||
struct telemetry_evtmap {
|
||||
const char *name;
|
||||
u32 evt_id;
|
||||
};
|
||||
|
||||
struct telemetry_unit_config {
|
||||
struct telemetry_evtmap *telem_evts;
|
||||
void __iomem *regmap;
|
||||
u32 ssram_base_addr;
|
||||
u8 ssram_evts_used;
|
||||
u8 curr_period;
|
||||
u8 max_period;
|
||||
u8 min_period;
|
||||
u32 ssram_size;
|
||||
|
||||
};
|
||||
|
||||
struct telemetry_plt_config {
|
||||
struct telemetry_unit_config pss_config;
|
||||
struct telemetry_unit_config ioss_config;
|
||||
struct mutex telem_trace_lock;
|
||||
struct mutex telem_lock;
|
||||
bool telem_in_use;
|
||||
};
|
||||
|
||||
struct telemetry_core_ops {
|
||||
int (*get_sampling_period)(u8 *pss_min_period, u8 *pss_max_period,
|
||||
u8 *ioss_min_period, u8 *ioss_max_period);
|
||||
|
||||
int (*get_eventconfig)(struct telemetry_evtconfig *pss_evtconfig,
|
||||
struct telemetry_evtconfig *ioss_evtconfig,
|
||||
int pss_len, int ioss_len);
|
||||
|
||||
int (*update_events)(struct telemetry_evtconfig pss_evtconfig,
|
||||
struct telemetry_evtconfig ioss_evtconfig);
|
||||
|
||||
int (*set_sampling_period)(u8 pss_period, u8 ioss_period);
|
||||
|
||||
int (*get_trace_verbosity)(enum telemetry_unit telem_unit,
|
||||
u32 *verbosity);
|
||||
|
||||
int (*set_trace_verbosity)(enum telemetry_unit telem_unit,
|
||||
u32 verbosity);
|
||||
|
||||
int (*raw_read_eventlog)(enum telemetry_unit telem_unit,
|
||||
struct telemetry_evtlog *evtlog,
|
||||
int len, int log_all_evts);
|
||||
|
||||
int (*read_eventlog)(enum telemetry_unit telem_unit,
|
||||
struct telemetry_evtlog *evtlog,
|
||||
int len, int log_all_evts);
|
||||
|
||||
int (*add_events)(u8 num_pss_evts, u8 num_ioss_evts,
|
||||
u32 *pss_evtmap, u32 *ioss_evtmap);
|
||||
|
||||
int (*reset_events)(void);
|
||||
};
|
||||
|
||||
int telemetry_set_pltdata(struct telemetry_core_ops *ops,
|
||||
struct telemetry_plt_config *pltconfig);
|
||||
|
||||
int telemetry_clear_pltdata(void);
|
||||
|
||||
int telemetry_pltconfig_valid(void);
|
||||
|
||||
int telemetry_get_evtname(enum telemetry_unit telem_unit,
|
||||
const char **name, int len);
|
||||
|
||||
int telemetry_update_events(struct telemetry_evtconfig pss_evtconfig,
|
||||
struct telemetry_evtconfig ioss_evtconfig);
|
||||
|
||||
int telemetry_add_events(u8 num_pss_evts, u8 num_ioss_evts,
|
||||
u32 *pss_evtmap, u32 *ioss_evtmap);
|
||||
|
||||
int telemetry_reset_events(void);
|
||||
|
||||
int telemetry_get_eventconfig(struct telemetry_evtconfig *pss_config,
|
||||
struct telemetry_evtconfig *ioss_config,
|
||||
int pss_len, int ioss_len);
|
||||
|
||||
int telemetry_read_events(enum telemetry_unit telem_unit,
|
||||
struct telemetry_evtlog *evtlog, int len);
|
||||
|
||||
int telemetry_raw_read_events(enum telemetry_unit telem_unit,
|
||||
struct telemetry_evtlog *evtlog, int len);
|
||||
|
||||
int telemetry_read_eventlog(enum telemetry_unit telem_unit,
|
||||
struct telemetry_evtlog *evtlog, int len);
|
||||
|
||||
int telemetry_raw_read_eventlog(enum telemetry_unit telem_unit,
|
||||
struct telemetry_evtlog *evtlog, int len);
|
||||
|
||||
int telemetry_get_sampling_period(u8 *pss_min_period, u8 *pss_max_period,
|
||||
u8 *ioss_min_period, u8 *ioss_max_period);
|
||||
|
||||
int telemetry_set_sampling_period(u8 pss_period, u8 ioss_period);
|
||||
|
||||
int telemetry_set_trace_verbosity(enum telemetry_unit telem_unit,
|
||||
u32 verbosity);
|
||||
|
||||
int telemetry_get_trace_verbosity(enum telemetry_unit telem_unit,
|
||||
u32 *verbosity);
|
||||
|
||||
#endif /* INTEL_TELEMETRY_H */
|
@ -587,6 +587,20 @@ config EEEPC_WMI
|
||||
If you have an ACPI-WMI compatible Eee PC laptop (>= 1000), say Y or M
|
||||
here.
|
||||
|
||||
config ASUS_WIRELESS
|
||||
tristate "Asus Wireless Radio Control Driver"
|
||||
depends on ACPI
|
||||
depends on INPUT
|
||||
---help---
|
||||
The Asus Wireless Radio Control handles the airplane mode hotkey
|
||||
present on some Asus laptops.
|
||||
|
||||
Say Y or M here if you have an ASUS notebook with an airplane mode
|
||||
hotkey.
|
||||
|
||||
If you choose to compile this driver as a module the module will be
|
||||
called asus-wireless.
|
||||
|
||||
config ACPI_WMI
|
||||
tristate "WMI"
|
||||
depends on ACPI
|
||||
@ -641,6 +655,7 @@ config ACPI_TOSHIBA
|
||||
depends on INPUT
|
||||
depends on SERIO_I8042 || SERIO_I8042 = n
|
||||
depends on ACPI_VIDEO || ACPI_VIDEO = n
|
||||
depends on RFKILL || RFKILL = n
|
||||
select INPUT_POLLDEV
|
||||
select INPUT_SPARSEKMAP
|
||||
---help---
|
||||
@ -731,6 +746,18 @@ config ACPI_CMPC
|
||||
keys as input device, backlight device, tablet and accelerometer
|
||||
devices.
|
||||
|
||||
config INTEL_HID_EVENT
|
||||
tristate "INTEL HID Event"
|
||||
depends on ACPI
|
||||
depends on INPUT
|
||||
select INPUT_SPARSEKMAP
|
||||
help
|
||||
This driver provides support for the Intel HID Event hotkey interface.
|
||||
Some laptops require this driver for hotkey support.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called intel_hid.
|
||||
|
||||
config INTEL_SCU_IPC
|
||||
bool "Intel SCU IPC Support"
|
||||
depends on X86_INTEL_MID
|
||||
@ -940,8 +967,25 @@ config INTEL_PMC_IPC
|
||||
with other entities in the CPU.
|
||||
|
||||
config SURFACE_PRO3_BUTTON
|
||||
tristate "Power/home/volume buttons driver for Microsoft Surface Pro 3 tablet"
|
||||
tristate "Power/home/volume buttons driver for Microsoft Surface Pro 3/4 tablet"
|
||||
depends on ACPI && INPUT
|
||||
---help---
|
||||
This driver handles the power/home/volume buttons on the Microsoft Surface Pro 3 tablet.
|
||||
This driver handles the power/home/volume buttons on the Microsoft Surface Pro 3/4 tablet.
|
||||
|
||||
config INTEL_PUNIT_IPC
|
||||
tristate "Intel P-Unit IPC Driver"
|
||||
---help---
|
||||
This driver provides support for Intel P-Unit Mailbox IPC mechanism,
|
||||
which is used to bridge the communications between kernel and P-Unit.
|
||||
|
||||
config INTEL_TELEMETRY
|
||||
tristate "Intel SoC Telemetry Driver"
|
||||
default n
|
||||
depends on INTEL_PMC_IPC && INTEL_PUNIT_IPC && X86_64
|
||||
---help---
|
||||
This driver provides interfaces to configure and use
|
||||
telemetry for INTEL SoC from APL onwards. It is also
|
||||
used to get various SoC events and parameters
|
||||
directly via debugfs files. Various tools may use
|
||||
this interface for SoC state monitoring.
|
||||
endif # X86_PLATFORM_DEVICES
|
||||
|
@ -5,6 +5,7 @@
|
||||
obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o
|
||||
obj-$(CONFIG_ASUS_WMI) += asus-wmi.o
|
||||
obj-$(CONFIG_ASUS_NB_WMI) += asus-nb-wmi.o
|
||||
obj-$(CONFIG_ASUS_WIRELESS) += asus-wireless.o
|
||||
obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o
|
||||
obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o
|
||||
obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
|
||||
@ -41,6 +42,7 @@ obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
|
||||
obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
|
||||
obj-$(CONFIG_TOSHIBA_HAPS) += toshiba_haps.o
|
||||
obj-$(CONFIG_TOSHIBA_WMI) += toshiba-wmi.o
|
||||
obj-$(CONFIG_INTEL_HID_EVENT) += intel-hid.o
|
||||
obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
|
||||
obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o
|
||||
obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o
|
||||
@ -62,3 +64,7 @@ obj-$(CONFIG_PVPANIC) += pvpanic.o
|
||||
obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o
|
||||
obj-$(CONFIG_INTEL_PMC_IPC) += intel_pmc_ipc.o
|
||||
obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o
|
||||
obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o
|
||||
obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \
|
||||
intel_telemetry_pltdrv.o \
|
||||
intel_telemetry_debugfs.o
|
||||
|
@ -701,18 +701,20 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
|
||||
gmux_data->gpe = -1;
|
||||
}
|
||||
|
||||
apple_gmux_data = gmux_data;
|
||||
init_completion(&gmux_data->powerchange_done);
|
||||
gmux_enable_interrupts(gmux_data);
|
||||
|
||||
if (vga_switcheroo_register_handler(&gmux_handler)) {
|
||||
ret = -ENODEV;
|
||||
goto err_register_handler;
|
||||
}
|
||||
|
||||
init_completion(&gmux_data->powerchange_done);
|
||||
apple_gmux_data = gmux_data;
|
||||
gmux_enable_interrupts(gmux_data);
|
||||
|
||||
return 0;
|
||||
|
||||
err_register_handler:
|
||||
gmux_disable_interrupts(gmux_data);
|
||||
apple_gmux_data = NULL;
|
||||
if (gmux_data->gpe >= 0)
|
||||
acpi_disable_gpe(NULL, gmux_data->gpe);
|
||||
err_enable_gpe:
|
||||
|
84
drivers/platform/x86/asus-wireless.c
Normal file
84
drivers/platform/x86/asus-wireless.c
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Asus Wireless Radio Control Driver
|
||||
*
|
||||
* Copyright (C) 2015-2016 Endless Mobile, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/pci_ids.h>
|
||||
|
||||
struct asus_wireless_data {
|
||||
struct input_dev *idev;
|
||||
};
|
||||
|
||||
static void asus_wireless_notify(struct acpi_device *adev, u32 event)
|
||||
{
|
||||
struct asus_wireless_data *data = acpi_driver_data(adev);
|
||||
|
||||
dev_dbg(&adev->dev, "event=%#x\n", event);
|
||||
if (event != 0x88) {
|
||||
dev_notice(&adev->dev, "Unknown ASHS event: %#x\n", event);
|
||||
return;
|
||||
}
|
||||
input_report_key(data->idev, KEY_RFKILL, 1);
|
||||
input_report_key(data->idev, KEY_RFKILL, 0);
|
||||
input_sync(data->idev);
|
||||
}
|
||||
|
||||
static int asus_wireless_add(struct acpi_device *adev)
|
||||
{
|
||||
struct asus_wireless_data *data;
|
||||
|
||||
data = devm_kzalloc(&adev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
adev->driver_data = data;
|
||||
|
||||
data->idev = devm_input_allocate_device(&adev->dev);
|
||||
if (!data->idev)
|
||||
return -ENOMEM;
|
||||
data->idev->name = "Asus Wireless Radio Control";
|
||||
data->idev->phys = "asus-wireless/input0";
|
||||
data->idev->id.bustype = BUS_HOST;
|
||||
data->idev->id.vendor = PCI_VENDOR_ID_ASUSTEK;
|
||||
set_bit(EV_KEY, data->idev->evbit);
|
||||
set_bit(KEY_RFKILL, data->idev->keybit);
|
||||
return input_register_device(data->idev);
|
||||
}
|
||||
|
||||
static int asus_wireless_remove(struct acpi_device *adev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id device_ids[] = {
|
||||
{"ATK4001", 0},
|
||||
{"ATK4002", 0},
|
||||
{"", 0},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, device_ids);
|
||||
|
||||
static struct acpi_driver asus_wireless_driver = {
|
||||
.name = "Asus Wireless Radio Control Driver",
|
||||
.class = "hotkey",
|
||||
.ids = device_ids,
|
||||
.ops = {
|
||||
.add = asus_wireless_add,
|
||||
.remove = asus_wireless_remove,
|
||||
.notify = asus_wireless_notify,
|
||||
},
|
||||
};
|
||||
module_acpi_driver(asus_wireless_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Asus Wireless Radio Control Driver");
|
||||
MODULE_AUTHOR("João Paulo Rechi Vita <jprvita@gmail.com>");
|
||||
MODULE_LICENSE("GPL");
|
@ -56,9 +56,6 @@ MODULE_AUTHOR("Corentin Chary <corentin.chary@gmail.com>, "
|
||||
MODULE_DESCRIPTION("Asus Generic WMI Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define to_platform_driver(drv) \
|
||||
(container_of((drv), struct platform_driver, driver))
|
||||
|
||||
#define to_asus_wmi_driver(pdrv) \
|
||||
(container_of((pdrv), struct asus_wmi_driver, platform_driver))
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
* Dell WMI hotkeys
|
||||
*
|
||||
* Copyright (C) 2008 Red Hat <mjg@redhat.com>
|
||||
* Copyright (C) 2014-2015 Pali Rohár <pali.rohar@gmail.com>
|
||||
*
|
||||
* Portions based on wistron_btns.c:
|
||||
* Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
|
||||
@ -38,12 +39,17 @@
|
||||
#include <acpi/video.h>
|
||||
|
||||
MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
|
||||
MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
|
||||
MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492"
|
||||
#define DELL_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492"
|
||||
|
||||
static u32 dell_wmi_interface_version;
|
||||
|
||||
MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
|
||||
MODULE_ALIAS("wmi:"DELL_DESCRIPTOR_GUID);
|
||||
|
||||
/*
|
||||
* Certain keys are flagged as KE_IGNORE. All of these are either
|
||||
@ -116,28 +122,48 @@ struct dell_bios_hotkey_table {
|
||||
|
||||
static const struct dell_bios_hotkey_table *dell_bios_hotkey_table;
|
||||
|
||||
/* Uninitialized entries here are KEY_RESERVED == 0. */
|
||||
static const u16 bios_to_linux_keycode[256] __initconst = {
|
||||
|
||||
KEY_MEDIA, KEY_NEXTSONG, KEY_PLAYPAUSE, KEY_PREVIOUSSONG,
|
||||
KEY_STOPCD, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
|
||||
KEY_WWW, KEY_UNKNOWN, KEY_VOLUMEDOWN, KEY_MUTE,
|
||||
KEY_VOLUMEUP, KEY_UNKNOWN, KEY_BATTERY, KEY_EJECTCD,
|
||||
KEY_UNKNOWN, KEY_SLEEP, KEY_PROG1, KEY_BRIGHTNESSDOWN,
|
||||
KEY_BRIGHTNESSUP, KEY_UNKNOWN, KEY_KBDILLUMTOGGLE,
|
||||
KEY_UNKNOWN, KEY_SWITCHVIDEOMODE, KEY_UNKNOWN, KEY_UNKNOWN,
|
||||
KEY_SWITCHVIDEOMODE, KEY_UNKNOWN, KEY_UNKNOWN, KEY_PROG2,
|
||||
KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
|
||||
KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_MICMUTE,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_PROG3
|
||||
[0] = KEY_MEDIA,
|
||||
[1] = KEY_NEXTSONG,
|
||||
[2] = KEY_PLAYPAUSE,
|
||||
[3] = KEY_PREVIOUSSONG,
|
||||
[4] = KEY_STOPCD,
|
||||
[5] = KEY_UNKNOWN,
|
||||
[6] = KEY_UNKNOWN,
|
||||
[7] = KEY_UNKNOWN,
|
||||
[8] = KEY_WWW,
|
||||
[9] = KEY_UNKNOWN,
|
||||
[10] = KEY_VOLUMEDOWN,
|
||||
[11] = KEY_MUTE,
|
||||
[12] = KEY_VOLUMEUP,
|
||||
[13] = KEY_UNKNOWN,
|
||||
[14] = KEY_BATTERY,
|
||||
[15] = KEY_EJECTCD,
|
||||
[16] = KEY_UNKNOWN,
|
||||
[17] = KEY_SLEEP,
|
||||
[18] = KEY_PROG1,
|
||||
[19] = KEY_BRIGHTNESSDOWN,
|
||||
[20] = KEY_BRIGHTNESSUP,
|
||||
[21] = KEY_UNKNOWN,
|
||||
[22] = KEY_KBDILLUMTOGGLE,
|
||||
[23] = KEY_UNKNOWN,
|
||||
[24] = KEY_SWITCHVIDEOMODE,
|
||||
[25] = KEY_UNKNOWN,
|
||||
[26] = KEY_UNKNOWN,
|
||||
[27] = KEY_SWITCHVIDEOMODE,
|
||||
[28] = KEY_UNKNOWN,
|
||||
[29] = KEY_UNKNOWN,
|
||||
[30] = KEY_PROG2,
|
||||
[31] = KEY_UNKNOWN,
|
||||
[32] = KEY_UNKNOWN,
|
||||
[33] = KEY_UNKNOWN,
|
||||
[34] = KEY_UNKNOWN,
|
||||
[35] = KEY_UNKNOWN,
|
||||
[36] = KEY_UNKNOWN,
|
||||
[37] = KEY_UNKNOWN,
|
||||
[38] = KEY_MICMUTE,
|
||||
[255] = KEY_PROG3,
|
||||
};
|
||||
|
||||
static struct input_dev *dell_wmi_input_dev;
|
||||
@ -149,7 +175,8 @@ static void dell_wmi_process_key(int reported_key)
|
||||
key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
|
||||
reported_key);
|
||||
if (!key) {
|
||||
pr_info("Unknown key %x pressed\n", reported_key);
|
||||
pr_info("Unknown key with scancode 0x%x pressed\n",
|
||||
reported_key);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -210,6 +237,22 @@ static void dell_wmi_notify(u32 value, void *context)
|
||||
|
||||
buffer_end = buffer_entry + buffer_size;
|
||||
|
||||
/*
|
||||
* BIOS/ACPI on devices with WMI interface version 0 does not clear
|
||||
* buffer before filling it. So next time when BIOS/ACPI send WMI event
|
||||
* which is smaller as previous then it contains garbage in buffer from
|
||||
* previous event.
|
||||
*
|
||||
* BIOS/ACPI on devices with WMI interface version 1 clears buffer and
|
||||
* sometimes send more events in buffer at one call.
|
||||
*
|
||||
* So to prevent reading garbage from buffer we will process only first
|
||||
* one event on devices with WMI interface version 0.
|
||||
*/
|
||||
if (dell_wmi_interface_version == 0 && buffer_entry < buffer_end)
|
||||
if (buffer_end > buffer_entry + buffer_entry[0] + 1)
|
||||
buffer_end = buffer_entry + buffer_entry[0] + 1;
|
||||
|
||||
while (buffer_entry < buffer_end) {
|
||||
|
||||
len = buffer_entry[0];
|
||||
@ -308,9 +351,23 @@ static const struct key_entry * __init dell_wmi_prepare_new_keymap(void)
|
||||
for (i = 0; i < hotkey_num; i++) {
|
||||
const struct dell_bios_keymap_entry *bios_entry =
|
||||
&dell_bios_hotkey_table->keymap[i];
|
||||
u16 keycode = bios_entry->keycode < 256 ?
|
||||
bios_to_linux_keycode[bios_entry->keycode] :
|
||||
KEY_RESERVED;
|
||||
|
||||
/* Uninitialized entries are 0 aka KEY_RESERVED. */
|
||||
u16 keycode = (bios_entry->keycode <
|
||||
ARRAY_SIZE(bios_to_linux_keycode)) ?
|
||||
bios_to_linux_keycode[bios_entry->keycode] :
|
||||
KEY_RESERVED;
|
||||
|
||||
/*
|
||||
* Log if we find an entry in the DMI table that we don't
|
||||
* understand. If this happens, we should figure out what
|
||||
* the entry means and add it to bios_to_linux_keycode.
|
||||
*/
|
||||
if (keycode == KEY_RESERVED) {
|
||||
pr_info("firmware scancode 0x%x maps to unrecognized keycode 0x%x\n",
|
||||
bios_entry->scancode, bios_entry->keycode);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (keycode == KEY_KBDILLUMTOGGLE)
|
||||
keymap[i].type = KE_IGNORE;
|
||||
@ -386,16 +443,87 @@ static void __init find_hk_type(const struct dmi_header *dm, void *dummy)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Descriptor buffer is 128 byte long and contains:
|
||||
*
|
||||
* Name Offset Length Value
|
||||
* Vendor Signature 0 4 "DELL"
|
||||
* Object Signature 4 4 " WMI"
|
||||
* WMI Interface Version 8 4 <version>
|
||||
* WMI buffer length 12 4 4096
|
||||
*/
|
||||
static int __init dell_wmi_check_descriptor_buffer(void)
|
||||
{
|
||||
struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
u32 *buffer;
|
||||
|
||||
status = wmi_query_block(DELL_DESCRIPTOR_GUID, 0, &out);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
pr_err("Cannot read Dell descriptor buffer - %d\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
obj = (union acpi_object *)out.pointer;
|
||||
if (!obj) {
|
||||
pr_err("Dell descriptor buffer is empty\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (obj->type != ACPI_TYPE_BUFFER) {
|
||||
pr_err("Cannot read Dell descriptor buffer\n");
|
||||
kfree(obj);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (obj->buffer.length != 128) {
|
||||
pr_err("Dell descriptor buffer has invalid length (%d)\n",
|
||||
obj->buffer.length);
|
||||
if (obj->buffer.length < 16) {
|
||||
kfree(obj);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
buffer = (u32 *)obj->buffer.pointer;
|
||||
|
||||
if (buffer[0] != 0x4C4C4544 && buffer[1] != 0x494D5720)
|
||||
pr_warn("Dell descriptor buffer has invalid signature (%*ph)\n",
|
||||
8, buffer);
|
||||
|
||||
if (buffer[2] != 0 && buffer[2] != 1)
|
||||
pr_warn("Dell descriptor buffer has unknown version (%d)\n",
|
||||
buffer[2]);
|
||||
|
||||
if (buffer[3] != 4096)
|
||||
pr_warn("Dell descriptor buffer has invalid buffer length (%d)\n",
|
||||
buffer[3]);
|
||||
|
||||
dell_wmi_interface_version = buffer[2];
|
||||
|
||||
pr_info("Detected Dell WMI interface version %u\n",
|
||||
dell_wmi_interface_version);
|
||||
|
||||
kfree(obj);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init dell_wmi_init(void)
|
||||
{
|
||||
int err;
|
||||
acpi_status status;
|
||||
|
||||
if (!wmi_has_guid(DELL_EVENT_GUID)) {
|
||||
pr_warn("No known WMI GUID found\n");
|
||||
if (!wmi_has_guid(DELL_EVENT_GUID) ||
|
||||
!wmi_has_guid(DELL_DESCRIPTOR_GUID)) {
|
||||
pr_warn("Dell WMI GUID were not found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = dell_wmi_check_descriptor_buffer();
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dmi_walk(find_hk_type, NULL);
|
||||
|
||||
err = dell_wmi_input_setup();
|
||||
|
@ -864,6 +864,13 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo G50-30"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Lenovo ideapad Y700-17ISK",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700-17ISK"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Lenovo Yoga 2 11 / 13 / Pro",
|
||||
.matches = {
|
||||
|
289
drivers/platform/x86/intel-hid.c
Normal file
289
drivers/platform/x86/intel-hid.c
Normal file
@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Intel HID event driver for Windows 8
|
||||
*
|
||||
* Copyright (C) 2015 Alex Hung <alex.hung@canonical.com>
|
||||
* Copyright (C) 2015 Andrew Lutomirski <luto@kernel.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <acpi/acpi_bus.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Alex Hung");
|
||||
|
||||
static const struct acpi_device_id intel_hid_ids[] = {
|
||||
{"INT33D5", 0},
|
||||
{"", 0},
|
||||
};
|
||||
|
||||
/* In theory, these are HID usages. */
|
||||
static const struct key_entry intel_hid_keymap[] = {
|
||||
/* 1: LSuper (Page 0x07, usage 0xE3) -- unclear what to do */
|
||||
/* 2: Toggle SW_ROTATE_LOCK -- easy to implement if seen in wild */
|
||||
{ KE_KEY, 3, { KEY_NUMLOCK } },
|
||||
{ KE_KEY, 4, { KEY_HOME } },
|
||||
{ KE_KEY, 5, { KEY_END } },
|
||||
{ KE_KEY, 6, { KEY_PAGEUP } },
|
||||
{ KE_KEY, 4, { KEY_PAGEDOWN } },
|
||||
{ KE_KEY, 4, { KEY_HOME } },
|
||||
{ KE_KEY, 8, { KEY_RFKILL } },
|
||||
{ KE_KEY, 9, { KEY_POWER } },
|
||||
{ KE_KEY, 11, { KEY_SLEEP } },
|
||||
/* 13 has two different meanings in the spec -- ignore it. */
|
||||
{ KE_KEY, 14, { KEY_STOPCD } },
|
||||
{ KE_KEY, 15, { KEY_PLAYPAUSE } },
|
||||
{ KE_KEY, 16, { KEY_MUTE } },
|
||||
{ KE_KEY, 17, { KEY_VOLUMEUP } },
|
||||
{ KE_KEY, 18, { KEY_VOLUMEDOWN } },
|
||||
{ KE_KEY, 19, { KEY_BRIGHTNESSUP } },
|
||||
{ KE_KEY, 20, { KEY_BRIGHTNESSDOWN } },
|
||||
/* 27: wake -- needs special handling */
|
||||
{ KE_END },
|
||||
};
|
||||
|
||||
struct intel_hid_priv {
|
||||
struct input_dev *input_dev;
|
||||
};
|
||||
|
||||
static int intel_hid_set_enable(struct device *device, int enable)
|
||||
{
|
||||
union acpi_object arg0 = { ACPI_TYPE_INTEGER };
|
||||
struct acpi_object_list args = { 1, &arg0 };
|
||||
acpi_status status;
|
||||
|
||||
arg0.integer.value = enable;
|
||||
status = acpi_evaluate_object(ACPI_HANDLE(device), "HDSM", &args, NULL);
|
||||
if (!ACPI_SUCCESS(status)) {
|
||||
dev_warn(device, "failed to %sable hotkeys\n",
|
||||
enable ? "en" : "dis");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_hid_pl_suspend_handler(struct device *device)
|
||||
{
|
||||
intel_hid_set_enable(device, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_hid_pl_resume_handler(struct device *device)
|
||||
{
|
||||
intel_hid_set_enable(device, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops intel_hid_pl_pm_ops = {
|
||||
.suspend = intel_hid_pl_suspend_handler,
|
||||
.resume = intel_hid_pl_resume_handler,
|
||||
};
|
||||
|
||||
static int intel_hid_input_setup(struct platform_device *device)
|
||||
{
|
||||
struct intel_hid_priv *priv = dev_get_drvdata(&device->dev);
|
||||
int ret;
|
||||
|
||||
priv->input_dev = input_allocate_device();
|
||||
if (!priv->input_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = sparse_keymap_setup(priv->input_dev, intel_hid_keymap, NULL);
|
||||
if (ret)
|
||||
goto err_free_device;
|
||||
|
||||
priv->input_dev->dev.parent = &device->dev;
|
||||
priv->input_dev->name = "Intel HID events";
|
||||
priv->input_dev->id.bustype = BUS_HOST;
|
||||
set_bit(KEY_RFKILL, priv->input_dev->keybit);
|
||||
|
||||
ret = input_register_device(priv->input_dev);
|
||||
if (ret)
|
||||
goto err_free_device;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_device:
|
||||
input_free_device(priv->input_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void intel_hid_input_destroy(struct platform_device *device)
|
||||
{
|
||||
struct intel_hid_priv *priv = dev_get_drvdata(&device->dev);
|
||||
|
||||
input_unregister_device(priv->input_dev);
|
||||
}
|
||||
|
||||
static void notify_handler(acpi_handle handle, u32 event, void *context)
|
||||
{
|
||||
struct platform_device *device = context;
|
||||
struct intel_hid_priv *priv = dev_get_drvdata(&device->dev);
|
||||
unsigned long long ev_index;
|
||||
acpi_status status;
|
||||
|
||||
/* The platform spec only defines one event code: 0xC0. */
|
||||
if (event != 0xc0) {
|
||||
dev_warn(&device->dev, "received unknown event (0x%x)\n",
|
||||
event);
|
||||
return;
|
||||
}
|
||||
|
||||
status = acpi_evaluate_integer(handle, "HDEM", NULL, &ev_index);
|
||||
if (!ACPI_SUCCESS(status)) {
|
||||
dev_warn(&device->dev, "failed to get event index\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sparse_keymap_report_event(priv->input_dev, ev_index, 1, true))
|
||||
dev_info(&device->dev, "unknown event index 0x%llx\n",
|
||||
ev_index);
|
||||
}
|
||||
|
||||
static int intel_hid_probe(struct platform_device *device)
|
||||
{
|
||||
acpi_handle handle = ACPI_HANDLE(&device->dev);
|
||||
struct intel_hid_priv *priv;
|
||||
unsigned long long mode;
|
||||
acpi_status status;
|
||||
int err;
|
||||
|
||||
status = acpi_evaluate_integer(handle, "HDMM", NULL, &mode);
|
||||
if (!ACPI_SUCCESS(status)) {
|
||||
dev_warn(&device->dev, "failed to read mode\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (mode != 0) {
|
||||
/*
|
||||
* This driver only implements "simple" mode. There appear
|
||||
* to be no other modes, but we should be paranoid and check
|
||||
* for compatibility.
|
||||
*/
|
||||
dev_info(&device->dev, "platform is not in simple mode\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
priv = devm_kzalloc(&device->dev,
|
||||
sizeof(struct intel_hid_priv *), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
dev_set_drvdata(&device->dev, priv);
|
||||
|
||||
err = intel_hid_input_setup(device);
|
||||
if (err) {
|
||||
pr_err("Failed to setup Intel HID hotkeys\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
status = acpi_install_notify_handler(handle,
|
||||
ACPI_DEVICE_NOTIFY,
|
||||
notify_handler,
|
||||
device);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
err = -EBUSY;
|
||||
goto err_remove_input;
|
||||
}
|
||||
|
||||
err = intel_hid_set_enable(&device->dev, 1);
|
||||
if (err)
|
||||
goto err_remove_notify;
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_notify:
|
||||
acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
|
||||
|
||||
err_remove_input:
|
||||
intel_hid_input_destroy(device);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int intel_hid_remove(struct platform_device *device)
|
||||
{
|
||||
acpi_handle handle = ACPI_HANDLE(&device->dev);
|
||||
|
||||
acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
|
||||
intel_hid_input_destroy(device);
|
||||
intel_hid_set_enable(&device->dev, 0);
|
||||
acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
|
||||
|
||||
/*
|
||||
* Even if we failed to shut off the event stream, we can still
|
||||
* safely detach from the device.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver intel_hid_pl_driver = {
|
||||
.driver = {
|
||||
.name = "intel-hid",
|
||||
.acpi_match_table = intel_hid_ids,
|
||||
.pm = &intel_hid_pl_pm_ops,
|
||||
},
|
||||
.probe = intel_hid_probe,
|
||||
.remove = intel_hid_remove,
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, intel_hid_ids);
|
||||
|
||||
/*
|
||||
* Unfortunately, some laptops provide a _HID="INT33D5" device with
|
||||
* _CID="PNP0C02". This causes the pnpacpi scan driver to claim the
|
||||
* ACPI node, so no platform device will be created. The pnpacpi
|
||||
* driver rejects this device in subsequent processing, so no physical
|
||||
* node is created at all.
|
||||
*
|
||||
* As a workaround until the ACPI core figures out how to handle
|
||||
* this corner case, manually ask the ACPI platform device code to
|
||||
* claim the ACPI node.
|
||||
*/
|
||||
static acpi_status __init
|
||||
check_acpi_dev(acpi_handle handle, u32 lvl, void *context, void **rv)
|
||||
{
|
||||
const struct acpi_device_id *ids = context;
|
||||
struct acpi_device *dev;
|
||||
|
||||
if (acpi_bus_get_device(handle, &dev) != 0)
|
||||
return AE_OK;
|
||||
|
||||
if (acpi_match_device_ids(dev, ids) == 0)
|
||||
if (acpi_create_platform_device(dev))
|
||||
dev_info(&dev->dev,
|
||||
"intel-hid: created platform device\n");
|
||||
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static int __init intel_hid_init(void)
|
||||
{
|
||||
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
||||
ACPI_UINT32_MAX, check_acpi_dev, NULL,
|
||||
(void *)intel_hid_ids, NULL);
|
||||
|
||||
return platform_driver_register(&intel_hid_pl_driver);
|
||||
}
|
||||
module_init(intel_hid_init);
|
||||
|
||||
static void __exit intel_hid_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&intel_hid_pl_driver);
|
||||
}
|
||||
module_exit(intel_hid_exit);
|
@ -68,8 +68,13 @@
|
||||
#define PLAT_RESOURCE_IPC_INDEX 0
|
||||
#define PLAT_RESOURCE_IPC_SIZE 0x1000
|
||||
#define PLAT_RESOURCE_GCR_SIZE 0x1000
|
||||
#define PLAT_RESOURCE_PUNIT_DATA_INDEX 1
|
||||
#define PLAT_RESOURCE_PUNIT_INTER_INDEX 2
|
||||
#define PLAT_RESOURCE_BIOS_DATA_INDEX 1
|
||||
#define PLAT_RESOURCE_BIOS_IFACE_INDEX 2
|
||||
#define PLAT_RESOURCE_TELEM_SSRAM_INDEX 3
|
||||
#define PLAT_RESOURCE_ISP_DATA_INDEX 4
|
||||
#define PLAT_RESOURCE_ISP_IFACE_INDEX 5
|
||||
#define PLAT_RESOURCE_GTD_DATA_INDEX 6
|
||||
#define PLAT_RESOURCE_GTD_IFACE_INDEX 7
|
||||
#define PLAT_RESOURCE_ACPI_IO_INDEX 0
|
||||
|
||||
/*
|
||||
@ -84,6 +89,10 @@
|
||||
#define TCO_BASE_OFFSET 0x60
|
||||
#define TCO_REGS_SIZE 16
|
||||
#define PUNIT_DEVICE_NAME "intel_punit_ipc"
|
||||
#define TELEMETRY_DEVICE_NAME "intel_telemetry"
|
||||
#define TELEM_SSRAM_SIZE 240
|
||||
#define TELEM_PMC_SSRAM_OFFSET 0x1B00
|
||||
#define TELEM_PUNIT_SSRAM_OFFSET 0x1A00
|
||||
|
||||
static const int iTCO_version = 3;
|
||||
|
||||
@ -105,11 +114,15 @@ static struct intel_pmc_ipc_dev {
|
||||
int gcr_size;
|
||||
|
||||
/* punit */
|
||||
resource_size_t punit_base;
|
||||
int punit_size;
|
||||
resource_size_t punit_base2;
|
||||
int punit_size2;
|
||||
struct platform_device *punit_dev;
|
||||
|
||||
/* Telemetry */
|
||||
resource_size_t telem_pmc_ssram_base;
|
||||
resource_size_t telem_punit_ssram_base;
|
||||
int telem_pmc_ssram_size;
|
||||
int telem_punit_ssram_size;
|
||||
u8 telem_res_inval;
|
||||
struct platform_device *telemetry_dev;
|
||||
} ipcdev;
|
||||
|
||||
static char *ipc_err_sources[] = {
|
||||
@ -444,9 +457,22 @@ static const struct attribute_group intel_ipc_group = {
|
||||
.attrs = intel_ipc_attrs,
|
||||
};
|
||||
|
||||
#define PUNIT_RESOURCE_INTER 1
|
||||
static struct resource punit_res[] = {
|
||||
/* Punit */
|
||||
static struct resource punit_res_array[] = {
|
||||
/* Punit BIOS */
|
||||
{
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
{
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
/* Punit ISP */
|
||||
{
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
{
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
/* Punit GTD */
|
||||
{
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
@ -478,10 +504,21 @@ static struct itco_wdt_platform_data tco_info = {
|
||||
.version = 3,
|
||||
};
|
||||
|
||||
#define TELEMETRY_RESOURCE_PUNIT_SSRAM 0
|
||||
#define TELEMETRY_RESOURCE_PMC_SSRAM 1
|
||||
static struct resource telemetry_res[] = {
|
||||
/*Telemetry*/
|
||||
{
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
{
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
static int ipc_create_punit_device(void)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
pdev = platform_device_alloc(PUNIT_DEVICE_NAME, -1);
|
||||
@ -491,17 +528,8 @@ static int ipc_create_punit_device(void)
|
||||
}
|
||||
|
||||
pdev->dev.parent = ipcdev.dev;
|
||||
|
||||
res = punit_res;
|
||||
res->start = ipcdev.punit_base;
|
||||
res->end = res->start + ipcdev.punit_size - 1;
|
||||
|
||||
res = punit_res + PUNIT_RESOURCE_INTER;
|
||||
res->start = ipcdev.punit_base2;
|
||||
res->end = res->start + ipcdev.punit_size2 - 1;
|
||||
|
||||
ret = platform_device_add_resources(pdev, punit_res,
|
||||
ARRAY_SIZE(punit_res));
|
||||
ret = platform_device_add_resources(pdev, punit_res_array,
|
||||
ARRAY_SIZE(punit_res_array));
|
||||
if (ret) {
|
||||
dev_err(ipcdev.dev, "Failed to add platform punit resources\n");
|
||||
goto err;
|
||||
@ -571,6 +599,51 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ipc_create_telemetry_device(void)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
pdev = platform_device_alloc(TELEMETRY_DEVICE_NAME, -1);
|
||||
if (!pdev) {
|
||||
dev_err(ipcdev.dev,
|
||||
"Failed to allocate telemetry platform device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pdev->dev.parent = ipcdev.dev;
|
||||
|
||||
res = telemetry_res + TELEMETRY_RESOURCE_PUNIT_SSRAM;
|
||||
res->start = ipcdev.telem_punit_ssram_base;
|
||||
res->end = res->start + ipcdev.telem_punit_ssram_size - 1;
|
||||
|
||||
res = telemetry_res + TELEMETRY_RESOURCE_PMC_SSRAM;
|
||||
res->start = ipcdev.telem_pmc_ssram_base;
|
||||
res->end = res->start + ipcdev.telem_pmc_ssram_size - 1;
|
||||
|
||||
ret = platform_device_add_resources(pdev, telemetry_res,
|
||||
ARRAY_SIZE(telemetry_res));
|
||||
if (ret) {
|
||||
dev_err(ipcdev.dev,
|
||||
"Failed to add telemetry platform resources\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = platform_device_add(pdev);
|
||||
if (ret) {
|
||||
dev_err(ipcdev.dev,
|
||||
"Failed to add telemetry platform device\n");
|
||||
goto err;
|
||||
}
|
||||
ipcdev.telemetry_dev = pdev;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
platform_device_put(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ipc_create_pmc_devices(void)
|
||||
{
|
||||
int ret;
|
||||
@ -585,12 +658,20 @@ static int ipc_create_pmc_devices(void)
|
||||
dev_err(ipcdev.dev, "Failed to add punit platform device\n");
|
||||
platform_device_unregister(ipcdev.tco_dev);
|
||||
}
|
||||
|
||||
if (!ipcdev.telem_res_inval) {
|
||||
ret = ipc_create_telemetry_device();
|
||||
if (ret)
|
||||
dev_warn(ipcdev.dev,
|
||||
"Failed to add telemetry platform device\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ipc_plat_get_res(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct resource *res, *punit_res;
|
||||
void __iomem *addr;
|
||||
int size;
|
||||
|
||||
@ -603,32 +684,68 @@ static int ipc_plat_get_res(struct platform_device *pdev)
|
||||
size = resource_size(res);
|
||||
ipcdev.acpi_io_base = res->start;
|
||||
ipcdev.acpi_io_size = size;
|
||||
dev_info(&pdev->dev, "io res: %llx %x\n",
|
||||
(long long)res->start, (int)resource_size(res));
|
||||
dev_info(&pdev->dev, "io res: %pR\n", res);
|
||||
|
||||
/* This is index 0 to cover BIOS data register */
|
||||
punit_res = punit_res_array;
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_PUNIT_DATA_INDEX);
|
||||
PLAT_RESOURCE_BIOS_DATA_INDEX);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get punit resource\n");
|
||||
dev_err(&pdev->dev, "Failed to get res of punit BIOS data\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
size = resource_size(res);
|
||||
ipcdev.punit_base = res->start;
|
||||
ipcdev.punit_size = size;
|
||||
dev_info(&pdev->dev, "punit data res: %llx %x\n",
|
||||
(long long)res->start, (int)resource_size(res));
|
||||
*punit_res = *res;
|
||||
dev_info(&pdev->dev, "punit BIOS data res: %pR\n", res);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_PUNIT_INTER_INDEX);
|
||||
PLAT_RESOURCE_BIOS_IFACE_INDEX);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get punit inter resource\n");
|
||||
dev_err(&pdev->dev, "Failed to get res of punit BIOS iface\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
size = resource_size(res);
|
||||
ipcdev.punit_base2 = res->start;
|
||||
ipcdev.punit_size2 = size;
|
||||
dev_info(&pdev->dev, "punit interface res: %llx %x\n",
|
||||
(long long)res->start, (int)resource_size(res));
|
||||
/* This is index 1 to cover BIOS interface register */
|
||||
*++punit_res = *res;
|
||||
dev_info(&pdev->dev, "punit BIOS interface res: %pR\n", res);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_ISP_DATA_INDEX);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get res of punit ISP data\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
/* This is index 2 to cover ISP data register */
|
||||
*++punit_res = *res;
|
||||
dev_info(&pdev->dev, "punit ISP data res: %pR\n", res);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_ISP_IFACE_INDEX);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get res of punit ISP iface\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
/* This is index 3 to cover ISP interface register */
|
||||
*++punit_res = *res;
|
||||
dev_info(&pdev->dev, "punit ISP interface res: %pR\n", res);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_GTD_DATA_INDEX);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get res of punit GTD data\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
/* This is index 4 to cover GTD data register */
|
||||
*++punit_res = *res;
|
||||
dev_info(&pdev->dev, "punit GTD data res: %pR\n", res);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_GTD_IFACE_INDEX);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get res of punit GTD iface\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
/* This is index 5 to cover GTD interface register */
|
||||
*++punit_res = *res;
|
||||
dev_info(&pdev->dev, "punit GTD interface res: %pR\n", res);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_IPC_INDEX);
|
||||
@ -651,8 +768,23 @@ static int ipc_plat_get_res(struct platform_device *pdev)
|
||||
|
||||
ipcdev.gcr_base = res->start + size;
|
||||
ipcdev.gcr_size = PLAT_RESOURCE_GCR_SIZE;
|
||||
dev_info(&pdev->dev, "ipc res: %llx %x\n",
|
||||
(long long)res->start, (int)resource_size(res));
|
||||
dev_info(&pdev->dev, "ipc res: %pR\n", res);
|
||||
|
||||
ipcdev.telem_res_inval = 0;
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_TELEM_SSRAM_INDEX);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get telemetry ssram resource\n");
|
||||
ipcdev.telem_res_inval = 1;
|
||||
} else {
|
||||
ipcdev.telem_punit_ssram_base = res->start +
|
||||
TELEM_PUNIT_SSRAM_OFFSET;
|
||||
ipcdev.telem_punit_ssram_size = TELEM_SSRAM_SIZE;
|
||||
ipcdev.telem_pmc_ssram_base = res->start +
|
||||
TELEM_PMC_SSRAM_OFFSET;
|
||||
ipcdev.telem_pmc_ssram_size = TELEM_SSRAM_SIZE;
|
||||
dev_info(&pdev->dev, "telemetry ssram res: %pR\n", res);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -711,6 +843,7 @@ err_sys:
|
||||
err_irq:
|
||||
platform_device_unregister(ipcdev.tco_dev);
|
||||
platform_device_unregister(ipcdev.punit_dev);
|
||||
platform_device_unregister(ipcdev.telemetry_dev);
|
||||
err_device:
|
||||
iounmap(ipcdev.ipc_base);
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
@ -728,6 +861,7 @@ static int ipc_plat_remove(struct platform_device *pdev)
|
||||
free_irq(ipcdev.irq, &ipcdev);
|
||||
platform_device_unregister(ipcdev.tco_dev);
|
||||
platform_device_unregister(ipcdev.punit_dev);
|
||||
platform_device_unregister(ipcdev.telemetry_dev);
|
||||
iounmap(ipcdev.ipc_base);
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_IPC_INDEX);
|
||||
|
342
drivers/platform/x86/intel_punit_ipc.c
Normal file
342
drivers/platform/x86/intel_punit_ipc.c
Normal file
@ -0,0 +1,342 @@
|
||||
/*
|
||||
* Driver for the Intel P-Unit Mailbox IPC mechanism
|
||||
*
|
||||
* (C) Copyright 2015 Intel Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* The heart of the P-Unit is the Foxton microcontroller and its firmware,
|
||||
* which provide mailbox interface for power management usage.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <asm/intel_punit_ipc.h>
|
||||
|
||||
/* IPC Mailbox registers */
|
||||
#define OFFSET_DATA_LOW 0x0
|
||||
#define OFFSET_DATA_HIGH 0x4
|
||||
/* bit field of interface register */
|
||||
#define CMD_RUN BIT(31)
|
||||
#define CMD_ERRCODE_MASK GENMASK(7, 0)
|
||||
#define CMD_PARA1_SHIFT 8
|
||||
#define CMD_PARA2_SHIFT 16
|
||||
|
||||
#define CMD_TIMEOUT_SECONDS 1
|
||||
|
||||
enum {
|
||||
BASE_DATA = 0,
|
||||
BASE_IFACE,
|
||||
BASE_MAX,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct device *dev;
|
||||
struct mutex lock;
|
||||
int irq;
|
||||
struct completion cmd_complete;
|
||||
/* base of interface and data registers */
|
||||
void __iomem *base[RESERVED_IPC][BASE_MAX];
|
||||
IPC_TYPE type;
|
||||
} IPC_DEV;
|
||||
|
||||
static IPC_DEV *punit_ipcdev;
|
||||
|
||||
static inline u32 ipc_read_status(IPC_DEV *ipcdev, IPC_TYPE type)
|
||||
{
|
||||
return readl(ipcdev->base[type][BASE_IFACE]);
|
||||
}
|
||||
|
||||
static inline void ipc_write_cmd(IPC_DEV *ipcdev, IPC_TYPE type, u32 cmd)
|
||||
{
|
||||
writel(cmd, ipcdev->base[type][BASE_IFACE]);
|
||||
}
|
||||
|
||||
static inline u32 ipc_read_data_low(IPC_DEV *ipcdev, IPC_TYPE type)
|
||||
{
|
||||
return readl(ipcdev->base[type][BASE_DATA] + OFFSET_DATA_LOW);
|
||||
}
|
||||
|
||||
static inline u32 ipc_read_data_high(IPC_DEV *ipcdev, IPC_TYPE type)
|
||||
{
|
||||
return readl(ipcdev->base[type][BASE_DATA] + OFFSET_DATA_HIGH);
|
||||
}
|
||||
|
||||
static inline void ipc_write_data_low(IPC_DEV *ipcdev, IPC_TYPE type, u32 data)
|
||||
{
|
||||
writel(data, ipcdev->base[type][BASE_DATA] + OFFSET_DATA_LOW);
|
||||
}
|
||||
|
||||
static inline void ipc_write_data_high(IPC_DEV *ipcdev, IPC_TYPE type, u32 data)
|
||||
{
|
||||
writel(data, ipcdev->base[type][BASE_DATA] + OFFSET_DATA_HIGH);
|
||||
}
|
||||
|
||||
static const char *ipc_err_string(int error)
|
||||
{
|
||||
if (error == IPC_PUNIT_ERR_SUCCESS)
|
||||
return "no error";
|
||||
else if (error == IPC_PUNIT_ERR_INVALID_CMD)
|
||||
return "invalid command";
|
||||
else if (error == IPC_PUNIT_ERR_INVALID_PARAMETER)
|
||||
return "invalid parameter";
|
||||
else if (error == IPC_PUNIT_ERR_CMD_TIMEOUT)
|
||||
return "command timeout";
|
||||
else if (error == IPC_PUNIT_ERR_CMD_LOCKED)
|
||||
return "command locked";
|
||||
else if (error == IPC_PUNIT_ERR_INVALID_VR_ID)
|
||||
return "invalid vr id";
|
||||
else if (error == IPC_PUNIT_ERR_VR_ERR)
|
||||
return "vr error";
|
||||
else
|
||||
return "unknown error";
|
||||
}
|
||||
|
||||
static int intel_punit_ipc_check_status(IPC_DEV *ipcdev, IPC_TYPE type)
|
||||
{
|
||||
int loops = CMD_TIMEOUT_SECONDS * USEC_PER_SEC;
|
||||
int errcode;
|
||||
int status;
|
||||
|
||||
if (ipcdev->irq) {
|
||||
if (!wait_for_completion_timeout(&ipcdev->cmd_complete,
|
||||
CMD_TIMEOUT_SECONDS * HZ)) {
|
||||
dev_err(ipcdev->dev, "IPC timed out\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
} else {
|
||||
while ((ipc_read_status(ipcdev, type) & CMD_RUN) && --loops)
|
||||
udelay(1);
|
||||
if (!loops) {
|
||||
dev_err(ipcdev->dev, "IPC timed out\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
status = ipc_read_status(ipcdev, type);
|
||||
errcode = status & CMD_ERRCODE_MASK;
|
||||
if (errcode) {
|
||||
dev_err(ipcdev->dev, "IPC failed: %s, IPC_STS=0x%x\n",
|
||||
ipc_err_string(errcode), status);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_punit_ipc_simple_command() - Simple IPC command
|
||||
* @cmd: IPC command code.
|
||||
* @para1: First 8bit parameter, set 0 if not used.
|
||||
* @para2: Second 8bit parameter, set 0 if not used.
|
||||
*
|
||||
* Send a IPC command to P-Unit when there is no data transaction
|
||||
*
|
||||
* Return: IPC error code or 0 on success.
|
||||
*/
|
||||
int intel_punit_ipc_simple_command(int cmd, int para1, int para2)
|
||||
{
|
||||
IPC_DEV *ipcdev = punit_ipcdev;
|
||||
IPC_TYPE type;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ipcdev->lock);
|
||||
|
||||
reinit_completion(&ipcdev->cmd_complete);
|
||||
type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET;
|
||||
|
||||
val = cmd & ~IPC_PUNIT_CMD_TYPE_MASK;
|
||||
val |= CMD_RUN | para2 << CMD_PARA2_SHIFT | para1 << CMD_PARA1_SHIFT;
|
||||
ipc_write_cmd(ipcdev, type, val);
|
||||
ret = intel_punit_ipc_check_status(ipcdev, type);
|
||||
|
||||
mutex_unlock(&ipcdev->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(intel_punit_ipc_simple_command);
|
||||
|
||||
/**
|
||||
* intel_punit_ipc_command() - IPC command with data and pointers
|
||||
* @cmd: IPC command code.
|
||||
* @para1: First 8bit parameter, set 0 if not used.
|
||||
* @para2: Second 8bit parameter, set 0 if not used.
|
||||
* @in: Input data, 32bit for BIOS cmd, two 32bit for GTD and ISPD.
|
||||
* @out: Output data.
|
||||
*
|
||||
* Send a IPC command to P-Unit with data transaction
|
||||
*
|
||||
* Return: IPC error code or 0 on success.
|
||||
*/
|
||||
int intel_punit_ipc_command(u32 cmd, u32 para1, u32 para2, u32 *in, u32 *out)
|
||||
{
|
||||
IPC_DEV *ipcdev = punit_ipcdev;
|
||||
IPC_TYPE type;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ipcdev->lock);
|
||||
|
||||
reinit_completion(&ipcdev->cmd_complete);
|
||||
type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET;
|
||||
|
||||
if (in) {
|
||||
ipc_write_data_low(ipcdev, type, *in);
|
||||
if (type == GTDRIVER_IPC || type == ISPDRIVER_IPC)
|
||||
ipc_write_data_high(ipcdev, type, *++in);
|
||||
}
|
||||
|
||||
val = cmd & ~IPC_PUNIT_CMD_TYPE_MASK;
|
||||
val |= CMD_RUN | para2 << CMD_PARA2_SHIFT | para1 << CMD_PARA1_SHIFT;
|
||||
ipc_write_cmd(ipcdev, type, val);
|
||||
|
||||
ret = intel_punit_ipc_check_status(ipcdev, type);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (out) {
|
||||
*out = ipc_read_data_low(ipcdev, type);
|
||||
if (type == GTDRIVER_IPC || type == ISPDRIVER_IPC)
|
||||
*++out = ipc_read_data_high(ipcdev, type);
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&ipcdev->lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_punit_ipc_command);
|
||||
|
||||
static irqreturn_t intel_punit_ioc(int irq, void *dev_id)
|
||||
{
|
||||
IPC_DEV *ipcdev = dev_id;
|
||||
|
||||
complete(&ipcdev->cmd_complete);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int intel_punit_get_bars(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
void __iomem *addr;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
addr = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(addr))
|
||||
return PTR_ERR(addr);
|
||||
punit_ipcdev->base[BIOS_IPC][BASE_DATA] = addr;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
addr = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(addr))
|
||||
return PTR_ERR(addr);
|
||||
punit_ipcdev->base[BIOS_IPC][BASE_IFACE] = addr;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
|
||||
addr = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(addr))
|
||||
return PTR_ERR(addr);
|
||||
punit_ipcdev->base[ISPDRIVER_IPC][BASE_DATA] = addr;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
|
||||
addr = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(addr))
|
||||
return PTR_ERR(addr);
|
||||
punit_ipcdev->base[ISPDRIVER_IPC][BASE_IFACE] = addr;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 4);
|
||||
addr = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(addr))
|
||||
return PTR_ERR(addr);
|
||||
punit_ipcdev->base[GTDRIVER_IPC][BASE_DATA] = addr;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 5);
|
||||
addr = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(addr))
|
||||
return PTR_ERR(addr);
|
||||
punit_ipcdev->base[GTDRIVER_IPC][BASE_IFACE] = addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_punit_ipc_probe(struct platform_device *pdev)
|
||||
{
|
||||
int irq, ret;
|
||||
|
||||
punit_ipcdev = devm_kzalloc(&pdev->dev,
|
||||
sizeof(*punit_ipcdev), GFP_KERNEL);
|
||||
if (!punit_ipcdev)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, punit_ipcdev);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
punit_ipcdev->irq = 0;
|
||||
dev_warn(&pdev->dev, "Invalid IRQ, using polling mode\n");
|
||||
} else {
|
||||
ret = devm_request_irq(&pdev->dev, irq, intel_punit_ioc,
|
||||
IRQF_NO_SUSPEND, "intel_punit_ipc",
|
||||
&punit_ipcdev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to request irq: %d\n", irq);
|
||||
return ret;
|
||||
}
|
||||
punit_ipcdev->irq = irq;
|
||||
}
|
||||
|
||||
ret = intel_punit_get_bars(pdev);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
punit_ipcdev->dev = &pdev->dev;
|
||||
mutex_init(&punit_ipcdev->lock);
|
||||
init_completion(&punit_ipcdev->cmd_complete);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int intel_punit_ipc_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id punit_ipc_acpi_ids[] = {
|
||||
{ "INT34D4", 0 },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct platform_driver intel_punit_ipc_driver = {
|
||||
.probe = intel_punit_ipc_probe,
|
||||
.remove = intel_punit_ipc_remove,
|
||||
.driver = {
|
||||
.name = "intel_punit_ipc",
|
||||
.acpi_match_table = ACPI_PTR(punit_ipc_acpi_ids),
|
||||
},
|
||||
};
|
||||
|
||||
static int __init intel_punit_ipc_init(void)
|
||||
{
|
||||
return platform_driver_register(&intel_punit_ipc_driver);
|
||||
}
|
||||
|
||||
static void __exit intel_punit_ipc_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&intel_punit_ipc_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
|
||||
MODULE_DESCRIPTION("Intel P-Unit IPC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
/* Some modules are dependent on this, so init earlier */
|
||||
fs_initcall(intel_punit_ipc_init);
|
||||
module_exit(intel_punit_ipc_exit);
|
464
drivers/platform/x86/intel_telemetry_core.c
Normal file
464
drivers/platform/x86/intel_telemetry_core.c
Normal file
@ -0,0 +1,464 @@
|
||||
/*
|
||||
* Intel SoC Core Telemetry Driver
|
||||
* Copyright (C) 2015, Intel Corporation.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* Telemetry Framework provides platform related PM and performance statistics.
|
||||
* This file provides the core telemetry API implementation.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <asm/intel_telemetry.h>
|
||||
|
||||
#define DRIVER_NAME "intel_telemetry_core"
|
||||
|
||||
struct telemetry_core_config {
|
||||
struct telemetry_plt_config *plt_config;
|
||||
struct telemetry_core_ops *telem_ops;
|
||||
};
|
||||
|
||||
static struct telemetry_core_config telm_core_conf;
|
||||
|
||||
static int telemetry_def_update_events(struct telemetry_evtconfig pss_evtconfig,
|
||||
struct telemetry_evtconfig ioss_evtconfig)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int telemetry_def_set_sampling_period(u8 pss_period, u8 ioss_period)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int telemetry_def_get_sampling_period(u8 *pss_min_period,
|
||||
u8 *pss_max_period,
|
||||
u8 *ioss_min_period,
|
||||
u8 *ioss_max_period)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int telemetry_def_get_eventconfig(
|
||||
struct telemetry_evtconfig *pss_evtconfig,
|
||||
struct telemetry_evtconfig *ioss_evtconfig,
|
||||
int pss_len, int ioss_len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int telemetry_def_get_trace_verbosity(enum telemetry_unit telem_unit,
|
||||
u32 *verbosity)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int telemetry_def_set_trace_verbosity(enum telemetry_unit telem_unit,
|
||||
u32 verbosity)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int telemetry_def_raw_read_eventlog(enum telemetry_unit telem_unit,
|
||||
struct telemetry_evtlog *evtlog,
|
||||
int len, int log_all_evts)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int telemetry_def_read_eventlog(enum telemetry_unit telem_unit,
|
||||
struct telemetry_evtlog *evtlog,
|
||||
int len, int log_all_evts)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int telemetry_def_add_events(u8 num_pss_evts, u8 num_ioss_evts,
|
||||
u32 *pss_evtmap, u32 *ioss_evtmap)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int telemetry_def_reset_events(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct telemetry_core_ops telm_defpltops = {
|
||||
.set_sampling_period = telemetry_def_set_sampling_period,
|
||||
.get_sampling_period = telemetry_def_get_sampling_period,
|
||||
.get_trace_verbosity = telemetry_def_get_trace_verbosity,
|
||||
.set_trace_verbosity = telemetry_def_set_trace_verbosity,
|
||||
.raw_read_eventlog = telemetry_def_raw_read_eventlog,
|
||||
.get_eventconfig = telemetry_def_get_eventconfig,
|
||||
.read_eventlog = telemetry_def_read_eventlog,
|
||||
.update_events = telemetry_def_update_events,
|
||||
.reset_events = telemetry_def_reset_events,
|
||||
.add_events = telemetry_def_add_events,
|
||||
};
|
||||
|
||||
/**
|
||||
* telemetry_update_events() - Update telemetry Configuration
|
||||
* @pss_evtconfig: PSS related config. No change if num_evts = 0.
|
||||
* @pss_evtconfig: IOSS related config. No change if num_evts = 0.
|
||||
*
|
||||
* This API updates the IOSS & PSS Telemetry configuration. Old config
|
||||
* is overwritten. Call telemetry_reset_events when logging is over
|
||||
* All sample period values should be in the form of:
|
||||
* bits[6:3] -> value; bits [0:2]-> Exponent; Period = (Value *16^Exponent)
|
||||
*
|
||||
* Return: 0 success, < 0 for failure
|
||||
*/
|
||||
int telemetry_update_events(struct telemetry_evtconfig pss_evtconfig,
|
||||
struct telemetry_evtconfig ioss_evtconfig)
|
||||
{
|
||||
return telm_core_conf.telem_ops->update_events(pss_evtconfig,
|
||||
ioss_evtconfig);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(telemetry_update_events);
|
||||
|
||||
|
||||
/**
|
||||
* telemetry_set_sampling_period() - Sets the IOSS & PSS sampling period
|
||||
* @pss_period: placeholder for PSS Period to be set.
|
||||
* Set to 0 if not required to be updated
|
||||
* @ioss_period: placeholder for IOSS Period to be set
|
||||
* Set to 0 if not required to be updated
|
||||
*
|
||||
* All values should be in the form of:
|
||||
* bits[6:3] -> value; bits [0:2]-> Exponent; Period = (Value *16^Exponent)
|
||||
*
|
||||
* Return: 0 success, < 0 for failure
|
||||
*/
|
||||
int telemetry_set_sampling_period(u8 pss_period, u8 ioss_period)
|
||||
{
|
||||
return telm_core_conf.telem_ops->set_sampling_period(pss_period,
|
||||
ioss_period);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(telemetry_set_sampling_period);
|
||||
|
||||
/**
|
||||
* telemetry_get_sampling_period() - Get IOSS & PSS min & max sampling period
|
||||
* @pss_min_period: placeholder for PSS Min Period supported
|
||||
* @pss_max_period: placeholder for PSS Max Period supported
|
||||
* @ioss_min_period: placeholder for IOSS Min Period supported
|
||||
* @ioss_max_period: placeholder for IOSS Max Period supported
|
||||
*
|
||||
* All values should be in the form of:
|
||||
* bits[6:3] -> value; bits [0:2]-> Exponent; Period = (Value *16^Exponent)
|
||||
*
|
||||
* Return: 0 success, < 0 for failure
|
||||
*/
|
||||
int telemetry_get_sampling_period(u8 *pss_min_period, u8 *pss_max_period,
|
||||
u8 *ioss_min_period, u8 *ioss_max_period)
|
||||
{
|
||||
return telm_core_conf.telem_ops->get_sampling_period(pss_min_period,
|
||||
pss_max_period,
|
||||
ioss_min_period,
|
||||
ioss_max_period);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(telemetry_get_sampling_period);
|
||||
|
||||
|
||||
/**
|
||||
* telemetry_reset_events() - Restore the IOSS & PSS configuration to default
|
||||
*
|
||||
* Return: 0 success, < 0 for failure
|
||||
*/
|
||||
int telemetry_reset_events(void)
|
||||
{
|
||||
return telm_core_conf.telem_ops->reset_events();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(telemetry_reset_events);
|
||||
|
||||
/**
|
||||
* telemetry_get_eventconfig() - Returns the pss and ioss events enabled
|
||||
* @pss_evtconfig: Pointer to PSS related configuration.
|
||||
* @pss_evtconfig: Pointer to IOSS related configuration.
|
||||
* @pss_len: Number of u32 elements allocated for pss_evtconfig array
|
||||
* @ioss_len: Number of u32 elements allocated for ioss_evtconfig array
|
||||
*
|
||||
* Return: 0 success, < 0 for failure
|
||||
*/
|
||||
int telemetry_get_eventconfig(struct telemetry_evtconfig *pss_evtconfig,
|
||||
struct telemetry_evtconfig *ioss_evtconfig,
|
||||
int pss_len, int ioss_len)
|
||||
{
|
||||
return telm_core_conf.telem_ops->get_eventconfig(pss_evtconfig,
|
||||
ioss_evtconfig,
|
||||
pss_len, ioss_len);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(telemetry_get_eventconfig);
|
||||
|
||||
/**
|
||||
* telemetry_add_events() - Add IOSS & PSS configuration to existing settings.
|
||||
* @num_pss_evts: Number of PSS Events (<29) in pss_evtmap. Can be 0.
|
||||
* @num_ioss_evts: Number of IOSS Events (<29) in ioss_evtmap. Can be 0.
|
||||
* @pss_evtmap: Array of PSS Event-IDs to Enable
|
||||
* @ioss_evtmap: Array of PSS Event-IDs to Enable
|
||||
*
|
||||
* Events are appended to Old Configuration. In case of total events > 28, it
|
||||
* returns error. Call telemetry_reset_events to reset after eventlog done
|
||||
*
|
||||
* Return: 0 success, < 0 for failure
|
||||
*/
|
||||
int telemetry_add_events(u8 num_pss_evts, u8 num_ioss_evts,
|
||||
u32 *pss_evtmap, u32 *ioss_evtmap)
|
||||
{
|
||||
return telm_core_conf.telem_ops->add_events(num_pss_evts,
|
||||
num_ioss_evts, pss_evtmap,
|
||||
ioss_evtmap);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(telemetry_add_events);
|
||||
|
||||
/**
|
||||
* telemetry_read_events() - Fetches samples as specified by evtlog.telem_evt_id
|
||||
* @telem_unit: Specify whether IOSS or PSS Read
|
||||
* @evtlog: Array of telemetry_evtlog structs to fill data
|
||||
* evtlog.telem_evt_id specifies the ids to read
|
||||
* @len: Length of array of evtlog
|
||||
*
|
||||
* Return: number of eventlogs read for success, < 0 for failure
|
||||
*/
|
||||
int telemetry_read_events(enum telemetry_unit telem_unit,
|
||||
struct telemetry_evtlog *evtlog, int len)
|
||||
{
|
||||
return telm_core_conf.telem_ops->read_eventlog(telem_unit, evtlog,
|
||||
len, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(telemetry_read_events);
|
||||
|
||||
/**
|
||||
* telemetry_raw_read_events() - Fetch samples specified by evtlog.telem_evt_id
|
||||
* @telem_unit: Specify whether IOSS or PSS Read
|
||||
* @evtlog: Array of telemetry_evtlog structs to fill data
|
||||
* evtlog.telem_evt_id specifies the ids to read
|
||||
* @len: Length of array of evtlog
|
||||
*
|
||||
* The caller must take care of locking in this case.
|
||||
*
|
||||
* Return: number of eventlogs read for success, < 0 for failure
|
||||
*/
|
||||
int telemetry_raw_read_events(enum telemetry_unit telem_unit,
|
||||
struct telemetry_evtlog *evtlog, int len)
|
||||
{
|
||||
return telm_core_conf.telem_ops->raw_read_eventlog(telem_unit, evtlog,
|
||||
len, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(telemetry_raw_read_events);
|
||||
|
||||
/**
|
||||
* telemetry_read_eventlog() - Fetch the Telemetry log from PSS or IOSS
|
||||
* @telem_unit: Specify whether IOSS or PSS Read
|
||||
* @evtlog: Array of telemetry_evtlog structs to fill data
|
||||
* @len: Length of array of evtlog
|
||||
*
|
||||
* Return: number of eventlogs read for success, < 0 for failure
|
||||
*/
|
||||
int telemetry_read_eventlog(enum telemetry_unit telem_unit,
|
||||
struct telemetry_evtlog *evtlog, int len)
|
||||
{
|
||||
return telm_core_conf.telem_ops->read_eventlog(telem_unit, evtlog,
|
||||
len, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(telemetry_read_eventlog);
|
||||
|
||||
/**
|
||||
* telemetry_raw_read_eventlog() - Fetch the Telemetry log from PSS or IOSS
|
||||
* @telem_unit: Specify whether IOSS or PSS Read
|
||||
* @evtlog: Array of telemetry_evtlog structs to fill data
|
||||
* @len: Length of array of evtlog
|
||||
*
|
||||
* The caller must take care of locking in this case.
|
||||
*
|
||||
* Return: number of eventlogs read for success, < 0 for failure
|
||||
*/
|
||||
int telemetry_raw_read_eventlog(enum telemetry_unit telem_unit,
|
||||
struct telemetry_evtlog *evtlog, int len)
|
||||
{
|
||||
return telm_core_conf.telem_ops->raw_read_eventlog(telem_unit, evtlog,
|
||||
len, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(telemetry_raw_read_eventlog);
|
||||
|
||||
|
||||
/**
|
||||
* telemetry_get_trace_verbosity() - Get the IOSS & PSS Trace verbosity
|
||||
* @telem_unit: Specify whether IOSS or PSS Read
|
||||
* @verbosity: Pointer to return Verbosity
|
||||
*
|
||||
* Return: 0 success, < 0 for failure
|
||||
*/
|
||||
int telemetry_get_trace_verbosity(enum telemetry_unit telem_unit,
|
||||
u32 *verbosity)
|
||||
{
|
||||
return telm_core_conf.telem_ops->get_trace_verbosity(telem_unit,
|
||||
verbosity);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(telemetry_get_trace_verbosity);
|
||||
|
||||
|
||||
/**
|
||||
* telemetry_set_trace_verbosity() - Update the IOSS & PSS Trace verbosity
|
||||
* @telem_unit: Specify whether IOSS or PSS Read
|
||||
* @verbosity: Verbosity to set
|
||||
*
|
||||
* Return: 0 success, < 0 for failure
|
||||
*/
|
||||
int telemetry_set_trace_verbosity(enum telemetry_unit telem_unit, u32 verbosity)
|
||||
{
|
||||
return telm_core_conf.telem_ops->set_trace_verbosity(telem_unit,
|
||||
verbosity);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(telemetry_set_trace_verbosity);
|
||||
|
||||
/**
|
||||
* telemetry_set_pltdata() - Set the platform specific Data
|
||||
* @ops: Pointer to ops structure
|
||||
* @pltconfig: Platform config data
|
||||
*
|
||||
* Usage by other than telemetry pltdrv module is invalid
|
||||
*
|
||||
* Return: 0 success, < 0 for failure
|
||||
*/
|
||||
int telemetry_set_pltdata(struct telemetry_core_ops *ops,
|
||||
struct telemetry_plt_config *pltconfig)
|
||||
{
|
||||
if (ops)
|
||||
telm_core_conf.telem_ops = ops;
|
||||
|
||||
if (pltconfig)
|
||||
telm_core_conf.plt_config = pltconfig;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(telemetry_set_pltdata);
|
||||
|
||||
/**
|
||||
* telemetry_clear_pltdata() - Clear the platform specific Data
|
||||
*
|
||||
* Usage by other than telemetry pltdrv module is invalid
|
||||
*
|
||||
* Return: 0 success, < 0 for failure
|
||||
*/
|
||||
int telemetry_clear_pltdata(void)
|
||||
{
|
||||
telm_core_conf.telem_ops = &telm_defpltops;
|
||||
telm_core_conf.plt_config = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(telemetry_clear_pltdata);
|
||||
|
||||
/**
|
||||
* telemetry_pltconfig_valid() - Checkif platform config is valid
|
||||
*
|
||||
* Usage by other than telemetry module is invalid
|
||||
*
|
||||
* Return: 0 success, < 0 for failure
|
||||
*/
|
||||
int telemetry_pltconfig_valid(void)
|
||||
{
|
||||
if (telm_core_conf.plt_config)
|
||||
return 0;
|
||||
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(telemetry_pltconfig_valid);
|
||||
|
||||
static inline int telemetry_get_pssevtname(enum telemetry_unit telem_unit,
|
||||
const char **name, int len)
|
||||
{
|
||||
struct telemetry_unit_config psscfg;
|
||||
int i;
|
||||
|
||||
if (!telm_core_conf.plt_config)
|
||||
return -EINVAL;
|
||||
|
||||
psscfg = telm_core_conf.plt_config->pss_config;
|
||||
|
||||
if (len > psscfg.ssram_evts_used)
|
||||
len = psscfg.ssram_evts_used;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
name[i] = psscfg.telem_evts[i].name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int telemetry_get_iossevtname(enum telemetry_unit telem_unit,
|
||||
const char **name, int len)
|
||||
{
|
||||
struct telemetry_unit_config iosscfg;
|
||||
int i;
|
||||
|
||||
if (!(telm_core_conf.plt_config))
|
||||
return -EINVAL;
|
||||
|
||||
iosscfg = telm_core_conf.plt_config->ioss_config;
|
||||
|
||||
if (len > iosscfg.ssram_evts_used)
|
||||
len = iosscfg.ssram_evts_used;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
name[i] = iosscfg.telem_evts[i].name;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* telemetry_get_evtname() - Checkif platform config is valid
|
||||
* @telem_unit: Telemetry Unit to check
|
||||
* @name: Array of character pointers to contain name
|
||||
* @len: length of array name provided by user
|
||||
*
|
||||
* Usage by other than telemetry debugfs module is invalid
|
||||
*
|
||||
* Return: 0 success, < 0 for failure
|
||||
*/
|
||||
int telemetry_get_evtname(enum telemetry_unit telem_unit,
|
||||
const char **name, int len)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (telem_unit == TELEM_PSS)
|
||||
ret = telemetry_get_pssevtname(telem_unit, name, len);
|
||||
|
||||
else if (telem_unit == TELEM_IOSS)
|
||||
ret = telemetry_get_iossevtname(telem_unit, name, len);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(telemetry_get_evtname);
|
||||
|
||||
static int __init telemetry_module_init(void)
|
||||
{
|
||||
pr_info(pr_fmt(DRIVER_NAME) " Init\n");
|
||||
|
||||
telm_core_conf.telem_ops = &telm_defpltops;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit telemetry_module_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(telemetry_module_init);
|
||||
module_exit(telemetry_module_exit);
|
||||
|
||||
MODULE_AUTHOR("Souvik Kumar Chakravarty <souvik.k.chakravarty@intel.com>");
|
||||
MODULE_DESCRIPTION("Intel SoC Telemetry Interface");
|
||||
MODULE_LICENSE("GPL");
|
1030
drivers/platform/x86/intel_telemetry_debugfs.c
Normal file
1030
drivers/platform/x86/intel_telemetry_debugfs.c
Normal file
File diff suppressed because it is too large
Load Diff
1206
drivers/platform/x86/intel_telemetry_pltdrv.c
Normal file
1206
drivers/platform/x86/intel_telemetry_pltdrv.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -1393,6 +1393,7 @@ static void sony_nc_function_setup(struct acpi_device *device,
|
||||
case 0x0143:
|
||||
case 0x014b:
|
||||
case 0x014c:
|
||||
case 0x0153:
|
||||
case 0x0163:
|
||||
result = sony_nc_kbd_backlight_setup(pf_device, handle);
|
||||
if (result)
|
||||
@ -1490,6 +1491,7 @@ static void sony_nc_function_cleanup(struct platform_device *pd)
|
||||
case 0x0143:
|
||||
case 0x014b:
|
||||
case 0x014c:
|
||||
case 0x0153:
|
||||
case 0x0163:
|
||||
sony_nc_kbd_backlight_cleanup(pd, handle);
|
||||
break;
|
||||
@ -1773,6 +1775,7 @@ struct kbd_backlight {
|
||||
unsigned int base;
|
||||
unsigned int mode;
|
||||
unsigned int timeout;
|
||||
unsigned int has_timeout;
|
||||
struct device_attribute mode_attr;
|
||||
struct device_attribute timeout_attr;
|
||||
};
|
||||
@ -1877,6 +1880,8 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
|
||||
unsigned int handle)
|
||||
{
|
||||
int result;
|
||||
int probe_base = 0;
|
||||
int ctl_base = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (kbdbl_ctl) {
|
||||
@ -1885,11 +1890,25 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* verify the kbd backlight presence, these handles are not used for
|
||||
* keyboard backlight only
|
||||
/* verify the kbd backlight presence, some of these handles are not used
|
||||
* for keyboard backlight only
|
||||
*/
|
||||
ret = sony_call_snc_handle(handle, handle == 0x0137 ? 0x0B00 : 0x0100,
|
||||
&result);
|
||||
switch (handle) {
|
||||
case 0x0153:
|
||||
probe_base = 0x0;
|
||||
ctl_base = 0x0;
|
||||
break;
|
||||
case 0x0137:
|
||||
probe_base = 0x0B00;
|
||||
ctl_base = 0x0C00;
|
||||
break;
|
||||
default:
|
||||
probe_base = 0x0100;
|
||||
ctl_base = 0x4000;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = sony_call_snc_handle(handle, probe_base, &result);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -1906,10 +1925,9 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
|
||||
kbdbl_ctl->mode = kbd_backlight;
|
||||
kbdbl_ctl->timeout = kbd_backlight_timeout;
|
||||
kbdbl_ctl->handle = handle;
|
||||
if (handle == 0x0137)
|
||||
kbdbl_ctl->base = 0x0C00;
|
||||
else
|
||||
kbdbl_ctl->base = 0x4000;
|
||||
kbdbl_ctl->base = ctl_base;
|
||||
/* Some models do not allow timeout control */
|
||||
kbdbl_ctl->has_timeout = handle != 0x0153;
|
||||
|
||||
sysfs_attr_init(&kbdbl_ctl->mode_attr.attr);
|
||||
kbdbl_ctl->mode_attr.attr.name = "kbd_backlight";
|
||||
@ -1917,22 +1935,28 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
|
||||
kbdbl_ctl->mode_attr.show = sony_nc_kbd_backlight_mode_show;
|
||||
kbdbl_ctl->mode_attr.store = sony_nc_kbd_backlight_mode_store;
|
||||
|
||||
sysfs_attr_init(&kbdbl_ctl->timeout_attr.attr);
|
||||
kbdbl_ctl->timeout_attr.attr.name = "kbd_backlight_timeout";
|
||||
kbdbl_ctl->timeout_attr.attr.mode = S_IRUGO | S_IWUSR;
|
||||
kbdbl_ctl->timeout_attr.show = sony_nc_kbd_backlight_timeout_show;
|
||||
kbdbl_ctl->timeout_attr.store = sony_nc_kbd_backlight_timeout_store;
|
||||
|
||||
ret = device_create_file(&pd->dev, &kbdbl_ctl->mode_attr);
|
||||
if (ret)
|
||||
goto outkzalloc;
|
||||
|
||||
ret = device_create_file(&pd->dev, &kbdbl_ctl->timeout_attr);
|
||||
if (ret)
|
||||
goto outmode;
|
||||
|
||||
__sony_nc_kbd_backlight_mode_set(kbdbl_ctl->mode);
|
||||
__sony_nc_kbd_backlight_timeout_set(kbdbl_ctl->timeout);
|
||||
|
||||
if (kbdbl_ctl->has_timeout) {
|
||||
sysfs_attr_init(&kbdbl_ctl->timeout_attr.attr);
|
||||
kbdbl_ctl->timeout_attr.attr.name = "kbd_backlight_timeout";
|
||||
kbdbl_ctl->timeout_attr.attr.mode = S_IRUGO | S_IWUSR;
|
||||
kbdbl_ctl->timeout_attr.show =
|
||||
sony_nc_kbd_backlight_timeout_show;
|
||||
kbdbl_ctl->timeout_attr.store =
|
||||
sony_nc_kbd_backlight_timeout_store;
|
||||
|
||||
ret = device_create_file(&pd->dev, &kbdbl_ctl->timeout_attr);
|
||||
if (ret)
|
||||
goto outmode;
|
||||
|
||||
__sony_nc_kbd_backlight_timeout_set(kbdbl_ctl->timeout);
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
|
||||
@ -1949,7 +1973,8 @@ static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd,
|
||||
{
|
||||
if (kbdbl_ctl && handle == kbdbl_ctl->handle) {
|
||||
device_remove_file(&pd->dev, &kbdbl_ctl->mode_attr);
|
||||
device_remove_file(&pd->dev, &kbdbl_ctl->timeout_attr);
|
||||
if (kbdbl_ctl->has_timeout)
|
||||
device_remove_file(&pd->dev, &kbdbl_ctl->timeout_attr);
|
||||
kfree(kbdbl_ctl);
|
||||
kbdbl_ctl = NULL;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* power/home/volume button support for
|
||||
* Microsoft Surface Pro 3 tablet.
|
||||
* Microsoft Surface Pro 3/4 tablet.
|
||||
*
|
||||
* Copyright (c) 2015 Intel Corporation.
|
||||
* All rights reserved.
|
||||
@ -19,9 +19,10 @@
|
||||
#include <linux/acpi.h>
|
||||
#include <acpi/button.h>
|
||||
|
||||
#define SURFACE_BUTTON_HID "MSHW0028"
|
||||
#define SURFACE_PRO3_BUTTON_HID "MSHW0028"
|
||||
#define SURFACE_PRO4_BUTTON_HID "MSHW0040"
|
||||
#define SURFACE_BUTTON_OBJ_NAME "VGBI"
|
||||
#define SURFACE_BUTTON_DEVICE_NAME "Surface Pro 3 Buttons"
|
||||
#define SURFACE_BUTTON_DEVICE_NAME "Surface Pro 3/4 Buttons"
|
||||
|
||||
#define SURFACE_BUTTON_NOTIFY_PRESS_POWER 0xc6
|
||||
#define SURFACE_BUTTON_NOTIFY_RELEASE_POWER 0xc7
|
||||
@ -54,7 +55,8 @@ MODULE_LICENSE("GPL v2");
|
||||
* acpi_driver.
|
||||
*/
|
||||
static const struct acpi_device_id surface_button_device_ids[] = {
|
||||
{SURFACE_BUTTON_HID, 0},
|
||||
{SURFACE_PRO3_BUTTON_HID, 0},
|
||||
{SURFACE_PRO4_BUTTON_HID, 0},
|
||||
{"", 0},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, surface_button_device_ids);
|
||||
@ -109,7 +111,7 @@ static void surface_button_notify(struct acpi_device *device, u32 event)
|
||||
break;
|
||||
}
|
||||
input = button->input;
|
||||
if (KEY_RESERVED == key_code)
|
||||
if (key_code == KEY_RESERVED)
|
||||
return;
|
||||
if (pressed)
|
||||
pm_wakeup_event(&device->dev, 0);
|
||||
|
@ -52,7 +52,9 @@ struct tc1100_data {
|
||||
u32 jogdial;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static struct tc1100_data suspend_data;
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
Device Management
|
||||
|
@ -303,6 +303,7 @@ static struct {
|
||||
u32 hotkey_mask:1;
|
||||
u32 hotkey_wlsw:1;
|
||||
u32 hotkey_tablet:1;
|
||||
u32 kbdlight:1;
|
||||
u32 light:1;
|
||||
u32 light_status:1;
|
||||
u32 bright_acpimode:1;
|
||||
@ -4985,6 +4986,207 @@ static struct ibm_struct video_driver_data = {
|
||||
|
||||
#endif /* CONFIG_THINKPAD_ACPI_VIDEO */
|
||||
|
||||
/*************************************************************************
|
||||
* Keyboard backlight subdriver
|
||||
*/
|
||||
|
||||
static int kbdlight_set_level(int level)
|
||||
{
|
||||
if (!hkey_handle)
|
||||
return -ENXIO;
|
||||
|
||||
if (!acpi_evalf(hkey_handle, NULL, "MLCS", "dd", level))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kbdlight_get_level(void)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
if (!hkey_handle)
|
||||
return -ENXIO;
|
||||
|
||||
if (!acpi_evalf(hkey_handle, &status, "MLCG", "dd", 0))
|
||||
return -EIO;
|
||||
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
return status & 0x3;
|
||||
}
|
||||
|
||||
static bool kbdlight_is_supported(void)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
if (!hkey_handle)
|
||||
return false;
|
||||
|
||||
if (!acpi_has_method(hkey_handle, "MLCG")) {
|
||||
vdbg_printk(TPACPI_DBG_INIT, "kbdlight MLCG is unavailable\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!acpi_evalf(hkey_handle, &status, "MLCG", "qdd", 0)) {
|
||||
vdbg_printk(TPACPI_DBG_INIT, "kbdlight MLCG failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (status < 0) {
|
||||
vdbg_printk(TPACPI_DBG_INIT, "kbdlight MLCG err: %d\n", status);
|
||||
return false;
|
||||
}
|
||||
|
||||
vdbg_printk(TPACPI_DBG_INIT, "kbdlight MLCG returned 0x%x\n", status);
|
||||
/*
|
||||
* Guessed test for keyboard backlight:
|
||||
*
|
||||
* Machines with backlight keyboard return:
|
||||
* b010100000010000000XX - ThinkPad X1 Carbon 3rd
|
||||
* b110100010010000000XX - ThinkPad x230
|
||||
* b010100000010000000XX - ThinkPad x240
|
||||
* b010100000010000000XX - ThinkPad W541
|
||||
* (XX is current backlight level)
|
||||
*
|
||||
* Machines without backlight keyboard return:
|
||||
* b10100001000000000000 - ThinkPad x230
|
||||
* b10110001000000000000 - ThinkPad E430
|
||||
* b00000000000000000000 - ThinkPad E450
|
||||
*
|
||||
* Candidate BITs for detection test (XOR):
|
||||
* b01000000001000000000
|
||||
* ^
|
||||
*/
|
||||
return status & BIT(9);
|
||||
}
|
||||
|
||||
static void kbdlight_set_worker(struct work_struct *work)
|
||||
{
|
||||
struct tpacpi_led_classdev *data =
|
||||
container_of(work, struct tpacpi_led_classdev, work);
|
||||
|
||||
if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
|
||||
kbdlight_set_level(data->new_state);
|
||||
}
|
||||
|
||||
static void kbdlight_sysfs_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct tpacpi_led_classdev *data =
|
||||
container_of(led_cdev,
|
||||
struct tpacpi_led_classdev,
|
||||
led_classdev);
|
||||
data->new_state = brightness;
|
||||
queue_work(tpacpi_wq, &data->work);
|
||||
}
|
||||
|
||||
static enum led_brightness kbdlight_sysfs_get(struct led_classdev *led_cdev)
|
||||
{
|
||||
int level;
|
||||
|
||||
level = kbdlight_get_level();
|
||||
if (level < 0)
|
||||
return 0;
|
||||
|
||||
return level;
|
||||
}
|
||||
|
||||
static struct tpacpi_led_classdev tpacpi_led_kbdlight = {
|
||||
.led_classdev = {
|
||||
.name = "tpacpi::kbd_backlight",
|
||||
.max_brightness = 2,
|
||||
.brightness_set = &kbdlight_sysfs_set,
|
||||
.brightness_get = &kbdlight_sysfs_get,
|
||||
.flags = LED_CORE_SUSPENDRESUME,
|
||||
}
|
||||
};
|
||||
|
||||
static int __init kbdlight_init(struct ibm_init_struct *iibm)
|
||||
{
|
||||
int rc;
|
||||
|
||||
vdbg_printk(TPACPI_DBG_INIT, "initializing kbdlight subdriver\n");
|
||||
|
||||
TPACPI_ACPIHANDLE_INIT(hkey);
|
||||
INIT_WORK(&tpacpi_led_kbdlight.work, kbdlight_set_worker);
|
||||
|
||||
if (!kbdlight_is_supported()) {
|
||||
tp_features.kbdlight = 0;
|
||||
vdbg_printk(TPACPI_DBG_INIT, "kbdlight is unsupported\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
tp_features.kbdlight = 1;
|
||||
|
||||
rc = led_classdev_register(&tpacpi_pdev->dev,
|
||||
&tpacpi_led_kbdlight.led_classdev);
|
||||
if (rc < 0) {
|
||||
tp_features.kbdlight = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kbdlight_exit(void)
|
||||
{
|
||||
if (tp_features.kbdlight)
|
||||
led_classdev_unregister(&tpacpi_led_kbdlight.led_classdev);
|
||||
flush_workqueue(tpacpi_wq);
|
||||
}
|
||||
|
||||
static int kbdlight_read(struct seq_file *m)
|
||||
{
|
||||
int level;
|
||||
|
||||
if (!tp_features.kbdlight) {
|
||||
seq_printf(m, "status:\t\tnot supported\n");
|
||||
} else {
|
||||
level = kbdlight_get_level();
|
||||
if (level < 0)
|
||||
seq_printf(m, "status:\t\terror %d\n", level);
|
||||
else
|
||||
seq_printf(m, "status:\t\t%d\n", level);
|
||||
seq_printf(m, "commands:\t0, 1, 2\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kbdlight_write(char *buf)
|
||||
{
|
||||
char *cmd;
|
||||
int level = -1;
|
||||
|
||||
if (!tp_features.kbdlight)
|
||||
return -ENODEV;
|
||||
|
||||
while ((cmd = next_cmd(&buf))) {
|
||||
if (strlencmp(cmd, "0") == 0)
|
||||
level = 0;
|
||||
else if (strlencmp(cmd, "1") == 0)
|
||||
level = 1;
|
||||
else if (strlencmp(cmd, "2") == 0)
|
||||
level = 2;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (level == -1)
|
||||
return -EINVAL;
|
||||
|
||||
return kbdlight_set_level(level);
|
||||
}
|
||||
|
||||
static struct ibm_struct kbdlight_driver_data = {
|
||||
.name = "kbdlight",
|
||||
.read = kbdlight_read,
|
||||
.write = kbdlight_write,
|
||||
.exit = kbdlight_exit,
|
||||
};
|
||||
|
||||
/*************************************************************************
|
||||
* Light (thinklight) subdriver
|
||||
*/
|
||||
@ -9206,6 +9408,10 @@ static struct ibm_init_struct ibms_init[] __initdata = {
|
||||
.data = &video_driver_data,
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.init = kbdlight_init,
|
||||
.data = &kbdlight_driver_data,
|
||||
},
|
||||
{
|
||||
.init = light_init,
|
||||
.data = &light_driver_data,
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/toshiba.h>
|
||||
#include <acpi/video.h>
|
||||
|
||||
@ -114,6 +115,7 @@ MODULE_LICENSE("GPL");
|
||||
#define HCI_VIDEO_OUT 0x001c
|
||||
#define HCI_HOTKEY_EVENT 0x001e
|
||||
#define HCI_LCD_BRIGHTNESS 0x002a
|
||||
#define HCI_WIRELESS 0x0056
|
||||
#define HCI_ACCELEROMETER 0x006d
|
||||
#define HCI_KBD_ILLUMINATION 0x0095
|
||||
#define HCI_ECO_MODE 0x0097
|
||||
@ -148,6 +150,10 @@ MODULE_LICENSE("GPL");
|
||||
#define SCI_KBD_MODE_ON 0x8
|
||||
#define SCI_KBD_MODE_OFF 0x10
|
||||
#define SCI_KBD_TIME_MAX 0x3c001a
|
||||
#define HCI_WIRELESS_STATUS 0x1
|
||||
#define HCI_WIRELESS_WWAN 0x3
|
||||
#define HCI_WIRELESS_WWAN_STATUS 0x2000
|
||||
#define HCI_WIRELESS_WWAN_POWER 0x4000
|
||||
#define SCI_USB_CHARGE_MODE_MASK 0xff
|
||||
#define SCI_USB_CHARGE_DISABLED 0x00
|
||||
#define SCI_USB_CHARGE_ALTERNATE 0x09
|
||||
@ -169,6 +175,7 @@ struct toshiba_acpi_dev {
|
||||
struct led_classdev kbd_led;
|
||||
struct led_classdev eco_led;
|
||||
struct miscdevice miscdev;
|
||||
struct rfkill *wwan_rfk;
|
||||
|
||||
int force_fan;
|
||||
int last_key_event;
|
||||
@ -197,12 +204,15 @@ struct toshiba_acpi_dev {
|
||||
unsigned int kbd_function_keys_supported:1;
|
||||
unsigned int panel_power_on_supported:1;
|
||||
unsigned int usb_three_supported:1;
|
||||
unsigned int wwan_supported:1;
|
||||
unsigned int sysfs_created:1;
|
||||
unsigned int special_functions;
|
||||
|
||||
bool kbd_event_generated;
|
||||
bool kbd_led_registered;
|
||||
bool illumination_led_registered;
|
||||
bool eco_led_registered;
|
||||
bool killswitch;
|
||||
};
|
||||
|
||||
static struct toshiba_acpi_dev *toshiba_acpi;
|
||||
@ -516,6 +526,7 @@ static void toshiba_kbd_illum_available(struct toshiba_acpi_dev *dev)
|
||||
|
||||
dev->kbd_illum_supported = 0;
|
||||
dev->kbd_led_registered = false;
|
||||
dev->kbd_event_generated = false;
|
||||
|
||||
if (!sci_open(dev))
|
||||
return;
|
||||
@ -1085,6 +1096,104 @@ static int toshiba_hotkey_event_type_get(struct toshiba_acpi_dev *dev,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Wireless status (RFKill, WLAN, BT, WWAN) */
|
||||
static int toshiba_wireless_status(struct toshiba_acpi_dev *dev)
|
||||
{
|
||||
u32 in[TCI_WORDS] = { HCI_GET, HCI_WIRELESS, 0, 0, 0, 0 };
|
||||
u32 out[TCI_WORDS];
|
||||
acpi_status status;
|
||||
|
||||
in[3] = HCI_WIRELESS_STATUS;
|
||||
status = tci_raw(dev, in, out);
|
||||
|
||||
if (ACPI_FAILURE(status)) {
|
||||
pr_err("ACPI call to get Wireless status failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (out[0] == TOS_NOT_SUPPORTED)
|
||||
return -ENODEV;
|
||||
|
||||
if (out[0] != TOS_SUCCESS)
|
||||
return -EIO;
|
||||
|
||||
dev->killswitch = !!(out[2] & HCI_WIRELESS_STATUS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* WWAN */
|
||||
static void toshiba_wwan_available(struct toshiba_acpi_dev *dev)
|
||||
{
|
||||
u32 in[TCI_WORDS] = { HCI_GET, HCI_WIRELESS, 0, 0, 0, 0 };
|
||||
u32 out[TCI_WORDS];
|
||||
acpi_status status;
|
||||
|
||||
dev->wwan_supported = 0;
|
||||
|
||||
/*
|
||||
* WWAN support can be queried by setting the in[3] value to
|
||||
* HCI_WIRELESS_WWAN (0x03).
|
||||
*
|
||||
* If supported, out[0] contains TOS_SUCCESS and out[2] contains
|
||||
* HCI_WIRELESS_WWAN_STATUS (0x2000).
|
||||
*
|
||||
* If not supported, out[0] contains TOS_INPUT_DATA_ERROR (0x8300)
|
||||
* or TOS_NOT_SUPPORTED (0x8000).
|
||||
*/
|
||||
in[3] = HCI_WIRELESS_WWAN;
|
||||
status = tci_raw(dev, in, out);
|
||||
|
||||
if (ACPI_FAILURE(status)) {
|
||||
pr_err("ACPI call to get WWAN status failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (out[0] != TOS_SUCCESS)
|
||||
return;
|
||||
|
||||
dev->wwan_supported = (out[2] == HCI_WIRELESS_WWAN_STATUS);
|
||||
}
|
||||
|
||||
static int toshiba_wwan_set(struct toshiba_acpi_dev *dev, u32 state)
|
||||
{
|
||||
u32 in[TCI_WORDS] = { HCI_SET, HCI_WIRELESS, state, 0, 0, 0 };
|
||||
u32 out[TCI_WORDS];
|
||||
acpi_status status;
|
||||
|
||||
in[3] = HCI_WIRELESS_WWAN_STATUS;
|
||||
status = tci_raw(dev, in, out);
|
||||
|
||||
if (ACPI_FAILURE(status)) {
|
||||
pr_err("ACPI call to set WWAN status failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (out[0] == TOS_NOT_SUPPORTED)
|
||||
return -ENODEV;
|
||||
|
||||
if (out[0] != TOS_SUCCESS)
|
||||
return -EIO;
|
||||
|
||||
/*
|
||||
* Some devices only need to call HCI_WIRELESS_WWAN_STATUS to
|
||||
* (de)activate the device, but some others need the
|
||||
* HCI_WIRELESS_WWAN_POWER call as well.
|
||||
*/
|
||||
in[3] = HCI_WIRELESS_WWAN_POWER;
|
||||
status = tci_raw(dev, in, out);
|
||||
|
||||
if (ACPI_FAILURE(status)) {
|
||||
pr_err("ACPI call to set WWAN power failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (out[0] == TOS_NOT_SUPPORTED)
|
||||
return -ENODEV;
|
||||
|
||||
return out[0] == TOS_SUCCESS ? 0 : -EIO;
|
||||
}
|
||||
|
||||
/* Transflective Backlight */
|
||||
static int get_tr_backlight_status(struct toshiba_acpi_dev *dev, u32 *status)
|
||||
{
|
||||
@ -1535,6 +1644,11 @@ static const struct backlight_ops toshiba_backlight_data = {
|
||||
.update_status = set_lcd_status,
|
||||
};
|
||||
|
||||
/* Keyboard backlight work */
|
||||
static void toshiba_acpi_kbd_bl_work(struct work_struct *work);
|
||||
|
||||
static DECLARE_WORK(kbd_bl_work, toshiba_acpi_kbd_bl_work);
|
||||
|
||||
/*
|
||||
* Sysfs files
|
||||
*/
|
||||
@ -1634,6 +1748,24 @@ static ssize_t kbd_backlight_mode_store(struct device *dev,
|
||||
return ret;
|
||||
|
||||
toshiba->kbd_mode = mode;
|
||||
|
||||
/*
|
||||
* Some laptop models with the second generation backlit
|
||||
* keyboard (type 2) do not generate the keyboard backlight
|
||||
* changed event (0x92), and thus, the driver will never update
|
||||
* the sysfs entries.
|
||||
*
|
||||
* The event is generated right when changing the keyboard
|
||||
* backlight mode and the *notify function will set the
|
||||
* kbd_event_generated to true.
|
||||
*
|
||||
* In case the event is not generated, schedule the keyboard
|
||||
* backlight work to update the sysfs entries and emulate the
|
||||
* event via genetlink.
|
||||
*/
|
||||
if (toshiba->kbd_type == 2 &&
|
||||
!toshiba_acpi->kbd_event_generated)
|
||||
schedule_work(&kbd_bl_work);
|
||||
}
|
||||
|
||||
return count;
|
||||
@ -2166,6 +2298,21 @@ static struct attribute_group toshiba_attr_group = {
|
||||
.attrs = toshiba_attributes,
|
||||
};
|
||||
|
||||
static void toshiba_acpi_kbd_bl_work(struct work_struct *work)
|
||||
{
|
||||
struct acpi_device *acpi_dev = toshiba_acpi->acpi_dev;
|
||||
|
||||
/* Update the sysfs entries */
|
||||
if (sysfs_update_group(&acpi_dev->dev.kobj,
|
||||
&toshiba_attr_group))
|
||||
pr_err("Unable to update sysfs entries\n");
|
||||
|
||||
/* Emulate the keyboard backlight event */
|
||||
acpi_bus_generate_netlink_event(acpi_dev->pnp.device_class,
|
||||
dev_name(&acpi_dev->dev),
|
||||
0x92, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Misc device
|
||||
*/
|
||||
@ -2241,6 +2388,67 @@ static const struct file_operations toshiba_acpi_fops = {
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
/*
|
||||
* WWAN RFKill handlers
|
||||
*/
|
||||
static int toshiba_acpi_wwan_set_block(void *data, bool blocked)
|
||||
{
|
||||
struct toshiba_acpi_dev *dev = data;
|
||||
int ret;
|
||||
|
||||
ret = toshiba_wireless_status(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!dev->killswitch)
|
||||
return 0;
|
||||
|
||||
return toshiba_wwan_set(dev, !blocked);
|
||||
}
|
||||
|
||||
static void toshiba_acpi_wwan_poll(struct rfkill *rfkill, void *data)
|
||||
{
|
||||
struct toshiba_acpi_dev *dev = data;
|
||||
|
||||
if (toshiba_wireless_status(dev))
|
||||
return;
|
||||
|
||||
rfkill_set_hw_state(dev->wwan_rfk, !dev->killswitch);
|
||||
}
|
||||
|
||||
static const struct rfkill_ops wwan_rfk_ops = {
|
||||
.set_block = toshiba_acpi_wwan_set_block,
|
||||
.poll = toshiba_acpi_wwan_poll,
|
||||
};
|
||||
|
||||
static int toshiba_acpi_setup_wwan_rfkill(struct toshiba_acpi_dev *dev)
|
||||
{
|
||||
int ret = toshiba_wireless_status(dev);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev->wwan_rfk = rfkill_alloc("Toshiba WWAN",
|
||||
&dev->acpi_dev->dev,
|
||||
RFKILL_TYPE_WWAN,
|
||||
&wwan_rfk_ops,
|
||||
dev);
|
||||
if (!dev->wwan_rfk) {
|
||||
pr_err("Unable to allocate WWAN rfkill device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
rfkill_set_hw_state(dev->wwan_rfk, !dev->killswitch);
|
||||
|
||||
ret = rfkill_register(dev->wwan_rfk);
|
||||
if (ret) {
|
||||
pr_err("Unable to register WWAN rfkill device\n");
|
||||
rfkill_destroy(dev->wwan_rfk);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hotkeys
|
||||
*/
|
||||
@ -2484,6 +2692,14 @@ static int toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev)
|
||||
brightness = __get_lcd_brightness(dev);
|
||||
if (brightness < 0)
|
||||
return 0;
|
||||
/*
|
||||
* If transflective backlight is supported and the brightness is zero
|
||||
* (lowest brightness level), the set_lcd_brightness function will
|
||||
* activate the transflective backlight, making the LCD appear to be
|
||||
* turned off, simply increment the brightness level to avoid that.
|
||||
*/
|
||||
if (dev->tr_backlight_supported && brightness == 0)
|
||||
brightness++;
|
||||
ret = set_lcd_brightness(dev, brightness);
|
||||
if (ret) {
|
||||
pr_debug("Backlight method is read-only, disabling backlight support\n");
|
||||
@ -2561,6 +2777,8 @@ static void print_supported_features(struct toshiba_acpi_dev *dev)
|
||||
pr_cont(" panel-power-on");
|
||||
if (dev->usb_three_supported)
|
||||
pr_cont(" usb3");
|
||||
if (dev->wwan_supported)
|
||||
pr_cont(" wwan");
|
||||
|
||||
pr_cont("\n");
|
||||
}
|
||||
@ -2598,6 +2816,11 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev)
|
||||
if (dev->eco_led_registered)
|
||||
led_classdev_unregister(&dev->eco_led);
|
||||
|
||||
if (dev->wwan_rfk) {
|
||||
rfkill_unregister(dev->wwan_rfk);
|
||||
rfkill_destroy(dev->wwan_rfk);
|
||||
}
|
||||
|
||||
if (toshiba_acpi)
|
||||
toshiba_acpi = NULL;
|
||||
|
||||
@ -2736,6 +2959,10 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
|
||||
ret = get_fan_status(dev, &dummy);
|
||||
dev->fan_supported = !ret;
|
||||
|
||||
toshiba_wwan_available(dev);
|
||||
if (dev->wwan_supported)
|
||||
toshiba_acpi_setup_wwan_rfkill(dev);
|
||||
|
||||
print_supported_features(dev);
|
||||
|
||||
ret = sysfs_create_group(&dev->acpi_dev->dev.kobj,
|
||||
@ -2760,7 +2987,6 @@ error:
|
||||
static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event)
|
||||
{
|
||||
struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
|
||||
int ret;
|
||||
|
||||
switch (event) {
|
||||
case 0x80: /* Hotkeys and some system events */
|
||||
@ -2790,10 +3016,10 @@ static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event)
|
||||
pr_info("SATA power event received %x\n", event);
|
||||
break;
|
||||
case 0x92: /* Keyboard backlight mode changed */
|
||||
toshiba_acpi->kbd_event_generated = true;
|
||||
/* Update sysfs entries */
|
||||
ret = sysfs_update_group(&acpi_dev->dev.kobj,
|
||||
&toshiba_attr_group);
|
||||
if (ret)
|
||||
if (sysfs_update_group(&acpi_dev->dev.kobj,
|
||||
&toshiba_attr_group))
|
||||
pr_err("Unable to update sysfs entries\n");
|
||||
break;
|
||||
case 0x85: /* Unknown */
|
||||
@ -2808,7 +3034,8 @@ static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event)
|
||||
|
||||
acpi_bus_generate_netlink_event(acpi_dev->pnp.device_class,
|
||||
dev_name(&acpi_dev->dev),
|
||||
event, 0);
|
||||
event, (event == 0x80) ?
|
||||
dev->last_key_event : 0);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
@ -2832,12 +3059,15 @@ static int toshiba_acpi_resume(struct device *device)
|
||||
struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device));
|
||||
|
||||
if (dev->hotkey_dev) {
|
||||
int error = toshiba_acpi_enable_hotkeys(dev);
|
||||
|
||||
if (error)
|
||||
if (toshiba_acpi_enable_hotkeys(dev))
|
||||
pr_info("Unable to re-enable hotkeys\n");
|
||||
}
|
||||
|
||||
if (dev->wwan_rfk) {
|
||||
if (!toshiba_wireless_status(dev))
|
||||
rfkill_set_hw_state(dev->wwan_rfk, !dev->killswitch);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@ -78,7 +78,7 @@ static int toshiba_bluetooth_present(acpi_handle handle)
|
||||
*/
|
||||
result = acpi_evaluate_integer(handle, "_STA", NULL, &bt_present);
|
||||
if (ACPI_FAILURE(result)) {
|
||||
pr_err("ACPI call to query Bluetooth presence failed");
|
||||
pr_err("ACPI call to query Bluetooth presence failed\n");
|
||||
return -ENXIO;
|
||||
} else if (!bt_present) {
|
||||
pr_info("Bluetooth device not present\n");
|
||||
|
Loading…
Reference in New Issue
Block a user