mirror of
https://github.com/torvalds/linux.git
synced 2024-11-25 05:32:00 +00:00
platform-drivers-x86 for v5.8-1
* Add a support of the media keys on the ASUS laptop UX325JA/UX425JA * ASUS WMI driver can now handle 2-in-1 models T100TA, T100CHI, T100HA, T200TA * Big refactoring of Intel SCU driver with Elkhart Lake support has been added * Slim Bootloarder firmware update signaling WMI driver has been added * Thinkpad ACPI driver can handle dual fan configuration on new P and X models * Touchscreen DMI driver has been extended to support - MP-man MPWIN895CL tablet - ONDA V891 v5 tablet - techBite Arc 11.6 - Trekstor Twin 10.1 - Trekstor Yourbook C11B - Vinga J116 * Virtual Button driver got a few fixes to detect mode of 2-in-1 tablet models * Intel Speed Select tools update * Plenty of small cleanups here and there The following is an automated git shortlog grouped by driver: acerhdf: - replace space by * in modalias New drivers: - Add Elkhart Lake SCU/PMC support - Add Slim Bootloader firmware update signaling driver asus-laptop: - Drop duplicate check for led_classdev_unregister() asus-nb-wmi: - Revert "Do not load on Asus T100TA and T200TA" - Do not load on Asus T100TA and T200TA asus-wmi: - Ignore WMI events with code 0x79 - Add support for SW_TABLET_MODE - Move asus_wmi_input_init and _exit lower in the file - Drop duplicate check for led_classdev_unregister() - Reserve more space for struct bias_args - remove redundant initialization of variable status dcdbas: - Check SMBIOS for protected buffer address dell-laptop: - don't register micmute LED if there is no token dell-wmi: - Ignore keyboard attached / detached events device property: - export set_secondary_fwnode() to modules eeepc-laptop: - Drop duplicate check for led_classdev_unregister() hp-wmi: - Introduce HPWMI_POWER_FW_OR_HW as convenient shortcut - Convert simple_strtoul() to kstrtou32() - Refactor postcode_store() to follow standard patterns intel_cht_int33fe: - Fix spelling issues - Switch to use acpi_dev_hid_uid_match() - Convert to use set_secondary_fwnode() - Convert software node array to group intel-hid: - Add a quirk to support HP Spectre X2 (2015) intel_mid_powerbtn: - Convert to use new SCU IPC API intel_pmc_core: - avoid unused-function warnings - Change Jasper Lake S0ix debug reg map back to ICL intel_pmc_ipc: - Convert to MFD - Move PCI IDs to intel_scu_pcidrv.c - Drop intel_pmc_ipc_command() - Start using SCU IPC intel_scu_ipc: - Add managed function to register SCU IPC - Introduce new SCU IPC API - Move legacy SCU IPC API to a separate header - Log more information if SCU IPC command fails - Split out SCU IPC functionality from the SCU driver intel_scu_ipcutil: - Convert to use new SCU IPC API intel-speed-select: - Fix speed-select-base-freq-properties output on CLX-N intel_telemetry: - Add telemetry_get_pltdata() - Convert to use new SCU IPC API intel-vbtn: - Only blacklist SW_TABLET_MODE on the 9 / "Laptop" chasis-type - Detect switch position before registering the input-device - Move detect_tablet_mode() to higher in the file - Fix probe failure on devices with only switches - Also handle tablet-mode switch on "Detachable" and "Portable" chassis-types - Do not advertise switches to userspace if they are not there - Split keymap into buttons and switches parts - Use acpi_evaluate_integer() ISST: - Increase timeout lg-laptop: - Drop duplicate check for led_classdev_unregister() MAINTAINERS: - Add me as maintainer of Intel SCU drivers - Update entry for Intel Broxton PMC driver Merges of immutable branches: - Merge branch 'for-next' - Merge branch 'ib-mfd-x86-usb-watchdog-v5.7' - Merge branch 'ib-pdx86-properties' mfd: - intel_soc_pmic_mrfld: Convert to use new SCU IPC API - intel_soc_pmic_bxtwc: Convert to use new SCU IPC API - intel_soc_pmic: Add SCU IPC member to struct intel_soc_pmic samsung-laptop: - Drop duplicate check for led_classdev_unregister() software node: - Allow register and unregister software node groups sony-laptop: - Make resuming thermal profile safer - SNC calls should handle BUFFER types thinkpad_acpi: - Replace custom approach by kstrtoint() - Use strndup_user() in dispatch_proc_write() - Replace next_cmd(&buf) with strsep(&buf, ",") - Drop duplicate check for led_classdev_unregister() - Remove always false 'value < 0' statement - Add support for dual fan control tools/power/x86/intel-speed-select: - Fix invalid core mask - Increase CPU count - Fix json perf-profile output output - Update version - Enable clos for turbo-freq enable - Fix CLX-N package information output - Check support status before enable - Change debug to error toshiba_acpi: - Drop duplicate check for led_classdev_unregister() touchscreen_dmi: - Update Trekstor Twin 10.1 entry - Add info for the Trekstor Yourbook C11B - Drop comma in terminator line - add Vinga J116 touchscreen - Add info for the ONDA V891 v5 tablet - Add touchscreen info for techBite Arc 11.6. - Add info for the MP-man MPWIN895CL tablet usb: - typec: mux: Convert the Intel PMC Mux driver to use new SCU IPC API watchdog: - iTCO: fix link error - intel-mid_wdt: Convert to use new SCU IPC API wmi: - Describe function parameters - Fix indentation in some cases - Replace UUID redefinitions by their originals x86/platform/intel-mid: - Add empty stubs for intel_scu_devices_[create|destroy]() -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEqaflIX74DDDzMJJtb7wzTHR8rCgFAl7WCcoACgkQb7wzTHR8 rCi+Pg//dDpMXTxCcXivHZPJHwuAxbwPeJRV9uDKKBSnKqfxyYu37oQf8AQiLTsL PZOAIiwlrXw0Jd+EH79zN2DyCujBg16B6mf4dx3fMK95OWhPoslofyKRwl8kOBP5 QRZVpuwo6ayKwXV3cyFwWjXyWYJFL7+J3x+jjBmufBsoDJTn9edOCUa3oeHG0BYB 4A91pVKwtfNqqdL/pwd+A9mEZrFJnVilyPRoxTipbpPJqvWQi9dYgb3wHKt/1NM3 xPNd1GQHCI0Of4NGChszY0XdN4SyxFuyLmn1mogYq82r084QA4pLROb0+VFD2npd DQ4jxJqOwQDtC3gm789OeN6bZ0qnkO9HBwEmzVH7rwiajZxGW7U5rCgNYBahlTgr gY4kXIBXyOCO2/bItmrSvWDNBvVxD/THCfL4Q/cn6bNTy4TLTHAl2psQcsXIBT6/ Z5SdmHMhxc80eDAOTtSJj0ODeDGvAgbV20n+X260FFAsefDBuXkYMHEaRBf9n2LJ 8k9tauXZ6JdIc4K8/K+BaVl761Okl6PJPMTL7JsFqueHpyzZS7WclCYH5QQ1iN56 10QzddSGp+4HfFFCG2cVkjXG2AnUgT3kQgEOHyLIxp6yKY1PghFXHTEmrLuheYum jK93qSva5tvvZzy9UejXXsIkDyg76zaIla3rmEEYAmgzPDawR9I= =pprB -----END PGP SIGNATURE----- Merge tag 'platform-drivers-x86-v5.8-1' of git://git.infradead.org/linux-platform-drivers-x86 Pull x86 platform driver updates from Andy Shevchenko: - Add a support of the media keys on the ASUS laptop UX325JA/UX425JA - ASUS WMI driver can now handle 2-in-1 models T100TA, T100CHI, T100HA, T200TA - Big refactoring of Intel SCU driver with Elkhart Lake support has been added - Slim Bootloarder firmware update signaling WMI driver has been added - Thinkpad ACPI driver can handle dual fan configuration on new P and X models - Touchscreen DMI driver has been extended to support - MP-man MPWIN895CL tablet - ONDA V891 v5 tablet - techBite Arc 11.6 - Trekstor Twin 10.1 - Trekstor Yourbook C11B - Vinga J116 - Virtual Button driver got a few fixes to detect mode of 2-in-1 tablet models - Intel Speed Select tools update - Plenty of small cleanups here and there * tag 'platform-drivers-x86-v5.8-1' of git://git.infradead.org/linux-platform-drivers-x86: (89 commits) platform/x86: dcdbas: Check SMBIOS for protected buffer address platform/x86: asus_wmi: Reserve more space for struct bias_args platform/x86: intel-vbtn: Only blacklist SW_TABLET_MODE on the 9 / "Laptop" chasis-type platform/x86: intel-hid: Add a quirk to support HP Spectre X2 (2015) platform/x86: touchscreen_dmi: Update Trekstor Twin 10.1 entry platform/x86: touchscreen_dmi: Add info for the Trekstor Yourbook C11B platform/x86: hp-wmi: Introduce HPWMI_POWER_FW_OR_HW as convenient shortcut platform/x86: hp-wmi: Convert simple_strtoul() to kstrtou32() platform/x86: hp-wmi: Refactor postcode_store() to follow standard patterns platform/x86: acerhdf: replace space by * in modalias platform/x86: ISST: Increase timeout tools/power/x86/intel-speed-select: Fix invalid core mask tools/power/x86/intel-speed-select: Increase CPU count tools/power/x86/intel-speed-select: Fix json perf-profile output output platform/x86: dell-wmi: Ignore keyboard attached / detached events platform/x86: dell-laptop: don't register micmute LED if there is no token platform/x86: thinkpad_acpi: Replace custom approach by kstrtoint() platform/x86: thinkpad_acpi: Use strndup_user() in dispatch_proc_write() platform/x86: thinkpad_acpi: Replace next_cmd(&buf) with strsep(&buf, ",") platform/x86: intel-vbtn: Detect switch position before registering the input-device ...
This commit is contained in:
commit
a5a82e0a59
22
Documentation/ABI/obsolete/sysfs-driver-intel_pmc_bxt
Normal file
22
Documentation/ABI/obsolete/sysfs-driver-intel_pmc_bxt
Normal file
@ -0,0 +1,22 @@
|
||||
These files allow sending arbitrary IPC commands to the PMC/SCU which
|
||||
may be dangerous. These will be removed eventually and should not be
|
||||
used in any new applications.
|
||||
|
||||
What: /sys/bus/platform/devices/INT34D2:00/simplecmd
|
||||
Date: Jun 2015
|
||||
KernelVersion: 4.1
|
||||
Contact: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
Description: This interface allows userspace to send an arbitrary
|
||||
IPC command to the PMC/SCU.
|
||||
|
||||
Format: %d %d where first number is command and
|
||||
second number is subcommand.
|
||||
|
||||
What: /sys/bus/platform/devices/INT34D2:00/northpeak
|
||||
Date: Jun 2015
|
||||
KernelVersion: 4.1
|
||||
Contact: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
Description: This interface allows userspace to enable and disable
|
||||
Northpeak through the PMC/SCU.
|
||||
|
||||
Format: %u.
|
@ -0,0 +1,12 @@
|
||||
What: /sys/bus/wmi/devices/44FADEB1-B204-40F2-8581-394BBDC1B651/firmware_update_request
|
||||
Date: April 2020
|
||||
KernelVersion: 5.7
|
||||
Contact: "Jithu Joseph" <jithu.joseph@intel.com>
|
||||
Description:
|
||||
Allow user space entities to trigger update of Slim
|
||||
Bootloader (SBL). This attribute normally has a value
|
||||
of 0 and userspace can signal SBL to update firmware,
|
||||
on next reboot, by writing a value of 1.
|
||||
There are two available states:
|
||||
* 0 -> Skip firmware update while rebooting
|
||||
* 1 -> Attempt firmware update on next reboot
|
36
MAINTAINERS
36
MAINTAINERS
@ -8529,6 +8529,13 @@ L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/intel_atomisp2_pm.c
|
||||
|
||||
INTEL BROXTON PMC DRIVER
|
||||
M: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
M: Zha Qipeng <qipeng.zha@intel.com>
|
||||
S: Maintained
|
||||
F: drivers/mfd/intel_pmc_bxt.c
|
||||
F: include/linux/mfd/intel_pmc_bxt.h
|
||||
|
||||
INTEL C600 SERIES SAS CONTROLLER DRIVER
|
||||
M: Intel SCU Linux support <intel-linux-scu@intel.com>
|
||||
M: Artur Paszkiewicz <artur.paszkiewicz@intel.com>
|
||||
@ -8736,6 +8743,13 @@ F: include/uapi/linux/mic_common.h
|
||||
F: include/uapi/linux/mic_ioctl.h
|
||||
F: include/uapi/linux/scif_ioctl.h
|
||||
|
||||
INTEL P-Unit IPC DRIVER
|
||||
M: Zha Qipeng <qipeng.zha@intel.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: arch/x86/include/asm/intel_punit_ipc.h
|
||||
F: drivers/platform/x86/intel_punit_ipc.c
|
||||
|
||||
INTEL PMC CORE DRIVER
|
||||
M: Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com>
|
||||
M: Vishwanath Somayaji <vishwanath.somayaji@intel.com>
|
||||
@ -8743,15 +8757,6 @@ L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/intel_pmc_core*
|
||||
|
||||
INTEL PMC/P-Unit IPC DRIVER
|
||||
M: Zha Qipeng<qipeng.zha@intel.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: arch/x86/include/asm/intel_pmc_ipc.h
|
||||
F: arch/x86/include/asm/intel_punit_ipc.h
|
||||
F: drivers/platform/x86/intel_pmc_ipc.c
|
||||
F: drivers/platform/x86/intel_punit_ipc.c
|
||||
|
||||
INTEL PMIC GPIO DRIVERS
|
||||
M: Andy Shevchenko <andy@kernel.org>
|
||||
S: Maintained
|
||||
@ -8790,6 +8795,12 @@ S: Supported
|
||||
F: drivers/infiniband/hw/i40iw/
|
||||
F: include/uapi/rdma/i40iw-abi.h
|
||||
|
||||
INTEL SCU DRIVERS
|
||||
M: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
S: Maintained
|
||||
F: arch/x86/include/asm/intel_scu_ipc.h
|
||||
F: drivers/platform/x86/intel_scu_*
|
||||
|
||||
INTEL SPEED SELECT TECHNOLOGY
|
||||
M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
@ -8856,6 +8867,13 @@ F: Documentation/admin-guide/wimax/i2400m.rst
|
||||
F: drivers/net/wimax/i2400m/
|
||||
F: include/uapi/linux/wimax/i2400m.h
|
||||
|
||||
INTEL WMI SLIM BOOTLOADER (SBL) FIRMWARE UPDATE DRIVER
|
||||
M: Jithu Joseph <jithu.joseph@intel.com>
|
||||
R: Maurice Ma <maurice.ma@intel.com>
|
||||
S: Maintained
|
||||
W: https://slimbootloader.github.io/security/firmware-update.html
|
||||
F: drivers/platform/x86/intel-wmi-sbl-fw-update.c
|
||||
|
||||
INTEL WMI THUNDERBOLT FORCE POWER DRIVER
|
||||
M: Mario Limonciello <mario.limonciello@dell.com>
|
||||
S: Maintained
|
||||
|
@ -597,7 +597,7 @@ config X86_INTEL_MID
|
||||
select I2C
|
||||
select DW_APB_TIMER
|
||||
select APB_TIMER
|
||||
select INTEL_SCU_IPC
|
||||
select INTEL_SCU_PCI
|
||||
select MFD_INTEL_MSIC
|
||||
---help---
|
||||
Select to build a kernel capable of supporting Intel MID (Mobile
|
||||
|
@ -88,11 +88,17 @@ static inline bool intel_mid_has_msic(void)
|
||||
return (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_PENWELL);
|
||||
}
|
||||
|
||||
extern void intel_scu_devices_create(void);
|
||||
extern void intel_scu_devices_destroy(void);
|
||||
|
||||
#else /* !CONFIG_X86_INTEL_MID */
|
||||
|
||||
#define intel_mid_identify_cpu() 0
|
||||
#define intel_mid_has_msic() 0
|
||||
|
||||
static inline void intel_scu_devices_create(void) { }
|
||||
static inline void intel_scu_devices_destroy(void) { }
|
||||
|
||||
#endif /* !CONFIG_X86_INTEL_MID */
|
||||
|
||||
enum intel_mid_timer_options {
|
||||
@ -115,9 +121,6 @@ extern enum intel_mid_timer_options intel_mid_timer_options;
|
||||
#define SFI_MTMR_MAX_NUM 8
|
||||
#define SFI_MRTC_MAX 8
|
||||
|
||||
extern void intel_scu_devices_create(void);
|
||||
extern void intel_scu_devices_destroy(void);
|
||||
|
||||
/* VRTC timer */
|
||||
#define MRST_VRTC_MAP_SZ 1024
|
||||
/* #define MRST_VRTC_PGOFFSET 0xc00 */
|
||||
|
@ -1,59 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _ASM_X86_INTEL_PMC_IPC_H_
|
||||
#define _ASM_X86_INTEL_PMC_IPC_H_
|
||||
|
||||
/* Commands */
|
||||
#define PMC_IPC_PMIC_ACCESS 0xFF
|
||||
#define PMC_IPC_PMIC_ACCESS_READ 0x0
|
||||
#define PMC_IPC_PMIC_ACCESS_WRITE 0x1
|
||||
#define PMC_IPC_USB_PWR_CTRL 0xF0
|
||||
#define PMC_IPC_PMIC_BLACKLIST_SEL 0xEF
|
||||
#define PMC_IPC_PHY_CONFIG 0xEE
|
||||
#define PMC_IPC_NORTHPEAK_CTRL 0xED
|
||||
#define PMC_IPC_PM_DEBUG 0xEC
|
||||
#define PMC_IPC_PMC_TELEMTRY 0xEB
|
||||
#define PMC_IPC_PMC_FW_MSG_CTRL 0xEA
|
||||
|
||||
/* IPC return code */
|
||||
#define IPC_ERR_NONE 0
|
||||
#define IPC_ERR_CMD_NOT_SUPPORTED 1
|
||||
#define IPC_ERR_CMD_NOT_SERVICED 2
|
||||
#define IPC_ERR_UNABLE_TO_SERVICE 3
|
||||
#define IPC_ERR_CMD_INVALID 4
|
||||
#define IPC_ERR_CMD_FAILED 5
|
||||
#define IPC_ERR_EMSECURITY 6
|
||||
#define IPC_ERR_UNSIGNEDKERNEL 7
|
||||
|
||||
/* GCR reg offsets from gcr base*/
|
||||
#define PMC_GCR_PMC_CFG_REG 0x08
|
||||
#define PMC_GCR_TELEM_DEEP_S0IX_REG 0x78
|
||||
#define PMC_GCR_TELEM_SHLW_S0IX_REG 0x80
|
||||
|
||||
#if IS_ENABLED(CONFIG_INTEL_PMC_IPC)
|
||||
|
||||
int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
|
||||
u32 *out, u32 outlen);
|
||||
int intel_pmc_s0ix_counter_read(u64 *data);
|
||||
int intel_pmc_gcr_read64(u32 offset, u64 *data);
|
||||
|
||||
#else
|
||||
|
||||
static inline int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
|
||||
u32 *out, u32 outlen)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int intel_pmc_s0ix_counter_read(u64 *data)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int intel_pmc_gcr_read64(u32 offset, u64 *data)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#endif /*CONFIG_INTEL_PMC_IPC*/
|
||||
|
||||
#endif
|
@ -2,61 +2,69 @@
|
||||
#ifndef _ASM_X86_INTEL_SCU_IPC_H_
|
||||
#define _ASM_X86_INTEL_SCU_IPC_H_
|
||||
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/ioport.h>
|
||||
|
||||
#define IPCMSG_INDIRECT_READ 0x02
|
||||
#define IPCMSG_INDIRECT_WRITE 0x05
|
||||
struct device;
|
||||
struct intel_scu_ipc_dev;
|
||||
|
||||
#define IPCMSG_COLD_OFF 0x80 /* Only for Tangier */
|
||||
/**
|
||||
* struct intel_scu_ipc_data - Data used to configure SCU IPC
|
||||
* @mem: Base address of SCU IPC MMIO registers
|
||||
* @irq: The IRQ number used for SCU (optional)
|
||||
*/
|
||||
struct intel_scu_ipc_data {
|
||||
struct resource mem;
|
||||
int irq;
|
||||
};
|
||||
|
||||
#define IPCMSG_WARM_RESET 0xF0
|
||||
#define IPCMSG_COLD_RESET 0xF1
|
||||
#define IPCMSG_SOFT_RESET 0xF2
|
||||
#define IPCMSG_COLD_BOOT 0xF3
|
||||
struct intel_scu_ipc_dev *
|
||||
__intel_scu_ipc_register(struct device *parent,
|
||||
const struct intel_scu_ipc_data *scu_data,
|
||||
struct module *owner);
|
||||
|
||||
#define IPCMSG_VRTC 0xFA /* Set vRTC device */
|
||||
/* Command id associated with message IPCMSG_VRTC */
|
||||
#define IPC_CMD_VRTC_SETTIME 1 /* Set time */
|
||||
#define IPC_CMD_VRTC_SETALARM 2 /* Set alarm */
|
||||
#define intel_scu_ipc_register(parent, scu_data) \
|
||||
__intel_scu_ipc_register(parent, scu_data, THIS_MODULE)
|
||||
|
||||
/* Read single register */
|
||||
int intel_scu_ipc_ioread8(u16 addr, u8 *data);
|
||||
void intel_scu_ipc_unregister(struct intel_scu_ipc_dev *scu);
|
||||
|
||||
/* Read a vector */
|
||||
int intel_scu_ipc_readv(u16 *addr, u8 *data, int len);
|
||||
struct intel_scu_ipc_dev *
|
||||
__devm_intel_scu_ipc_register(struct device *parent,
|
||||
const struct intel_scu_ipc_data *scu_data,
|
||||
struct module *owner);
|
||||
|
||||
/* Write single register */
|
||||
int intel_scu_ipc_iowrite8(u16 addr, u8 data);
|
||||
#define devm_intel_scu_ipc_register(parent, scu_data) \
|
||||
__devm_intel_scu_ipc_register(parent, scu_data, THIS_MODULE)
|
||||
|
||||
/* Write a vector */
|
||||
int intel_scu_ipc_writev(u16 *addr, u8 *data, int len);
|
||||
struct intel_scu_ipc_dev *intel_scu_ipc_dev_get(void);
|
||||
void intel_scu_ipc_dev_put(struct intel_scu_ipc_dev *scu);
|
||||
struct intel_scu_ipc_dev *devm_intel_scu_ipc_dev_get(struct device *dev);
|
||||
|
||||
/* Update single register based on the mask */
|
||||
int intel_scu_ipc_update_register(u16 addr, u8 data, u8 mask);
|
||||
int intel_scu_ipc_dev_ioread8(struct intel_scu_ipc_dev *scu, u16 addr,
|
||||
u8 *data);
|
||||
int intel_scu_ipc_dev_iowrite8(struct intel_scu_ipc_dev *scu, u16 addr,
|
||||
u8 data);
|
||||
int intel_scu_ipc_dev_readv(struct intel_scu_ipc_dev *scu, u16 *addr,
|
||||
u8 *data, size_t len);
|
||||
int intel_scu_ipc_dev_writev(struct intel_scu_ipc_dev *scu, u16 *addr,
|
||||
u8 *data, size_t len);
|
||||
|
||||
/* Issue commands to the SCU with or without data */
|
||||
int intel_scu_ipc_simple_command(int cmd, int sub);
|
||||
int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
|
||||
u32 *out, int outlen);
|
||||
int intel_scu_ipc_dev_update(struct intel_scu_ipc_dev *scu, u16 addr,
|
||||
u8 data, u8 mask);
|
||||
|
||||
extern struct blocking_notifier_head intel_scu_notifier;
|
||||
int intel_scu_ipc_dev_simple_command(struct intel_scu_ipc_dev *scu, int cmd,
|
||||
int sub);
|
||||
int intel_scu_ipc_dev_command_with_size(struct intel_scu_ipc_dev *scu, int cmd,
|
||||
int sub, const void *in, size_t inlen,
|
||||
size_t size, void *out, size_t outlen);
|
||||
|
||||
static inline void intel_scu_notifier_add(struct notifier_block *nb)
|
||||
static inline int intel_scu_ipc_dev_command(struct intel_scu_ipc_dev *scu, int cmd,
|
||||
int sub, const void *in, size_t inlen,
|
||||
void *out, size_t outlen)
|
||||
{
|
||||
blocking_notifier_chain_register(&intel_scu_notifier, nb);
|
||||
return intel_scu_ipc_dev_command_with_size(scu, cmd, sub, in, inlen,
|
||||
inlen, out, outlen);
|
||||
}
|
||||
|
||||
static inline void intel_scu_notifier_remove(struct notifier_block *nb)
|
||||
{
|
||||
blocking_notifier_chain_unregister(&intel_scu_notifier, nb);
|
||||
}
|
||||
|
||||
static inline int intel_scu_notifier_post(unsigned long v, void *p)
|
||||
{
|
||||
return blocking_notifier_call_chain(&intel_scu_notifier, v, p);
|
||||
}
|
||||
|
||||
#define SCU_AVAILABLE 1
|
||||
#define SCU_DOWN 2
|
||||
#include <asm/intel_scu_ipc_legacy.h>
|
||||
|
||||
#endif
|
||||
|
91
arch/x86/include/asm/intel_scu_ipc_legacy.h
Normal file
91
arch/x86/include/asm/intel_scu_ipc_legacy.h
Normal file
@ -0,0 +1,91 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _ASM_X86_INTEL_SCU_IPC_LEGACY_H_
|
||||
#define _ASM_X86_INTEL_SCU_IPC_LEGACY_H_
|
||||
|
||||
#include <linux/notifier.h>
|
||||
|
||||
#define IPCMSG_INDIRECT_READ 0x02
|
||||
#define IPCMSG_INDIRECT_WRITE 0x05
|
||||
|
||||
#define IPCMSG_COLD_OFF 0x80 /* Only for Tangier */
|
||||
|
||||
#define IPCMSG_WARM_RESET 0xF0
|
||||
#define IPCMSG_COLD_RESET 0xF1
|
||||
#define IPCMSG_SOFT_RESET 0xF2
|
||||
#define IPCMSG_COLD_BOOT 0xF3
|
||||
|
||||
#define IPCMSG_VRTC 0xFA /* Set vRTC device */
|
||||
/* Command id associated with message IPCMSG_VRTC */
|
||||
#define IPC_CMD_VRTC_SETTIME 1 /* Set time */
|
||||
#define IPC_CMD_VRTC_SETALARM 2 /* Set alarm */
|
||||
|
||||
/* Don't call these in new code - they will be removed eventually */
|
||||
|
||||
/* Read single register */
|
||||
static inline int intel_scu_ipc_ioread8(u16 addr, u8 *data)
|
||||
{
|
||||
return intel_scu_ipc_dev_ioread8(NULL, addr, data);
|
||||
}
|
||||
|
||||
/* Read a vector */
|
||||
static inline int intel_scu_ipc_readv(u16 *addr, u8 *data, int len)
|
||||
{
|
||||
return intel_scu_ipc_dev_readv(NULL, addr, data, len);
|
||||
}
|
||||
|
||||
/* Write single register */
|
||||
static inline int intel_scu_ipc_iowrite8(u16 addr, u8 data)
|
||||
{
|
||||
return intel_scu_ipc_dev_iowrite8(NULL, addr, data);
|
||||
}
|
||||
|
||||
/* Write a vector */
|
||||
static inline int intel_scu_ipc_writev(u16 *addr, u8 *data, int len)
|
||||
{
|
||||
return intel_scu_ipc_dev_writev(NULL, addr, data, len);
|
||||
}
|
||||
|
||||
/* Update single register based on the mask */
|
||||
static inline int intel_scu_ipc_update_register(u16 addr, u8 data, u8 mask)
|
||||
{
|
||||
return intel_scu_ipc_dev_update(NULL, addr, data, mask);
|
||||
}
|
||||
|
||||
/* Issue commands to the SCU with or without data */
|
||||
static inline int intel_scu_ipc_simple_command(int cmd, int sub)
|
||||
{
|
||||
return intel_scu_ipc_dev_simple_command(NULL, cmd, sub);
|
||||
}
|
||||
|
||||
static inline int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
|
||||
u32 *out, int outlen)
|
||||
{
|
||||
/* New API takes both inlen and outlen as bytes so convert here */
|
||||
size_t inbytes = inlen * sizeof(u32);
|
||||
size_t outbytes = outlen * sizeof(u32);
|
||||
|
||||
return intel_scu_ipc_dev_command_with_size(NULL, cmd, sub, in, inbytes,
|
||||
inlen, out, outbytes);
|
||||
}
|
||||
|
||||
extern struct blocking_notifier_head intel_scu_notifier;
|
||||
|
||||
static inline void intel_scu_notifier_add(struct notifier_block *nb)
|
||||
{
|
||||
blocking_notifier_chain_register(&intel_scu_notifier, nb);
|
||||
}
|
||||
|
||||
static inline void intel_scu_notifier_remove(struct notifier_block *nb)
|
||||
{
|
||||
blocking_notifier_chain_unregister(&intel_scu_notifier, nb);
|
||||
}
|
||||
|
||||
static inline int intel_scu_notifier_post(unsigned long v, void *p)
|
||||
{
|
||||
return blocking_notifier_call_chain(&intel_scu_notifier, v, p);
|
||||
}
|
||||
|
||||
#define SCU_AVAILABLE 1
|
||||
#define SCU_DOWN 2
|
||||
|
||||
#endif
|
@ -10,6 +10,8 @@
|
||||
#define TELEM_MAX_EVENTS_SRAM 28
|
||||
#define TELEM_MAX_OS_ALLOCATED_EVENTS 20
|
||||
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
enum telemetry_unit {
|
||||
TELEM_PSS = 0,
|
||||
TELEM_IOSS,
|
||||
@ -51,6 +53,8 @@ struct telemetry_plt_config {
|
||||
struct telemetry_unit_config ioss_config;
|
||||
struct mutex telem_trace_lock;
|
||||
struct mutex telem_lock;
|
||||
struct intel_pmc_dev *pmc;
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
bool telem_in_use;
|
||||
};
|
||||
|
||||
@ -92,7 +96,7 @@ int telemetry_set_pltdata(const struct telemetry_core_ops *ops,
|
||||
|
||||
int telemetry_clear_pltdata(void);
|
||||
|
||||
int telemetry_pltconfig_valid(void);
|
||||
struct telemetry_plt_config *telemetry_get_pltdata(void);
|
||||
|
||||
int telemetry_get_evtname(enum telemetry_unit telem_unit,
|
||||
const char **name, int len);
|
||||
|
@ -3915,6 +3915,7 @@ void set_secondary_fwnode(struct device *dev, struct fwnode_handle *fwnode)
|
||||
else
|
||||
dev->fwnode = fwnode;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(set_secondary_fwnode);
|
||||
|
||||
/**
|
||||
* device_set_of_node_from_dev - reuse device-tree node of another device
|
||||
|
@ -726,6 +726,54 @@ void software_node_unregister_nodes(const struct software_node *nodes)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(software_node_unregister_nodes);
|
||||
|
||||
/**
|
||||
* software_node_register_node_group - Register a group of software nodes
|
||||
* @node_group: NULL terminated array of software node pointers to be registered
|
||||
*
|
||||
* Register multiple software nodes at once.
|
||||
*/
|
||||
int software_node_register_node_group(const struct software_node **node_group)
|
||||
{
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
if (!node_group)
|
||||
return 0;
|
||||
|
||||
for (i = 0; node_group[i]; i++) {
|
||||
ret = software_node_register(node_group[i]);
|
||||
if (ret) {
|
||||
software_node_unregister_node_group(node_group);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(software_node_register_node_group);
|
||||
|
||||
/**
|
||||
* software_node_unregister_node_group - Unregister a group of software nodes
|
||||
* @node_group: NULL terminated array of software node pointers to be unregistered
|
||||
*
|
||||
* Unregister multiple software nodes at once.
|
||||
*/
|
||||
void software_node_unregister_node_group(const struct software_node **node_group)
|
||||
{
|
||||
struct swnode *swnode;
|
||||
unsigned int i;
|
||||
|
||||
if (!node_group)
|
||||
return;
|
||||
|
||||
for (i = 0; node_group[i]; i++) {
|
||||
swnode = software_node_to_swnode(node_group[i]);
|
||||
if (swnode)
|
||||
fwnode_remove_software_node(&swnode->fwnode);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(software_node_unregister_node_group);
|
||||
|
||||
/**
|
||||
* software_node_register - Register static software node
|
||||
* @node: The software node to be registered
|
||||
|
@ -566,7 +566,7 @@ config INTEL_SOC_PMIC
|
||||
|
||||
config INTEL_SOC_PMIC_BXTWC
|
||||
tristate "Support for Intel Broxton Whiskey Cove PMIC"
|
||||
depends on INTEL_PMC_IPC
|
||||
depends on MFD_INTEL_PMC_BXT
|
||||
select MFD_CORE
|
||||
select REGMAP_IRQ
|
||||
help
|
||||
@ -608,7 +608,7 @@ config INTEL_SOC_PMIC_MRFLD
|
||||
tristate "Support for Intel Merrifield Basin Cove PMIC"
|
||||
depends on GPIOLIB
|
||||
depends on ACPI
|
||||
depends on INTEL_SCU_IPC
|
||||
depends on INTEL_SCU
|
||||
select MFD_CORE
|
||||
select REGMAP_IRQ
|
||||
help
|
||||
@ -640,13 +640,27 @@ config MFD_INTEL_LPSS_PCI
|
||||
|
||||
config MFD_INTEL_MSIC
|
||||
bool "Intel MSIC"
|
||||
depends on INTEL_SCU_IPC
|
||||
depends on INTEL_SCU
|
||||
select MFD_CORE
|
||||
help
|
||||
Select this option to enable access to Intel MSIC (Avatele
|
||||
Passage) chip. This chip embeds audio, battery, GPIO, etc.
|
||||
devices used in Intel Medfield platforms.
|
||||
|
||||
config MFD_INTEL_PMC_BXT
|
||||
tristate "Intel PMC Driver for Broxton"
|
||||
depends on X86
|
||||
depends on X86_PLATFORM_DEVICES
|
||||
depends on ACPI
|
||||
select INTEL_SCU_IPC
|
||||
select MFD_CORE
|
||||
help
|
||||
This driver provides support for the PMC (Power Management
|
||||
Controller) on Intel Broxton and Apollo Lake. The PMC is a
|
||||
multi-function device that exposes IPC, General Control
|
||||
Register and P-unit access. In addition this creates devices
|
||||
for iTCO watchdog and telemetry that are part of the PMC.
|
||||
|
||||
config MFD_IPAQ_MICRO
|
||||
bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support"
|
||||
depends on SA1100_H3100 || SA1100_H3600
|
||||
|
@ -213,6 +213,7 @@ obj-$(CONFIG_MFD_INTEL_LPSS) += intel-lpss.o
|
||||
obj-$(CONFIG_MFD_INTEL_LPSS_PCI) += intel-lpss-pci.o
|
||||
obj-$(CONFIG_MFD_INTEL_LPSS_ACPI) += intel-lpss-acpi.o
|
||||
obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
|
||||
obj-$(CONFIG_MFD_INTEL_PMC_BXT) += intel_pmc_bxt.o
|
||||
obj-$(CONFIG_MFD_PALMAS) += palmas.o
|
||||
obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o
|
||||
obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
|
||||
|
468
drivers/mfd/intel_pmc_bxt.c
Normal file
468
drivers/mfd/intel_pmc_bxt.c
Normal file
@ -0,0 +1,468 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Driver for the Intel Broxton PMC
|
||||
*
|
||||
* (C) Copyright 2014 - 2020 Intel Corporation
|
||||
*
|
||||
* This driver is based on Intel SCU IPC driver (intel_scu_ipc.c) by
|
||||
* Sreedhara DS <sreedhara.ds@intel.com>
|
||||
*
|
||||
* The PMC (Power Management Controller) running on the ARC processor
|
||||
* communicates with another entity running in the IA (Intel Architecture)
|
||||
* core through an IPC (Intel Processor Communications) mechanism which in
|
||||
* turn sends messages between the IA and the PMC.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/intel_pmc_bxt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/itco_wdt.h>
|
||||
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
/* Residency with clock rate at 19.2MHz to usecs */
|
||||
#define S0IX_RESIDENCY_IN_USECS(d, s) \
|
||||
({ \
|
||||
u64 result = 10ull * ((d) + (s)); \
|
||||
do_div(result, 192); \
|
||||
result; \
|
||||
})
|
||||
|
||||
/* Resources exported from IFWI */
|
||||
#define PLAT_RESOURCE_IPC_INDEX 0
|
||||
#define PLAT_RESOURCE_IPC_SIZE 0x1000
|
||||
#define PLAT_RESOURCE_GCR_OFFSET 0x1000
|
||||
#define PLAT_RESOURCE_GCR_SIZE 0x1000
|
||||
#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
|
||||
|
||||
/*
|
||||
* BIOS does not create an ACPI device for each PMC function, but
|
||||
* exports multiple resources from one ACPI device (IPC) for multiple
|
||||
* functions. This driver is responsible for creating a child device and
|
||||
* to export resources for those functions.
|
||||
*/
|
||||
#define SMI_EN_OFFSET 0x0040
|
||||
#define SMI_EN_SIZE 4
|
||||
#define TCO_BASE_OFFSET 0x0060
|
||||
#define TCO_REGS_SIZE 16
|
||||
#define TELEM_SSRAM_SIZE 240
|
||||
#define TELEM_PMC_SSRAM_OFFSET 0x1b00
|
||||
#define TELEM_PUNIT_SSRAM_OFFSET 0x1a00
|
||||
|
||||
/* Commands */
|
||||
#define PMC_NORTHPEAK_CTRL 0xed
|
||||
|
||||
static inline bool is_gcr_valid(u32 offset)
|
||||
{
|
||||
return offset < PLAT_RESOURCE_GCR_SIZE - 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_pmc_gcr_read64() - Read a 64-bit PMC GCR register
|
||||
* @pmc: PMC device pointer
|
||||
* @offset: offset of GCR register from GCR address base
|
||||
* @data: data pointer for storing the register output
|
||||
*
|
||||
* Reads the 64-bit PMC GCR register at given offset.
|
||||
*
|
||||
* Return: Negative value on error or 0 on success.
|
||||
*/
|
||||
int intel_pmc_gcr_read64(struct intel_pmc_dev *pmc, u32 offset, u64 *data)
|
||||
{
|
||||
if (!is_gcr_valid(offset))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&pmc->gcr_lock);
|
||||
*data = readq(pmc->gcr_mem_base + offset);
|
||||
spin_unlock(&pmc->gcr_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_pmc_gcr_read64);
|
||||
|
||||
/**
|
||||
* intel_pmc_gcr_update() - Update PMC GCR register bits
|
||||
* @pmc: PMC device pointer
|
||||
* @offset: offset of GCR register from GCR address base
|
||||
* @mask: bit mask for update operation
|
||||
* @val: update value
|
||||
*
|
||||
* Updates the bits of given GCR register as specified by
|
||||
* @mask and @val.
|
||||
*
|
||||
* Return: Negative value on error or 0 on success.
|
||||
*/
|
||||
int intel_pmc_gcr_update(struct intel_pmc_dev *pmc, u32 offset, u32 mask, u32 val)
|
||||
{
|
||||
u32 new_val;
|
||||
|
||||
if (!is_gcr_valid(offset))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&pmc->gcr_lock);
|
||||
new_val = readl(pmc->gcr_mem_base + offset);
|
||||
|
||||
new_val = (new_val & ~mask) | (val & mask);
|
||||
writel(new_val, pmc->gcr_mem_base + offset);
|
||||
|
||||
new_val = readl(pmc->gcr_mem_base + offset);
|
||||
spin_unlock(&pmc->gcr_lock);
|
||||
|
||||
/* Check whether the bit update is successful */
|
||||
return (new_val & mask) != (val & mask) ? -EIO : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_pmc_gcr_update);
|
||||
|
||||
/**
|
||||
* intel_pmc_s0ix_counter_read() - Read S0ix residency
|
||||
* @pmc: PMC device pointer
|
||||
* @data: Out param that contains current S0ix residency count.
|
||||
*
|
||||
* Writes to @data how many usecs the system has been in low-power S0ix
|
||||
* state.
|
||||
*
|
||||
* Return: An error code or 0 on success.
|
||||
*/
|
||||
int intel_pmc_s0ix_counter_read(struct intel_pmc_dev *pmc, u64 *data)
|
||||
{
|
||||
u64 deep, shlw;
|
||||
|
||||
spin_lock(&pmc->gcr_lock);
|
||||
deep = readq(pmc->gcr_mem_base + PMC_GCR_TELEM_DEEP_S0IX_REG);
|
||||
shlw = readq(pmc->gcr_mem_base + PMC_GCR_TELEM_SHLW_S0IX_REG);
|
||||
spin_unlock(&pmc->gcr_lock);
|
||||
|
||||
*data = S0IX_RESIDENCY_IN_USECS(deep, shlw);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_pmc_s0ix_counter_read);
|
||||
|
||||
/**
|
||||
* simplecmd_store() - Send a simple IPC command
|
||||
* @dev: Device under the attribute is
|
||||
* @attr: Attribute in question
|
||||
* @buf: Buffer holding data to be stored to the attribute
|
||||
* @count: Number of bytes in @buf
|
||||
*
|
||||
* Expects a string with two integers separated with space. These two
|
||||
* values hold command and subcommand that is send to PMC.
|
||||
*
|
||||
* Return: Number number of bytes written (@count) or negative errno in
|
||||
* case of error.
|
||||
*/
|
||||
static ssize_t simplecmd_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct intel_pmc_dev *pmc = dev_get_drvdata(dev);
|
||||
struct intel_scu_ipc_dev *scu = pmc->scu;
|
||||
int subcmd;
|
||||
int cmd;
|
||||
int ret;
|
||||
|
||||
ret = sscanf(buf, "%d %d", &cmd, &subcmd);
|
||||
if (ret != 2) {
|
||||
dev_err(dev, "Invalid values, expected: cmd subcmd\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = intel_scu_ipc_dev_simple_command(scu, cmd, subcmd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR_WO(simplecmd);
|
||||
|
||||
/**
|
||||
* northpeak_store() - Enable or disable Northpeak
|
||||
* @dev: Device under the attribute is
|
||||
* @attr: Attribute in question
|
||||
* @buf: Buffer holding data to be stored to the attribute
|
||||
* @count: Number of bytes in @buf
|
||||
*
|
||||
* Expects an unsigned integer. Non-zero enables Northpeak and zero
|
||||
* disables it.
|
||||
*
|
||||
* Return: Number number of bytes written (@count) or negative errno in
|
||||
* case of error.
|
||||
*/
|
||||
static ssize_t northpeak_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct intel_pmc_dev *pmc = dev_get_drvdata(dev);
|
||||
struct intel_scu_ipc_dev *scu = pmc->scu;
|
||||
unsigned long val;
|
||||
int subcmd;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 0, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Northpeak is enabled if subcmd == 1 and disabled if it is 0 */
|
||||
if (val)
|
||||
subcmd = 1;
|
||||
else
|
||||
subcmd = 0;
|
||||
|
||||
ret = intel_scu_ipc_dev_simple_command(scu, PMC_NORTHPEAK_CTRL, subcmd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR_WO(northpeak);
|
||||
|
||||
static struct attribute *intel_pmc_attrs[] = {
|
||||
&dev_attr_northpeak.attr,
|
||||
&dev_attr_simplecmd.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group intel_pmc_group = {
|
||||
.attrs = intel_pmc_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *intel_pmc_groups[] = {
|
||||
&intel_pmc_group,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct resource punit_res[6];
|
||||
|
||||
static struct mfd_cell punit = {
|
||||
.name = "intel_punit_ipc",
|
||||
.resources = punit_res,
|
||||
};
|
||||
|
||||
static struct itco_wdt_platform_data tco_pdata = {
|
||||
.name = "Apollo Lake SoC",
|
||||
.version = 5,
|
||||
.no_reboot_use_pmc = true,
|
||||
};
|
||||
|
||||
static struct resource tco_res[2];
|
||||
|
||||
static const struct mfd_cell tco = {
|
||||
.name = "iTCO_wdt",
|
||||
.ignore_resource_conflicts = true,
|
||||
.resources = tco_res,
|
||||
.num_resources = ARRAY_SIZE(tco_res),
|
||||
.platform_data = &tco_pdata,
|
||||
.pdata_size = sizeof(tco_pdata),
|
||||
};
|
||||
|
||||
static const struct resource telem_res[] = {
|
||||
DEFINE_RES_MEM(TELEM_PUNIT_SSRAM_OFFSET, TELEM_SSRAM_SIZE),
|
||||
DEFINE_RES_MEM(TELEM_PMC_SSRAM_OFFSET, TELEM_SSRAM_SIZE),
|
||||
};
|
||||
|
||||
static const struct mfd_cell telem = {
|
||||
.name = "intel_telemetry",
|
||||
.resources = telem_res,
|
||||
.num_resources = ARRAY_SIZE(telem_res),
|
||||
};
|
||||
|
||||
static int intel_pmc_get_tco_resources(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
|
||||
if (acpi_has_watchdog())
|
||||
return 0;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO,
|
||||
PLAT_RESOURCE_ACPI_IO_INDEX);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get IO resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tco_res[0].flags = IORESOURCE_IO;
|
||||
tco_res[0].start = res->start + TCO_BASE_OFFSET;
|
||||
tco_res[0].end = tco_res[0].start + TCO_REGS_SIZE - 1;
|
||||
tco_res[1].flags = IORESOURCE_IO;
|
||||
tco_res[1].start = res->start + SMI_EN_OFFSET;
|
||||
tco_res[1].end = tco_res[1].start + SMI_EN_SIZE - 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_pmc_get_resources(struct platform_device *pdev,
|
||||
struct intel_pmc_dev *pmc,
|
||||
struct intel_scu_ipc_data *scu_data)
|
||||
{
|
||||
struct resource gcr_res;
|
||||
size_t npunit_res = 0;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
scu_data->irq = platform_get_irq_optional(pdev, 0);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_IPC_INDEX);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get IPC resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* IPC registers */
|
||||
scu_data->mem.flags = res->flags;
|
||||
scu_data->mem.start = res->start;
|
||||
scu_data->mem.end = res->start + PLAT_RESOURCE_IPC_SIZE - 1;
|
||||
|
||||
/* GCR registers */
|
||||
gcr_res.flags = res->flags;
|
||||
gcr_res.start = res->start + PLAT_RESOURCE_GCR_OFFSET;
|
||||
gcr_res.end = gcr_res.start + PLAT_RESOURCE_GCR_SIZE - 1;
|
||||
|
||||
pmc->gcr_mem_base = devm_ioremap_resource(&pdev->dev, &gcr_res);
|
||||
if (IS_ERR(pmc->gcr_mem_base))
|
||||
return PTR_ERR(pmc->gcr_mem_base);
|
||||
|
||||
/* Only register iTCO watchdog if there is no WDAT ACPI table */
|
||||
ret = intel_pmc_get_tco_resources(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* BIOS data register */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_BIOS_DATA_INDEX);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get resource of P-unit BIOS data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
punit_res[npunit_res++] = *res;
|
||||
|
||||
/* BIOS interface register */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_BIOS_IFACE_INDEX);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get resource of P-unit BIOS interface\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
punit_res[npunit_res++] = *res;
|
||||
|
||||
/* ISP data register, optional */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_ISP_DATA_INDEX);
|
||||
if (res)
|
||||
punit_res[npunit_res++] = *res;
|
||||
|
||||
/* ISP interface register, optional */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_ISP_IFACE_INDEX);
|
||||
if (res)
|
||||
punit_res[npunit_res++] = *res;
|
||||
|
||||
/* GTD data register, optional */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_GTD_DATA_INDEX);
|
||||
if (res)
|
||||
punit_res[npunit_res++] = *res;
|
||||
|
||||
/* GTD interface register, optional */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_GTD_IFACE_INDEX);
|
||||
if (res)
|
||||
punit_res[npunit_res++] = *res;
|
||||
|
||||
punit.num_resources = npunit_res;
|
||||
|
||||
/* Telemetry SSRAM is optional */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_TELEM_SSRAM_INDEX);
|
||||
if (res)
|
||||
pmc->telem_base = res;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_pmc_create_devices(struct intel_pmc_dev *pmc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!acpi_has_watchdog()) {
|
||||
ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO, &tco,
|
||||
1, NULL, 0, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO, &punit, 1,
|
||||
NULL, 0, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pmc->telem_base) {
|
||||
ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO,
|
||||
&telem, 1, pmc->telem_base, 0, NULL);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id intel_pmc_acpi_ids[] = {
|
||||
{ "INT34D2" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, intel_pmc_acpi_ids);
|
||||
|
||||
static int intel_pmc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct intel_scu_ipc_data scu_data = {};
|
||||
struct intel_pmc_dev *pmc;
|
||||
int ret;
|
||||
|
||||
pmc = devm_kzalloc(&pdev->dev, sizeof(*pmc), GFP_KERNEL);
|
||||
if (!pmc)
|
||||
return -ENOMEM;
|
||||
|
||||
pmc->dev = &pdev->dev;
|
||||
spin_lock_init(&pmc->gcr_lock);
|
||||
|
||||
ret = intel_pmc_get_resources(pdev, pmc, &scu_data);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to request resources\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
pmc->scu = devm_intel_scu_ipc_register(&pdev->dev, &scu_data);
|
||||
if (IS_ERR(pmc->scu))
|
||||
return PTR_ERR(pmc->scu);
|
||||
|
||||
platform_set_drvdata(pdev, pmc);
|
||||
|
||||
ret = intel_pmc_create_devices(pmc);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "Failed to create PMC devices\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver intel_pmc_driver = {
|
||||
.probe = intel_pmc_probe,
|
||||
.driver = {
|
||||
.name = "intel_pmc_bxt",
|
||||
.acpi_match_table = intel_pmc_acpi_ids,
|
||||
.dev_groups = intel_pmc_groups,
|
||||
},
|
||||
};
|
||||
module_platform_driver(intel_pmc_driver);
|
||||
|
||||
MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
|
||||
MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
|
||||
MODULE_DESCRIPTION("Intel Broxton PMC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -15,7 +15,7 @@
|
||||
#include <linux/mfd/intel_soc_pmic_bxtwc.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/intel_pmc_ipc.h>
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
/* PMIC device registers */
|
||||
#define REG_ADDR_MASK 0xFF00
|
||||
@ -58,6 +58,10 @@
|
||||
/* Whiskey Cove PMIC share same ACPI ID between different platforms */
|
||||
#define BROXTON_PMIC_WC_HRV 4
|
||||
|
||||
#define PMC_PMIC_ACCESS 0xFF
|
||||
#define PMC_PMIC_READ 0x0
|
||||
#define PMC_PMIC_WRITE 0x1
|
||||
|
||||
enum bxtwc_irqs {
|
||||
BXTWC_PWRBTN_LVL1_IRQ = 0,
|
||||
BXTWC_TMU_LVL1_IRQ,
|
||||
@ -288,13 +292,12 @@ static int regmap_ipc_byte_reg_read(void *context, unsigned int reg,
|
||||
|
||||
ipc_in[0] = reg;
|
||||
ipc_in[1] = i2c_addr;
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS,
|
||||
PMC_IPC_PMIC_ACCESS_READ,
|
||||
ipc_in, sizeof(ipc_in), (u32 *)ipc_out, 1);
|
||||
if (ret) {
|
||||
dev_err(pmic->dev, "Failed to read from PMIC\n");
|
||||
ret = intel_scu_ipc_dev_command(pmic->scu, PMC_PMIC_ACCESS,
|
||||
PMC_PMIC_READ, ipc_in, sizeof(ipc_in),
|
||||
ipc_out, sizeof(ipc_out));
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = ipc_out[0];
|
||||
|
||||
return 0;
|
||||
@ -303,7 +306,6 @@ static int regmap_ipc_byte_reg_read(void *context, unsigned int reg,
|
||||
static int regmap_ipc_byte_reg_write(void *context, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
int ret;
|
||||
int i2c_addr;
|
||||
u8 ipc_in[3];
|
||||
struct intel_soc_pmic *pmic = context;
|
||||
@ -321,15 +323,9 @@ static int regmap_ipc_byte_reg_write(void *context, unsigned int reg,
|
||||
ipc_in[0] = reg;
|
||||
ipc_in[1] = i2c_addr;
|
||||
ipc_in[2] = val;
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS,
|
||||
PMC_IPC_PMIC_ACCESS_WRITE,
|
||||
ipc_in, sizeof(ipc_in), NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(pmic->dev, "Failed to write to PMIC\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return intel_scu_ipc_dev_command(pmic->scu, PMC_PMIC_ACCESS,
|
||||
PMC_PMIC_WRITE, ipc_in, sizeof(ipc_in),
|
||||
NULL, 0);
|
||||
}
|
||||
|
||||
/* sysfs interfaces to r/w PMIC registers, required by initial script */
|
||||
@ -457,6 +453,10 @@ static int bxtwc_probe(struct platform_device *pdev)
|
||||
dev_set_drvdata(&pdev->dev, pmic);
|
||||
pmic->dev = &pdev->dev;
|
||||
|
||||
pmic->scu = devm_intel_scu_ipc_dev_get(&pdev->dev);
|
||||
if (!pmic->scu)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
pmic->regmap = devm_regmap_init(&pdev->dev, NULL, pmic,
|
||||
&bxtwc_regmap_config);
|
||||
if (IS_ERR(pmic->regmap)) {
|
||||
|
@ -74,10 +74,11 @@ static const struct mfd_cell bcove_dev[] = {
|
||||
static int bcove_ipc_byte_reg_read(void *context, unsigned int reg,
|
||||
unsigned int *val)
|
||||
{
|
||||
struct intel_soc_pmic *pmic = context;
|
||||
u8 ipc_out;
|
||||
int ret;
|
||||
|
||||
ret = intel_scu_ipc_ioread8(reg, &ipc_out);
|
||||
ret = intel_scu_ipc_dev_ioread8(pmic->scu, reg, &ipc_out);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -88,10 +89,11 @@ static int bcove_ipc_byte_reg_read(void *context, unsigned int reg,
|
||||
static int bcove_ipc_byte_reg_write(void *context, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
struct intel_soc_pmic *pmic = context;
|
||||
u8 ipc_in = val;
|
||||
int ret;
|
||||
|
||||
ret = intel_scu_ipc_iowrite8(reg, ipc_in);
|
||||
ret = intel_scu_ipc_dev_iowrite8(pmic->scu, reg, ipc_in);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -117,6 +119,10 @@ static int bcove_probe(struct platform_device *pdev)
|
||||
if (!pmic)
|
||||
return -ENOMEM;
|
||||
|
||||
pmic->scu = devm_intel_scu_ipc_dev_get(dev);
|
||||
if (!pmic->scu)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, pmic);
|
||||
pmic->dev = &pdev->dev;
|
||||
|
||||
|
@ -78,6 +78,16 @@ config HUAWEI_WMI
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called huawei-wmi.
|
||||
|
||||
config INTEL_WMI_SBL_FW_UPDATE
|
||||
tristate "Intel WMI Slim Bootloader firmware update signaling driver"
|
||||
depends on ACPI_WMI
|
||||
help
|
||||
Say Y here if you want to be able to use the WMI interface to signal
|
||||
Slim Bootloader to trigger update on next reboot.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called intel-wmi-sbl-fw-update.
|
||||
|
||||
config INTEL_WMI_THUNDERBOLT
|
||||
tristate "Intel WMI thunderbolt force power driver"
|
||||
depends on ACPI_WMI
|
||||
@ -1269,7 +1279,8 @@ config INTEL_UNCORE_FREQ_CONTROL
|
||||
config INTEL_BXTWC_PMIC_TMU
|
||||
tristate "Intel BXT Whiskey Cove TMU Driver"
|
||||
depends on REGMAP
|
||||
depends on INTEL_SOC_PMIC_BXTWC && INTEL_PMC_IPC
|
||||
depends on MFD_INTEL_PMC_BXT
|
||||
depends on INTEL_SOC_PMIC_BXTWC
|
||||
---help---
|
||||
Select this driver to use Intel BXT Whiskey Cove PMIC TMU feature.
|
||||
This driver enables the alarm wakeup functionality in the TMU unit
|
||||
@ -1295,7 +1306,7 @@ config INTEL_MFLD_THERMAL
|
||||
|
||||
config INTEL_MID_POWER_BUTTON
|
||||
tristate "power button driver for Intel MID platforms"
|
||||
depends on INTEL_SCU_IPC && INPUT
|
||||
depends on INTEL_SCU && INPUT
|
||||
help
|
||||
This driver handles the power button on the Intel MID platforms.
|
||||
|
||||
@ -1327,14 +1338,6 @@ config INTEL_PMC_CORE
|
||||
- LTR Ignore
|
||||
- MPHY/PLL gating status (Sunrisepoint PCH only)
|
||||
|
||||
config INTEL_PMC_IPC
|
||||
tristate "Intel PMC IPC Driver"
|
||||
depends on ACPI && PCI
|
||||
---help---
|
||||
This driver provides support for PMC control on some Intel platforms.
|
||||
The PMC is an ARC processor which defines IPC commands for communication
|
||||
with other entities in the CPU.
|
||||
|
||||
config INTEL_PUNIT_IPC
|
||||
tristate "Intel P-Unit IPC Driver"
|
||||
---help---
|
||||
@ -1342,17 +1345,39 @@ config INTEL_PUNIT_IPC
|
||||
which is used to bridge the communications between kernel and P-Unit.
|
||||
|
||||
config INTEL_SCU_IPC
|
||||
bool "Intel SCU IPC Support"
|
||||
depends on X86_INTEL_MID
|
||||
default y
|
||||
---help---
|
||||
IPC is used to bridge the communications between kernel and SCU on
|
||||
some embedded Intel x86 platforms. This is not needed for PC-type
|
||||
machines.
|
||||
bool
|
||||
|
||||
config INTEL_SCU
|
||||
bool
|
||||
select INTEL_SCU_IPC
|
||||
|
||||
config INTEL_SCU_PCI
|
||||
bool "Intel SCU PCI driver"
|
||||
depends on PCI
|
||||
select INTEL_SCU
|
||||
help
|
||||
This driver is used to bridge the communications between kernel
|
||||
and SCU on some embedded Intel x86 platforms. It also creates
|
||||
devices that are connected to the SoC through the SCU.
|
||||
Platforms supported:
|
||||
Medfield
|
||||
Clovertrail
|
||||
Merrifield
|
||||
Broxton
|
||||
Apollo Lake
|
||||
|
||||
config INTEL_SCU_PLATFORM
|
||||
tristate "Intel SCU platform driver"
|
||||
depends on ACPI
|
||||
select INTEL_SCU
|
||||
help
|
||||
This driver is used to bridge the communications between kernel
|
||||
and SCU (sometimes called PMC as well). The driver currently
|
||||
supports Intel Elkhart Lake and compatible platforms.
|
||||
|
||||
config INTEL_SCU_IPC_UTIL
|
||||
tristate "Intel SCU IPC utility driver"
|
||||
depends on INTEL_SCU_IPC
|
||||
depends on INTEL_SCU
|
||||
---help---
|
||||
The IPC Util driver provides an interface with the SCU enabling
|
||||
low level access for debug work and updating the firmware. Say
|
||||
@ -1360,7 +1385,9 @@ config INTEL_SCU_IPC_UTIL
|
||||
|
||||
config INTEL_TELEMETRY
|
||||
tristate "Intel SoC Telemetry Driver"
|
||||
depends on INTEL_PMC_IPC && INTEL_PUNIT_IPC && X86_64
|
||||
depends on X86_64
|
||||
depends on MFD_INTEL_PMC_BXT
|
||||
depends on INTEL_PUNIT_IPC
|
||||
---help---
|
||||
This driver provides interfaces to configure and use
|
||||
telemetry for INTEL SoC from APL onwards. It is also
|
||||
|
@ -11,6 +11,7 @@ obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o
|
||||
# WMI drivers
|
||||
obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o
|
||||
obj-$(CONFIG_HUAWEI_WMI) += huawei-wmi.o
|
||||
obj-$(CONFIG_INTEL_WMI_SBL_FW_UPDATE) += intel-wmi-sbl-fw-update.o
|
||||
obj-$(CONFIG_INTEL_WMI_THUNDERBOLT) += intel-wmi-thunderbolt.o
|
||||
obj-$(CONFIG_MXM_WMI) += mxm-wmi.o
|
||||
obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o
|
||||
@ -138,9 +139,10 @@ obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o
|
||||
obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o
|
||||
obj-$(CONFIG_INTEL_MRFLD_PWRBTN) += intel_mrfld_pwrbtn.o
|
||||
obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o intel_pmc_core_pltdrv.o
|
||||
obj-$(CONFIG_INTEL_PMC_IPC) += intel_pmc_ipc.o
|
||||
obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o
|
||||
obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
|
||||
obj-$(CONFIG_INTEL_SCU_PCI) += intel_scu_pcidrv.o
|
||||
obj-$(CONFIG_INTEL_SCU_PLATFORM) += intel_scu_pltdrv.o
|
||||
obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o
|
||||
obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \
|
||||
intel_telemetry_pltdrv.o \
|
||||
|
@ -827,7 +827,7 @@ MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTMU*:");
|
||||
MODULE_ALIAS("dmi:*:*Packard*Bell*:pnENBFT*:");
|
||||
MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTMA*:");
|
||||
MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTVR46*:");
|
||||
MODULE_ALIAS("dmi:*:*Acer*:pnExtensa 5420*:");
|
||||
MODULE_ALIAS("dmi:*:*Acer*:pnExtensa*5420*:");
|
||||
|
||||
module_init(acerhdf_init);
|
||||
module_exit(acerhdf_exit);
|
||||
|
@ -640,22 +640,15 @@ static enum led_brightness asus_kled_cdev_get(struct led_classdev *led_cdev)
|
||||
|
||||
static void asus_led_exit(struct asus_laptop *asus)
|
||||
{
|
||||
if (!IS_ERR_OR_NULL(asus->wled.led.dev))
|
||||
led_classdev_unregister(&asus->wled.led);
|
||||
if (!IS_ERR_OR_NULL(asus->bled.led.dev))
|
||||
led_classdev_unregister(&asus->bled.led);
|
||||
if (!IS_ERR_OR_NULL(asus->mled.led.dev))
|
||||
led_classdev_unregister(&asus->mled.led);
|
||||
if (!IS_ERR_OR_NULL(asus->tled.led.dev))
|
||||
led_classdev_unregister(&asus->tled.led);
|
||||
if (!IS_ERR_OR_NULL(asus->pled.led.dev))
|
||||
led_classdev_unregister(&asus->pled.led);
|
||||
if (!IS_ERR_OR_NULL(asus->rled.led.dev))
|
||||
led_classdev_unregister(&asus->rled.led);
|
||||
if (!IS_ERR_OR_NULL(asus->gled.led.dev))
|
||||
led_classdev_unregister(&asus->gled.led);
|
||||
if (!IS_ERR_OR_NULL(asus->kled.led.dev))
|
||||
led_classdev_unregister(&asus->kled.led);
|
||||
led_classdev_unregister(&asus->wled.led);
|
||||
led_classdev_unregister(&asus->bled.led);
|
||||
led_classdev_unregister(&asus->mled.led);
|
||||
led_classdev_unregister(&asus->tled.led);
|
||||
led_classdev_unregister(&asus->pled.led);
|
||||
led_classdev_unregister(&asus->rled.led);
|
||||
led_classdev_unregister(&asus->gled.led);
|
||||
led_classdev_unregister(&asus->kled.led);
|
||||
|
||||
if (asus->led_workqueue) {
|
||||
destroy_workqueue(asus->led_workqueue);
|
||||
asus->led_workqueue = NULL;
|
||||
|
@ -472,6 +472,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
|
||||
{ KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } },
|
||||
{ KE_IGNORE, 0x6E, }, /* Low Battery notification */
|
||||
{ KE_KEY, 0x71, { KEY_F13 } }, /* General-purpose button */
|
||||
{ KE_IGNORE, 0x79, }, /* Charger type dectection notification */
|
||||
{ KE_KEY, 0x7a, { KEY_ALS_TOGGLE } }, /* Ambient Light Sensor Toggle */
|
||||
{ KE_KEY, 0x7c, { KEY_MICMUTE } },
|
||||
{ KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */
|
||||
|
@ -57,6 +57,7 @@ MODULE_LICENSE("GPL");
|
||||
#define NOTIFY_BRNDOWN_MIN 0x20
|
||||
#define NOTIFY_BRNDOWN_MAX 0x2e
|
||||
#define NOTIFY_FNLOCK_TOGGLE 0x4e
|
||||
#define NOTIFY_KBD_DOCK_CHANGE 0x75
|
||||
#define NOTIFY_KBD_BRTUP 0xc4
|
||||
#define NOTIFY_KBD_BRTDWN 0xc5
|
||||
#define NOTIFY_KBD_BRTTOGGLE 0xc7
|
||||
@ -116,6 +117,8 @@ struct bios_args {
|
||||
u32 arg0;
|
||||
u32 arg1;
|
||||
u32 arg2; /* At least TUF Gaming series uses 3 dword input buffer. */
|
||||
u32 arg4;
|
||||
u32 arg5;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
@ -222,45 +225,6 @@ struct asus_wmi {
|
||||
struct asus_wmi_driver *driver;
|
||||
};
|
||||
|
||||
/* Input **********************************************************************/
|
||||
|
||||
static int asus_wmi_input_init(struct asus_wmi *asus)
|
||||
{
|
||||
int err;
|
||||
|
||||
asus->inputdev = input_allocate_device();
|
||||
if (!asus->inputdev)
|
||||
return -ENOMEM;
|
||||
|
||||
asus->inputdev->name = asus->driver->input_name;
|
||||
asus->inputdev->phys = asus->driver->input_phys;
|
||||
asus->inputdev->id.bustype = BUS_HOST;
|
||||
asus->inputdev->dev.parent = &asus->platform_device->dev;
|
||||
set_bit(EV_REP, asus->inputdev->evbit);
|
||||
|
||||
err = sparse_keymap_setup(asus->inputdev, asus->driver->keymap, NULL);
|
||||
if (err)
|
||||
goto err_free_dev;
|
||||
|
||||
err = input_register_device(asus->inputdev);
|
||||
if (err)
|
||||
goto err_free_dev;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_dev:
|
||||
input_free_device(asus->inputdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void asus_wmi_input_exit(struct asus_wmi *asus)
|
||||
{
|
||||
if (asus->inputdev)
|
||||
input_unregister_device(asus->inputdev);
|
||||
|
||||
asus->inputdev = NULL;
|
||||
}
|
||||
|
||||
/* WMI ************************************************************************/
|
||||
|
||||
static int asus_wmi_evaluate_method3(u32 method_id,
|
||||
@ -309,7 +273,7 @@ static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args)
|
||||
struct acpi_buffer input;
|
||||
u64 phys_addr;
|
||||
u32 retval;
|
||||
u32 status = -1;
|
||||
u32 status;
|
||||
|
||||
/*
|
||||
* Copy to dma capable address otherwise memory corruption occurs as
|
||||
@ -381,6 +345,53 @@ static bool asus_wmi_dev_is_present(struct asus_wmi *asus, u32 dev_id)
|
||||
return status == 0 && (retval & ASUS_WMI_DSTS_PRESENCE_BIT);
|
||||
}
|
||||
|
||||
/* Input **********************************************************************/
|
||||
|
||||
static int asus_wmi_input_init(struct asus_wmi *asus)
|
||||
{
|
||||
int err, result;
|
||||
|
||||
asus->inputdev = input_allocate_device();
|
||||
if (!asus->inputdev)
|
||||
return -ENOMEM;
|
||||
|
||||
asus->inputdev->name = asus->driver->input_name;
|
||||
asus->inputdev->phys = asus->driver->input_phys;
|
||||
asus->inputdev->id.bustype = BUS_HOST;
|
||||
asus->inputdev->dev.parent = &asus->platform_device->dev;
|
||||
set_bit(EV_REP, asus->inputdev->evbit);
|
||||
|
||||
err = sparse_keymap_setup(asus->inputdev, asus->driver->keymap, NULL);
|
||||
if (err)
|
||||
goto err_free_dev;
|
||||
|
||||
result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_KBD_DOCK);
|
||||
if (result >= 0) {
|
||||
input_set_capability(asus->inputdev, EV_SW, SW_TABLET_MODE);
|
||||
input_report_switch(asus->inputdev, SW_TABLET_MODE, !result);
|
||||
} else if (result != -ENODEV) {
|
||||
pr_err("Error checking for keyboard-dock: %d\n", result);
|
||||
}
|
||||
|
||||
err = input_register_device(asus->inputdev);
|
||||
if (err)
|
||||
goto err_free_dev;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_dev:
|
||||
input_free_device(asus->inputdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void asus_wmi_input_exit(struct asus_wmi *asus)
|
||||
{
|
||||
if (asus->inputdev)
|
||||
input_unregister_device(asus->inputdev);
|
||||
|
||||
asus->inputdev = NULL;
|
||||
}
|
||||
|
||||
/* Battery ********************************************************************/
|
||||
|
||||
/* The battery maximum charging percentage */
|
||||
@ -675,14 +686,11 @@ static enum led_brightness lightbar_led_get(struct led_classdev *led_cdev)
|
||||
|
||||
static void asus_wmi_led_exit(struct asus_wmi *asus)
|
||||
{
|
||||
if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
|
||||
led_classdev_unregister(&asus->kbd_led);
|
||||
if (!IS_ERR_OR_NULL(asus->tpd_led.dev))
|
||||
led_classdev_unregister(&asus->tpd_led);
|
||||
if (!IS_ERR_OR_NULL(asus->wlan_led.dev))
|
||||
led_classdev_unregister(&asus->wlan_led);
|
||||
if (!IS_ERR_OR_NULL(asus->lightbar_led.dev))
|
||||
led_classdev_unregister(&asus->lightbar_led);
|
||||
led_classdev_unregister(&asus->kbd_led);
|
||||
led_classdev_unregister(&asus->tpd_led);
|
||||
led_classdev_unregister(&asus->wlan_led);
|
||||
led_classdev_unregister(&asus->lightbar_led);
|
||||
|
||||
if (asus->led_workqueue)
|
||||
destroy_workqueue(asus->led_workqueue);
|
||||
}
|
||||
@ -2058,9 +2066,9 @@ static int asus_wmi_get_event_code(u32 value)
|
||||
|
||||
static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
|
||||
{
|
||||
int orig_code;
|
||||
unsigned int key_value = 1;
|
||||
bool autorelease = 1;
|
||||
int result, orig_code;
|
||||
|
||||
orig_code = code;
|
||||
|
||||
@ -2105,6 +2113,17 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
|
||||
return;
|
||||
}
|
||||
|
||||
if (code == NOTIFY_KBD_DOCK_CHANGE) {
|
||||
result = asus_wmi_get_devstate_simple(asus,
|
||||
ASUS_WMI_DEVID_KBD_DOCK);
|
||||
if (result >= 0) {
|
||||
input_report_switch(asus->inputdev, SW_TABLET_MODE,
|
||||
!result);
|
||||
input_sync(asus->inputdev);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (asus->fan_boost_mode_available && code == NOTIFY_KBD_FBM) {
|
||||
fan_boost_mode_switch_next(asus);
|
||||
return;
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/gfp.h>
|
||||
@ -34,7 +35,7 @@
|
||||
#include "dcdbas.h"
|
||||
|
||||
#define DRIVER_NAME "dcdbas"
|
||||
#define DRIVER_VERSION "5.6.0-3.3"
|
||||
#define DRIVER_VERSION "5.6.0-3.4"
|
||||
#define DRIVER_DESCRIPTION "Dell Systems Management Base Driver"
|
||||
|
||||
static struct platform_device *dcdbas_pdev;
|
||||
@ -45,7 +46,7 @@ static unsigned long smi_data_buf_size;
|
||||
static unsigned long max_smi_data_buf_size = MAX_SMI_DATA_BUF_SIZE;
|
||||
static u32 smi_data_buf_phys_addr;
|
||||
static DEFINE_MUTEX(smi_data_lock);
|
||||
static u8 *eps_buffer;
|
||||
static u8 *bios_buffer;
|
||||
|
||||
static unsigned int host_control_action;
|
||||
static unsigned int host_control_smi_type;
|
||||
@ -518,8 +519,10 @@ static inline struct smm_eps_table *check_eps_table(u8 *addr)
|
||||
|
||||
static int dcdbas_check_wsmt(void)
|
||||
{
|
||||
const struct dmi_device *dev = NULL;
|
||||
struct acpi_table_wsmt *wsmt = NULL;
|
||||
struct smm_eps_table *eps = NULL;
|
||||
u64 bios_buf_paddr;
|
||||
u64 remap_size;
|
||||
u8 *addr;
|
||||
|
||||
@ -532,6 +535,17 @@ static int dcdbas_check_wsmt(void)
|
||||
!(wsmt->protection_flags & ACPI_WSMT_COMM_BUFFER_NESTED_PTR_PROTECTION))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* BIOS could provide the address/size of the protected buffer
|
||||
* in an SMBIOS string or in an EPS structure in 0xFxxxx.
|
||||
*/
|
||||
|
||||
/* Check SMBIOS for buffer address */
|
||||
while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev)))
|
||||
if (sscanf(dev->name, "30[%16llx;%8llx]", &bios_buf_paddr,
|
||||
&remap_size) == 2)
|
||||
goto remap;
|
||||
|
||||
/* Scan for EPS (entry point structure) */
|
||||
for (addr = (u8 *)__va(0xf0000);
|
||||
addr < (u8 *)__va(0x100000 - sizeof(struct smm_eps_table));
|
||||
@ -542,34 +556,37 @@ static int dcdbas_check_wsmt(void)
|
||||
}
|
||||
|
||||
if (!eps) {
|
||||
dev_dbg(&dcdbas_pdev->dev, "found WSMT, but no EPS found\n");
|
||||
dev_dbg(&dcdbas_pdev->dev, "found WSMT, but no firmware buffer found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
bios_buf_paddr = eps->smm_comm_buff_addr;
|
||||
remap_size = eps->num_of_4k_pages * PAGE_SIZE;
|
||||
|
||||
remap:
|
||||
/*
|
||||
* Get physical address of buffer and map to virtual address.
|
||||
* Table gives size in 4K pages, regardless of actual system page size.
|
||||
*/
|
||||
if (upper_32_bits(eps->smm_comm_buff_addr + 8)) {
|
||||
dev_warn(&dcdbas_pdev->dev, "found WSMT, but EPS buffer address is above 4GB\n");
|
||||
if (upper_32_bits(bios_buf_paddr + 8)) {
|
||||
dev_warn(&dcdbas_pdev->dev, "found WSMT, but buffer address is above 4GB\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
/*
|
||||
* Limit remap size to MAX_SMI_DATA_BUF_SIZE + 8 (since the first 8
|
||||
* bytes are used for a semaphore, not the data buffer itself).
|
||||
*/
|
||||
remap_size = eps->num_of_4k_pages * PAGE_SIZE;
|
||||
if (remap_size > MAX_SMI_DATA_BUF_SIZE + 8)
|
||||
remap_size = MAX_SMI_DATA_BUF_SIZE + 8;
|
||||
eps_buffer = memremap(eps->smm_comm_buff_addr, remap_size, MEMREMAP_WB);
|
||||
if (!eps_buffer) {
|
||||
dev_warn(&dcdbas_pdev->dev, "found WSMT, but failed to map EPS buffer\n");
|
||||
|
||||
bios_buffer = memremap(bios_buf_paddr, remap_size, MEMREMAP_WB);
|
||||
if (!bios_buffer) {
|
||||
dev_warn(&dcdbas_pdev->dev, "found WSMT, but failed to map buffer\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* First 8 bytes is for a semaphore, not part of the smi_data_buf */
|
||||
smi_data_buf_phys_addr = eps->smm_comm_buff_addr + 8;
|
||||
smi_data_buf = eps_buffer + 8;
|
||||
smi_data_buf_phys_addr = bios_buf_paddr + 8;
|
||||
smi_data_buf = bios_buffer + 8;
|
||||
smi_data_buf_size = remap_size - 8;
|
||||
max_smi_data_buf_size = smi_data_buf_size;
|
||||
wsmt_enabled = true;
|
||||
@ -736,8 +753,8 @@ static void __exit dcdbas_exit(void)
|
||||
*/
|
||||
if (dcdbas_pdev)
|
||||
smi_data_buf_free();
|
||||
if (eps_buffer)
|
||||
memunmap(eps_buffer);
|
||||
if (bios_buffer)
|
||||
memunmap(bios_buffer);
|
||||
platform_device_unregister(dcdbas_pdev_reg);
|
||||
platform_driver_unregister(&dcdbas_driver);
|
||||
}
|
||||
|
@ -2204,10 +2204,13 @@ static int __init dell_init(void)
|
||||
|
||||
dell_laptop_register_notifier(&dell_laptop_notifier);
|
||||
|
||||
micmute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
|
||||
ret = led_classdev_register(&platform_device->dev, &micmute_led_cdev);
|
||||
if (ret < 0)
|
||||
goto fail_led;
|
||||
if (dell_smbios_find_token(GLOBAL_MIC_MUTE_DISABLE) &&
|
||||
dell_smbios_find_token(GLOBAL_MIC_MUTE_ENABLE)) {
|
||||
micmute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
|
||||
ret = led_classdev_register(&platform_device->dev, &micmute_led_cdev);
|
||||
if (ret < 0)
|
||||
goto fail_led;
|
||||
}
|
||||
|
||||
if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
|
||||
return 0;
|
||||
|
@ -310,6 +310,16 @@ static const struct key_entry dell_wmi_keymap_type_0011[] = {
|
||||
/* Battery inserted */
|
||||
{ KE_IGNORE, 0xfff1, { KEY_RESERVED } },
|
||||
|
||||
/*
|
||||
* Detachable keyboard detached / undocked
|
||||
* Note SW_TABLET_MODE is already reported through the intel_vbtn
|
||||
* driver for this, so we ignore it.
|
||||
*/
|
||||
{ KE_IGNORE, 0xfff2, { KEY_RESERVED } },
|
||||
|
||||
/* Detachable keyboard attached / docked */
|
||||
{ KE_IGNORE, 0xfff3, { KEY_RESERVED } },
|
||||
|
||||
/* Keyboard backlight level changed */
|
||||
{ KE_IGNORE, KBD_LED_OFF_TOKEN, { KEY_RESERVED } },
|
||||
{ KE_IGNORE, KBD_LED_ON_TOKEN, { KEY_RESERVED } },
|
||||
|
@ -541,13 +541,11 @@ static int eeepc_led_init(struct eeepc_laptop *eeepc)
|
||||
|
||||
static void eeepc_led_exit(struct eeepc_laptop *eeepc)
|
||||
{
|
||||
if (!IS_ERR_OR_NULL(eeepc->tpd_led.dev))
|
||||
led_classdev_unregister(&eeepc->tpd_led);
|
||||
led_classdev_unregister(&eeepc->tpd_led);
|
||||
if (eeepc->led_workqueue)
|
||||
destroy_workqueue(eeepc->led_workqueue);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* PCI hotplug (for wlan rfkill)
|
||||
*/
|
||||
|
@ -111,10 +111,10 @@ enum hp_wireless2_bits {
|
||||
HPWMI_POWER_SOFT = 0x02,
|
||||
HPWMI_POWER_BIOS = 0x04,
|
||||
HPWMI_POWER_HARD = 0x08,
|
||||
HPWMI_POWER_FW_OR_HW = HPWMI_POWER_BIOS | HPWMI_POWER_HARD,
|
||||
};
|
||||
|
||||
#define IS_HWBLOCKED(x) ((x & (HPWMI_POWER_BIOS | HPWMI_POWER_HARD)) \
|
||||
!= (HPWMI_POWER_BIOS | HPWMI_POWER_HARD))
|
||||
#define IS_HWBLOCKED(x) ((x & HPWMI_POWER_FW_OR_HW) != HPWMI_POWER_FW_OR_HW)
|
||||
#define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT)
|
||||
|
||||
struct bios_rfkill2_device_state {
|
||||
@ -461,8 +461,14 @@ static ssize_t postcode_show(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t als_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
u32 tmp = simple_strtoul(buf, NULL, 10);
|
||||
int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, HPWMI_WRITE, &tmp,
|
||||
u32 tmp;
|
||||
int ret;
|
||||
|
||||
ret = kstrtou32(buf, 10, &tmp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, HPWMI_WRITE, &tmp,
|
||||
sizeof(tmp), sizeof(tmp));
|
||||
if (ret)
|
||||
return ret < 0 ? ret : -EINVAL;
|
||||
@ -473,22 +479,20 @@ static ssize_t als_store(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t postcode_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
long unsigned int tmp2;
|
||||
u32 tmp = 1;
|
||||
bool clear;
|
||||
int ret;
|
||||
u32 tmp;
|
||||
|
||||
ret = kstrtoul(buf, 10, &tmp2);
|
||||
if (!ret && tmp2 != 1)
|
||||
ret = -EINVAL;
|
||||
ret = kstrtobool(buf, &clear);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
if (clear == false)
|
||||
return -EINVAL;
|
||||
|
||||
/* Clear the POST error code. It is kept until until cleared. */
|
||||
tmp = (u32) tmp2;
|
||||
ret = hp_wmi_perform_query(HPWMI_POSTCODEERROR_QUERY, HPWMI_WRITE, &tmp,
|
||||
sizeof(tmp), sizeof(tmp));
|
||||
|
||||
out:
|
||||
if (ret)
|
||||
return ret < 0 ? ret : -EINVAL;
|
||||
|
||||
|
@ -79,6 +79,13 @@ static const struct dmi_system_id button_array_table[] = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Wacom MobileStudio Pro 16"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "HP Spectre x2 (2015)",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "HP"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "HP Spectre x2 Detachable"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -40,28 +40,70 @@ static const struct key_entry intel_vbtn_keymap[] = {
|
||||
{ KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } }, /* volume-down key release */
|
||||
{ KE_KEY, 0xC8, { KEY_ROTATE_LOCK_TOGGLE } }, /* rotate-lock key press */
|
||||
{ KE_KEY, 0xC9, { KEY_ROTATE_LOCK_TOGGLE } }, /* rotate-lock key release */
|
||||
};
|
||||
|
||||
static const struct key_entry intel_vbtn_switchmap[] = {
|
||||
{ KE_SW, 0xCA, { .sw = { SW_DOCK, 1 } } }, /* Docked */
|
||||
{ KE_SW, 0xCB, { .sw = { SW_DOCK, 0 } } }, /* Undocked */
|
||||
{ KE_SW, 0xCC, { .sw = { SW_TABLET_MODE, 1 } } }, /* Tablet */
|
||||
{ KE_SW, 0xCD, { .sw = { SW_TABLET_MODE, 0 } } }, /* Laptop */
|
||||
{ KE_END },
|
||||
};
|
||||
|
||||
#define KEYMAP_LEN \
|
||||
(ARRAY_SIZE(intel_vbtn_keymap) + ARRAY_SIZE(intel_vbtn_switchmap) + 1)
|
||||
|
||||
struct intel_vbtn_priv {
|
||||
struct key_entry keymap[KEYMAP_LEN];
|
||||
struct input_dev *input_dev;
|
||||
bool has_buttons;
|
||||
bool has_switches;
|
||||
bool wakeup_mode;
|
||||
};
|
||||
|
||||
static void detect_tablet_mode(struct platform_device *device)
|
||||
{
|
||||
struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
|
||||
acpi_handle handle = ACPI_HANDLE(&device->dev);
|
||||
unsigned long long vgbs;
|
||||
acpi_status status;
|
||||
int m;
|
||||
|
||||
status = acpi_evaluate_integer(handle, "VGBS", NULL, &vgbs);
|
||||
if (ACPI_FAILURE(status))
|
||||
return;
|
||||
|
||||
m = !(vgbs & TABLET_MODE_FLAG);
|
||||
input_report_switch(priv->input_dev, SW_TABLET_MODE, m);
|
||||
m = (vgbs & DOCK_MODE_FLAG) ? 1 : 0;
|
||||
input_report_switch(priv->input_dev, SW_DOCK, m);
|
||||
}
|
||||
|
||||
static int intel_vbtn_input_setup(struct platform_device *device)
|
||||
{
|
||||
struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
|
||||
int ret;
|
||||
int ret, keymap_len = 0;
|
||||
|
||||
if (priv->has_buttons) {
|
||||
memcpy(&priv->keymap[keymap_len], intel_vbtn_keymap,
|
||||
ARRAY_SIZE(intel_vbtn_keymap) *
|
||||
sizeof(struct key_entry));
|
||||
keymap_len += ARRAY_SIZE(intel_vbtn_keymap);
|
||||
}
|
||||
|
||||
if (priv->has_switches) {
|
||||
memcpy(&priv->keymap[keymap_len], intel_vbtn_switchmap,
|
||||
ARRAY_SIZE(intel_vbtn_switchmap) *
|
||||
sizeof(struct key_entry));
|
||||
keymap_len += ARRAY_SIZE(intel_vbtn_switchmap);
|
||||
}
|
||||
|
||||
priv->keymap[keymap_len].type = KE_END;
|
||||
|
||||
priv->input_dev = devm_input_allocate_device(&device->dev);
|
||||
if (!priv->input_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = sparse_keymap_setup(priv->input_dev, intel_vbtn_keymap, NULL);
|
||||
ret = sparse_keymap_setup(priv->input_dev, priv->keymap, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -69,6 +111,9 @@ static int intel_vbtn_input_setup(struct platform_device *device)
|
||||
priv->input_dev->name = "Intel Virtual Button driver";
|
||||
priv->input_dev->id.bustype = BUS_HOST;
|
||||
|
||||
if (priv->has_switches)
|
||||
detect_tablet_mode(device);
|
||||
|
||||
return input_register_device(priv->input_dev);
|
||||
}
|
||||
|
||||
@ -114,44 +159,46 @@ out_unknown:
|
||||
dev_dbg(&device->dev, "unknown event index 0x%x\n", event);
|
||||
}
|
||||
|
||||
static void detect_tablet_mode(struct platform_device *device)
|
||||
static bool intel_vbtn_has_buttons(acpi_handle handle)
|
||||
{
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_object(handle, "VBDL", NULL, NULL);
|
||||
return ACPI_SUCCESS(status);
|
||||
}
|
||||
|
||||
static bool intel_vbtn_has_switches(acpi_handle handle)
|
||||
{
|
||||
const char *chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE);
|
||||
struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
|
||||
acpi_handle handle = ACPI_HANDLE(&device->dev);
|
||||
struct acpi_buffer vgbs_output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *obj;
|
||||
unsigned long long vgbs;
|
||||
acpi_status status;
|
||||
int m;
|
||||
|
||||
if (!(chassis_type && strcmp(chassis_type, "31") == 0))
|
||||
goto out;
|
||||
/*
|
||||
* Some normal laptops have a VGBS method despite being non-convertible
|
||||
* and their VGBS method always returns 0, causing detect_tablet_mode()
|
||||
* to report SW_TABLET_MODE=1 to userspace, which causes issues.
|
||||
* These laptops have a DMI chassis_type of 9 ("Laptop"), do not report
|
||||
* switches on any devices with a DMI chassis_type of 9.
|
||||
*/
|
||||
if (chassis_type && strcmp(chassis_type, "9") == 0)
|
||||
return false;
|
||||
|
||||
status = acpi_evaluate_object(handle, "VGBS", NULL, &vgbs_output);
|
||||
if (ACPI_FAILURE(status))
|
||||
goto out;
|
||||
|
||||
obj = vgbs_output.pointer;
|
||||
if (!(obj && obj->type == ACPI_TYPE_INTEGER))
|
||||
goto out;
|
||||
|
||||
m = !(obj->integer.value & TABLET_MODE_FLAG);
|
||||
input_report_switch(priv->input_dev, SW_TABLET_MODE, m);
|
||||
m = (obj->integer.value & DOCK_MODE_FLAG) ? 1 : 0;
|
||||
input_report_switch(priv->input_dev, SW_DOCK, m);
|
||||
out:
|
||||
kfree(vgbs_output.pointer);
|
||||
status = acpi_evaluate_integer(handle, "VGBS", NULL, &vgbs);
|
||||
return ACPI_SUCCESS(status);
|
||||
}
|
||||
|
||||
static int intel_vbtn_probe(struct platform_device *device)
|
||||
{
|
||||
acpi_handle handle = ACPI_HANDLE(&device->dev);
|
||||
bool has_buttons, has_switches;
|
||||
struct intel_vbtn_priv *priv;
|
||||
acpi_status status;
|
||||
int err;
|
||||
|
||||
status = acpi_evaluate_object(handle, "VBDL", NULL, NULL);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
has_buttons = intel_vbtn_has_buttons(handle);
|
||||
has_switches = intel_vbtn_has_switches(handle);
|
||||
|
||||
if (!has_buttons && !has_switches) {
|
||||
dev_warn(&device->dev, "failed to read Intel Virtual Button driver\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
@ -161,14 +208,15 @@ static int intel_vbtn_probe(struct platform_device *device)
|
||||
return -ENOMEM;
|
||||
dev_set_drvdata(&device->dev, priv);
|
||||
|
||||
priv->has_buttons = has_buttons;
|
||||
priv->has_switches = has_switches;
|
||||
|
||||
err = intel_vbtn_input_setup(device);
|
||||
if (err) {
|
||||
pr_err("Failed to setup Intel Virtual Button\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
detect_tablet_mode(device);
|
||||
|
||||
status = acpi_install_notify_handler(handle,
|
||||
ACPI_DEVICE_NOTIFY,
|
||||
notify_handler,
|
||||
|
145
drivers/platform/x86/intel-wmi-sbl-fw-update.c
Normal file
145
drivers/platform/x86/intel-wmi-sbl-fw-update.c
Normal file
@ -0,0 +1,145 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Slim Bootloader(SBL) firmware update signaling driver
|
||||
*
|
||||
* Slim Bootloader is a small, open-source, non UEFI compliant, boot firmware
|
||||
* optimized for running on certain Intel platforms.
|
||||
*
|
||||
* SBL exposes an ACPI-WMI device via /sys/bus/wmi/devices/<INTEL_WMI_SBL_GUID>.
|
||||
* This driver further adds "firmware_update_request" device attribute.
|
||||
* This attribute normally has a value of 0 and userspace can signal SBL
|
||||
* to update firmware, on next reboot, by writing a value of 1.
|
||||
*
|
||||
* More details of SBL firmware update process is available at:
|
||||
* https://slimbootloader.github.io/security/firmware-update.html
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/wmi.h>
|
||||
|
||||
#define INTEL_WMI_SBL_GUID "44FADEB1-B204-40F2-8581-394BBDC1B651"
|
||||
|
||||
static int get_fwu_request(struct device *dev, u32 *out)
|
||||
{
|
||||
struct acpi_buffer result = {ACPI_ALLOCATE_BUFFER, NULL};
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
|
||||
status = wmi_query_block(INTEL_WMI_SBL_GUID, 0, &result);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_err(dev, "wmi_query_block failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
obj = (union acpi_object *)result.pointer;
|
||||
if (!obj || obj->type != ACPI_TYPE_INTEGER) {
|
||||
dev_warn(dev, "wmi_query_block returned invalid value\n");
|
||||
kfree(obj);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*out = obj->integer.value;
|
||||
kfree(obj);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_fwu_request(struct device *dev, u32 in)
|
||||
{
|
||||
struct acpi_buffer input;
|
||||
acpi_status status;
|
||||
u32 value;
|
||||
|
||||
value = in;
|
||||
input.length = sizeof(u32);
|
||||
input.pointer = &value;
|
||||
|
||||
status = wmi_set_block(INTEL_WMI_SBL_GUID, 0, &input);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_err(dev, "wmi_set_block failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t firmware_update_request_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = get_fwu_request(dev, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t firmware_update_request_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtouint(buf, 0, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* May later be extended to support values other than 0 and 1 */
|
||||
if (val > 1)
|
||||
return -ERANGE;
|
||||
|
||||
ret = set_fwu_request(dev, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR_RW(firmware_update_request);
|
||||
|
||||
static struct attribute *firmware_update_attrs[] = {
|
||||
&dev_attr_firmware_update_request.attr,
|
||||
NULL
|
||||
};
|
||||
ATTRIBUTE_GROUPS(firmware_update);
|
||||
|
||||
static int intel_wmi_sbl_fw_update_probe(struct wmi_device *wdev,
|
||||
const void *context)
|
||||
{
|
||||
dev_info(&wdev->dev, "Slim Bootloader signaling driver attached\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_wmi_sbl_fw_update_remove(struct wmi_device *wdev)
|
||||
{
|
||||
dev_info(&wdev->dev, "Slim Bootloader signaling driver removed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct wmi_device_id intel_wmi_sbl_id_table[] = {
|
||||
{ .guid_string = INTEL_WMI_SBL_GUID },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(wmi, intel_wmi_sbl_id_table);
|
||||
|
||||
static struct wmi_driver intel_wmi_sbl_fw_update_driver = {
|
||||
.driver = {
|
||||
.name = "intel-wmi-sbl-fw-update",
|
||||
.dev_groups = firmware_update_groups,
|
||||
},
|
||||
.probe = intel_wmi_sbl_fw_update_probe,
|
||||
.remove = intel_wmi_sbl_fw_update_remove,
|
||||
.id_table = intel_wmi_sbl_id_table,
|
||||
};
|
||||
module_wmi_driver(intel_wmi_sbl_fw_update_driver);
|
||||
|
||||
MODULE_AUTHOR("Jithu Joseph <jithu.joseph@intel.com>");
|
||||
MODULE_DESCRIPTION("Slim Bootloader firmware update signaling driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -6,14 +6,14 @@
|
||||
*
|
||||
* Some Intel Cherry Trail based device which ship with Windows 10, have
|
||||
* this weird INT33FE ACPI device with a CRS table with 4 I2cSerialBusV2
|
||||
* resources, for 4 different chips attached to various i2c busses:
|
||||
* 1. The Whiskey Cove pmic, which is also described by the INT34D3 ACPI device
|
||||
* resources, for 4 different chips attached to various I²C buses:
|
||||
* 1. The Whiskey Cove PMIC, which is also described by the INT34D3 ACPI device
|
||||
* 2. Maxim MAX17047 Fuel Gauge Controller
|
||||
* 3. FUSB302 USB Type-C Controller
|
||||
* 4. PI3USB30532 USB switch
|
||||
*
|
||||
* So this driver is a stub / pseudo driver whose only purpose is to
|
||||
* instantiate i2c-clients for chips 2 - 4, so that standard i2c drivers
|
||||
* instantiate I²C clients for chips 2 - 4, so that standard I²C drivers
|
||||
* for these chips can bind to the them.
|
||||
*/
|
||||
|
||||
@ -21,43 +21,32 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb/pd.h>
|
||||
|
||||
#include "intel_cht_int33fe_common.h"
|
||||
|
||||
enum {
|
||||
INT33FE_NODE_FUSB302,
|
||||
INT33FE_NODE_MAX17047,
|
||||
INT33FE_NODE_PI3USB30532,
|
||||
INT33FE_NODE_DISPLAYPORT,
|
||||
INT33FE_NODE_USB_CONNECTOR,
|
||||
INT33FE_NODE_MAX,
|
||||
};
|
||||
|
||||
/*
|
||||
* Grrr I severly dislike buggy BIOS-es. At least one BIOS enumerates
|
||||
* Grrr, I severely dislike buggy BIOS-es. At least one BIOS enumerates
|
||||
* the max17047 both through the INT33FE ACPI device (it is right there
|
||||
* in the resources table) as well as through a separate MAX17047 device.
|
||||
*
|
||||
* These helpers are used to work around this by checking if an i2c-client
|
||||
* These helpers are used to work around this by checking if an I²C client
|
||||
* for the max17047 has already been registered.
|
||||
*/
|
||||
static int cht_int33fe_check_for_max17047(struct device *dev, void *data)
|
||||
{
|
||||
struct i2c_client **max17047 = data;
|
||||
struct acpi_device *adev;
|
||||
const char *hid;
|
||||
|
||||
adev = ACPI_COMPANION(dev);
|
||||
if (!adev)
|
||||
return 0;
|
||||
|
||||
hid = acpi_device_hid(adev);
|
||||
|
||||
/* The MAX17047 ACPI node doesn't have an UID, so we don't check that */
|
||||
if (strcmp(hid, "MAX17047"))
|
||||
if (!acpi_dev_hid_uid_match(adev, "MAX17047", NULL))
|
||||
return 0;
|
||||
|
||||
*max17047 = to_i2c_client(dev);
|
||||
@ -66,11 +55,16 @@ static int cht_int33fe_check_for_max17047(struct device *dev, void *data)
|
||||
|
||||
static const char * const max17047_suppliers[] = { "bq24190-charger" };
|
||||
|
||||
static const struct property_entry max17047_props[] = {
|
||||
static const struct property_entry max17047_properties[] = {
|
||||
PROPERTY_ENTRY_STRING_ARRAY("supplied-from", max17047_suppliers),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node max17047_node = {
|
||||
.name = "max17047",
|
||||
.properties = max17047_properties,
|
||||
};
|
||||
|
||||
/*
|
||||
* We are not using inline property here because those are constant,
|
||||
* and we need to adjust this one at runtime to point to real
|
||||
@ -80,12 +74,17 @@ static struct software_node_ref_args fusb302_mux_refs[] = {
|
||||
{ .node = NULL },
|
||||
};
|
||||
|
||||
static const struct property_entry fusb302_props[] = {
|
||||
static const struct property_entry fusb302_properties[] = {
|
||||
PROPERTY_ENTRY_STRING("linux,extcon-name", "cht_wcove_pwrsrc"),
|
||||
PROPERTY_ENTRY_REF_ARRAY("usb-role-switch", fusb302_mux_refs),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node fusb302_node = {
|
||||
.name = "fusb302",
|
||||
.properties = fusb302_properties,
|
||||
};
|
||||
|
||||
#define PDO_FIXED_FLAGS \
|
||||
(PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP | PDO_FIXED_USB_COMM)
|
||||
|
||||
@ -98,31 +97,40 @@ static const u32 snk_pdo[] = {
|
||||
PDO_VAR(5000, 12000, 3000),
|
||||
};
|
||||
|
||||
static const struct software_node nodes[];
|
||||
static const struct software_node pi3usb30532_node = {
|
||||
.name = "pi3usb30532",
|
||||
};
|
||||
|
||||
static const struct property_entry usb_connector_props[] = {
|
||||
static const struct software_node displayport_node = {
|
||||
.name = "displayport",
|
||||
};
|
||||
|
||||
static const struct property_entry usb_connector_properties[] = {
|
||||
PROPERTY_ENTRY_STRING("data-role", "dual"),
|
||||
PROPERTY_ENTRY_STRING("power-role", "dual"),
|
||||
PROPERTY_ENTRY_STRING("try-power-role", "sink"),
|
||||
PROPERTY_ENTRY_U32_ARRAY("source-pdos", src_pdo),
|
||||
PROPERTY_ENTRY_U32_ARRAY("sink-pdos", snk_pdo),
|
||||
PROPERTY_ENTRY_U32("op-sink-microwatt", 2500000),
|
||||
PROPERTY_ENTRY_REF("orientation-switch",
|
||||
&nodes[INT33FE_NODE_PI3USB30532]),
|
||||
PROPERTY_ENTRY_REF("mode-switch",
|
||||
&nodes[INT33FE_NODE_PI3USB30532]),
|
||||
PROPERTY_ENTRY_REF("displayport",
|
||||
&nodes[INT33FE_NODE_DISPLAYPORT]),
|
||||
PROPERTY_ENTRY_REF("orientation-switch", &pi3usb30532_node),
|
||||
PROPERTY_ENTRY_REF("mode-switch", &pi3usb30532_node),
|
||||
PROPERTY_ENTRY_REF("displayport", &displayport_node),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node nodes[] = {
|
||||
{ "fusb302", NULL, fusb302_props },
|
||||
{ "max17047", NULL, max17047_props },
|
||||
{ "pi3usb30532" },
|
||||
{ "displayport" },
|
||||
{ "connector", &nodes[0], usb_connector_props },
|
||||
{ }
|
||||
static const struct software_node usb_connector_node = {
|
||||
.name = "connector",
|
||||
.parent = &fusb302_node,
|
||||
.properties = usb_connector_properties,
|
||||
};
|
||||
|
||||
static const struct software_node *node_group[] = {
|
||||
&fusb302_node,
|
||||
&max17047_node,
|
||||
&pi3usb30532_node,
|
||||
&displayport_node,
|
||||
&usb_connector_node,
|
||||
NULL
|
||||
};
|
||||
|
||||
static int cht_int33fe_setup_dp(struct cht_int33fe_data *data)
|
||||
@ -130,7 +138,7 @@ static int cht_int33fe_setup_dp(struct cht_int33fe_data *data)
|
||||
struct fwnode_handle *fwnode;
|
||||
struct pci_dev *pdev;
|
||||
|
||||
fwnode = software_node_fwnode(&nodes[INT33FE_NODE_DISPLAYPORT]);
|
||||
fwnode = software_node_fwnode(&displayport_node);
|
||||
if (!fwnode)
|
||||
return -ENODEV;
|
||||
|
||||
@ -155,11 +163,10 @@ static int cht_int33fe_setup_dp(struct cht_int33fe_data *data)
|
||||
|
||||
static void cht_int33fe_remove_nodes(struct cht_int33fe_data *data)
|
||||
{
|
||||
software_node_unregister_nodes(nodes);
|
||||
software_node_unregister_node_group(node_group);
|
||||
|
||||
if (fusb302_mux_refs[0].node) {
|
||||
fwnode_handle_put(
|
||||
software_node_fwnode(fusb302_mux_refs[0].node));
|
||||
fwnode_handle_put(software_node_fwnode(fusb302_mux_refs[0].node));
|
||||
fusb302_mux_refs[0].node = NULL;
|
||||
}
|
||||
|
||||
@ -192,7 +199,7 @@ static int cht_int33fe_add_nodes(struct cht_int33fe_data *data)
|
||||
*/
|
||||
fusb302_mux_refs[0].node = mux_ref_node;
|
||||
|
||||
ret = software_node_register_nodes(nodes);
|
||||
ret = software_node_register_node_group(node_group);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -222,16 +229,15 @@ cht_int33fe_register_max17047(struct device *dev, struct cht_int33fe_data *data)
|
||||
struct fwnode_handle *fwnode;
|
||||
int ret;
|
||||
|
||||
fwnode = software_node_fwnode(&nodes[INT33FE_NODE_MAX17047]);
|
||||
fwnode = software_node_fwnode(&max17047_node);
|
||||
if (!fwnode)
|
||||
return -ENODEV;
|
||||
|
||||
i2c_for_each_dev(&max17047, cht_int33fe_check_for_max17047);
|
||||
if (max17047) {
|
||||
/* Pre-existing i2c-client for the max17047, add device-props */
|
||||
fwnode->secondary = ERR_PTR(-ENODEV);
|
||||
max17047->dev.fwnode->secondary = fwnode;
|
||||
/* And re-probe to get the new device-props applied. */
|
||||
/* Pre-existing I²C client for the max17047, add device properties */
|
||||
set_secondary_fwnode(&max17047->dev, fwnode);
|
||||
/* And re-probe to get the new device properties applied */
|
||||
ret = device_reprobe(&max17047->dev);
|
||||
if (ret)
|
||||
dev_warn(dev, "Reprobing max17047 error: %d\n", ret);
|
||||
@ -266,7 +272,7 @@ int cht_int33fe_typec_probe(struct cht_int33fe_data *data)
|
||||
* must be registered before the fusb302 is instantiated, otherwise
|
||||
* it will end up with a dummy-regulator.
|
||||
* Note "cht_wc_usb_typec_vbus" comes from the regulator_init_data
|
||||
* which is defined in i2c-cht-wc.c from where the bq24292i i2c-client
|
||||
* which is defined in i2c-cht-wc.c from where the bq24292i I²C client
|
||||
* gets instantiated. We use regulator_get_optional here so that we
|
||||
* don't end up getting a dummy-regulator ourselves.
|
||||
*/
|
||||
@ -277,7 +283,7 @@ int cht_int33fe_typec_probe(struct cht_int33fe_data *data)
|
||||
}
|
||||
regulator_put(regulator);
|
||||
|
||||
/* The FUSB302 uses the irq at index 1 and is the only irq user */
|
||||
/* The FUSB302 uses the IRQ at index 1 and is the only IRQ user */
|
||||
fusb302_irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 1);
|
||||
if (fusb302_irq < 0) {
|
||||
if (fusb302_irq != -EPROBE_DEFER)
|
||||
@ -289,12 +295,12 @@ int cht_int33fe_typec_probe(struct cht_int33fe_data *data)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Work around BIOS bug, see comment on cht_int33fe_check_for_max17047 */
|
||||
/* Work around BIOS bug, see comment on cht_int33fe_check_for_max17047() */
|
||||
ret = cht_int33fe_register_max17047(dev, data);
|
||||
if (ret)
|
||||
goto out_remove_nodes;
|
||||
|
||||
fwnode = software_node_fwnode(&nodes[INT33FE_NODE_FUSB302]);
|
||||
fwnode = software_node_fwnode(&fusb302_node);
|
||||
if (!fwnode) {
|
||||
ret = -ENODEV;
|
||||
goto out_unregister_max17047;
|
||||
@ -312,7 +318,7 @@ int cht_int33fe_typec_probe(struct cht_int33fe_data *data)
|
||||
goto out_unregister_max17047;
|
||||
}
|
||||
|
||||
fwnode = software_node_fwnode(&nodes[INT33FE_NODE_PI3USB30532]);
|
||||
fwnode = software_node_fwnode(&pi3usb30532_node);
|
||||
if (!fwnode) {
|
||||
ret = -ENODEV;
|
||||
goto out_unregister_fusb302;
|
||||
|
@ -46,6 +46,7 @@ struct mid_pb_ddata {
|
||||
unsigned short mirqlvl1_addr;
|
||||
unsigned short pbstat_addr;
|
||||
u8 pbstat_mask;
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
int (*setup)(struct mid_pb_ddata *ddata);
|
||||
};
|
||||
|
||||
@ -55,7 +56,8 @@ static int mid_pbstat(struct mid_pb_ddata *ddata, int *value)
|
||||
int ret;
|
||||
u8 pbstat;
|
||||
|
||||
ret = intel_scu_ipc_ioread8(ddata->pbstat_addr, &pbstat);
|
||||
ret = intel_scu_ipc_dev_ioread8(ddata->scu, ddata->pbstat_addr,
|
||||
&pbstat);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -67,14 +69,15 @@ static int mid_pbstat(struct mid_pb_ddata *ddata, int *value)
|
||||
|
||||
static int mid_irq_ack(struct mid_pb_ddata *ddata)
|
||||
{
|
||||
return intel_scu_ipc_update_register(ddata->mirqlvl1_addr, 0, MSIC_PWRBTNM);
|
||||
return intel_scu_ipc_dev_update(ddata->scu, ddata->mirqlvl1_addr, 0,
|
||||
MSIC_PWRBTNM);
|
||||
}
|
||||
|
||||
static int mrfld_setup(struct mid_pb_ddata *ddata)
|
||||
{
|
||||
/* Unmask the PBIRQ and MPBIRQ on Tangier */
|
||||
intel_scu_ipc_update_register(BCOVE_PBIRQ, 0, MSIC_PWRBTNM);
|
||||
intel_scu_ipc_update_register(BCOVE_PBIRQMASK, 0, MSIC_PWRBTNM);
|
||||
intel_scu_ipc_dev_update(ddata->scu, BCOVE_PBIRQ, 0, MSIC_PWRBTNM);
|
||||
intel_scu_ipc_dev_update(ddata->scu, BCOVE_PBIRQMASK, 0, MSIC_PWRBTNM);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -161,6 +164,10 @@ static int mid_pb_probe(struct platform_device *pdev)
|
||||
return error;
|
||||
}
|
||||
|
||||
ddata->scu = devm_intel_scu_ipc_dev_get(&pdev->dev);
|
||||
if (!ddata->scu)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
error = devm_request_threaded_irq(&pdev->dev, irq, NULL, mid_pb_isr,
|
||||
IRQF_ONESHOT, DRIVER_NAME, ddata);
|
||||
if (error) {
|
||||
|
@ -1,949 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Driver for the Intel PMC IPC mechanism
|
||||
*
|
||||
* (C) Copyright 2014-2015 Intel Corporation
|
||||
*
|
||||
* This driver is based on Intel SCU IPC driver(intel_scu_ipc.c) by
|
||||
* Sreedhara DS <sreedhara.ds@intel.com>
|
||||
*
|
||||
* PMC running in ARC processor communicates with other entity running in IA
|
||||
* core through IPC mechanism which in turn messaging between IA core ad PMC.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/intel_pmc_ipc.h>
|
||||
|
||||
#include <linux/platform_data/itco_wdt.h>
|
||||
|
||||
/*
|
||||
* IPC registers
|
||||
* The IA write to IPC_CMD command register triggers an interrupt to the ARC,
|
||||
* The ARC handles the interrupt and services it, writing optional data to
|
||||
* the IPC1 registers, updates the IPC_STS response register with the status.
|
||||
*/
|
||||
#define IPC_CMD 0x00
|
||||
#define IPC_CMD_MSI BIT(8)
|
||||
#define IPC_CMD_SIZE 16
|
||||
#define IPC_CMD_SUBCMD 12
|
||||
#define IPC_STATUS 0x04
|
||||
#define IPC_STATUS_IRQ BIT(2)
|
||||
#define IPC_STATUS_ERR BIT(1)
|
||||
#define IPC_STATUS_BUSY BIT(0)
|
||||
#define IPC_SPTR 0x08
|
||||
#define IPC_DPTR 0x0C
|
||||
#define IPC_WRITE_BUFFER 0x80
|
||||
#define IPC_READ_BUFFER 0x90
|
||||
|
||||
/* Residency with clock rate at 19.2MHz to usecs */
|
||||
#define S0IX_RESIDENCY_IN_USECS(d, s) \
|
||||
({ \
|
||||
u64 result = 10ull * ((d) + (s)); \
|
||||
do_div(result, 192); \
|
||||
result; \
|
||||
})
|
||||
|
||||
/*
|
||||
* 16-byte buffer for sending data associated with IPC command.
|
||||
*/
|
||||
#define IPC_DATA_BUFFER_SIZE 16
|
||||
|
||||
#define IPC_LOOP_CNT 3000000
|
||||
#define IPC_MAX_SEC 3
|
||||
|
||||
#define IPC_TRIGGER_MODE_IRQ true
|
||||
|
||||
/* exported resources from IFWI */
|
||||
#define PLAT_RESOURCE_IPC_INDEX 0
|
||||
#define PLAT_RESOURCE_IPC_SIZE 0x1000
|
||||
#define PLAT_RESOURCE_GCR_OFFSET 0x1000
|
||||
#define PLAT_RESOURCE_GCR_SIZE 0x1000
|
||||
#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
|
||||
|
||||
/*
|
||||
* BIOS does not create an ACPI device for each PMC function,
|
||||
* but exports multiple resources from one ACPI device(IPC) for
|
||||
* multiple functions. This driver is responsible to create a
|
||||
* platform device and to export resources for those functions.
|
||||
*/
|
||||
#define TCO_DEVICE_NAME "iTCO_wdt"
|
||||
#define SMI_EN_OFFSET 0x40
|
||||
#define SMI_EN_SIZE 4
|
||||
#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
|
||||
#define TCO_PMC_OFFSET 0x08
|
||||
#define TCO_PMC_SIZE 0x04
|
||||
|
||||
/* PMC register bit definitions */
|
||||
|
||||
/* PMC_CFG_REG bit masks */
|
||||
#define PMC_CFG_NO_REBOOT_MASK BIT_MASK(4)
|
||||
#define PMC_CFG_NO_REBOOT_EN (1 << 4)
|
||||
#define PMC_CFG_NO_REBOOT_DIS (0 << 4)
|
||||
|
||||
static struct intel_pmc_ipc_dev {
|
||||
struct device *dev;
|
||||
void __iomem *ipc_base;
|
||||
bool irq_mode;
|
||||
int irq;
|
||||
int cmd;
|
||||
struct completion cmd_complete;
|
||||
|
||||
/* The following PMC BARs share the same ACPI device with the IPC */
|
||||
resource_size_t acpi_io_base;
|
||||
int acpi_io_size;
|
||||
struct platform_device *tco_dev;
|
||||
|
||||
/* gcr */
|
||||
void __iomem *gcr_mem_base;
|
||||
bool has_gcr_regs;
|
||||
spinlock_t gcr_lock;
|
||||
|
||||
/* punit */
|
||||
struct platform_device *punit_dev;
|
||||
unsigned int punit_res_count;
|
||||
|
||||
/* 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[] = {
|
||||
[IPC_ERR_NONE] =
|
||||
"no error",
|
||||
[IPC_ERR_CMD_NOT_SUPPORTED] =
|
||||
"command not supported",
|
||||
[IPC_ERR_CMD_NOT_SERVICED] =
|
||||
"command not serviced",
|
||||
[IPC_ERR_UNABLE_TO_SERVICE] =
|
||||
"unable to service",
|
||||
[IPC_ERR_CMD_INVALID] =
|
||||
"command invalid",
|
||||
[IPC_ERR_CMD_FAILED] =
|
||||
"command failed",
|
||||
[IPC_ERR_EMSECURITY] =
|
||||
"Invalid Battery",
|
||||
[IPC_ERR_UNSIGNEDKERNEL] =
|
||||
"Unsigned kernel",
|
||||
};
|
||||
|
||||
/* Prevent concurrent calls to the PMC */
|
||||
static DEFINE_MUTEX(ipclock);
|
||||
|
||||
static inline void ipc_send_command(u32 cmd)
|
||||
{
|
||||
ipcdev.cmd = cmd;
|
||||
if (ipcdev.irq_mode) {
|
||||
reinit_completion(&ipcdev.cmd_complete);
|
||||
cmd |= IPC_CMD_MSI;
|
||||
}
|
||||
writel(cmd, ipcdev.ipc_base + IPC_CMD);
|
||||
}
|
||||
|
||||
static inline u32 ipc_read_status(void)
|
||||
{
|
||||
return readl(ipcdev.ipc_base + IPC_STATUS);
|
||||
}
|
||||
|
||||
static inline void ipc_data_writel(u32 data, u32 offset)
|
||||
{
|
||||
writel(data, ipcdev.ipc_base + IPC_WRITE_BUFFER + offset);
|
||||
}
|
||||
|
||||
static inline u32 ipc_data_readl(u32 offset)
|
||||
{
|
||||
return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
|
||||
}
|
||||
|
||||
static inline u64 gcr_data_readq(u32 offset)
|
||||
{
|
||||
return readq(ipcdev.gcr_mem_base + offset);
|
||||
}
|
||||
|
||||
static inline int is_gcr_valid(u32 offset)
|
||||
{
|
||||
if (!ipcdev.has_gcr_regs)
|
||||
return -EACCES;
|
||||
|
||||
if (offset > PLAT_RESOURCE_GCR_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_pmc_gcr_read64() - Read a 64-bit PMC GCR register
|
||||
* @offset: offset of GCR register from GCR address base
|
||||
* @data: data pointer for storing the register output
|
||||
*
|
||||
* Reads the 64-bit PMC GCR register at given offset.
|
||||
*
|
||||
* Return: negative value on error or 0 on success.
|
||||
*/
|
||||
int intel_pmc_gcr_read64(u32 offset, u64 *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
spin_lock(&ipcdev.gcr_lock);
|
||||
|
||||
ret = is_gcr_valid(offset);
|
||||
if (ret < 0) {
|
||||
spin_unlock(&ipcdev.gcr_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*data = readq(ipcdev.gcr_mem_base + offset);
|
||||
|
||||
spin_unlock(&ipcdev.gcr_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_pmc_gcr_read64);
|
||||
|
||||
/**
|
||||
* intel_pmc_gcr_update() - Update PMC GCR register bits
|
||||
* @offset: offset of GCR register from GCR address base
|
||||
* @mask: bit mask for update operation
|
||||
* @val: update value
|
||||
*
|
||||
* Updates the bits of given GCR register as specified by
|
||||
* @mask and @val.
|
||||
*
|
||||
* Return: negative value on error or 0 on success.
|
||||
*/
|
||||
static int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val)
|
||||
{
|
||||
u32 new_val;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock(&ipcdev.gcr_lock);
|
||||
|
||||
ret = is_gcr_valid(offset);
|
||||
if (ret < 0)
|
||||
goto gcr_ipc_unlock;
|
||||
|
||||
new_val = readl(ipcdev.gcr_mem_base + offset);
|
||||
|
||||
new_val &= ~mask;
|
||||
new_val |= val & mask;
|
||||
|
||||
writel(new_val, ipcdev.gcr_mem_base + offset);
|
||||
|
||||
new_val = readl(ipcdev.gcr_mem_base + offset);
|
||||
|
||||
/* check whether the bit update is successful */
|
||||
if ((new_val & mask) != (val & mask)) {
|
||||
ret = -EIO;
|
||||
goto gcr_ipc_unlock;
|
||||
}
|
||||
|
||||
gcr_ipc_unlock:
|
||||
spin_unlock(&ipcdev.gcr_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int update_no_reboot_bit(void *priv, bool set)
|
||||
{
|
||||
u32 value = set ? PMC_CFG_NO_REBOOT_EN : PMC_CFG_NO_REBOOT_DIS;
|
||||
|
||||
return intel_pmc_gcr_update(PMC_GCR_PMC_CFG_REG,
|
||||
PMC_CFG_NO_REBOOT_MASK, value);
|
||||
}
|
||||
|
||||
static int intel_pmc_ipc_check_status(void)
|
||||
{
|
||||
int status;
|
||||
int ret = 0;
|
||||
|
||||
if (ipcdev.irq_mode) {
|
||||
if (0 == wait_for_completion_timeout(
|
||||
&ipcdev.cmd_complete, IPC_MAX_SEC * HZ))
|
||||
ret = -ETIMEDOUT;
|
||||
} else {
|
||||
int loop_count = IPC_LOOP_CNT;
|
||||
|
||||
while ((ipc_read_status() & IPC_STATUS_BUSY) && --loop_count)
|
||||
udelay(1);
|
||||
if (loop_count == 0)
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
status = ipc_read_status();
|
||||
if (ret == -ETIMEDOUT) {
|
||||
dev_err(ipcdev.dev,
|
||||
"IPC timed out, TS=0x%x, CMD=0x%x\n",
|
||||
status, ipcdev.cmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (status & IPC_STATUS_ERR) {
|
||||
int i;
|
||||
|
||||
ret = -EIO;
|
||||
i = (status >> IPC_CMD_SIZE) & 0xFF;
|
||||
if (i < ARRAY_SIZE(ipc_err_sources))
|
||||
dev_err(ipcdev.dev,
|
||||
"IPC failed: %s, STS=0x%x, CMD=0x%x\n",
|
||||
ipc_err_sources[i], status, ipcdev.cmd);
|
||||
else
|
||||
dev_err(ipcdev.dev,
|
||||
"IPC failed: unknown, STS=0x%x, CMD=0x%x\n",
|
||||
status, ipcdev.cmd);
|
||||
if ((i == IPC_ERR_UNSIGNEDKERNEL) || (i == IPC_ERR_EMSECURITY))
|
||||
ret = -EACCES;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_pmc_ipc_simple_command() - Simple IPC command
|
||||
* @cmd: IPC command code.
|
||||
* @sub: IPC command sub type.
|
||||
*
|
||||
* Send a simple IPC command to PMC when don't need to specify
|
||||
* input/output data and source/dest pointers.
|
||||
*
|
||||
* Return: an IPC error code or 0 on success.
|
||||
*/
|
||||
static int intel_pmc_ipc_simple_command(int cmd, int sub)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ipclock);
|
||||
if (ipcdev.dev == NULL) {
|
||||
mutex_unlock(&ipclock);
|
||||
return -ENODEV;
|
||||
}
|
||||
ipc_send_command(sub << IPC_CMD_SUBCMD | cmd);
|
||||
ret = intel_pmc_ipc_check_status();
|
||||
mutex_unlock(&ipclock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_pmc_ipc_raw_cmd() - IPC command with data and pointers
|
||||
* @cmd: IPC command code.
|
||||
* @sub: IPC command sub type.
|
||||
* @in: input data of this IPC command.
|
||||
* @inlen: input data length in bytes.
|
||||
* @out: output data of this IPC command.
|
||||
* @outlen: output data length in dwords.
|
||||
* @sptr: data writing to SPTR register.
|
||||
* @dptr: data writing to DPTR register.
|
||||
*
|
||||
* Send an IPC command to PMC with input/output data and source/dest pointers.
|
||||
*
|
||||
* Return: an IPC error code or 0 on success.
|
||||
*/
|
||||
static int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen, u32 *out,
|
||||
u32 outlen, u32 dptr, u32 sptr)
|
||||
{
|
||||
u32 wbuf[4] = { 0 };
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (inlen > IPC_DATA_BUFFER_SIZE || outlen > IPC_DATA_BUFFER_SIZE / 4)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&ipclock);
|
||||
if (ipcdev.dev == NULL) {
|
||||
mutex_unlock(&ipclock);
|
||||
return -ENODEV;
|
||||
}
|
||||
memcpy(wbuf, in, inlen);
|
||||
writel(dptr, ipcdev.ipc_base + IPC_DPTR);
|
||||
writel(sptr, ipcdev.ipc_base + IPC_SPTR);
|
||||
/* The input data register is 32bit register and inlen is in Byte */
|
||||
for (i = 0; i < ((inlen + 3) / 4); i++)
|
||||
ipc_data_writel(wbuf[i], 4 * i);
|
||||
ipc_send_command((inlen << IPC_CMD_SIZE) |
|
||||
(sub << IPC_CMD_SUBCMD) | cmd);
|
||||
ret = intel_pmc_ipc_check_status();
|
||||
if (!ret) {
|
||||
/* out is read from 32bit register and outlen is in 32bit */
|
||||
for (i = 0; i < outlen; i++)
|
||||
*out++ = ipc_data_readl(4 * i);
|
||||
}
|
||||
mutex_unlock(&ipclock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_pmc_ipc_command() - IPC command with input/output data
|
||||
* @cmd: IPC command code.
|
||||
* @sub: IPC command sub type.
|
||||
* @in: input data of this IPC command.
|
||||
* @inlen: input data length in bytes.
|
||||
* @out: output data of this IPC command.
|
||||
* @outlen: output data length in dwords.
|
||||
*
|
||||
* Send an IPC command to PMC with input/output data.
|
||||
*
|
||||
* Return: an IPC error code or 0 on success.
|
||||
*/
|
||||
int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
|
||||
u32 *out, u32 outlen)
|
||||
{
|
||||
return intel_pmc_ipc_raw_cmd(cmd, sub, in, inlen, out, outlen, 0, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_pmc_ipc_command);
|
||||
|
||||
static irqreturn_t ioc(int irq, void *dev_id)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (ipcdev.irq_mode) {
|
||||
status = ipc_read_status();
|
||||
writel(status | IPC_STATUS_IRQ, ipcdev.ipc_base + IPC_STATUS);
|
||||
}
|
||||
complete(&ipcdev.cmd_complete);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
struct intel_pmc_ipc_dev *pmc = &ipcdev;
|
||||
int ret;
|
||||
|
||||
/* Only one PMC is supported */
|
||||
if (pmc->dev)
|
||||
return -EBUSY;
|
||||
|
||||
pmc->irq_mode = IPC_TRIGGER_MODE_IRQ;
|
||||
|
||||
spin_lock_init(&ipcdev.gcr_lock);
|
||||
|
||||
ret = pcim_enable_device(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
init_completion(&pmc->cmd_complete);
|
||||
|
||||
pmc->ipc_base = pcim_iomap_table(pdev)[0];
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_pmc_ipc",
|
||||
pmc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to request irq\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
pmc->dev = &pdev->dev;
|
||||
|
||||
pci_set_drvdata(pdev, pmc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pci_device_id ipc_pci_ids[] = {
|
||||
{PCI_VDEVICE(INTEL, 0x0a94), 0},
|
||||
{PCI_VDEVICE(INTEL, 0x1a94), 0},
|
||||
{PCI_VDEVICE(INTEL, 0x5a94), 0},
|
||||
{ 0,}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, ipc_pci_ids);
|
||||
|
||||
static struct pci_driver ipc_pci_driver = {
|
||||
.name = "intel_pmc_ipc",
|
||||
.id_table = ipc_pci_ids,
|
||||
.probe = ipc_pci_probe,
|
||||
};
|
||||
|
||||
static ssize_t intel_pmc_ipc_simple_cmd_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int subcmd;
|
||||
int cmd;
|
||||
int ret;
|
||||
|
||||
ret = sscanf(buf, "%d %d", &cmd, &subcmd);
|
||||
if (ret != 2) {
|
||||
dev_err(dev, "Error args\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = intel_pmc_ipc_simple_command(cmd, subcmd);
|
||||
if (ret) {
|
||||
dev_err(dev, "command %d error with %d\n", cmd, ret);
|
||||
return ret;
|
||||
}
|
||||
return (ssize_t)count;
|
||||
}
|
||||
static DEVICE_ATTR(simplecmd, 0200, NULL, intel_pmc_ipc_simple_cmd_store);
|
||||
|
||||
static ssize_t intel_pmc_ipc_northpeak_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
unsigned long val;
|
||||
int subcmd;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 0, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val)
|
||||
subcmd = 1;
|
||||
else
|
||||
subcmd = 0;
|
||||
ret = intel_pmc_ipc_simple_command(PMC_IPC_NORTHPEAK_CTRL, subcmd);
|
||||
if (ret) {
|
||||
dev_err(dev, "command north %d error with %d\n", subcmd, ret);
|
||||
return ret;
|
||||
}
|
||||
return (ssize_t)count;
|
||||
}
|
||||
static DEVICE_ATTR(northpeak, 0200, NULL, intel_pmc_ipc_northpeak_store);
|
||||
|
||||
static struct attribute *intel_ipc_attrs[] = {
|
||||
&dev_attr_northpeak.attr,
|
||||
&dev_attr_simplecmd.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group intel_ipc_group = {
|
||||
.attrs = intel_ipc_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *intel_ipc_groups[] = {
|
||||
&intel_ipc_group,
|
||||
NULL
|
||||
};
|
||||
|
||||
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,
|
||||
},
|
||||
{
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
#define TCO_RESOURCE_ACPI_IO 0
|
||||
#define TCO_RESOURCE_SMI_EN_IO 1
|
||||
#define TCO_RESOURCE_GCR_MEM 2
|
||||
static struct resource tco_res[] = {
|
||||
/* ACPI - TCO */
|
||||
{
|
||||
.flags = IORESOURCE_IO,
|
||||
},
|
||||
/* ACPI - SMI */
|
||||
{
|
||||
.flags = IORESOURCE_IO,
|
||||
},
|
||||
};
|
||||
|
||||
static struct itco_wdt_platform_data tco_info = {
|
||||
.name = "Apollo Lake SoC",
|
||||
.version = 5,
|
||||
.no_reboot_priv = &ipcdev,
|
||||
.update_no_reboot_bit = update_no_reboot_bit,
|
||||
};
|
||||
|
||||
#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;
|
||||
const struct platform_device_info pdevinfo = {
|
||||
.parent = ipcdev.dev,
|
||||
.name = PUNIT_DEVICE_NAME,
|
||||
.id = -1,
|
||||
.res = punit_res_array,
|
||||
.num_res = ipcdev.punit_res_count,
|
||||
};
|
||||
|
||||
pdev = platform_device_register_full(&pdevinfo);
|
||||
if (IS_ERR(pdev))
|
||||
return PTR_ERR(pdev);
|
||||
|
||||
ipcdev.punit_dev = pdev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipc_create_tco_device(void)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct resource *res;
|
||||
const struct platform_device_info pdevinfo = {
|
||||
.parent = ipcdev.dev,
|
||||
.name = TCO_DEVICE_NAME,
|
||||
.id = -1,
|
||||
.res = tco_res,
|
||||
.num_res = ARRAY_SIZE(tco_res),
|
||||
.data = &tco_info,
|
||||
.size_data = sizeof(tco_info),
|
||||
};
|
||||
|
||||
res = tco_res + TCO_RESOURCE_ACPI_IO;
|
||||
res->start = ipcdev.acpi_io_base + TCO_BASE_OFFSET;
|
||||
res->end = res->start + TCO_REGS_SIZE - 1;
|
||||
|
||||
res = tco_res + TCO_RESOURCE_SMI_EN_IO;
|
||||
res->start = ipcdev.acpi_io_base + SMI_EN_OFFSET;
|
||||
res->end = res->start + SMI_EN_SIZE - 1;
|
||||
|
||||
pdev = platform_device_register_full(&pdevinfo);
|
||||
if (IS_ERR(pdev))
|
||||
return PTR_ERR(pdev);
|
||||
|
||||
ipcdev.tco_dev = pdev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipc_create_telemetry_device(void)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct resource *res;
|
||||
const struct platform_device_info pdevinfo = {
|
||||
.parent = ipcdev.dev,
|
||||
.name = TELEMETRY_DEVICE_NAME,
|
||||
.id = -1,
|
||||
.res = telemetry_res,
|
||||
.num_res = ARRAY_SIZE(telemetry_res),
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
pdev = platform_device_register_full(&pdevinfo);
|
||||
if (IS_ERR(pdev))
|
||||
return PTR_ERR(pdev);
|
||||
|
||||
ipcdev.telemetry_dev = pdev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipc_create_pmc_devices(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* If we have ACPI based watchdog use that instead */
|
||||
if (!acpi_has_watchdog()) {
|
||||
ret = ipc_create_tco_device();
|
||||
if (ret) {
|
||||
dev_err(ipcdev.dev, "Failed to add tco platform device\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = ipc_create_punit_device();
|
||||
if (ret) {
|
||||
dev_err(ipcdev.dev, "Failed to add punit platform device\n");
|
||||
platform_device_unregister(ipcdev.tco_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!ipcdev.telem_res_inval) {
|
||||
ret = ipc_create_telemetry_device();
|
||||
if (ret) {
|
||||
dev_warn(ipcdev.dev,
|
||||
"Failed to add telemetry platform device\n");
|
||||
platform_device_unregister(ipcdev.punit_dev);
|
||||
platform_device_unregister(ipcdev.tco_dev);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ipc_plat_get_res(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res, *punit_res = punit_res_array;
|
||||
void __iomem *addr;
|
||||
int size;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO,
|
||||
PLAT_RESOURCE_ACPI_IO_INDEX);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get io resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
size = resource_size(res);
|
||||
ipcdev.acpi_io_base = res->start;
|
||||
ipcdev.acpi_io_size = size;
|
||||
dev_info(&pdev->dev, "io res: %pR\n", res);
|
||||
|
||||
ipcdev.punit_res_count = 0;
|
||||
|
||||
/* This is index 0 to cover BIOS data register */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_BIOS_DATA_INDEX);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get res of punit BIOS data\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
punit_res[ipcdev.punit_res_count++] = *res;
|
||||
dev_info(&pdev->dev, "punit BIOS data res: %pR\n", res);
|
||||
|
||||
/* This is index 1 to cover BIOS interface register */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_BIOS_IFACE_INDEX);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get res of punit BIOS iface\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
punit_res[ipcdev.punit_res_count++] = *res;
|
||||
dev_info(&pdev->dev, "punit BIOS interface res: %pR\n", res);
|
||||
|
||||
/* This is index 2 to cover ISP data register, optional */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_ISP_DATA_INDEX);
|
||||
if (res) {
|
||||
punit_res[ipcdev.punit_res_count++] = *res;
|
||||
dev_info(&pdev->dev, "punit ISP data res: %pR\n", res);
|
||||
}
|
||||
|
||||
/* This is index 3 to cover ISP interface register, optional */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_ISP_IFACE_INDEX);
|
||||
if (res) {
|
||||
punit_res[ipcdev.punit_res_count++] = *res;
|
||||
dev_info(&pdev->dev, "punit ISP interface res: %pR\n", res);
|
||||
}
|
||||
|
||||
/* This is index 4 to cover GTD data register, optional */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_GTD_DATA_INDEX);
|
||||
if (res) {
|
||||
punit_res[ipcdev.punit_res_count++] = *res;
|
||||
dev_info(&pdev->dev, "punit GTD data res: %pR\n", res);
|
||||
}
|
||||
|
||||
/* This is index 5 to cover GTD interface register, optional */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_GTD_IFACE_INDEX);
|
||||
if (res) {
|
||||
punit_res[ipcdev.punit_res_count++] = *res;
|
||||
dev_info(&pdev->dev, "punit GTD interface res: %pR\n", res);
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_IPC_INDEX);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get ipc resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
size = PLAT_RESOURCE_IPC_SIZE + PLAT_RESOURCE_GCR_SIZE;
|
||||
res->end = res->start + size - 1;
|
||||
|
||||
addr = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(addr))
|
||||
return PTR_ERR(addr);
|
||||
|
||||
ipcdev.ipc_base = addr;
|
||||
|
||||
ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_pmc_s0ix_counter_read() - Read S0ix residency.
|
||||
* @data: Out param that contains current S0ix residency count.
|
||||
*
|
||||
* Return: an error code or 0 on success.
|
||||
*/
|
||||
int intel_pmc_s0ix_counter_read(u64 *data)
|
||||
{
|
||||
u64 deep, shlw;
|
||||
|
||||
if (!ipcdev.has_gcr_regs)
|
||||
return -EACCES;
|
||||
|
||||
deep = gcr_data_readq(PMC_GCR_TELEM_DEEP_S0IX_REG);
|
||||
shlw = gcr_data_readq(PMC_GCR_TELEM_SHLW_S0IX_REG);
|
||||
|
||||
*data = S0IX_RESIDENCY_IN_USECS(deep, shlw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_pmc_s0ix_counter_read);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id ipc_acpi_ids[] = {
|
||||
{ "INT34D2", 0},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, ipc_acpi_ids);
|
||||
#endif
|
||||
|
||||
static int ipc_plat_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ipcdev.dev = &pdev->dev;
|
||||
ipcdev.irq_mode = IPC_TRIGGER_MODE_IRQ;
|
||||
init_completion(&ipcdev.cmd_complete);
|
||||
spin_lock_init(&ipcdev.gcr_lock);
|
||||
|
||||
ipcdev.irq = platform_get_irq(pdev, 0);
|
||||
if (ipcdev.irq < 0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = ipc_plat_get_res(pdev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to request resource\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ipc_create_pmc_devices();
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to create pmc devices\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (devm_request_irq(&pdev->dev, ipcdev.irq, ioc, IRQF_NO_SUSPEND,
|
||||
"intel_pmc_ipc", &ipcdev)) {
|
||||
dev_err(&pdev->dev, "Failed to request irq\n");
|
||||
ret = -EBUSY;
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
ipcdev.has_gcr_regs = true;
|
||||
|
||||
return 0;
|
||||
|
||||
err_irq:
|
||||
platform_device_unregister(ipcdev.tco_dev);
|
||||
platform_device_unregister(ipcdev.punit_dev);
|
||||
platform_device_unregister(ipcdev.telemetry_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ipc_plat_remove(struct platform_device *pdev)
|
||||
{
|
||||
devm_free_irq(&pdev->dev, ipcdev.irq, &ipcdev);
|
||||
platform_device_unregister(ipcdev.tco_dev);
|
||||
platform_device_unregister(ipcdev.punit_dev);
|
||||
platform_device_unregister(ipcdev.telemetry_dev);
|
||||
ipcdev.dev = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ipc_plat_driver = {
|
||||
.remove = ipc_plat_remove,
|
||||
.probe = ipc_plat_probe,
|
||||
.driver = {
|
||||
.name = "pmc-ipc-plat",
|
||||
.acpi_match_table = ACPI_PTR(ipc_acpi_ids),
|
||||
.dev_groups = intel_ipc_groups,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init intel_pmc_ipc_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_register(&ipc_plat_driver);
|
||||
if (ret) {
|
||||
pr_err("Failed to register PMC ipc platform driver\n");
|
||||
return ret;
|
||||
}
|
||||
ret = pci_register_driver(&ipc_pci_driver);
|
||||
if (ret) {
|
||||
pr_err("Failed to register PMC ipc pci driver\n");
|
||||
platform_driver_unregister(&ipc_plat_driver);
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit intel_pmc_ipc_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&ipc_pci_driver);
|
||||
platform_driver_unregister(&ipc_plat_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
|
||||
MODULE_DESCRIPTION("Intel PMC IPC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
/* Some modules are dependent on this, so init earlier */
|
||||
fs_initcall(intel_pmc_ipc_init);
|
||||
module_exit(intel_pmc_ipc_exit);
|
@ -18,11 +18,10 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/sfi.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/intel-mid.h>
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
/* IPC defines the following message types */
|
||||
@ -55,14 +54,14 @@
|
||||
#define IPC_IOC 0x100 /* IPC command register IOC bit */
|
||||
|
||||
struct intel_scu_ipc_dev {
|
||||
struct device *dev;
|
||||
struct device dev;
|
||||
struct resource mem;
|
||||
struct module *owner;
|
||||
int irq;
|
||||
void __iomem *ipc_base;
|
||||
struct completion cmd_complete;
|
||||
u8 irq_mode;
|
||||
};
|
||||
|
||||
static struct intel_scu_ipc_dev ipcdev; /* Only one for now */
|
||||
|
||||
#define IPC_STATUS 0x04
|
||||
#define IPC_STATUS_IRQ BIT(2)
|
||||
#define IPC_STATUS_ERR BIT(1)
|
||||
@ -78,8 +77,110 @@ static struct intel_scu_ipc_dev ipcdev; /* Only one for now */
|
||||
/* Timeout in jiffies */
|
||||
#define IPC_TIMEOUT (3 * HZ)
|
||||
|
||||
static struct intel_scu_ipc_dev *ipcdev; /* Only one for now */
|
||||
static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */
|
||||
|
||||
static struct class intel_scu_ipc_class = {
|
||||
.name = "intel_scu_ipc",
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
/**
|
||||
* intel_scu_ipc_dev_get() - Get SCU IPC instance
|
||||
*
|
||||
* The recommended new API takes SCU IPC instance as parameter and this
|
||||
* function can be called by driver to get the instance. This also makes
|
||||
* sure the driver providing the IPC functionality cannot be unloaded
|
||||
* while the caller has the instance.
|
||||
*
|
||||
* Call intel_scu_ipc_dev_put() to release the instance.
|
||||
*
|
||||
* Returns %NULL if SCU IPC is not currently available.
|
||||
*/
|
||||
struct intel_scu_ipc_dev *intel_scu_ipc_dev_get(void)
|
||||
{
|
||||
struct intel_scu_ipc_dev *scu = NULL;
|
||||
|
||||
mutex_lock(&ipclock);
|
||||
if (ipcdev) {
|
||||
get_device(&ipcdev->dev);
|
||||
/*
|
||||
* Prevent the IPC provider from being unloaded while it
|
||||
* is being used.
|
||||
*/
|
||||
if (!try_module_get(ipcdev->owner))
|
||||
put_device(&ipcdev->dev);
|
||||
else
|
||||
scu = ipcdev;
|
||||
}
|
||||
|
||||
mutex_unlock(&ipclock);
|
||||
return scu;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_scu_ipc_dev_get);
|
||||
|
||||
/**
|
||||
* intel_scu_ipc_dev_put() - Put SCU IPC instance
|
||||
* @scu: SCU IPC instance
|
||||
*
|
||||
* This function releases the SCU IPC instance retrieved from
|
||||
* intel_scu_ipc_dev_get() and allows the driver providing IPC to be
|
||||
* unloaded.
|
||||
*/
|
||||
void intel_scu_ipc_dev_put(struct intel_scu_ipc_dev *scu)
|
||||
{
|
||||
if (scu) {
|
||||
module_put(scu->owner);
|
||||
put_device(&scu->dev);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_scu_ipc_dev_put);
|
||||
|
||||
struct intel_scu_ipc_devres {
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
};
|
||||
|
||||
static void devm_intel_scu_ipc_dev_release(struct device *dev, void *res)
|
||||
{
|
||||
struct intel_scu_ipc_devres *dr = res;
|
||||
struct intel_scu_ipc_dev *scu = dr->scu;
|
||||
|
||||
intel_scu_ipc_dev_put(scu);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_intel_scu_ipc_dev_get() - Allocate managed SCU IPC device
|
||||
* @dev: Device requesting the SCU IPC device
|
||||
*
|
||||
* The recommended new API takes SCU IPC instance as parameter and this
|
||||
* function can be called by driver to get the instance. This also makes
|
||||
* sure the driver providing the IPC functionality cannot be unloaded
|
||||
* while the caller has the instance.
|
||||
*
|
||||
* Returns %NULL if SCU IPC is not currently available.
|
||||
*/
|
||||
struct intel_scu_ipc_dev *devm_intel_scu_ipc_dev_get(struct device *dev)
|
||||
{
|
||||
struct intel_scu_ipc_devres *dr;
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
|
||||
dr = devres_alloc(devm_intel_scu_ipc_dev_release, sizeof(*dr), GFP_KERNEL);
|
||||
if (!dr)
|
||||
return NULL;
|
||||
|
||||
scu = intel_scu_ipc_dev_get();
|
||||
if (!scu) {
|
||||
devres_free(dr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dr->scu = scu;
|
||||
devres_add(dev, dr);
|
||||
|
||||
return scu;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_intel_scu_ipc_dev_get);
|
||||
|
||||
/*
|
||||
* Send ipc command
|
||||
* Command Register (Write Only):
|
||||
@ -143,7 +244,6 @@ static inline int busy_loop(struct intel_scu_ipc_dev *scu)
|
||||
usleep_range(50, 100);
|
||||
} while (time_before(jiffies, end));
|
||||
|
||||
dev_err(scu->dev, "IPC timed out");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
@ -152,10 +252,8 @@ static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (!wait_for_completion_timeout(&scu->cmd_complete, IPC_TIMEOUT)) {
|
||||
dev_err(scu->dev, "IPC timed out\n");
|
||||
if (!wait_for_completion_timeout(&scu->cmd_complete, IPC_TIMEOUT))
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
status = ipc_read_status(scu);
|
||||
if (status & IPC_STATUS_ERR)
|
||||
@ -166,13 +264,13 @@ static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu)
|
||||
|
||||
static int intel_scu_ipc_check_status(struct intel_scu_ipc_dev *scu)
|
||||
{
|
||||
return scu->irq_mode ? ipc_wait_for_interrupt(scu) : busy_loop(scu);
|
||||
return scu->irq > 0 ? ipc_wait_for_interrupt(scu) : busy_loop(scu);
|
||||
}
|
||||
|
||||
/* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */
|
||||
static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
|
||||
static int pwr_reg_rdwr(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data,
|
||||
u32 count, u32 op, u32 id)
|
||||
{
|
||||
struct intel_scu_ipc_dev *scu = &ipcdev;
|
||||
int nc;
|
||||
u32 offset = 0;
|
||||
int err;
|
||||
@ -182,8 +280,9 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
|
||||
memset(cbuf, 0, sizeof(cbuf));
|
||||
|
||||
mutex_lock(&ipclock);
|
||||
|
||||
if (scu->dev == NULL) {
|
||||
if (!scu)
|
||||
scu = ipcdev;
|
||||
if (!scu) {
|
||||
mutex_unlock(&ipclock);
|
||||
return -ENODEV;
|
||||
}
|
||||
@ -222,7 +321,8 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_scu_ipc_ioread8 - read a word via the SCU
|
||||
* intel_scu_ipc_dev_ioread8() - Read a byte via the SCU
|
||||
* @scu: Optional SCU IPC instance
|
||||
* @addr: Register on SCU
|
||||
* @data: Return pointer for read byte
|
||||
*
|
||||
@ -231,14 +331,15 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
|
||||
*
|
||||
* This function may sleep.
|
||||
*/
|
||||
int intel_scu_ipc_ioread8(u16 addr, u8 *data)
|
||||
int intel_scu_ipc_dev_ioread8(struct intel_scu_ipc_dev *scu, u16 addr, u8 *data)
|
||||
{
|
||||
return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
|
||||
return pwr_reg_rdwr(scu, &addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
|
||||
}
|
||||
EXPORT_SYMBOL(intel_scu_ipc_ioread8);
|
||||
EXPORT_SYMBOL(intel_scu_ipc_dev_ioread8);
|
||||
|
||||
/**
|
||||
* intel_scu_ipc_iowrite8 - write a byte via the SCU
|
||||
* intel_scu_ipc_dev_iowrite8() - Write a byte via the SCU
|
||||
* @scu: Optional SCU IPC instance
|
||||
* @addr: Register on SCU
|
||||
* @data: Byte to write
|
||||
*
|
||||
@ -247,14 +348,15 @@ EXPORT_SYMBOL(intel_scu_ipc_ioread8);
|
||||
*
|
||||
* This function may sleep.
|
||||
*/
|
||||
int intel_scu_ipc_iowrite8(u16 addr, u8 data)
|
||||
int intel_scu_ipc_dev_iowrite8(struct intel_scu_ipc_dev *scu, u16 addr, u8 data)
|
||||
{
|
||||
return pwr_reg_rdwr(&addr, &data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
|
||||
return pwr_reg_rdwr(scu, &addr, &data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
|
||||
}
|
||||
EXPORT_SYMBOL(intel_scu_ipc_iowrite8);
|
||||
EXPORT_SYMBOL(intel_scu_ipc_dev_iowrite8);
|
||||
|
||||
/**
|
||||
* intel_scu_ipc_readvv - read a set of registers
|
||||
* intel_scu_ipc_dev_readv() - Read a set of registers
|
||||
* @scu: Optional SCU IPC instance
|
||||
* @addr: Register list
|
||||
* @data: Bytes to return
|
||||
* @len: Length of array
|
||||
@ -266,14 +368,16 @@ EXPORT_SYMBOL(intel_scu_ipc_iowrite8);
|
||||
*
|
||||
* This function may sleep.
|
||||
*/
|
||||
int intel_scu_ipc_readv(u16 *addr, u8 *data, int len)
|
||||
int intel_scu_ipc_dev_readv(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data,
|
||||
size_t len)
|
||||
{
|
||||
return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
|
||||
return pwr_reg_rdwr(scu, addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
|
||||
}
|
||||
EXPORT_SYMBOL(intel_scu_ipc_readv);
|
||||
EXPORT_SYMBOL(intel_scu_ipc_dev_readv);
|
||||
|
||||
/**
|
||||
* intel_scu_ipc_writev - write a set of registers
|
||||
* intel_scu_ipc_dev_writev() - Write a set of registers
|
||||
* @scu: Optional SCU IPC instance
|
||||
* @addr: Register list
|
||||
* @data: Bytes to write
|
||||
* @len: Length of array
|
||||
@ -285,16 +389,18 @@ EXPORT_SYMBOL(intel_scu_ipc_readv);
|
||||
*
|
||||
* This function may sleep.
|
||||
*/
|
||||
int intel_scu_ipc_writev(u16 *addr, u8 *data, int len)
|
||||
int intel_scu_ipc_dev_writev(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data,
|
||||
size_t len)
|
||||
{
|
||||
return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
|
||||
return pwr_reg_rdwr(scu, addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
|
||||
}
|
||||
EXPORT_SYMBOL(intel_scu_ipc_writev);
|
||||
EXPORT_SYMBOL(intel_scu_ipc_dev_writev);
|
||||
|
||||
/**
|
||||
* intel_scu_ipc_update_register - r/m/w a register
|
||||
* intel_scu_ipc_dev_update() - Update a register
|
||||
* @scu: Optional SCU IPC instance
|
||||
* @addr: Register address
|
||||
* @bits: Bits to update
|
||||
* @data: Bits to update
|
||||
* @mask: Mask of bits to update
|
||||
*
|
||||
* Read-modify-write power control unit register. The first data argument
|
||||
@ -305,15 +411,17 @@ EXPORT_SYMBOL(intel_scu_ipc_writev);
|
||||
* This function may sleep. Locking between SCU accesses is handled
|
||||
* for the caller.
|
||||
*/
|
||||
int intel_scu_ipc_update_register(u16 addr, u8 bits, u8 mask)
|
||||
int intel_scu_ipc_dev_update(struct intel_scu_ipc_dev *scu, u16 addr, u8 data,
|
||||
u8 mask)
|
||||
{
|
||||
u8 data[2] = { bits, mask };
|
||||
return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_M);
|
||||
u8 tmp[2] = { data, mask };
|
||||
return pwr_reg_rdwr(scu, &addr, tmp, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_M);
|
||||
}
|
||||
EXPORT_SYMBOL(intel_scu_ipc_update_register);
|
||||
EXPORT_SYMBOL(intel_scu_ipc_dev_update);
|
||||
|
||||
/**
|
||||
* intel_scu_ipc_simple_command - send a simple command
|
||||
* intel_scu_ipc_dev_simple_command() - Send a simple command
|
||||
* @scu: Optional SCU IPC instance
|
||||
* @cmd: Command
|
||||
* @sub: Sub type
|
||||
*
|
||||
@ -324,62 +432,89 @@ EXPORT_SYMBOL(intel_scu_ipc_update_register);
|
||||
* This function may sleep. Locking for SCU accesses is handled for the
|
||||
* caller.
|
||||
*/
|
||||
int intel_scu_ipc_simple_command(int cmd, int sub)
|
||||
int intel_scu_ipc_dev_simple_command(struct intel_scu_ipc_dev *scu, int cmd,
|
||||
int sub)
|
||||
{
|
||||
struct intel_scu_ipc_dev *scu = &ipcdev;
|
||||
u32 cmdval;
|
||||
int err;
|
||||
|
||||
mutex_lock(&ipclock);
|
||||
if (scu->dev == NULL) {
|
||||
if (!scu)
|
||||
scu = ipcdev;
|
||||
if (!scu) {
|
||||
mutex_unlock(&ipclock);
|
||||
return -ENODEV;
|
||||
}
|
||||
ipc_command(scu, sub << 12 | cmd);
|
||||
scu = ipcdev;
|
||||
cmdval = sub << 12 | cmd;
|
||||
ipc_command(scu, cmdval);
|
||||
err = intel_scu_ipc_check_status(scu);
|
||||
mutex_unlock(&ipclock);
|
||||
if (err)
|
||||
dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(intel_scu_ipc_simple_command);
|
||||
EXPORT_SYMBOL(intel_scu_ipc_dev_simple_command);
|
||||
|
||||
/**
|
||||
* intel_scu_ipc_command - command with data
|
||||
* intel_scu_ipc_command_with_size() - Command with data
|
||||
* @scu: Optional SCU IPC instance
|
||||
* @cmd: Command
|
||||
* @sub: Sub type
|
||||
* @in: Input data
|
||||
* @inlen: Input length in dwords
|
||||
* @inlen: Input length in bytes
|
||||
* @size: Input size written to the IPC command register in whatever
|
||||
* units (dword, byte) the particular firmware requires. Normally
|
||||
* should be the same as @inlen.
|
||||
* @out: Output data
|
||||
* @outlen: Output length in dwords
|
||||
* @outlen: Output length in bytes
|
||||
*
|
||||
* Issue a command to the SCU which involves data transfers. Do the
|
||||
* data copies under the lock but leave it for the caller to interpret.
|
||||
*/
|
||||
int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
|
||||
u32 *out, int outlen)
|
||||
int intel_scu_ipc_dev_command_with_size(struct intel_scu_ipc_dev *scu, int cmd,
|
||||
int sub, const void *in, size_t inlen,
|
||||
size_t size, void *out, size_t outlen)
|
||||
{
|
||||
struct intel_scu_ipc_dev *scu = &ipcdev;
|
||||
size_t outbuflen = DIV_ROUND_UP(outlen, sizeof(u32));
|
||||
size_t inbuflen = DIV_ROUND_UP(inlen, sizeof(u32));
|
||||
u32 cmdval, inbuf[4] = {};
|
||||
int i, err;
|
||||
|
||||
if (inbuflen > 4 || outbuflen > 4)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&ipclock);
|
||||
if (scu->dev == NULL) {
|
||||
if (!scu)
|
||||
scu = ipcdev;
|
||||
if (!scu) {
|
||||
mutex_unlock(&ipclock);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
for (i = 0; i < inlen; i++)
|
||||
ipc_data_writel(scu, *in++, 4 * i);
|
||||
memcpy(inbuf, in, inlen);
|
||||
for (i = 0; i < inbuflen; i++)
|
||||
ipc_data_writel(scu, inbuf[i], 4 * i);
|
||||
|
||||
ipc_command(scu, (inlen << 16) | (sub << 12) | cmd);
|
||||
cmdval = (size << 16) | (sub << 12) | cmd;
|
||||
ipc_command(scu, cmdval);
|
||||
err = intel_scu_ipc_check_status(scu);
|
||||
|
||||
if (!err) {
|
||||
for (i = 0; i < outlen; i++)
|
||||
*out++ = ipc_data_readl(scu, 4 * i);
|
||||
u32 outbuf[4] = {};
|
||||
|
||||
for (i = 0; i < outbuflen; i++)
|
||||
outbuf[i] = ipc_data_readl(scu, 4 * i);
|
||||
|
||||
memcpy(out, outbuf, outlen);
|
||||
}
|
||||
|
||||
mutex_unlock(&ipclock);
|
||||
if (err)
|
||||
dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(intel_scu_ipc_command);
|
||||
EXPORT_SYMBOL(intel_scu_ipc_dev_command_with_size);
|
||||
|
||||
/*
|
||||
* Interrupt handler gets called when ioc bit of IPC_COMMAND_REG set to 1
|
||||
@ -399,61 +534,179 @@ static irqreturn_t ioc(int irq, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipc_probe - probe an Intel SCU IPC
|
||||
* @pdev: the PCI device matching
|
||||
* @id: entry in the match table
|
||||
*
|
||||
* Enable and install an intel SCU IPC. This appears in the PCI space
|
||||
* but uses some hard coded addresses as well.
|
||||
*/
|
||||
static int ipc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
static void intel_scu_ipc_release(struct device *dev)
|
||||
{
|
||||
int err;
|
||||
struct intel_scu_ipc_dev *scu = &ipcdev;
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
|
||||
if (scu->dev) /* We support only one SCU */
|
||||
return -EBUSY;
|
||||
|
||||
err = pcim_enable_device(pdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
init_completion(&scu->cmd_complete);
|
||||
|
||||
scu->ipc_base = pcim_iomap_table(pdev)[0];
|
||||
|
||||
err = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_scu_ipc",
|
||||
scu);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Assign device at last */
|
||||
scu->dev = &pdev->dev;
|
||||
|
||||
intel_scu_devices_create();
|
||||
|
||||
pci_set_drvdata(pdev, scu);
|
||||
return 0;
|
||||
scu = container_of(dev, struct intel_scu_ipc_dev, dev);
|
||||
if (scu->irq > 0)
|
||||
free_irq(scu->irq, scu);
|
||||
iounmap(scu->ipc_base);
|
||||
release_mem_region(scu->mem.start, resource_size(&scu->mem));
|
||||
kfree(scu);
|
||||
}
|
||||
|
||||
static const struct pci_device_id pci_ids[] = {
|
||||
{ PCI_VDEVICE(INTEL, 0x080e) },
|
||||
{ PCI_VDEVICE(INTEL, 0x08ea) },
|
||||
{ PCI_VDEVICE(INTEL, 0x11a0) },
|
||||
{}
|
||||
};
|
||||
/**
|
||||
* __intel_scu_ipc_register() - Register SCU IPC device
|
||||
* @parent: Parent device
|
||||
* @scu_data: Data used to configure SCU IPC
|
||||
* @owner: Module registering the SCU IPC device
|
||||
*
|
||||
* Call this function to register SCU IPC mechanism under @parent.
|
||||
* Returns pointer to the new SCU IPC device or ERR_PTR() in case of
|
||||
* failure. The caller may use the returned instance if it needs to do
|
||||
* SCU IPC calls itself.
|
||||
*/
|
||||
struct intel_scu_ipc_dev *
|
||||
__intel_scu_ipc_register(struct device *parent,
|
||||
const struct intel_scu_ipc_data *scu_data,
|
||||
struct module *owner)
|
||||
{
|
||||
int err;
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
void __iomem *ipc_base;
|
||||
|
||||
static struct pci_driver ipc_driver = {
|
||||
.driver = {
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.name = "intel_scu_ipc",
|
||||
.id_table = pci_ids,
|
||||
.probe = ipc_probe,
|
||||
};
|
||||
builtin_pci_driver(ipc_driver);
|
||||
mutex_lock(&ipclock);
|
||||
/* We support only one IPC */
|
||||
if (ipcdev) {
|
||||
err = -EBUSY;
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
scu = kzalloc(sizeof(*scu), GFP_KERNEL);
|
||||
if (!scu) {
|
||||
err = -ENOMEM;
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
scu->owner = owner;
|
||||
scu->dev.parent = parent;
|
||||
scu->dev.class = &intel_scu_ipc_class;
|
||||
scu->dev.release = intel_scu_ipc_release;
|
||||
dev_set_name(&scu->dev, "intel_scu_ipc");
|
||||
|
||||
if (!request_mem_region(scu_data->mem.start, resource_size(&scu_data->mem),
|
||||
"intel_scu_ipc")) {
|
||||
err = -EBUSY;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
ipc_base = ioremap(scu_data->mem.start, resource_size(&scu_data->mem));
|
||||
if (!ipc_base) {
|
||||
err = -ENOMEM;
|
||||
goto err_release;
|
||||
}
|
||||
|
||||
scu->ipc_base = ipc_base;
|
||||
scu->mem = scu_data->mem;
|
||||
scu->irq = scu_data->irq;
|
||||
init_completion(&scu->cmd_complete);
|
||||
|
||||
if (scu->irq > 0) {
|
||||
err = request_irq(scu->irq, ioc, 0, "intel_scu_ipc", scu);
|
||||
if (err)
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
/*
|
||||
* After this point intel_scu_ipc_release() takes care of
|
||||
* releasing the SCU IPC resources once refcount drops to zero.
|
||||
*/
|
||||
err = device_register(&scu->dev);
|
||||
if (err) {
|
||||
put_device(&scu->dev);
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
/* Assign device at last */
|
||||
ipcdev = scu;
|
||||
mutex_unlock(&ipclock);
|
||||
|
||||
return scu;
|
||||
|
||||
err_unmap:
|
||||
iounmap(ipc_base);
|
||||
err_release:
|
||||
release_mem_region(scu_data->mem.start, resource_size(&scu_data->mem));
|
||||
err_free:
|
||||
kfree(scu);
|
||||
err_unlock:
|
||||
mutex_unlock(&ipclock);
|
||||
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__intel_scu_ipc_register);
|
||||
|
||||
/**
|
||||
* intel_scu_ipc_unregister() - Unregister SCU IPC
|
||||
* @scu: SCU IPC handle
|
||||
*
|
||||
* This unregisters the SCU IPC device and releases the acquired
|
||||
* resources once the refcount goes to zero.
|
||||
*/
|
||||
void intel_scu_ipc_unregister(struct intel_scu_ipc_dev *scu)
|
||||
{
|
||||
mutex_lock(&ipclock);
|
||||
if (!WARN_ON(!ipcdev)) {
|
||||
ipcdev = NULL;
|
||||
device_unregister(&scu->dev);
|
||||
}
|
||||
mutex_unlock(&ipclock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_scu_ipc_unregister);
|
||||
|
||||
static void devm_intel_scu_ipc_unregister(struct device *dev, void *res)
|
||||
{
|
||||
struct intel_scu_ipc_devres *dr = res;
|
||||
struct intel_scu_ipc_dev *scu = dr->scu;
|
||||
|
||||
intel_scu_ipc_unregister(scu);
|
||||
}
|
||||
|
||||
/**
|
||||
* __devm_intel_scu_ipc_register() - Register managed SCU IPC device
|
||||
* @parent: Parent device
|
||||
* @scu_data: Data used to configure SCU IPC
|
||||
* @owner: Module registering the SCU IPC device
|
||||
*
|
||||
* Call this function to register managed SCU IPC mechanism under
|
||||
* @parent. Returns pointer to the new SCU IPC device or ERR_PTR() in
|
||||
* case of failure. The caller may use the returned instance if it needs
|
||||
* to do SCU IPC calls itself.
|
||||
*/
|
||||
struct intel_scu_ipc_dev *
|
||||
__devm_intel_scu_ipc_register(struct device *parent,
|
||||
const struct intel_scu_ipc_data *scu_data,
|
||||
struct module *owner)
|
||||
{
|
||||
struct intel_scu_ipc_devres *dr;
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
|
||||
dr = devres_alloc(devm_intel_scu_ipc_unregister, sizeof(*dr), GFP_KERNEL);
|
||||
if (!dr)
|
||||
return NULL;
|
||||
|
||||
scu = __intel_scu_ipc_register(parent, scu_data, owner);
|
||||
if (IS_ERR(scu)) {
|
||||
devres_free(dr);
|
||||
return scu;
|
||||
}
|
||||
|
||||
dr->scu = scu;
|
||||
devres_add(parent, dr);
|
||||
|
||||
return scu;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__devm_intel_scu_ipc_register);
|
||||
|
||||
static int __init intel_scu_ipc_init(void)
|
||||
{
|
||||
return class_register(&intel_scu_ipc_class);
|
||||
}
|
||||
subsys_initcall(intel_scu_ipc_init);
|
||||
|
||||
static void __exit intel_scu_ipc_exit(void)
|
||||
{
|
||||
class_unregister(&intel_scu_ipc_class);
|
||||
}
|
||||
module_exit(intel_scu_ipc_exit);
|
||||
|
@ -22,6 +22,9 @@
|
||||
|
||||
static int major;
|
||||
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
static DEFINE_MUTEX(scu_lock);
|
||||
|
||||
/* IOCTL commands */
|
||||
#define INTE_SCU_IPC_REGISTER_READ 0
|
||||
#define INTE_SCU_IPC_REGISTER_WRITE 1
|
||||
@ -52,12 +55,12 @@ static int scu_reg_access(u32 cmd, struct scu_ipc_data *data)
|
||||
|
||||
switch (cmd) {
|
||||
case INTE_SCU_IPC_REGISTER_READ:
|
||||
return intel_scu_ipc_readv(data->addr, data->data, count);
|
||||
return intel_scu_ipc_dev_readv(scu, data->addr, data->data, count);
|
||||
case INTE_SCU_IPC_REGISTER_WRITE:
|
||||
return intel_scu_ipc_writev(data->addr, data->data, count);
|
||||
return intel_scu_ipc_dev_writev(scu, data->addr, data->data, count);
|
||||
case INTE_SCU_IPC_REGISTER_UPDATE:
|
||||
return intel_scu_ipc_update_register(data->addr[0],
|
||||
data->data[0], data->mask);
|
||||
return intel_scu_ipc_dev_update(scu, data->addr[0], data->data[0],
|
||||
data->mask);
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
@ -91,8 +94,40 @@ static long scu_ipc_ioctl(struct file *fp, unsigned int cmd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scu_ipc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* Only single open at the time */
|
||||
mutex_lock(&scu_lock);
|
||||
if (scu) {
|
||||
ret = -EBUSY;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
scu = intel_scu_ipc_dev_get();
|
||||
if (!scu)
|
||||
ret = -ENODEV;
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&scu_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scu_ipc_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
mutex_lock(&scu_lock);
|
||||
intel_scu_ipc_dev_put(scu);
|
||||
scu = NULL;
|
||||
mutex_unlock(&scu_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations scu_ipc_fops = {
|
||||
.unlocked_ioctl = scu_ipc_ioctl,
|
||||
.open = scu_ipc_open,
|
||||
.release = scu_ipc_release,
|
||||
};
|
||||
|
||||
static int __init ipc_module_init(void)
|
||||
|
68
drivers/platform/x86/intel_scu_pcidrv.c
Normal file
68
drivers/platform/x86/intel_scu_pcidrv.c
Normal file
@ -0,0 +1,68 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* PCI driver for the Intel SCU.
|
||||
*
|
||||
* Copyright (C) 2008-2010, 2015, 2020 Intel Corporation
|
||||
* Authors: Sreedhara DS (sreedhara.ds@intel.com)
|
||||
* Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include <asm/intel-mid.h>
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
static int intel_scu_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
void (*setup_fn)(void) = (void (*)(void))id->driver_data;
|
||||
struct intel_scu_ipc_data scu_data = {};
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
int ret;
|
||||
|
||||
ret = pcim_enable_device(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
scu_data.mem = pdev->resource[0];
|
||||
scu_data.irq = pdev->irq;
|
||||
|
||||
scu = intel_scu_ipc_register(&pdev->dev, &scu_data);
|
||||
if (IS_ERR(scu))
|
||||
return PTR_ERR(scu);
|
||||
|
||||
if (setup_fn)
|
||||
setup_fn();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void intel_mid_scu_setup(void)
|
||||
{
|
||||
intel_scu_devices_create();
|
||||
}
|
||||
|
||||
static const struct pci_device_id pci_ids[] = {
|
||||
{ PCI_VDEVICE(INTEL, 0x080e),
|
||||
.driver_data = (kernel_ulong_t)intel_mid_scu_setup },
|
||||
{ PCI_VDEVICE(INTEL, 0x08ea),
|
||||
.driver_data = (kernel_ulong_t)intel_mid_scu_setup },
|
||||
{ PCI_VDEVICE(INTEL, 0x0a94) },
|
||||
{ PCI_VDEVICE(INTEL, 0x11a0),
|
||||
.driver_data = (kernel_ulong_t)intel_mid_scu_setup },
|
||||
{ PCI_VDEVICE(INTEL, 0x1a94) },
|
||||
{ PCI_VDEVICE(INTEL, 0x5a94) },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct pci_driver intel_scu_pci_driver = {
|
||||
.driver = {
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.name = "intel_scu",
|
||||
.id_table = pci_ids,
|
||||
.probe = intel_scu_pci_probe,
|
||||
};
|
||||
|
||||
builtin_pci_driver(intel_scu_pci_driver);
|
60
drivers/platform/x86/intel_scu_pltdrv.c
Normal file
60
drivers/platform/x86/intel_scu_pltdrv.c
Normal file
@ -0,0 +1,60 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Platform driver for the Intel SCU.
|
||||
*
|
||||
* Copyright (C) 2019, Intel Corporation
|
||||
* Authors: Divya Sasidharan <divya.s.sasidharan@intel.com>
|
||||
* Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
* Rajmohan Mani <rajmohan.mani@intel.com>
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
static int intel_scu_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct intel_scu_ipc_data scu_data = {};
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
const struct resource *res;
|
||||
|
||||
scu_data.irq = platform_get_irq_optional(pdev, 0);
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENOMEM;
|
||||
|
||||
scu_data.mem = *res;
|
||||
|
||||
scu = devm_intel_scu_ipc_register(&pdev->dev, &scu_data);
|
||||
if (IS_ERR(scu))
|
||||
return PTR_ERR(scu);
|
||||
|
||||
platform_set_drvdata(pdev, scu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id intel_scu_acpi_ids[] = {
|
||||
{ "INTC1026" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, intel_scu_acpi_ids);
|
||||
|
||||
static struct platform_driver intel_scu_platform_driver = {
|
||||
.probe = intel_scu_platform_probe,
|
||||
.driver = {
|
||||
.name = "intel_scu",
|
||||
.acpi_match_table = intel_scu_acpi_ids,
|
||||
},
|
||||
};
|
||||
module_platform_driver(intel_scu_platform_driver);
|
||||
|
||||
MODULE_AUTHOR("Divya Sasidharan <divya.s.sasidharan@intel.com>");
|
||||
MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com");
|
||||
MODULE_AUTHOR("Rajmohan Mani <rajmohan.mani@intel.com>");
|
||||
MODULE_DESCRIPTION("Intel SCU platform driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -21,13 +21,12 @@
|
||||
#define PUNIT_MAILBOX_BUSY_BIT 31
|
||||
|
||||
/*
|
||||
* Commands has variable amount of processing time. Most of the commands will
|
||||
* be done in 0-3 tries, but some takes up to 50.
|
||||
* The real processing time was observed as 25us for the most of the commands
|
||||
* at 2GHz. It is possible to optimize this count taking samples on customer
|
||||
* systems.
|
||||
* The average time to complete some commands is about 40us. The current
|
||||
* count is enough to satisfy 40us. But when the firmware is very busy, this
|
||||
* causes timeout occasionally. So increase to deal with some worst case
|
||||
* scenarios. Most of the command still complete in few us.
|
||||
*/
|
||||
#define OS_MAILBOX_RETRY_COUNT 50
|
||||
#define OS_MAILBOX_RETRY_COUNT 100
|
||||
|
||||
struct isst_if_device {
|
||||
struct mutex mutex;
|
||||
|
@ -353,21 +353,16 @@ int telemetry_clear_pltdata(void)
|
||||
EXPORT_SYMBOL_GPL(telemetry_clear_pltdata);
|
||||
|
||||
/**
|
||||
* telemetry_pltconfig_valid() - Checkif platform config is valid
|
||||
* telemetry_get_pltdata() - Return telemetry platform config
|
||||
*
|
||||
* Usage by other than telemetry module is invalid
|
||||
*
|
||||
* Return: 0 success, < 0 for failure
|
||||
* May be used by other telemetry modules to get platform specific
|
||||
* configuration.
|
||||
*/
|
||||
int telemetry_pltconfig_valid(void)
|
||||
struct telemetry_plt_config *telemetry_get_pltdata(void)
|
||||
{
|
||||
if (telm_core_conf.plt_config)
|
||||
return 0;
|
||||
|
||||
else
|
||||
return -EINVAL;
|
||||
return telm_core_conf.plt_config;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(telemetry_pltconfig_valid);
|
||||
EXPORT_SYMBOL_GPL(telemetry_get_pltdata);
|
||||
|
||||
static inline int telemetry_get_pssevtname(enum telemetry_unit telem_unit,
|
||||
const char **name, int len)
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mfd/intel_pmc_bxt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/seq_file.h>
|
||||
@ -22,7 +23,6 @@
|
||||
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/intel-family.h>
|
||||
#include <asm/intel_pmc_ipc.h>
|
||||
#include <asm/intel_telemetry.h>
|
||||
|
||||
#define DRIVER_NAME "telemetry_soc_debugfs"
|
||||
@ -647,10 +647,11 @@ DEFINE_SHOW_ATTRIBUTE(telem_soc_states);
|
||||
|
||||
static int telem_s0ix_res_get(void *data, u64 *val)
|
||||
{
|
||||
struct telemetry_plt_config *plt_config = telemetry_get_pltdata();
|
||||
u64 s0ix_total_res;
|
||||
int ret;
|
||||
|
||||
ret = intel_pmc_s0ix_counter_read(&s0ix_total_res);
|
||||
ret = intel_pmc_s0ix_counter_read(plt_config->pmc, &s0ix_total_res);
|
||||
if (ret) {
|
||||
pr_err("Failed to read S0ix residency");
|
||||
return ret;
|
||||
@ -837,12 +838,15 @@ static int pm_suspend_exit_cb(void)
|
||||
*/
|
||||
if (suspend_shlw_ctr_exit == suspend_shlw_ctr_temp &&
|
||||
suspend_deep_ctr_exit == suspend_deep_ctr_temp) {
|
||||
ret = intel_pmc_gcr_read64(PMC_GCR_TELEM_SHLW_S0IX_REG,
|
||||
struct telemetry_plt_config *plt_config = telemetry_get_pltdata();
|
||||
struct intel_pmc_dev *pmc = plt_config->pmc;
|
||||
|
||||
ret = intel_pmc_gcr_read64(pmc, PMC_GCR_TELEM_SHLW_S0IX_REG,
|
||||
&suspend_shlw_res_exit);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = intel_pmc_gcr_read64(PMC_GCR_TELEM_DEEP_S0IX_REG,
|
||||
ret = intel_pmc_gcr_read64(pmc, PMC_GCR_TELEM_DEEP_S0IX_REG,
|
||||
&suspend_deep_res_exit);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
@ -910,8 +914,7 @@ static int __init telemetry_debugfs_init(void)
|
||||
|
||||
debugfs_conf = (struct telemetry_debugfs_conf *)id->driver_data;
|
||||
|
||||
err = telemetry_pltconfig_valid();
|
||||
if (err < 0) {
|
||||
if (!telemetry_get_pltdata()) {
|
||||
pr_info("Invalid pltconfig, ensure IPC1 device is enabled in BIOS\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -15,7 +15,6 @@
|
||||
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/intel-family.h>
|
||||
#include <asm/intel_pmc_ipc.h>
|
||||
#include <asm/intel_punit_ipc.h>
|
||||
#include <asm/intel_telemetry.h>
|
||||
|
||||
@ -35,6 +34,7 @@
|
||||
#define TELEM_SSRAM_STARTTIME_OFFSET 8
|
||||
#define TELEM_SSRAM_EVTLOG_OFFSET 16
|
||||
|
||||
#define IOSS_TELEM 0xeb
|
||||
#define IOSS_TELEM_EVENT_READ 0x0
|
||||
#define IOSS_TELEM_EVENT_WRITE 0x1
|
||||
#define IOSS_TELEM_INFO_READ 0x2
|
||||
@ -42,9 +42,6 @@
|
||||
#define IOSS_TELEM_TRACE_CTL_WRITE 0x6
|
||||
#define IOSS_TELEM_EVENT_CTL_READ 0x7
|
||||
#define IOSS_TELEM_EVENT_CTL_WRITE 0x8
|
||||
#define IOSS_TELEM_EVT_CTRL_WRITE_SIZE 0x4
|
||||
#define IOSS_TELEM_READ_WORD 0x1
|
||||
#define IOSS_TELEM_WRITE_FOURBYTES 0x4
|
||||
#define IOSS_TELEM_EVT_WRITE_SIZE 0x3
|
||||
|
||||
#define TELEM_INFO_SRAMEVTS_MASK 0xFF00
|
||||
@ -250,17 +247,14 @@ static int telemetry_check_evtid(enum telemetry_unit telem_unit,
|
||||
static inline int telemetry_plt_config_ioss_event(u32 evt_id, int index)
|
||||
{
|
||||
u32 write_buf;
|
||||
int ret;
|
||||
|
||||
write_buf = evt_id | TELEM_EVENT_ENABLE;
|
||||
write_buf <<= BITS_PER_BYTE;
|
||||
write_buf |= index;
|
||||
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
|
||||
IOSS_TELEM_EVENT_WRITE, (u8 *)&write_buf,
|
||||
IOSS_TELEM_EVT_WRITE_SIZE, NULL, 0);
|
||||
|
||||
return ret;
|
||||
return intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM,
|
||||
IOSS_TELEM_EVENT_WRITE, &write_buf,
|
||||
IOSS_TELEM_EVT_WRITE_SIZE, NULL, 0);
|
||||
}
|
||||
|
||||
static inline int telemetry_plt_config_pss_event(u32 evt_id, int index)
|
||||
@ -278,6 +272,7 @@ static inline int telemetry_plt_config_pss_event(u32 evt_id, int index)
|
||||
static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
|
||||
enum telemetry_action action)
|
||||
{
|
||||
struct intel_scu_ipc_dev *scu = telm_conf->scu;
|
||||
u8 num_ioss_evts, ioss_period;
|
||||
int ret, index, idx;
|
||||
u32 *ioss_evtmap;
|
||||
@ -288,9 +283,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
|
||||
ioss_evtmap = evtconfig.evtmap;
|
||||
|
||||
/* Get telemetry EVENT CTL */
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
|
||||
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
|
||||
IOSS_TELEM_EVENT_CTL_READ, NULL, 0,
|
||||
&telem_ctrl, IOSS_TELEM_READ_WORD);
|
||||
&telem_ctrl, sizeof(telem_ctrl));
|
||||
if (ret) {
|
||||
pr_err("IOSS TELEM_CTRL Read Failed\n");
|
||||
return ret;
|
||||
@ -299,11 +294,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
|
||||
/* Disable Telemetry */
|
||||
TELEM_DISABLE(telem_ctrl);
|
||||
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
|
||||
IOSS_TELEM_EVENT_CTL_WRITE,
|
||||
(u8 *)&telem_ctrl,
|
||||
IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
|
||||
NULL, 0);
|
||||
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
|
||||
IOSS_TELEM_EVENT_CTL_WRITE, &telem_ctrl,
|
||||
sizeof(telem_ctrl), NULL, 0);
|
||||
if (ret) {
|
||||
pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
|
||||
return ret;
|
||||
@ -315,10 +308,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
|
||||
/* Clear All Events */
|
||||
TELEM_CLEAR_EVENTS(telem_ctrl);
|
||||
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
|
||||
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
|
||||
IOSS_TELEM_EVENT_CTL_WRITE,
|
||||
(u8 *)&telem_ctrl,
|
||||
IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
|
||||
&telem_ctrl, sizeof(telem_ctrl),
|
||||
NULL, 0);
|
||||
if (ret) {
|
||||
pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
|
||||
@ -344,10 +336,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
|
||||
/* Clear All Events */
|
||||
TELEM_CLEAR_EVENTS(telem_ctrl);
|
||||
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
|
||||
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
|
||||
IOSS_TELEM_EVENT_CTL_WRITE,
|
||||
(u8 *)&telem_ctrl,
|
||||
IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
|
||||
&telem_ctrl, sizeof(telem_ctrl),
|
||||
NULL, 0);
|
||||
if (ret) {
|
||||
pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
|
||||
@ -396,10 +387,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
|
||||
TELEM_ENABLE_PERIODIC(telem_ctrl);
|
||||
telem_ctrl |= ioss_period;
|
||||
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
|
||||
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
|
||||
IOSS_TELEM_EVENT_CTL_WRITE,
|
||||
(u8 *)&telem_ctrl,
|
||||
IOSS_TELEM_EVT_CTRL_WRITE_SIZE, NULL, 0);
|
||||
&telem_ctrl, sizeof(telem_ctrl), NULL, 0);
|
||||
if (ret) {
|
||||
pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n");
|
||||
return ret;
|
||||
@ -586,8 +576,9 @@ static int telemetry_setup(struct platform_device *pdev)
|
||||
u32 read_buf, events, event_regs;
|
||||
int ret;
|
||||
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, IOSS_TELEM_INFO_READ,
|
||||
NULL, 0, &read_buf, IOSS_TELEM_READ_WORD);
|
||||
ret = intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM,
|
||||
IOSS_TELEM_INFO_READ, NULL, 0,
|
||||
&read_buf, sizeof(read_buf));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "IOSS TELEM_INFO Read Failed\n");
|
||||
return ret;
|
||||
@ -681,6 +672,8 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
|
||||
|
||||
mutex_lock(&(telm_conf->telem_lock));
|
||||
if (ioss_period) {
|
||||
struct intel_scu_ipc_dev *scu = telm_conf->scu;
|
||||
|
||||
if (TELEM_SAMPLE_PERIOD_INVALID(ioss_period)) {
|
||||
pr_err("IOSS Sampling Period Out of Range\n");
|
||||
ret = -EINVAL;
|
||||
@ -688,9 +681,9 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
|
||||
}
|
||||
|
||||
/* Get telemetry EVENT CTL */
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
|
||||
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
|
||||
IOSS_TELEM_EVENT_CTL_READ, NULL, 0,
|
||||
&telem_ctrl, IOSS_TELEM_READ_WORD);
|
||||
&telem_ctrl, sizeof(telem_ctrl));
|
||||
if (ret) {
|
||||
pr_err("IOSS TELEM_CTRL Read Failed\n");
|
||||
goto out;
|
||||
@ -699,11 +692,10 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
|
||||
/* Disable Telemetry */
|
||||
TELEM_DISABLE(telem_ctrl);
|
||||
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
|
||||
IOSS_TELEM_EVENT_CTL_WRITE,
|
||||
(u8 *)&telem_ctrl,
|
||||
IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
|
||||
NULL, 0);
|
||||
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
|
||||
IOSS_TELEM_EVENT_CTL_WRITE,
|
||||
&telem_ctrl, sizeof(telem_ctrl),
|
||||
NULL, 0);
|
||||
if (ret) {
|
||||
pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
|
||||
goto out;
|
||||
@ -715,11 +707,10 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
|
||||
TELEM_ENABLE_PERIODIC(telem_ctrl);
|
||||
telem_ctrl |= ioss_period;
|
||||
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
|
||||
IOSS_TELEM_EVENT_CTL_WRITE,
|
||||
(u8 *)&telem_ctrl,
|
||||
IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
|
||||
NULL, 0);
|
||||
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
|
||||
IOSS_TELEM_EVENT_CTL_WRITE,
|
||||
&telem_ctrl, sizeof(telem_ctrl),
|
||||
NULL, 0);
|
||||
if (ret) {
|
||||
pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n");
|
||||
goto out;
|
||||
@ -1014,9 +1005,9 @@ static int telemetry_plt_get_trace_verbosity(enum telemetry_unit telem_unit,
|
||||
break;
|
||||
|
||||
case TELEM_IOSS:
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
|
||||
IOSS_TELEM_TRACE_CTL_READ, NULL, 0, &temp,
|
||||
IOSS_TELEM_READ_WORD);
|
||||
ret = intel_scu_ipc_dev_command(telm_conf->scu,
|
||||
IOSS_TELEM, IOSS_TELEM_TRACE_CTL_READ,
|
||||
NULL, 0, &temp, sizeof(temp));
|
||||
if (ret) {
|
||||
pr_err("IOSS TRACE_CTL Read Failed\n");
|
||||
goto out;
|
||||
@ -1068,9 +1059,9 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
|
||||
break;
|
||||
|
||||
case TELEM_IOSS:
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
|
||||
IOSS_TELEM_TRACE_CTL_READ, NULL, 0, &temp,
|
||||
IOSS_TELEM_READ_WORD);
|
||||
ret = intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM,
|
||||
IOSS_TELEM_TRACE_CTL_READ,
|
||||
NULL, 0, &temp, sizeof(temp));
|
||||
if (ret) {
|
||||
pr_err("IOSS TRACE_CTL Read Failed\n");
|
||||
goto out;
|
||||
@ -1079,9 +1070,9 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
|
||||
TELEM_CLEAR_VERBOSITY_BITS(temp);
|
||||
TELEM_SET_VERBOSITY_BITS(temp, verbosity);
|
||||
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
|
||||
IOSS_TELEM_TRACE_CTL_WRITE, (u8 *)&temp,
|
||||
IOSS_TELEM_WRITE_FOURBYTES, NULL, 0);
|
||||
ret = intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM,
|
||||
IOSS_TELEM_TRACE_CTL_WRITE,
|
||||
&temp, sizeof(temp), NULL, 0);
|
||||
if (ret) {
|
||||
pr_err("IOSS TRACE_CTL Verbosity Set Failed\n");
|
||||
goto out;
|
||||
@ -1124,6 +1115,8 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev)
|
||||
|
||||
telm_conf = (struct telemetry_plt_config *)id->driver_data;
|
||||
|
||||
telm_conf->pmc = dev_get_drvdata(pdev->dev.parent);
|
||||
|
||||
mem = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(mem))
|
||||
return PTR_ERR(mem);
|
||||
@ -1136,6 +1129,12 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev)
|
||||
|
||||
telm_conf->ioss_config.regmap = mem;
|
||||
|
||||
telm_conf->scu = devm_intel_scu_ipc_dev_get(&pdev->dev);
|
||||
if (!telm_conf->scu) {
|
||||
ret = -EPROBE_DEFER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mutex_init(&telm_conf->telem_lock);
|
||||
mutex_init(&telm_conf->telem_trace_lock);
|
||||
|
||||
|
@ -67,9 +67,7 @@ static u32 inited;
|
||||
#define INIT_INPUT_WMI_0 0x01
|
||||
#define INIT_INPUT_WMI_2 0x02
|
||||
#define INIT_INPUT_ACPI 0x04
|
||||
#define INIT_TPAD_LED 0x08
|
||||
#define INIT_KBD_LED 0x10
|
||||
#define INIT_SPARSE_KEYMAP 0x80
|
||||
#define INIT_SPARSE_KEYMAP 0x80
|
||||
|
||||
static const struct key_entry wmi_keymap[] = {
|
||||
{KE_KEY, 0x70, {KEY_F15} }, /* LG control panel (F1) */
|
||||
@ -626,11 +624,9 @@ static int acpi_add(struct acpi_device *device)
|
||||
if (ret)
|
||||
goto out_platform_device;
|
||||
|
||||
if (!led_classdev_register(&pf_device->dev, &kbd_backlight))
|
||||
inited |= INIT_KBD_LED;
|
||||
|
||||
if (!led_classdev_register(&pf_device->dev, &tpad_led))
|
||||
inited |= INIT_TPAD_LED;
|
||||
/* LEDs are optional */
|
||||
led_classdev_register(&pf_device->dev, &kbd_backlight);
|
||||
led_classdev_register(&pf_device->dev, &tpad_led);
|
||||
|
||||
wmi_input_setup();
|
||||
|
||||
@ -646,11 +642,9 @@ out_platform_registered:
|
||||
static int acpi_remove(struct acpi_device *device)
|
||||
{
|
||||
sysfs_remove_group(&pf_device->dev.kobj, &dev_attribute_group);
|
||||
if (inited & INIT_KBD_LED)
|
||||
led_classdev_unregister(&kbd_backlight);
|
||||
|
||||
if (inited & INIT_TPAD_LED)
|
||||
led_classdev_unregister(&tpad_led);
|
||||
led_classdev_unregister(&tpad_led);
|
||||
led_classdev_unregister(&kbd_backlight);
|
||||
|
||||
wmi_input_destroy();
|
||||
platform_device_unregister(pf_device);
|
||||
|
@ -1138,8 +1138,7 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
|
||||
|
||||
static void samsung_leds_exit(struct samsung_laptop *samsung)
|
||||
{
|
||||
if (!IS_ERR_OR_NULL(samsung->kbd_led.dev))
|
||||
led_classdev_unregister(&samsung->kbd_led);
|
||||
led_classdev_unregister(&samsung->kbd_led);
|
||||
if (samsung->led_workqueue)
|
||||
destroy_workqueue(samsung->led_workqueue);
|
||||
}
|
||||
|
@ -757,33 +757,6 @@ static union acpi_object *__call_snc_method(acpi_handle handle, char *method,
|
||||
return result;
|
||||
}
|
||||
|
||||
static int sony_nc_int_call(acpi_handle handle, char *name, int *value,
|
||||
int *result)
|
||||
{
|
||||
union acpi_object *object = NULL;
|
||||
if (value) {
|
||||
u64 v = *value;
|
||||
object = __call_snc_method(handle, name, &v);
|
||||
} else
|
||||
object = __call_snc_method(handle, name, NULL);
|
||||
|
||||
if (!object)
|
||||
return -EINVAL;
|
||||
|
||||
if (object->type != ACPI_TYPE_INTEGER) {
|
||||
pr_warn("Invalid acpi_object: expected 0x%x got 0x%x\n",
|
||||
ACPI_TYPE_INTEGER, object->type);
|
||||
kfree(object);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (result)
|
||||
*result = object->integer.value;
|
||||
|
||||
kfree(object);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MIN(a, b) (a > b ? b : a)
|
||||
static int sony_nc_buffer_call(acpi_handle handle, char *name, u64 *value,
|
||||
void *buffer, size_t buflen)
|
||||
@ -795,17 +768,20 @@ static int sony_nc_buffer_call(acpi_handle handle, char *name, u64 *value,
|
||||
if (!object)
|
||||
return -EINVAL;
|
||||
|
||||
if (object->type == ACPI_TYPE_BUFFER) {
|
||||
if (!buffer) {
|
||||
/* do nothing */
|
||||
} else if (object->type == ACPI_TYPE_BUFFER) {
|
||||
len = MIN(buflen, object->buffer.length);
|
||||
memset(buffer, 0, buflen);
|
||||
memcpy(buffer, object->buffer.pointer, len);
|
||||
|
||||
} else if (object->type == ACPI_TYPE_INTEGER) {
|
||||
len = MIN(buflen, sizeof(object->integer.value));
|
||||
memset(buffer, 0, buflen);
|
||||
memcpy(buffer, &object->integer.value, len);
|
||||
|
||||
} else {
|
||||
pr_warn("Invalid acpi_object: expected 0x%x got 0x%x\n",
|
||||
ACPI_TYPE_BUFFER, object->type);
|
||||
pr_warn("Unexpected acpi_object: 0x%x\n", object->type);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
@ -813,6 +789,23 @@ static int sony_nc_buffer_call(acpi_handle handle, char *name, u64 *value,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sony_nc_int_call(acpi_handle handle, char *name, int *value, int
|
||||
*result)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (value) {
|
||||
u64 v = *value;
|
||||
|
||||
ret = sony_nc_buffer_call(handle, name, &v, result,
|
||||
sizeof(*result));
|
||||
} else {
|
||||
ret = sony_nc_buffer_call(handle, name, NULL, result,
|
||||
sizeof(*result));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct sony_nc_handles {
|
||||
u16 cap[0x10];
|
||||
struct device_attribute devattr;
|
||||
@ -2295,7 +2288,12 @@ static void sony_nc_thermal_cleanup(struct platform_device *pd)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static void sony_nc_thermal_resume(void)
|
||||
{
|
||||
unsigned int status = sony_nc_thermal_mode_get();
|
||||
int status;
|
||||
|
||||
if (!th_handle)
|
||||
return;
|
||||
|
||||
status = sony_nc_thermal_mode_get();
|
||||
|
||||
if (status != th_handle->mode)
|
||||
sony_nc_thermal_mode_set(th_handle->mode);
|
||||
|
@ -318,6 +318,7 @@ static struct {
|
||||
u32 uwb:1;
|
||||
u32 fan_ctrl_status_undef:1;
|
||||
u32 second_fan:1;
|
||||
u32 second_fan_ctl:1;
|
||||
u32 beep_needs_two_args:1;
|
||||
u32 mixer_no_level_control:1;
|
||||
u32 battery_force_primary:1;
|
||||
@ -884,20 +885,11 @@ static ssize_t dispatch_proc_write(struct file *file,
|
||||
|
||||
if (!ibm || !ibm->write)
|
||||
return -EINVAL;
|
||||
if (count > PAGE_SIZE - 2)
|
||||
return -EINVAL;
|
||||
|
||||
kernbuf = kmalloc(count + 2, GFP_KERNEL);
|
||||
if (!kernbuf)
|
||||
return -ENOMEM;
|
||||
kernbuf = strndup_user(userbuf, PAGE_SIZE);
|
||||
if (IS_ERR(kernbuf))
|
||||
return PTR_ERR(kernbuf);
|
||||
|
||||
if (copy_from_user(kernbuf, userbuf, count)) {
|
||||
kfree(kernbuf);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
kernbuf[count] = 0;
|
||||
strcat(kernbuf, ",");
|
||||
ret = ibm->write(kernbuf);
|
||||
if (ret == 0)
|
||||
ret = count;
|
||||
@ -915,23 +907,6 @@ static const struct proc_ops dispatch_proc_ops = {
|
||||
.proc_write = dispatch_proc_write,
|
||||
};
|
||||
|
||||
static char *next_cmd(char **cmds)
|
||||
{
|
||||
char *start = *cmds;
|
||||
char *end;
|
||||
|
||||
while ((end = strchr(start, ',')) && end == start)
|
||||
start = end + 1;
|
||||
|
||||
if (!end)
|
||||
return NULL;
|
||||
|
||||
*end = 0;
|
||||
*cmds = end + 1;
|
||||
return start;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
****************************************************************************
|
||||
*
|
||||
@ -1422,7 +1397,7 @@ static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf)
|
||||
if (id >= TPACPI_RFK_SW_MAX)
|
||||
return -ENODEV;
|
||||
|
||||
while ((cmd = next_cmd(&buf))) {
|
||||
while ((cmd = strsep(&buf, ","))) {
|
||||
if (strlencmp(cmd, "enable") == 0)
|
||||
status = TPACPI_RFK_RADIO_ON;
|
||||
else if (strlencmp(cmd, "disable") == 0)
|
||||
@ -4305,7 +4280,7 @@ static int hotkey_write(char *buf)
|
||||
mask = hotkey_user_mask;
|
||||
|
||||
res = 0;
|
||||
while ((cmd = next_cmd(&buf))) {
|
||||
while ((cmd = strsep(&buf, ","))) {
|
||||
if (strlencmp(cmd, "enable") == 0) {
|
||||
hotkey_enabledisable_warn(1);
|
||||
} else if (strlencmp(cmd, "disable") == 0) {
|
||||
@ -5232,7 +5207,7 @@ static int video_write(char *buf)
|
||||
enable = 0;
|
||||
disable = 0;
|
||||
|
||||
while ((cmd = next_cmd(&buf))) {
|
||||
while ((cmd = strsep(&buf, ","))) {
|
||||
if (strlencmp(cmd, "lcd_enable") == 0) {
|
||||
enable |= TP_ACPI_VIDEO_S_LCD;
|
||||
} else if (strlencmp(cmd, "lcd_disable") == 0) {
|
||||
@ -5433,8 +5408,7 @@ static int __init kbdlight_init(struct ibm_init_struct *iibm)
|
||||
|
||||
static void kbdlight_exit(void)
|
||||
{
|
||||
if (tp_features.kbdlight)
|
||||
led_classdev_unregister(&tpacpi_led_kbdlight.led_classdev);
|
||||
led_classdev_unregister(&tpacpi_led_kbdlight.led_classdev);
|
||||
}
|
||||
|
||||
static int kbdlight_set_level_and_update(int level)
|
||||
@ -5472,23 +5446,18 @@ static int kbdlight_read(struct seq_file *m)
|
||||
static int kbdlight_write(char *buf)
|
||||
{
|
||||
char *cmd;
|
||||
int level = -1;
|
||||
int res, level = -EINVAL;
|
||||
|
||||
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;
|
||||
while ((cmd = strsep(&buf, ","))) {
|
||||
res = kstrtoint(cmd, 10, &level);
|
||||
if (res < 0)
|
||||
return res;
|
||||
}
|
||||
|
||||
if (level == -1)
|
||||
if (level >= 3 || level < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return kbdlight_set_level_and_update(level);
|
||||
@ -5657,7 +5626,7 @@ static int light_write(char *buf)
|
||||
if (!tp_features.light)
|
||||
return -ENODEV;
|
||||
|
||||
while ((cmd = next_cmd(&buf))) {
|
||||
while ((cmd = strsep(&buf, ","))) {
|
||||
if (strlencmp(cmd, "on") == 0) {
|
||||
newstatus = 1;
|
||||
} else if (strlencmp(cmd, "off") == 0) {
|
||||
@ -5742,7 +5711,7 @@ static int cmos_write(char *buf)
|
||||
char *cmd;
|
||||
int cmos_cmd, res;
|
||||
|
||||
while ((cmd = next_cmd(&buf))) {
|
||||
while ((cmd = strsep(&buf, ","))) {
|
||||
if (sscanf(cmd, "%u", &cmos_cmd) == 1 &&
|
||||
cmos_cmd >= 0 && cmos_cmd <= 21) {
|
||||
/* cmos_cmd set */
|
||||
@ -5948,20 +5917,14 @@ static void led_exit(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
|
||||
if (tpacpi_leds[i].led_classdev.name)
|
||||
led_classdev_unregister(&tpacpi_leds[i].led_classdev);
|
||||
}
|
||||
for (i = 0; i < TPACPI_LED_NUMLEDS; i++)
|
||||
led_classdev_unregister(&tpacpi_leds[i].led_classdev);
|
||||
|
||||
kfree(tpacpi_leds);
|
||||
}
|
||||
|
||||
static int __init tpacpi_init_led(unsigned int led)
|
||||
{
|
||||
int rc;
|
||||
|
||||
tpacpi_leds[led].led = led;
|
||||
|
||||
/* LEDs with no name don't get registered */
|
||||
if (!tpacpi_led_names[led])
|
||||
return 0;
|
||||
@ -5969,17 +5932,12 @@ static int __init tpacpi_init_led(unsigned int led)
|
||||
tpacpi_leds[led].led_classdev.brightness_set_blocking = &led_sysfs_set;
|
||||
tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set;
|
||||
if (led_supported == TPACPI_LED_570)
|
||||
tpacpi_leds[led].led_classdev.brightness_get =
|
||||
&led_sysfs_get;
|
||||
tpacpi_leds[led].led_classdev.brightness_get = &led_sysfs_get;
|
||||
|
||||
tpacpi_leds[led].led_classdev.name = tpacpi_led_names[led];
|
||||
tpacpi_leds[led].led = led;
|
||||
|
||||
rc = led_classdev_register(&tpacpi_pdev->dev,
|
||||
&tpacpi_leds[led].led_classdev);
|
||||
if (rc < 0)
|
||||
tpacpi_leds[led].led_classdev.name = NULL;
|
||||
|
||||
return rc;
|
||||
return led_classdev_register(&tpacpi_pdev->dev, &tpacpi_leds[led].led_classdev);
|
||||
}
|
||||
|
||||
static const struct tpacpi_quirk led_useful_qtable[] __initconst = {
|
||||
@ -6089,8 +6047,7 @@ static int __init led_init(struct ibm_init_struct *iibm)
|
||||
for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
|
||||
tpacpi_leds[i].led = -1;
|
||||
|
||||
if (!tpacpi_is_led_restricted(i) &&
|
||||
test_bit(i, &useful_leds)) {
|
||||
if (!tpacpi_is_led_restricted(i) && test_bit(i, &useful_leds)) {
|
||||
rc = tpacpi_init_led(i);
|
||||
if (rc < 0) {
|
||||
led_exit();
|
||||
@ -6143,12 +6100,14 @@ static int led_write(char *buf)
|
||||
if (!led_supported)
|
||||
return -ENODEV;
|
||||
|
||||
while ((cmd = next_cmd(&buf))) {
|
||||
while ((cmd = strsep(&buf, ","))) {
|
||||
if (sscanf(cmd, "%d", &led) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (led < 0 || led > (TPACPI_LED_NUMLEDS - 1) ||
|
||||
tpacpi_leds[led].led < 0)
|
||||
if (led < 0 || led > (TPACPI_LED_NUMLEDS - 1))
|
||||
return -ENODEV;
|
||||
|
||||
if (tpacpi_leds[led].led < 0)
|
||||
return -ENODEV;
|
||||
|
||||
if (strstr(cmd, "off")) {
|
||||
@ -6228,7 +6187,7 @@ static int beep_write(char *buf)
|
||||
if (!beep_handle)
|
||||
return -ENODEV;
|
||||
|
||||
while ((cmd = next_cmd(&buf))) {
|
||||
while ((cmd = strsep(&buf, ","))) {
|
||||
if (sscanf(cmd, "%u", &beep_cmd) == 1 &&
|
||||
beep_cmd >= 0 && beep_cmd <= 17) {
|
||||
/* beep_cmd set */
|
||||
@ -7116,7 +7075,7 @@ static int brightness_write(char *buf)
|
||||
if (level < 0)
|
||||
return level;
|
||||
|
||||
while ((cmd = next_cmd(&buf))) {
|
||||
while ((cmd = strsep(&buf, ","))) {
|
||||
if (strlencmp(cmd, "up") == 0) {
|
||||
if (level < bright_maxlvl)
|
||||
level++;
|
||||
@ -7868,7 +7827,7 @@ static int volume_write(char *buf)
|
||||
new_level = s & TP_EC_AUDIO_LVL_MSK;
|
||||
new_mute = s & TP_EC_AUDIO_MUTESW_MSK;
|
||||
|
||||
while ((cmd = next_cmd(&buf))) {
|
||||
while ((cmd = strsep(&buf, ","))) {
|
||||
if (!tp_features.mixer_no_level_control) {
|
||||
if (strlencmp(cmd, "up") == 0) {
|
||||
if (new_mute)
|
||||
@ -8324,11 +8283,19 @@ static int fan_set_level(int level)
|
||||
|
||||
switch (fan_control_access_mode) {
|
||||
case TPACPI_FAN_WR_ACPI_SFAN:
|
||||
if (level >= 0 && level <= 7) {
|
||||
if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))
|
||||
return -EIO;
|
||||
} else
|
||||
if ((level < 0) || (level > 7))
|
||||
return -EINVAL;
|
||||
|
||||
if (tp_features.second_fan_ctl) {
|
||||
if (!fan_select_fan2() ||
|
||||
!acpi_evalf(sfan_handle, NULL, NULL, "vd", level)) {
|
||||
pr_warn("Couldn't set 2nd fan level, disabling support\n");
|
||||
tp_features.second_fan_ctl = 0;
|
||||
}
|
||||
fan_select_fan1();
|
||||
}
|
||||
if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))
|
||||
return -EIO;
|
||||
break;
|
||||
|
||||
case TPACPI_FAN_WR_ACPI_FANS:
|
||||
@ -8345,6 +8312,15 @@ static int fan_set_level(int level)
|
||||
else if (level & TP_EC_FAN_AUTO)
|
||||
level |= 4; /* safety min speed 4 */
|
||||
|
||||
if (tp_features.second_fan_ctl) {
|
||||
if (!fan_select_fan2() ||
|
||||
!acpi_ec_write(fan_status_offset, level)) {
|
||||
pr_warn("Couldn't set 2nd fan level, disabling support\n");
|
||||
tp_features.second_fan_ctl = 0;
|
||||
}
|
||||
fan_select_fan1();
|
||||
|
||||
}
|
||||
if (!acpi_ec_write(fan_status_offset, level))
|
||||
return -EIO;
|
||||
else
|
||||
@ -8763,6 +8739,7 @@ static const struct attribute_group fan_attr_group = {
|
||||
|
||||
#define TPACPI_FAN_Q1 0x0001 /* Unitialized HFSP */
|
||||
#define TPACPI_FAN_2FAN 0x0002 /* EC 0x31 bit 0 selects fan2 */
|
||||
#define TPACPI_FAN_2CTL 0x0004 /* selects fan2 control */
|
||||
|
||||
static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
|
||||
TPACPI_QEC_IBM('1', 'Y', TPACPI_FAN_Q1),
|
||||
@ -8771,6 +8748,13 @@ static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
|
||||
TPACPI_QEC_IBM('7', '0', TPACPI_FAN_Q1),
|
||||
TPACPI_QEC_LNV('7', 'M', TPACPI_FAN_2FAN),
|
||||
TPACPI_Q_LNV('N', '1', TPACPI_FAN_2FAN),
|
||||
TPACPI_Q_LNV3('N', '1', 'D', TPACPI_FAN_2CTL), /* P70 */
|
||||
TPACPI_Q_LNV3('N', '1', 'E', TPACPI_FAN_2CTL), /* P50 */
|
||||
TPACPI_Q_LNV3('N', '1', 'T', TPACPI_FAN_2CTL), /* P71 */
|
||||
TPACPI_Q_LNV3('N', '1', 'U', TPACPI_FAN_2CTL), /* P51 */
|
||||
TPACPI_Q_LNV3('N', '2', 'C', TPACPI_FAN_2CTL), /* P52 / P72 */
|
||||
TPACPI_Q_LNV3('N', '2', 'E', TPACPI_FAN_2CTL), /* P1 / X1 Extreme (1st gen) */
|
||||
TPACPI_Q_LNV3('N', '2', 'O', TPACPI_FAN_2CTL), /* P1 / X1 Extreme (2nd gen) */
|
||||
};
|
||||
|
||||
static int __init fan_init(struct ibm_init_struct *iibm)
|
||||
@ -8788,6 +8772,7 @@ static int __init fan_init(struct ibm_init_struct *iibm)
|
||||
fan_watchdog_maxinterval = 0;
|
||||
tp_features.fan_ctrl_status_undef = 0;
|
||||
tp_features.second_fan = 0;
|
||||
tp_features.second_fan_ctl = 0;
|
||||
fan_control_desired_level = 7;
|
||||
|
||||
if (tpacpi_is_ibm()) {
|
||||
@ -8812,8 +8797,12 @@ static int __init fan_init(struct ibm_init_struct *iibm)
|
||||
fan_quirk1_setup();
|
||||
if (quirks & TPACPI_FAN_2FAN) {
|
||||
tp_features.second_fan = 1;
|
||||
dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN,
|
||||
"secondary fan support enabled\n");
|
||||
pr_info("secondary fan support enabled\n");
|
||||
}
|
||||
if (quirks & TPACPI_FAN_2CTL) {
|
||||
tp_features.second_fan = 1;
|
||||
tp_features.second_fan_ctl = 1;
|
||||
pr_info("secondary fan control enabled\n");
|
||||
}
|
||||
} else {
|
||||
pr_err("ThinkPad ACPI EC access misbehaving, fan status and control unavailable\n");
|
||||
@ -9148,7 +9137,7 @@ static int fan_write(char *buf)
|
||||
char *cmd;
|
||||
int rc = 0;
|
||||
|
||||
while (!rc && (cmd = next_cmd(&buf))) {
|
||||
while (!rc && (cmd = strsep(&buf, ","))) {
|
||||
if (!((fan_control_commands & TPACPI_FAN_CMD_LEVEL) &&
|
||||
fan_write_cmd_level(cmd, &rc)) &&
|
||||
!((fan_control_commands & TPACPI_FAN_CMD_ENABLE) &&
|
||||
@ -9271,10 +9260,8 @@ static int mute_led_init(struct ibm_init_struct *iibm)
|
||||
mute_led_cdev[i].brightness = ledtrig_audio_get(i);
|
||||
err = led_classdev_register(&tpacpi_pdev->dev, &mute_led_cdev[i]);
|
||||
if (err < 0) {
|
||||
while (i--) {
|
||||
if (led_tables[i].state >= 0)
|
||||
led_classdev_unregister(&mute_led_cdev[i]);
|
||||
}
|
||||
while (i--)
|
||||
led_classdev_unregister(&mute_led_cdev[i]);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
@ -9286,10 +9273,8 @@ static void mute_led_exit(void)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < TPACPI_LED_MAX; i++) {
|
||||
if (led_tables[i].state >= 0) {
|
||||
led_classdev_unregister(&mute_led_cdev[i]);
|
||||
tpacpi_led_set(i, false);
|
||||
}
|
||||
led_classdev_unregister(&mute_led_cdev[i]);
|
||||
tpacpi_led_set(i, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -9786,19 +9771,18 @@ static int lcdshadow_read(struct seq_file *m)
|
||||
static int lcdshadow_write(char *buf)
|
||||
{
|
||||
char *cmd;
|
||||
int state = -1;
|
||||
int res, state = -EINVAL;
|
||||
|
||||
if (lcdshadow_state < 0)
|
||||
return -ENODEV;
|
||||
|
||||
while ((cmd = next_cmd(&buf))) {
|
||||
if (strlencmp(cmd, "0") == 0)
|
||||
state = 0;
|
||||
else if (strlencmp(cmd, "1") == 0)
|
||||
state = 1;
|
||||
while ((cmd = strsep(&buf, ","))) {
|
||||
res = kstrtoint(cmd, 10, &state);
|
||||
if (res < 0)
|
||||
return res;
|
||||
}
|
||||
|
||||
if (state == -1)
|
||||
if (state >= 2 || state < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return lcdshadow_set(state);
|
||||
@ -10314,10 +10298,9 @@ static int __init set_ibm_param(const char *val, const struct kernel_param *kp)
|
||||
continue;
|
||||
|
||||
if (strcmp(ibm->name, kp->name) == 0 && ibm->write) {
|
||||
if (strlen(val) > sizeof(ibms_init[i].param) - 2)
|
||||
if (strlen(val) > sizeof(ibms_init[i].param) - 1)
|
||||
return -ENOSPC;
|
||||
strcpy(ibms_init[i].param, val);
|
||||
strcat(ibms_init[i].param, ",");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -205,9 +205,6 @@ struct toshiba_acpi_dev {
|
||||
unsigned int special_functions;
|
||||
|
||||
bool kbd_event_generated;
|
||||
bool kbd_led_registered;
|
||||
bool illumination_led_registered;
|
||||
bool eco_led_registered;
|
||||
bool killswitch;
|
||||
};
|
||||
|
||||
@ -458,7 +455,6 @@ static void toshiba_illumination_available(struct toshiba_acpi_dev *dev)
|
||||
acpi_status status;
|
||||
|
||||
dev->illumination_supported = 0;
|
||||
dev->illumination_led_registered = false;
|
||||
|
||||
if (!sci_open(dev))
|
||||
return;
|
||||
@ -528,7 +524,6 @@ static void toshiba_kbd_illum_available(struct toshiba_acpi_dev *dev)
|
||||
acpi_status status;
|
||||
|
||||
dev->kbd_illum_supported = 0;
|
||||
dev->kbd_led_registered = false;
|
||||
dev->kbd_event_generated = false;
|
||||
|
||||
if (!sci_open(dev))
|
||||
@ -673,7 +668,6 @@ static void toshiba_eco_mode_available(struct toshiba_acpi_dev *dev)
|
||||
acpi_status status;
|
||||
|
||||
dev->eco_supported = 0;
|
||||
dev->eco_led_registered = false;
|
||||
|
||||
status = tci_raw(dev, in, out);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
@ -2993,14 +2987,9 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev)
|
||||
|
||||
backlight_device_unregister(dev->backlight_dev);
|
||||
|
||||
if (dev->illumination_led_registered)
|
||||
led_classdev_unregister(&dev->led_dev);
|
||||
|
||||
if (dev->kbd_led_registered)
|
||||
led_classdev_unregister(&dev->kbd_led);
|
||||
|
||||
if (dev->eco_led_registered)
|
||||
led_classdev_unregister(&dev->eco_led);
|
||||
led_classdev_unregister(&dev->led_dev);
|
||||
led_classdev_unregister(&dev->kbd_led);
|
||||
led_classdev_unregister(&dev->eco_led);
|
||||
|
||||
if (dev->wwan_rfk) {
|
||||
rfkill_unregister(dev->wwan_rfk);
|
||||
@ -3092,8 +3081,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
|
||||
dev->led_dev.max_brightness = 1;
|
||||
dev->led_dev.brightness_set = toshiba_illumination_set;
|
||||
dev->led_dev.brightness_get = toshiba_illumination_get;
|
||||
if (!led_classdev_register(&acpi_dev->dev, &dev->led_dev))
|
||||
dev->illumination_led_registered = true;
|
||||
led_classdev_register(&acpi_dev->dev, &dev->led_dev);
|
||||
}
|
||||
|
||||
toshiba_eco_mode_available(dev);
|
||||
@ -3102,8 +3090,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
|
||||
dev->eco_led.max_brightness = 1;
|
||||
dev->eco_led.brightness_set = toshiba_eco_mode_set_status;
|
||||
dev->eco_led.brightness_get = toshiba_eco_mode_get_status;
|
||||
if (!led_classdev_register(&dev->acpi_dev->dev, &dev->eco_led))
|
||||
dev->eco_led_registered = true;
|
||||
led_classdev_register(&dev->acpi_dev->dev, &dev->eco_led);
|
||||
}
|
||||
|
||||
toshiba_kbd_illum_available(dev);
|
||||
@ -3119,8 +3106,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
|
||||
dev->kbd_led.max_brightness = 1;
|
||||
dev->kbd_led.brightness_set = toshiba_kbd_backlight_set;
|
||||
dev->kbd_led.brightness_get = toshiba_kbd_backlight_get;
|
||||
if (!led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led))
|
||||
dev->kbd_led_registered = true;
|
||||
led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led);
|
||||
}
|
||||
|
||||
ret = toshiba_touchpad_get(dev, &dummy);
|
||||
|
@ -373,6 +373,23 @@ static const struct ts_dmi_data jumper_ezpad_mini3_data = {
|
||||
.properties = jumper_ezpad_mini3_props,
|
||||
};
|
||||
|
||||
static const struct property_entry mpman_mpwin895cl_props[] = {
|
||||
PROPERTY_ENTRY_U32("touchscreen-min-x", 3),
|
||||
PROPERTY_ENTRY_U32("touchscreen-min-y", 9),
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-x", 1728),
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-y", 1150),
|
||||
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
|
||||
PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-mpman-mpwin895cl.fw"),
|
||||
PROPERTY_ENTRY_U32("silead,max-fingers", 10),
|
||||
PROPERTY_ENTRY_BOOL("silead,home-button"),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct ts_dmi_data mpman_mpwin895cl_data = {
|
||||
.acpi_name = "MSSL1680:00",
|
||||
.properties = mpman_mpwin895cl_props,
|
||||
};
|
||||
|
||||
static const struct property_entry myria_my8307_props[] = {
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-x", 1720),
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-y", 1140),
|
||||
@ -448,6 +465,24 @@ static const struct ts_dmi_data onda_v820w_32g_data = {
|
||||
.properties = onda_v820w_32g_props,
|
||||
};
|
||||
|
||||
static const struct property_entry onda_v891_v5_props[] = {
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-x", 1715),
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-y", 1140),
|
||||
PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"),
|
||||
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
|
||||
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
|
||||
PROPERTY_ENTRY_STRING("firmware-name",
|
||||
"gsl3676-onda-v891-v5.fw"),
|
||||
PROPERTY_ENTRY_U32("silead,max-fingers", 10),
|
||||
PROPERTY_ENTRY_BOOL("silead,home-button"),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct ts_dmi_data onda_v891_v5_data = {
|
||||
.acpi_name = "MSSL1680:00",
|
||||
.properties = onda_v891_v5_props,
|
||||
};
|
||||
|
||||
static const struct property_entry onda_v891w_v1_props[] = {
|
||||
PROPERTY_ENTRY_U32("touchscreen-min-x", 46),
|
||||
PROPERTY_ENTRY_U32("touchscreen-min-y", 8),
|
||||
@ -588,6 +623,22 @@ static const struct ts_dmi_data schneider_sct101ctm_data = {
|
||||
.properties = schneider_sct101ctm_props,
|
||||
};
|
||||
|
||||
static const struct property_entry techbite_arc_11_6_props[] = {
|
||||
PROPERTY_ENTRY_U32("touchscreen-min-x", 5),
|
||||
PROPERTY_ENTRY_U32("touchscreen-min-y", 7),
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-x", 1981),
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-y", 1270),
|
||||
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
|
||||
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-techbite-arc-11-6.fw"),
|
||||
PROPERTY_ENTRY_U32("silead,max-fingers", 10),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct ts_dmi_data techbite_arc_11_6_data = {
|
||||
.acpi_name = "MSSL1680:00",
|
||||
.properties = techbite_arc_11_6_props,
|
||||
};
|
||||
|
||||
static const struct property_entry teclast_x3_plus_props[] = {
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-x", 1980),
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-y", 1500),
|
||||
@ -662,11 +713,14 @@ static const struct ts_dmi_data trekstor_primetab_t13b_data = {
|
||||
};
|
||||
|
||||
static const struct property_entry trekstor_surftab_twin_10_1_props[] = {
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-x", 1900),
|
||||
PROPERTY_ENTRY_U32("touchscreen-min-x", 20),
|
||||
PROPERTY_ENTRY_U32("touchscreen-min-y", 0),
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-x", 1890),
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-y", 1280),
|
||||
PROPERTY_ENTRY_U32("touchscreen-inverted-y", 1),
|
||||
PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-surftab-twin-10-1-st10432-8.fw"),
|
||||
PROPERTY_ENTRY_U32("silead,max-fingers", 10),
|
||||
PROPERTY_ENTRY_BOOL("silead,home-button"),
|
||||
{ }
|
||||
};
|
||||
|
||||
@ -691,6 +745,20 @@ static const struct ts_dmi_data trekstor_surftab_wintron70_data = {
|
||||
.properties = trekstor_surftab_wintron70_props,
|
||||
};
|
||||
|
||||
static const struct property_entry vinga_twizzle_j116_props[] = {
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-x", 1920),
|
||||
PROPERTY_ENTRY_U32("touchscreen-size-y", 1280),
|
||||
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-vinga-twizzle_j116.fw"),
|
||||
PROPERTY_ENTRY_U32("silead,max-fingers", 10),
|
||||
PROPERTY_ENTRY_BOOL("silead,home-button"),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct ts_dmi_data vinga_twizzle_j116_data = {
|
||||
.acpi_name = "MSSL1680:00",
|
||||
.properties = vinga_twizzle_j116_props,
|
||||
};
|
||||
|
||||
/* NOTE: Please keep this table sorted alphabetically */
|
||||
const struct dmi_system_id touchscreen_dmi_table[] = {
|
||||
{
|
||||
@ -908,6 +976,14 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "FlexBook edge11 - M-FBE11"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* MP Man MPWIN895CL */
|
||||
.driver_data = (void *)&mpman_mpwin895cl_data,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "MPMAN"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "MPWIN8900CL"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Myria MY8307 */
|
||||
.driver_data = (void *)&myria_my8307_data,
|
||||
@ -940,6 +1016,15 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "V820w DualOS")
|
||||
},
|
||||
},
|
||||
{
|
||||
/* ONDA V891 v5 */
|
||||
.driver_data = (void *)&onda_v891_v5_data,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ONDA"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "ONDA Tablet"),
|
||||
DMI_MATCH(DMI_BIOS_VERSION, "ONDA.D869CJABNRBA06"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* ONDA V891w revision P891WBEBV1B00 aka v1 */
|
||||
.driver_data = (void *)&onda_v891w_v1_data,
|
||||
@ -1029,6 +1114,15 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "SCT101CTM"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Techbite Arc 11.6 */
|
||||
.driver_data = (void *)&techbite_arc_11_6_data,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "mPTech"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "techBite Arc 11.6"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "G8316_272B"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Teclast X3 Plus */
|
||||
.driver_data = (void *)&teclast_x3_plus_data,
|
||||
@ -1106,6 +1200,21 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
|
||||
DMI_MATCH(DMI_BIOS_VERSION, "TREK.G.WI71C.JGBMRBA05"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Trekstor Yourbook C11B (same touchscreen as the Primebook C11) */
|
||||
.driver_data = (void *)&trekstor_primebook_c11_data,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "TREKSTOR"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "YOURBOOK C11B"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Vinga Twizzle J116 */
|
||||
.driver_data = (void *)&vinga_twizzle_j116_data,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "VINGA Twizzle J116"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Yours Y8W81, same case and touchscreen as Chuwi Vi8 */
|
||||
.driver_data = (void *)&chuwi_vi8_data,
|
||||
@ -1114,7 +1223,7 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Y8W81"),
|
||||
},
|
||||
},
|
||||
{ },
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct ts_dmi_data *ts_data;
|
||||
|
@ -111,11 +111,11 @@ static struct platform_driver acpi_wmi_driver = {
|
||||
|
||||
static bool find_guid(const char *guid_string, struct wmi_block **out)
|
||||
{
|
||||
uuid_le guid_input;
|
||||
guid_t guid_input;
|
||||
struct wmi_block *wblock;
|
||||
struct guid_block *block;
|
||||
|
||||
if (uuid_le_to_bin(guid_string, &guid_input))
|
||||
if (guid_parse(guid_string, &guid_input))
|
||||
return false;
|
||||
|
||||
list_for_each_entry(wblock, &wmi_block_list, list) {
|
||||
@ -134,7 +134,7 @@ static const void *find_guid_context(struct wmi_block *wblock,
|
||||
struct wmi_driver *wdriver)
|
||||
{
|
||||
const struct wmi_device_id *id;
|
||||
uuid_le guid_input;
|
||||
guid_t guid_input;
|
||||
|
||||
if (wblock == NULL || wdriver == NULL)
|
||||
return NULL;
|
||||
@ -143,7 +143,7 @@ static const void *find_guid_context(struct wmi_block *wblock,
|
||||
|
||||
id = wdriver->id_table;
|
||||
while (*id->guid_string) {
|
||||
if (uuid_le_to_bin(id->guid_string, &guid_input))
|
||||
if (guid_parse(id->guid_string, &guid_input))
|
||||
continue;
|
||||
if (!memcmp(wblock->gblock.guid, &guid_input, 16))
|
||||
return id->context;
|
||||
@ -202,7 +202,7 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
|
||||
/**
|
||||
* set_required_buffer_size - Sets the buffer size needed for performing IOCTL
|
||||
* @wdev: A wmi bus device from a driver
|
||||
* @instance: Instance index
|
||||
* @length: Required buffer size
|
||||
*
|
||||
* Allocates memory needed for buffer, stores the buffer size in that memory
|
||||
*/
|
||||
@ -222,8 +222,8 @@ EXPORT_SYMBOL_GPL(set_required_buffer_size);
|
||||
* @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
|
||||
* @instance: Instance index
|
||||
* @method_id: Method ID to call
|
||||
* &in: Buffer containing input for the method call
|
||||
* &out: Empty buffer to return the method results
|
||||
* @in: Buffer containing input for the method call
|
||||
* @out: Empty buffer to return the method results
|
||||
*
|
||||
* Call an ACPI-WMI method
|
||||
*/
|
||||
@ -244,8 +244,8 @@ EXPORT_SYMBOL_GPL(wmi_evaluate_method);
|
||||
* @wdev: A wmi bus device from a driver
|
||||
* @instance: Instance index
|
||||
* @method_id: Method ID to call
|
||||
* &in: Buffer containing input for the method call
|
||||
* &out: Empty buffer to return the method results
|
||||
* @in: Buffer containing input for the method call
|
||||
* @out: Empty buffer to return the method results
|
||||
*
|
||||
* Call an ACPI-WMI method
|
||||
*/
|
||||
@ -364,7 +364,7 @@ static acpi_status __query_block(struct wmi_block *wblock, u8 instance,
|
||||
* wmi_query_block - Return contents of a WMI block (deprecated)
|
||||
* @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
|
||||
* @instance: Instance index
|
||||
* &out: Empty buffer to return the contents of the data block to
|
||||
* @out: Empty buffer to return the contents of the data block to
|
||||
*
|
||||
* Return the contents of an ACPI-WMI data block to a buffer
|
||||
*/
|
||||
@ -399,7 +399,7 @@ EXPORT_SYMBOL_GPL(wmidev_block_query);
|
||||
* wmi_set_block - Write to a WMI block
|
||||
* @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
|
||||
* @instance: Instance index
|
||||
* &in: Buffer containing new values for the data block
|
||||
* @in: Buffer containing new values for the data block
|
||||
*
|
||||
* Write the contents of the input buffer to an ACPI-WMI data block
|
||||
*/
|
||||
@ -510,6 +510,7 @@ static void wmi_notify_debug(u32 value, void *context)
|
||||
|
||||
/**
|
||||
* wmi_install_notify_handler - Register handler for WMI events
|
||||
* @guid: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
|
||||
* @handler: Function to handle notifications
|
||||
* @data: Data to be returned to handler when event is fired
|
||||
*
|
||||
@ -520,12 +521,12 @@ wmi_notify_handler handler, void *data)
|
||||
{
|
||||
struct wmi_block *block;
|
||||
acpi_status status = AE_NOT_EXIST;
|
||||
uuid_le guid_input;
|
||||
guid_t guid_input;
|
||||
|
||||
if (!guid || !handler)
|
||||
return AE_BAD_PARAMETER;
|
||||
|
||||
if (uuid_le_to_bin(guid, &guid_input))
|
||||
if (guid_parse(guid, &guid_input))
|
||||
return AE_BAD_PARAMETER;
|
||||
|
||||
list_for_each_entry(block, &wmi_block_list, list) {
|
||||
@ -552,6 +553,7 @@ EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
|
||||
|
||||
/**
|
||||
* wmi_uninstall_notify_handler - Unregister handler for WMI events
|
||||
* @guid: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
|
||||
*
|
||||
* Unregister handler for events sent to the ACPI-WMI mapper device.
|
||||
*/
|
||||
@ -559,12 +561,12 @@ acpi_status wmi_remove_notify_handler(const char *guid)
|
||||
{
|
||||
struct wmi_block *block;
|
||||
acpi_status status = AE_NOT_EXIST;
|
||||
uuid_le guid_input;
|
||||
guid_t guid_input;
|
||||
|
||||
if (!guid)
|
||||
return AE_BAD_PARAMETER;
|
||||
|
||||
if (uuid_le_to_bin(guid, &guid_input))
|
||||
if (guid_parse(guid, &guid_input))
|
||||
return AE_BAD_PARAMETER;
|
||||
|
||||
list_for_each_entry(block, &wmi_block_list, list) {
|
||||
@ -795,9 +797,9 @@ static int wmi_dev_match(struct device *dev, struct device_driver *driver)
|
||||
return 0;
|
||||
|
||||
while (*id->guid_string) {
|
||||
uuid_le driver_guid;
|
||||
guid_t driver_guid;
|
||||
|
||||
if (WARN_ON(uuid_le_to_bin(id->guid_string, &driver_guid)))
|
||||
if (WARN_ON(guid_parse(id->guid_string, &driver_guid)))
|
||||
continue;
|
||||
if (!memcmp(&driver_guid, wblock->gblock.guid, 16))
|
||||
return 1;
|
||||
@ -1116,8 +1118,7 @@ static void wmi_free_devices(struct acpi_device *device)
|
||||
}
|
||||
}
|
||||
|
||||
static bool guid_already_parsed(struct acpi_device *device,
|
||||
const u8 *guid)
|
||||
static bool guid_already_parsed(struct acpi_device *device, const u8 *guid)
|
||||
{
|
||||
struct wmi_block *wblock;
|
||||
|
||||
@ -1327,10 +1328,8 @@ static void acpi_wmi_notify_handler(acpi_handle handle, u32 event,
|
||||
wblock->handler(event, wblock->handler_data);
|
||||
}
|
||||
|
||||
if (debug_event) {
|
||||
pr_info("DEBUG Event GUID: %pUL\n",
|
||||
wblock->gblock.guid);
|
||||
}
|
||||
if (debug_event)
|
||||
pr_info("DEBUG Event GUID: %pUL\n", wblock->gblock.guid);
|
||||
|
||||
acpi_bus_generate_netlink_event(
|
||||
wblock->acpi_device->pnp.device_class,
|
||||
|
@ -11,7 +11,7 @@ config TYPEC_MUX_PI3USB30532
|
||||
|
||||
config TYPEC_MUX_INTEL_PMC
|
||||
tristate "Intel PMC mux control"
|
||||
depends on INTEL_PMC_IPC
|
||||
depends on INTEL_SCU_IPC
|
||||
select USB_ROLE_SWITCH
|
||||
help
|
||||
Driver for USB muxes controlled by Intel PMC FW. Intel PMC FW can
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include <linux/usb/typec_dp.h>
|
||||
#include <linux/usb/typec_tbt.h>
|
||||
|
||||
#include <asm/intel_pmc_ipc.h>
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
#define PMC_USBC_CMD 0xa7
|
||||
|
||||
@ -97,6 +97,7 @@ struct pmc_usb_port {
|
||||
struct pmc_usb {
|
||||
u8 num_ports;
|
||||
struct device *dev;
|
||||
struct intel_scu_ipc_dev *ipc;
|
||||
struct pmc_usb_port *port;
|
||||
};
|
||||
|
||||
@ -108,9 +109,8 @@ static int pmc_usb_command(struct pmc_usb_port *port, u8 *msg, u32 len)
|
||||
* Error bit will always be 0 with the USBC command.
|
||||
* Status can be checked from the response message.
|
||||
*/
|
||||
intel_pmc_ipc_command(PMC_USBC_CMD, 0, msg, len,
|
||||
(void *)response, 1);
|
||||
|
||||
intel_scu_ipc_dev_command(port->pmc->ipc, PMC_USBC_CMD, 0, msg, len,
|
||||
response, sizeof(response));
|
||||
if (response[2]) {
|
||||
if (response[2] & BIT(1))
|
||||
return -EIO;
|
||||
@ -374,6 +374,10 @@ static int pmc_usb_probe(struct platform_device *pdev)
|
||||
if (!pmc->port)
|
||||
return -ENOMEM;
|
||||
|
||||
pmc->ipc = devm_intel_scu_ipc_dev_get(&pdev->dev);
|
||||
if (!pmc->ipc)
|
||||
return -ENODEV;
|
||||
|
||||
pmc->dev = &pdev->dev;
|
||||
|
||||
/*
|
||||
|
@ -41,8 +41,8 @@ config TYPEC_FUSB302
|
||||
config TYPEC_WCOVE
|
||||
tristate "Intel WhiskeyCove PMIC USB Type-C PHY driver"
|
||||
depends on ACPI
|
||||
depends on MFD_INTEL_PMC_BXT
|
||||
depends on INTEL_SOC_PMIC
|
||||
depends on INTEL_PMC_IPC
|
||||
depends on BXT_WC_PMIC_OPREGION
|
||||
help
|
||||
This driver adds support for USB Type-C on Intel Broxton platforms
|
||||
|
@ -1217,6 +1217,7 @@ config ITCO_WDT
|
||||
depends on (X86 || IA64) && PCI
|
||||
select WATCHDOG_CORE
|
||||
depends on I2C || I2C=n
|
||||
depends on MFD_INTEL_PMC_BXT || !MFD_INTEL_PMC_BXT
|
||||
select LPC_ICH if !EXPERT
|
||||
select I2C_I801 if !EXPERT && I2C
|
||||
---help---
|
||||
|
@ -64,6 +64,7 @@
|
||||
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
|
||||
#include <linux/io.h> /* For inb/outb/... */
|
||||
#include <linux/platform_data/itco_wdt.h>
|
||||
#include <linux/mfd/intel_pmc_bxt.h>
|
||||
|
||||
#include "iTCO_vendor.h"
|
||||
|
||||
@ -233,12 +234,24 @@ static int update_no_reboot_bit_cnt(void *priv, bool set)
|
||||
return val != newval ? -EIO : 0;
|
||||
}
|
||||
|
||||
static void iTCO_wdt_no_reboot_bit_setup(struct iTCO_wdt_private *p,
|
||||
struct itco_wdt_platform_data *pdata)
|
||||
static int update_no_reboot_bit_pmc(void *priv, bool set)
|
||||
{
|
||||
if (pdata->update_no_reboot_bit) {
|
||||
p->update_no_reboot_bit = pdata->update_no_reboot_bit;
|
||||
p->no_reboot_priv = pdata->no_reboot_priv;
|
||||
struct intel_pmc_dev *pmc = priv;
|
||||
u32 bits = PMC_CFG_NO_REBOOT_EN;
|
||||
u32 value = set ? bits : 0;
|
||||
|
||||
return intel_pmc_gcr_update(pmc, PMC_GCR_PMC_CFG_REG, bits, value);
|
||||
}
|
||||
|
||||
static void iTCO_wdt_no_reboot_bit_setup(struct iTCO_wdt_private *p,
|
||||
struct platform_device *pdev,
|
||||
struct itco_wdt_platform_data *pdata)
|
||||
{
|
||||
if (pdata->no_reboot_use_pmc) {
|
||||
struct intel_pmc_dev *pmc = dev_get_drvdata(pdev->dev.parent);
|
||||
|
||||
p->update_no_reboot_bit = update_no_reboot_bit_pmc;
|
||||
p->no_reboot_priv = pmc;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -478,14 +491,14 @@ static int iTCO_wdt_probe(struct platform_device *pdev)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
iTCO_wdt_no_reboot_bit_setup(p, pdata);
|
||||
iTCO_wdt_no_reboot_bit_setup(p, pdev, pdata);
|
||||
|
||||
/*
|
||||
* Get the Memory-Mapped GCS or PMC register, we need it for the
|
||||
* NO_REBOOT flag (TCO v2 and v3).
|
||||
*/
|
||||
if (p->iTCO_version >= 2 && p->iTCO_version < 6 &&
|
||||
!pdata->update_no_reboot_bit) {
|
||||
!pdata->no_reboot_use_pmc) {
|
||||
p->gcs_pmc_res = platform_get_resource(pdev,
|
||||
IORESOURCE_MEM,
|
||||
ICH_RES_MEM_GCS_PMC);
|
||||
|
@ -33,14 +33,24 @@ enum {
|
||||
SCU_WATCHDOG_KEEPALIVE,
|
||||
};
|
||||
|
||||
static inline int wdt_command(int sub, u32 *in, int inlen)
|
||||
struct mid_wdt {
|
||||
struct watchdog_device wd;
|
||||
struct device *dev;
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
};
|
||||
|
||||
static inline int
|
||||
wdt_command(struct mid_wdt *mid, int sub, const void *in, size_t inlen, size_t size)
|
||||
{
|
||||
return intel_scu_ipc_command(IPC_WATCHDOG, sub, in, inlen, NULL, 0);
|
||||
struct intel_scu_ipc_dev *scu = mid->scu;
|
||||
|
||||
return intel_scu_ipc_dev_command_with_size(scu, IPC_WATCHDOG, sub, in,
|
||||
inlen, size, NULL, 0);
|
||||
}
|
||||
|
||||
static int wdt_start(struct watchdog_device *wd)
|
||||
{
|
||||
struct device *dev = watchdog_get_drvdata(wd);
|
||||
struct mid_wdt *mid = watchdog_get_drvdata(wd);
|
||||
int ret, in_size;
|
||||
int timeout = wd->timeout;
|
||||
struct ipc_wd_start {
|
||||
@ -49,38 +59,41 @@ static int wdt_start(struct watchdog_device *wd)
|
||||
} ipc_wd_start = { timeout - MID_WDT_PRETIMEOUT, timeout };
|
||||
|
||||
/*
|
||||
* SCU expects the input size for watchdog IPC to
|
||||
* be based on 4 bytes
|
||||
* SCU expects the input size for watchdog IPC to be 2 which is the
|
||||
* size of the structure in dwords. SCU IPC normally takes bytes
|
||||
* but this is a special case where we specify size to be different
|
||||
* than inlen.
|
||||
*/
|
||||
in_size = DIV_ROUND_UP(sizeof(ipc_wd_start), 4);
|
||||
|
||||
ret = wdt_command(SCU_WATCHDOG_START, (u32 *)&ipc_wd_start, in_size);
|
||||
ret = wdt_command(mid, SCU_WATCHDOG_START, &ipc_wd_start,
|
||||
sizeof(ipc_wd_start), in_size);
|
||||
if (ret)
|
||||
dev_crit(dev, "error starting watchdog: %d\n", ret);
|
||||
dev_crit(mid->dev, "error starting watchdog: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wdt_ping(struct watchdog_device *wd)
|
||||
{
|
||||
struct device *dev = watchdog_get_drvdata(wd);
|
||||
struct mid_wdt *mid = watchdog_get_drvdata(wd);
|
||||
int ret;
|
||||
|
||||
ret = wdt_command(SCU_WATCHDOG_KEEPALIVE, NULL, 0);
|
||||
ret = wdt_command(mid, SCU_WATCHDOG_KEEPALIVE, NULL, 0, 0);
|
||||
if (ret)
|
||||
dev_crit(dev, "Error executing keepalive: %d\n", ret);
|
||||
dev_crit(mid->dev, "Error executing keepalive: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wdt_stop(struct watchdog_device *wd)
|
||||
{
|
||||
struct device *dev = watchdog_get_drvdata(wd);
|
||||
struct mid_wdt *mid = watchdog_get_drvdata(wd);
|
||||
int ret;
|
||||
|
||||
ret = wdt_command(SCU_WATCHDOG_STOP, NULL, 0);
|
||||
ret = wdt_command(mid, SCU_WATCHDOG_STOP, NULL, 0, 0);
|
||||
if (ret)
|
||||
dev_crit(dev, "Error stopping watchdog: %d\n", ret);
|
||||
dev_crit(mid->dev, "Error stopping watchdog: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -110,6 +123,7 @@ static int mid_wdt_probe(struct platform_device *pdev)
|
||||
struct device *dev = &pdev->dev;
|
||||
struct watchdog_device *wdt_dev;
|
||||
struct intel_mid_wdt_pdata *pdata = dev->platform_data;
|
||||
struct mid_wdt *mid;
|
||||
int ret;
|
||||
|
||||
if (!pdata) {
|
||||
@ -123,10 +137,13 @@ static int mid_wdt_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
wdt_dev = devm_kzalloc(dev, sizeof(*wdt_dev), GFP_KERNEL);
|
||||
if (!wdt_dev)
|
||||
mid = devm_kzalloc(dev, sizeof(*mid), GFP_KERNEL);
|
||||
if (!mid)
|
||||
return -ENOMEM;
|
||||
|
||||
mid->dev = dev;
|
||||
wdt_dev = &mid->wd;
|
||||
|
||||
wdt_dev->info = &mid_wdt_info;
|
||||
wdt_dev->ops = &mid_wdt_ops;
|
||||
wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN;
|
||||
@ -135,7 +152,7 @@ static int mid_wdt_probe(struct platform_device *pdev)
|
||||
wdt_dev->parent = dev;
|
||||
|
||||
watchdog_set_nowayout(wdt_dev, WATCHDOG_NOWAYOUT);
|
||||
watchdog_set_drvdata(wdt_dev, dev);
|
||||
watchdog_set_drvdata(wdt_dev, mid);
|
||||
|
||||
ret = devm_request_irq(dev, pdata->irq, mid_wdt_irq,
|
||||
IRQF_SHARED | IRQF_NO_SUSPEND, "watchdog",
|
||||
@ -145,6 +162,10 @@ static int mid_wdt_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
mid->scu = devm_intel_scu_ipc_dev_get(dev);
|
||||
if (!mid->scu)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
/*
|
||||
* The firmware followed by U-Boot leaves the watchdog running
|
||||
* with the default threshold which may vary. When we get here
|
||||
|
53
include/linux/mfd/intel_pmc_bxt.h
Normal file
53
include/linux/mfd/intel_pmc_bxt.h
Normal file
@ -0,0 +1,53 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef MFD_INTEL_PMC_BXT_H
|
||||
#define MFD_INTEL_PMC_BXT_H
|
||||
|
||||
/* GCR reg offsets from GCR base */
|
||||
#define PMC_GCR_PMC_CFG_REG 0x08
|
||||
#define PMC_GCR_TELEM_DEEP_S0IX_REG 0x78
|
||||
#define PMC_GCR_TELEM_SHLW_S0IX_REG 0x80
|
||||
|
||||
/* PMC_CFG_REG bit masks */
|
||||
#define PMC_CFG_NO_REBOOT_EN BIT(4)
|
||||
|
||||
/**
|
||||
* struct intel_pmc_dev - Intel PMC device structure
|
||||
* @dev: Pointer to the parent PMC device
|
||||
* @scu: Pointer to the SCU IPC device data structure
|
||||
* @gcr_mem_base: Virtual base address of GCR (Global Configuration Registers)
|
||||
* @gcr_lock: Lock used to serialize access to GCR registers
|
||||
* @telem_base: Pointer to telemetry SSRAM base resource or %NULL if not
|
||||
* available
|
||||
*/
|
||||
struct intel_pmc_dev {
|
||||
struct device *dev;
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
void __iomem *gcr_mem_base;
|
||||
spinlock_t gcr_lock;
|
||||
struct resource *telem_base;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_MFD_INTEL_PMC_BXT)
|
||||
int intel_pmc_gcr_read64(struct intel_pmc_dev *pmc, u32 offset, u64 *data);
|
||||
int intel_pmc_gcr_update(struct intel_pmc_dev *pmc, u32 offset, u32 mask, u32 val);
|
||||
int intel_pmc_s0ix_counter_read(struct intel_pmc_dev *pmc, u64 *data);
|
||||
#else
|
||||
static inline int intel_pmc_gcr_read64(struct intel_pmc_dev *pmc, u32 offset,
|
||||
u64 *data)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static inline int intel_pmc_gcr_update(struct intel_pmc_dev *pmc, u32 offset,
|
||||
u32 mask, u32 val)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static inline int intel_pmc_s0ix_counter_read(struct intel_pmc_dev *pmc, u64 *data)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* MFD_INTEL_PMC_BXT_H */
|
@ -13,6 +13,20 @@
|
||||
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/**
|
||||
* struct intel_soc_pmic - Intel SoC PMIC data
|
||||
* @irq: Master interrupt number of the parent PMIC device
|
||||
* @regmap: Pointer to the parent PMIC device regmap structure
|
||||
* @irq_chip_data: IRQ chip data for the PMIC itself
|
||||
* @irq_chip_data_pwrbtn: Chained IRQ chip data for the Power Button
|
||||
* @irq_chip_data_tmu: Chained IRQ chip data for the Time Management Unit
|
||||
* @irq_chip_data_bcu: Chained IRQ chip data for the Burst Control Unit
|
||||
* @irq_chip_data_adc: Chained IRQ chip data for the General Purpose ADC
|
||||
* @irq_chip_data_chgr: Chained IRQ chip data for the External Charger
|
||||
* @irq_chip_data_crit: Chained IRQ chip data for the Critical Event Handler
|
||||
* @dev: Pointer to the parent PMIC device
|
||||
* @scu: Pointer to the SCU IPC device data structure
|
||||
*/
|
||||
struct intel_soc_pmic {
|
||||
int irq;
|
||||
struct regmap *regmap;
|
||||
@ -24,6 +38,7 @@ struct intel_soc_pmic {
|
||||
struct regmap_irq_chip_data *irq_chip_data_chgr;
|
||||
struct regmap_irq_chip_data *irq_chip_data_crit;
|
||||
struct device *dev;
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
};
|
||||
|
||||
int intel_soc_pmic_exec_mipi_pmic_seq_element(u16 i2c_address, u32 reg_address,
|
||||
|
@ -12,13 +12,16 @@
|
||||
#define ICH_RES_MEM_OFF 2
|
||||
#define ICH_RES_MEM_GCS_PMC 0
|
||||
|
||||
/**
|
||||
* struct itco_wdt_platform_data - iTCO_wdt platform data
|
||||
* @name: Name of the platform
|
||||
* @version: iTCO version
|
||||
* @no_reboot_use_pmc: Use PMC BXT API to set and clear NO_REBOOT bit
|
||||
*/
|
||||
struct itco_wdt_platform_data {
|
||||
char name[32];
|
||||
unsigned int version;
|
||||
/* private data to be passed to update_no_reboot_bit API */
|
||||
void *no_reboot_priv;
|
||||
/* pointer for platform specific no reboot update function */
|
||||
int (*update_no_reboot_bit)(void *priv, bool set);
|
||||
bool no_reboot_use_pmc;
|
||||
};
|
||||
|
||||
#endif /* _ITCO_WDT_H_ */
|
||||
|
@ -85,6 +85,9 @@
|
||||
/* Maximum charging percentage */
|
||||
#define ASUS_WMI_DEVID_RSOC 0x00120057
|
||||
|
||||
/* Keyboard dock */
|
||||
#define ASUS_WMI_DEVID_KBD_DOCK 0x00120063
|
||||
|
||||
/* DSTS masks */
|
||||
#define ASUS_WMI_DSTS_STATUS_BIT 0x00000001
|
||||
#define ASUS_WMI_DSTS_UNKNOWN_BIT 0x00000002
|
||||
|
@ -440,6 +440,9 @@ software_node_find_by_name(const struct software_node *parent,
|
||||
int software_node_register_nodes(const struct software_node *nodes);
|
||||
void software_node_unregister_nodes(const struct software_node *nodes);
|
||||
|
||||
int software_node_register_node_group(const struct software_node **node_group);
|
||||
void software_node_unregister_node_group(const struct software_node **node_group);
|
||||
|
||||
int software_node_register(const struct software_node *node);
|
||||
|
||||
int software_node_notify(struct device *dev, unsigned long action);
|
||||
|
@ -15,7 +15,7 @@ struct process_cmd_struct {
|
||||
int arg;
|
||||
};
|
||||
|
||||
static const char *version_str = "v1.3";
|
||||
static const char *version_str = "v1.4";
|
||||
static const int supported_api_ver = 1;
|
||||
static struct isst_if_platform_info isst_platform_info;
|
||||
static char *progname;
|
||||
@ -25,7 +25,7 @@ static FILE *outf;
|
||||
static int cpu_model;
|
||||
static int cpu_stepping;
|
||||
|
||||
#define MAX_CPUS_IN_ONE_REQ 64
|
||||
#define MAX_CPUS_IN_ONE_REQ 256
|
||||
static short max_target_cpus;
|
||||
static unsigned short target_cpus[MAX_CPUS_IN_ONE_REQ];
|
||||
|
||||
@ -653,7 +653,7 @@ void set_cpu_mask_from_punit_coremask(int cpu, unsigned long long core_mask,
|
||||
pkg_id = get_physical_package_id(cpu);
|
||||
|
||||
for (i = 0; i < 64; ++i) {
|
||||
if (core_mask & BIT(i)) {
|
||||
if (core_mask & BIT_ULL(i)) {
|
||||
int j;
|
||||
|
||||
for (j = 0; j < topo_max_cpus; ++j) {
|
||||
@ -1169,6 +1169,7 @@ static void dump_clx_n_config_for_cpu(int cpu, void *arg1, void *arg2,
|
||||
|
||||
ctdp_level = &clx_n_pkg_dev.ctdp_level[0];
|
||||
pbf_info = &ctdp_level->pbf_info;
|
||||
clx_n_pkg_dev.processed = 1;
|
||||
isst_ctdp_display_information(cpu, outf, tdp_level, &clx_n_pkg_dev);
|
||||
free_cpu_set(ctdp_level->core_cpumask);
|
||||
free_cpu_set(pbf_info->core_cpumask);
|
||||
@ -1631,6 +1632,8 @@ static int set_pbf_core_power(int cpu)
|
||||
static void set_pbf_for_cpu(int cpu, void *arg1, void *arg2, void *arg3,
|
||||
void *arg4)
|
||||
{
|
||||
struct isst_pkg_ctdp_level_info ctdp_level;
|
||||
struct isst_pkg_ctdp pkg_dev;
|
||||
int ret;
|
||||
int status = *(int *)arg4;
|
||||
|
||||
@ -1646,6 +1649,24 @@ static void set_pbf_for_cpu(int cpu, void *arg1, void *arg2, void *arg3,
|
||||
goto disp_result;
|
||||
}
|
||||
|
||||
ret = isst_get_ctdp_levels(cpu, &pkg_dev);
|
||||
if (ret) {
|
||||
isst_display_error_info_message(1, "Failed to get number of levels", 0, 0);
|
||||
goto disp_result;
|
||||
}
|
||||
|
||||
ret = isst_get_ctdp_control(cpu, pkg_dev.current_level, &ctdp_level);
|
||||
if (ret) {
|
||||
isst_display_error_info_message(1, "Failed to get current level", 0, 0);
|
||||
goto disp_result;
|
||||
}
|
||||
|
||||
if (!ctdp_level.pbf_support) {
|
||||
isst_display_error_info_message(1, "base-freq feature is not present at this level", 1, pkg_dev.current_level);
|
||||
ret = -1;
|
||||
goto disp_result;
|
||||
}
|
||||
|
||||
if (auto_mode && status) {
|
||||
ret = set_pbf_core_power(cpu);
|
||||
if (ret)
|
||||
@ -1772,10 +1793,30 @@ static void dump_fact_config(int arg)
|
||||
static void set_fact_for_cpu(int cpu, void *arg1, void *arg2, void *arg3,
|
||||
void *arg4)
|
||||
{
|
||||
struct isst_pkg_ctdp_level_info ctdp_level;
|
||||
struct isst_pkg_ctdp pkg_dev;
|
||||
int ret;
|
||||
int status = *(int *)arg4;
|
||||
|
||||
if (auto_mode && status) {
|
||||
ret = isst_get_ctdp_levels(cpu, &pkg_dev);
|
||||
if (ret) {
|
||||
isst_display_error_info_message(1, "Failed to get number of levels", 0, 0);
|
||||
goto disp_results;
|
||||
}
|
||||
|
||||
ret = isst_get_ctdp_control(cpu, pkg_dev.current_level, &ctdp_level);
|
||||
if (ret) {
|
||||
isst_display_error_info_message(1, "Failed to get current level", 0, 0);
|
||||
goto disp_results;
|
||||
}
|
||||
|
||||
if (!ctdp_level.fact_support) {
|
||||
isst_display_error_info_message(1, "turbo-freq feature is not present at this level", 1, pkg_dev.current_level);
|
||||
ret = -1;
|
||||
goto disp_results;
|
||||
}
|
||||
|
||||
if (status) {
|
||||
ret = isst_pm_qos_config(cpu, 1, 1);
|
||||
if (ret)
|
||||
goto disp_results;
|
||||
|
@ -912,16 +912,16 @@ int isst_pm_qos_config(int cpu, int enable_clos, int priority_type)
|
||||
return ret;
|
||||
|
||||
if (ctdp_level.fact_enabled) {
|
||||
debug_printf("Turbo-freq feature must be disabled first\n");
|
||||
isst_display_error_info_message(1, "Ignoring request, turbo-freq feature is still enabled", 0, 0);
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = isst_write_pm_config(cpu, 0);
|
||||
if (ret)
|
||||
isst_display_error_info_message(0, "WRITE_PM_CONFIG command failed, ignoring error\n", 0, 0);
|
||||
isst_display_error_info_message(0, "WRITE_PM_CONFIG command failed, ignoring error", 0, 0);
|
||||
} else {
|
||||
ret = isst_write_pm_config(cpu, 1);
|
||||
if (ret)
|
||||
isst_display_error_info_message(0, "WRITE_PM_CONFIG command failed, ignoring error\n", 0, 0);
|
||||
isst_display_error_info_message(0, "WRITE_PM_CONFIG command failed, ignoring error", 0, 0);
|
||||
}
|
||||
|
||||
ret = isst_send_mbox_command(cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0, 0,
|
||||
|
@ -316,21 +316,31 @@ void isst_ctdp_display_core_info(int cpu, FILE *outf, char *prefix,
|
||||
{
|
||||
char header[256];
|
||||
char value[256];
|
||||
int level = 1;
|
||||
|
||||
if (out_format_is_json()) {
|
||||
snprintf(header, sizeof(header), "package-%d:die-%d:cpu-%d",
|
||||
get_physical_package_id(cpu), get_physical_die_id(cpu),
|
||||
cpu);
|
||||
format_and_print(outf, level++, header, NULL);
|
||||
} else {
|
||||
snprintf(header, sizeof(header), "package-%d",
|
||||
get_physical_package_id(cpu));
|
||||
format_and_print(outf, level++, header, NULL);
|
||||
snprintf(header, sizeof(header), "die-%d",
|
||||
get_physical_die_id(cpu));
|
||||
format_and_print(outf, level++, header, NULL);
|
||||
snprintf(header, sizeof(header), "cpu-%d", cpu);
|
||||
format_and_print(outf, level++, header, NULL);
|
||||
}
|
||||
|
||||
snprintf(header, sizeof(header), "package-%d",
|
||||
get_physical_package_id(cpu));
|
||||
format_and_print(outf, 1, header, NULL);
|
||||
snprintf(header, sizeof(header), "die-%d", get_physical_die_id(cpu));
|
||||
format_and_print(outf, 2, header, NULL);
|
||||
snprintf(header, sizeof(header), "cpu-%d", cpu);
|
||||
format_and_print(outf, 3, header, NULL);
|
||||
if (str0 && !val)
|
||||
snprintf(value, sizeof(value), "%s", str0);
|
||||
else if (str1 && val)
|
||||
snprintf(value, sizeof(value), "%s", str1);
|
||||
else
|
||||
snprintf(value, sizeof(value), "%u", val);
|
||||
format_and_print(outf, 4, prefix, value);
|
||||
format_and_print(outf, level, prefix, value);
|
||||
|
||||
format_and_print(outf, 1, NULL, NULL);
|
||||
}
|
||||
@ -470,7 +480,7 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level,
|
||||
_isst_pbf_display_information(cpu, outf,
|
||||
tdp_level,
|
||||
&ctdp_level->pbf_info,
|
||||
level + 1);
|
||||
level + 2);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#define BIT(x) (1 << (x))
|
||||
#define BIT_ULL(nr) (1ULL << (nr))
|
||||
#define GENMASK(h, l) (((~0UL) << (l)) & (~0UL >> (sizeof(long) * 8 - 1 - (h))))
|
||||
#define GENMASK_ULL(h, l) \
|
||||
(((~0ULL) << (l)) & (~0ULL >> (sizeof(long long) * 8 - 1 - (h))))
|
||||
|
Loading…
Reference in New Issue
Block a user