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:
Linus Torvalds 2016-01-19 17:54:15 -08:00
commit 2b4015e9fb
22 changed files with 4580 additions and 111 deletions

View File

@ -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>

View 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

View 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 */

View File

@ -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

View File

@ -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

View File

@ -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:

View 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");

View File

@ -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))

View File

@ -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();

View File

@ -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 = {

View 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);

View File

@ -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);

View 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);

View 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");

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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);

View File

@ -52,7 +52,9 @@ struct tc1100_data {
u32 jogdial;
};
#ifdef CONFIG_PM
static struct tc1100_data suspend_data;
#endif
/* --------------------------------------------------------------------------
Device Management

View File

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

View File

@ -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

View File

@ -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");