Commit a5a82e0a authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents c5d6c138 47a94c55
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.
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
......@@ -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 */
......
/* 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>
#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 */
/* Read single register */
int intel_scu_ipc_ioread8(u16 addr, u8 *data);
/* Read a vector */
int intel_scu_ipc_readv(u16 *addr, u8 *data, int len);
/* Write single register */
int intel_scu_ipc_iowrite8(u16 addr, u8 data);
/* Write a vector */
int intel_scu_ipc_writev(u16 *addr, u8 *data, int len);
/* Update single register based on the mask */
int intel_scu_ipc_update_register(u16 addr, u8 data, u8 mask);
/* 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);
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)
#include <linux/ioport.h>
struct device;
struct intel_scu_ipc_dev;
/**
* 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;
};
struct intel_scu_ipc_dev *
__intel_scu_ipc_register(struct device *parent,
const struct intel_scu_ipc_data *scu_data,
struct module *owner);
#define intel_scu_ipc_register(parent, scu_data) \
__intel_scu_ipc_register(parent, scu_data, THIS_MODULE)
void intel_scu_ipc_unregister(struct intel_scu_ipc_dev *scu);
struct intel_scu_ipc_dev *
__devm_intel_scu_ipc_register(struct device *parent,
const struct intel_scu_ipc_data *scu_data,
struct module *owner);
#define devm_intel_scu_ipc_register(parent, scu_data) \
__devm_intel_scu_ipc_register(parent, scu_data, THIS_MODULE)
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);
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);
int intel_scu_ipc_dev_update(struct intel_scu_ipc_dev *scu, u16 addr,
u8 data, u8 mask);
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 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)
{
return blocking_notifier_call_chain(&intel_scu_notifier, v, p);
return intel_scu_ipc_dev_command_with_size(scu, cmd, sub, in, inlen,
inlen, out, outlen);
}
#define SCU_AVAILABLE 1
#define SCU_DOWN 2
#include <asm/intel_scu_ipc_legacy.h>
#endif
/* 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
......
This diff is collapsed.
......@@ -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 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)
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)
{
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;
acpi_status status;
int m;
if (!(chassis_type && strcmp(chassis_type, "31") == 0))
goto out;
status = acpi_evaluate_object(handle, "VBDL", NULL, NULL);
return ACPI_SUCCESS(status);
}
status = acpi_evaluate_object(handle, "VGBS", NULL, &vgbs_output);
if (ACPI_FAILURE(status))
goto out;
static bool intel_vbtn_has_switches(acpi_handle handle)
{
const char *chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE);
unsigned long long vgbs;
acpi_status status;
obj = vgbs_output.pointer;
if (!(obj && obj->type == ACPI_TYPE_INTEGER))
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;
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,
......
// 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 software_node displayport_node = {
.name = "displayport",
};
static const struct property_entry usb_connector_props[] = {
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) {
......
This diff is collapsed.
This diff is collapsed.
......@@ -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)
......
// 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);
// 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 @@ static int acpi_add(struct acpi_device *device)
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);
......
This diff is collapsed.
......@@ -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);
......
This diff is collapsed.
This diff is collapsed.
......@@ -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
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment