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:
Linus Torvalds 2020-06-02 12:56:58 -07:00
commit a5a82e0a59
63 changed files with 2331 additions and 1708 deletions

View 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.

View File

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

View File

@ -8529,6 +8529,13 @@ L: platform-driver-x86@vger.kernel.org
S: Maintained S: Maintained
F: drivers/platform/x86/intel_atomisp2_pm.c 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 INTEL C600 SERIES SAS CONTROLLER DRIVER
M: Intel SCU Linux support <intel-linux-scu@intel.com> M: Intel SCU Linux support <intel-linux-scu@intel.com>
M: Artur Paszkiewicz <artur.paszkiewicz@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/mic_ioctl.h
F: include/uapi/linux/scif_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 INTEL PMC CORE DRIVER
M: Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com> M: Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com>
M: Vishwanath Somayaji <vishwanath.somayaji@intel.com> M: Vishwanath Somayaji <vishwanath.somayaji@intel.com>
@ -8743,15 +8757,6 @@ L: platform-driver-x86@vger.kernel.org
S: Maintained S: Maintained
F: drivers/platform/x86/intel_pmc_core* 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 INTEL PMIC GPIO DRIVERS
M: Andy Shevchenko <andy@kernel.org> M: Andy Shevchenko <andy@kernel.org>
S: Maintained S: Maintained
@ -8790,6 +8795,12 @@ S: Supported
F: drivers/infiniband/hw/i40iw/ F: drivers/infiniband/hw/i40iw/
F: include/uapi/rdma/i40iw-abi.h 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 INTEL SPEED SELECT TECHNOLOGY
M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
L: platform-driver-x86@vger.kernel.org L: platform-driver-x86@vger.kernel.org
@ -8856,6 +8867,13 @@ F: Documentation/admin-guide/wimax/i2400m.rst
F: drivers/net/wimax/i2400m/ F: drivers/net/wimax/i2400m/
F: include/uapi/linux/wimax/i2400m.h 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 INTEL WMI THUNDERBOLT FORCE POWER DRIVER
M: Mario Limonciello <mario.limonciello@dell.com> M: Mario Limonciello <mario.limonciello@dell.com>
S: Maintained S: Maintained

View File

@ -597,7 +597,7 @@ config X86_INTEL_MID
select I2C select I2C
select DW_APB_TIMER select DW_APB_TIMER
select APB_TIMER select APB_TIMER
select INTEL_SCU_IPC select INTEL_SCU_PCI
select MFD_INTEL_MSIC select MFD_INTEL_MSIC
---help--- ---help---
Select to build a kernel capable of supporting Intel MID (Mobile Select to build a kernel capable of supporting Intel MID (Mobile

View File

@ -88,11 +88,17 @@ static inline bool intel_mid_has_msic(void)
return (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_PENWELL); 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 */ #else /* !CONFIG_X86_INTEL_MID */
#define intel_mid_identify_cpu() 0 #define intel_mid_identify_cpu() 0
#define intel_mid_has_msic() 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 */ #endif /* !CONFIG_X86_INTEL_MID */
enum intel_mid_timer_options { 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_MTMR_MAX_NUM 8
#define SFI_MRTC_MAX 8 #define SFI_MRTC_MAX 8
extern void intel_scu_devices_create(void);
extern void intel_scu_devices_destroy(void);
/* VRTC timer */ /* VRTC timer */
#define MRST_VRTC_MAP_SZ 1024 #define MRST_VRTC_MAP_SZ 1024
/* #define MRST_VRTC_PGOFFSET 0xc00 */ /* #define MRST_VRTC_PGOFFSET 0xc00 */

View File

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

View File

@ -2,61 +2,69 @@
#ifndef _ASM_X86_INTEL_SCU_IPC_H_ #ifndef _ASM_X86_INTEL_SCU_IPC_H_
#define _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 struct device;
#define IPCMSG_INDIRECT_WRITE 0x05 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 struct intel_scu_ipc_dev *
#define IPCMSG_COLD_RESET 0xF1 __intel_scu_ipc_register(struct device *parent,
#define IPCMSG_SOFT_RESET 0xF2 const struct intel_scu_ipc_data *scu_data,
#define IPCMSG_COLD_BOOT 0xF3 struct module *owner);
#define IPCMSG_VRTC 0xFA /* Set vRTC device */ #define intel_scu_ipc_register(parent, scu_data) \
/* Command id associated with message IPCMSG_VRTC */ __intel_scu_ipc_register(parent, scu_data, THIS_MODULE)
#define IPC_CMD_VRTC_SETTIME 1 /* Set time */
#define IPC_CMD_VRTC_SETALARM 2 /* Set alarm */
/* Read single register */ void intel_scu_ipc_unregister(struct intel_scu_ipc_dev *scu);
int intel_scu_ipc_ioread8(u16 addr, u8 *data);
/* Read a vector */ struct intel_scu_ipc_dev *
int intel_scu_ipc_readv(u16 *addr, u8 *data, int len); __devm_intel_scu_ipc_register(struct device *parent,
const struct intel_scu_ipc_data *scu_data,
struct module *owner);
/* Write single register */ #define devm_intel_scu_ipc_register(parent, scu_data) \
int intel_scu_ipc_iowrite8(u16 addr, u8 data); __devm_intel_scu_ipc_register(parent, scu_data, THIS_MODULE)
/* Write a vector */ struct intel_scu_ipc_dev *intel_scu_ipc_dev_get(void);
int intel_scu_ipc_writev(u16 *addr, u8 *data, int len); 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_dev_ioread8(struct intel_scu_ipc_dev *scu, u16 addr,
int intel_scu_ipc_update_register(u16 addr, u8 data, u8 mask); 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_dev_update(struct intel_scu_ipc_dev *scu, u16 addr,
int intel_scu_ipc_simple_command(int cmd, int sub); u8 data, u8 mask);
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; 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) #include <asm/intel_scu_ipc_legacy.h>
{
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 #endif

View 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

View File

@ -10,6 +10,8 @@
#define TELEM_MAX_EVENTS_SRAM 28 #define TELEM_MAX_EVENTS_SRAM 28
#define TELEM_MAX_OS_ALLOCATED_EVENTS 20 #define TELEM_MAX_OS_ALLOCATED_EVENTS 20
#include <asm/intel_scu_ipc.h>
enum telemetry_unit { enum telemetry_unit {
TELEM_PSS = 0, TELEM_PSS = 0,
TELEM_IOSS, TELEM_IOSS,
@ -51,6 +53,8 @@ struct telemetry_plt_config {
struct telemetry_unit_config ioss_config; struct telemetry_unit_config ioss_config;
struct mutex telem_trace_lock; struct mutex telem_trace_lock;
struct mutex telem_lock; struct mutex telem_lock;
struct intel_pmc_dev *pmc;
struct intel_scu_ipc_dev *scu;
bool telem_in_use; 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_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, int telemetry_get_evtname(enum telemetry_unit telem_unit,
const char **name, int len); const char **name, int len);

View File

@ -3915,6 +3915,7 @@ void set_secondary_fwnode(struct device *dev, struct fwnode_handle *fwnode)
else else
dev->fwnode = fwnode; dev->fwnode = fwnode;
} }
EXPORT_SYMBOL_GPL(set_secondary_fwnode);
/** /**
* device_set_of_node_from_dev - reuse device-tree node of another device * device_set_of_node_from_dev - reuse device-tree node of another device

View File

@ -726,6 +726,54 @@ void software_node_unregister_nodes(const struct software_node *nodes)
} }
EXPORT_SYMBOL_GPL(software_node_unregister_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 * software_node_register - Register static software node
* @node: The software node to be registered * @node: The software node to be registered

View File

@ -566,7 +566,7 @@ config INTEL_SOC_PMIC
config INTEL_SOC_PMIC_BXTWC config INTEL_SOC_PMIC_BXTWC
tristate "Support for Intel Broxton Whiskey Cove PMIC" tristate "Support for Intel Broxton Whiskey Cove PMIC"
depends on INTEL_PMC_IPC depends on MFD_INTEL_PMC_BXT
select MFD_CORE select MFD_CORE
select REGMAP_IRQ select REGMAP_IRQ
help help
@ -608,7 +608,7 @@ config INTEL_SOC_PMIC_MRFLD
tristate "Support for Intel Merrifield Basin Cove PMIC" tristate "Support for Intel Merrifield Basin Cove PMIC"
depends on GPIOLIB depends on GPIOLIB
depends on ACPI depends on ACPI
depends on INTEL_SCU_IPC depends on INTEL_SCU
select MFD_CORE select MFD_CORE
select REGMAP_IRQ select REGMAP_IRQ
help help
@ -640,13 +640,27 @@ config MFD_INTEL_LPSS_PCI
config MFD_INTEL_MSIC config MFD_INTEL_MSIC
bool "Intel MSIC" bool "Intel MSIC"
depends on INTEL_SCU_IPC depends on INTEL_SCU
select MFD_CORE select MFD_CORE
help help
Select this option to enable access to Intel MSIC (Avatele Select this option to enable access to Intel MSIC (Avatele
Passage) chip. This chip embeds audio, battery, GPIO, etc. Passage) chip. This chip embeds audio, battery, GPIO, etc.
devices used in Intel Medfield platforms. 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 config MFD_IPAQ_MICRO
bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support" bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support"
depends on SA1100_H3100 || SA1100_H3600 depends on SA1100_H3100 || SA1100_H3600

View File

@ -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_PCI) += intel-lpss-pci.o
obj-$(CONFIG_MFD_INTEL_LPSS_ACPI) += intel-lpss-acpi.o obj-$(CONFIG_MFD_INTEL_LPSS_ACPI) += intel-lpss-acpi.o
obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.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_PALMAS) += palmas.o
obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o
obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o

468
drivers/mfd/intel_pmc_bxt.c Normal file
View 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");

View File

@ -15,7 +15,7 @@
#include <linux/mfd/intel_soc_pmic_bxtwc.h> #include <linux/mfd/intel_soc_pmic_bxtwc.h>
#include <linux/module.h> #include <linux/module.h>
#include <asm/intel_pmc_ipc.h> #include <asm/intel_scu_ipc.h>
/* PMIC device registers */ /* PMIC device registers */
#define REG_ADDR_MASK 0xFF00 #define REG_ADDR_MASK 0xFF00
@ -58,6 +58,10 @@
/* Whiskey Cove PMIC share same ACPI ID between different platforms */ /* Whiskey Cove PMIC share same ACPI ID between different platforms */
#define BROXTON_PMIC_WC_HRV 4 #define BROXTON_PMIC_WC_HRV 4
#define PMC_PMIC_ACCESS 0xFF
#define PMC_PMIC_READ 0x0
#define PMC_PMIC_WRITE 0x1
enum bxtwc_irqs { enum bxtwc_irqs {
BXTWC_PWRBTN_LVL1_IRQ = 0, BXTWC_PWRBTN_LVL1_IRQ = 0,
BXTWC_TMU_LVL1_IRQ, 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[0] = reg;
ipc_in[1] = i2c_addr; ipc_in[1] = i2c_addr;
ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS, ret = intel_scu_ipc_dev_command(pmic->scu, PMC_PMIC_ACCESS,
PMC_IPC_PMIC_ACCESS_READ, PMC_PMIC_READ, ipc_in, sizeof(ipc_in),
ipc_in, sizeof(ipc_in), (u32 *)ipc_out, 1); ipc_out, sizeof(ipc_out));
if (ret) { if (ret)
dev_err(pmic->dev, "Failed to read from PMIC\n");
return ret; return ret;
}
*val = ipc_out[0]; *val = ipc_out[0];
return 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, static int regmap_ipc_byte_reg_write(void *context, unsigned int reg,
unsigned int val) unsigned int val)
{ {
int ret;
int i2c_addr; int i2c_addr;
u8 ipc_in[3]; u8 ipc_in[3];
struct intel_soc_pmic *pmic = context; 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[0] = reg;
ipc_in[1] = i2c_addr; ipc_in[1] = i2c_addr;
ipc_in[2] = val; ipc_in[2] = val;
ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS, return intel_scu_ipc_dev_command(pmic->scu, PMC_PMIC_ACCESS,
PMC_IPC_PMIC_ACCESS_WRITE, PMC_PMIC_WRITE, ipc_in, sizeof(ipc_in),
ipc_in, sizeof(ipc_in), NULL, 0); NULL, 0);
if (ret) {
dev_err(pmic->dev, "Failed to write to PMIC\n");
return ret;
}
return 0;
} }
/* sysfs interfaces to r/w PMIC registers, required by initial script */ /* 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); dev_set_drvdata(&pdev->dev, pmic);
pmic->dev = &pdev->dev; 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, pmic->regmap = devm_regmap_init(&pdev->dev, NULL, pmic,
&bxtwc_regmap_config); &bxtwc_regmap_config);
if (IS_ERR(pmic->regmap)) { if (IS_ERR(pmic->regmap)) {

View File

@ -74,10 +74,11 @@ static const struct mfd_cell bcove_dev[] = {
static int bcove_ipc_byte_reg_read(void *context, unsigned int reg, static int bcove_ipc_byte_reg_read(void *context, unsigned int reg,
unsigned int *val) unsigned int *val)
{ {
struct intel_soc_pmic *pmic = context;
u8 ipc_out; u8 ipc_out;
int ret; int ret;
ret = intel_scu_ipc_ioread8(reg, &ipc_out); ret = intel_scu_ipc_dev_ioread8(pmic->scu, reg, &ipc_out);
if (ret) if (ret)
return 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, static int bcove_ipc_byte_reg_write(void *context, unsigned int reg,
unsigned int val) unsigned int val)
{ {
struct intel_soc_pmic *pmic = context;
u8 ipc_in = val; u8 ipc_in = val;
int ret; int ret;
ret = intel_scu_ipc_iowrite8(reg, ipc_in); ret = intel_scu_ipc_dev_iowrite8(pmic->scu, reg, ipc_in);
if (ret) if (ret)
return ret; return ret;
@ -117,6 +119,10 @@ static int bcove_probe(struct platform_device *pdev)
if (!pmic) if (!pmic)
return -ENOMEM; return -ENOMEM;
pmic->scu = devm_intel_scu_ipc_dev_get(dev);
if (!pmic->scu)
return -ENOMEM;
platform_set_drvdata(pdev, pmic); platform_set_drvdata(pdev, pmic);
pmic->dev = &pdev->dev; pmic->dev = &pdev->dev;

View File

@ -78,6 +78,16 @@ config HUAWEI_WMI
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called huawei-wmi. 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 config INTEL_WMI_THUNDERBOLT
tristate "Intel WMI thunderbolt force power driver" tristate "Intel WMI thunderbolt force power driver"
depends on ACPI_WMI depends on ACPI_WMI
@ -1269,7 +1279,8 @@ config INTEL_UNCORE_FREQ_CONTROL
config INTEL_BXTWC_PMIC_TMU config INTEL_BXTWC_PMIC_TMU
tristate "Intel BXT Whiskey Cove TMU Driver" tristate "Intel BXT Whiskey Cove TMU Driver"
depends on REGMAP 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--- ---help---
Select this driver to use Intel BXT Whiskey Cove PMIC TMU feature. Select this driver to use Intel BXT Whiskey Cove PMIC TMU feature.
This driver enables the alarm wakeup functionality in the TMU unit This driver enables the alarm wakeup functionality in the TMU unit
@ -1295,7 +1306,7 @@ config INTEL_MFLD_THERMAL
config INTEL_MID_POWER_BUTTON config INTEL_MID_POWER_BUTTON
tristate "power button driver for Intel MID platforms" tristate "power button driver for Intel MID platforms"
depends on INTEL_SCU_IPC && INPUT depends on INTEL_SCU && INPUT
help help
This driver handles the power button on the Intel MID platforms. This driver handles the power button on the Intel MID platforms.
@ -1327,14 +1338,6 @@ config INTEL_PMC_CORE
- LTR Ignore - LTR Ignore
- MPHY/PLL gating status (Sunrisepoint PCH only) - 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 config INTEL_PUNIT_IPC
tristate "Intel P-Unit IPC Driver" tristate "Intel P-Unit IPC Driver"
---help--- ---help---
@ -1342,17 +1345,39 @@ config INTEL_PUNIT_IPC
which is used to bridge the communications between kernel and P-Unit. which is used to bridge the communications between kernel and P-Unit.
config INTEL_SCU_IPC config INTEL_SCU_IPC
bool "Intel SCU IPC Support" bool
depends on X86_INTEL_MID
default y config INTEL_SCU
---help--- bool
IPC is used to bridge the communications between kernel and SCU on select INTEL_SCU_IPC
some embedded Intel x86 platforms. This is not needed for PC-type
machines. 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 config INTEL_SCU_IPC_UTIL
tristate "Intel SCU IPC utility driver" tristate "Intel SCU IPC utility driver"
depends on INTEL_SCU_IPC depends on INTEL_SCU
---help--- ---help---
The IPC Util driver provides an interface with the SCU enabling The IPC Util driver provides an interface with the SCU enabling
low level access for debug work and updating the firmware. Say low level access for debug work and updating the firmware. Say
@ -1360,7 +1385,9 @@ config INTEL_SCU_IPC_UTIL
config INTEL_TELEMETRY config INTEL_TELEMETRY
tristate "Intel SoC Telemetry Driver" 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--- ---help---
This driver provides interfaces to configure and use This driver provides interfaces to configure and use
telemetry for INTEL SoC from APL onwards. It is also telemetry for INTEL SoC from APL onwards. It is also

View File

@ -11,6 +11,7 @@ obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o
# WMI drivers # WMI drivers
obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o
obj-$(CONFIG_HUAWEI_WMI) += huawei-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_INTEL_WMI_THUNDERBOLT) += intel-wmi-thunderbolt.o
obj-$(CONFIG_MXM_WMI) += mxm-wmi.o obj-$(CONFIG_MXM_WMI) += mxm-wmi.o
obj-$(CONFIG_PEAQ_WMI) += peaq-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_MID_POWER_BUTTON) += intel_mid_powerbtn.o
obj-$(CONFIG_INTEL_MRFLD_PWRBTN) += intel_mrfld_pwrbtn.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_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_PUNIT_IPC) += intel_punit_ipc.o
obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_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_SCU_IPC_UTIL) += intel_scu_ipcutil.o
obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \ obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \
intel_telemetry_pltdrv.o \ intel_telemetry_pltdrv.o \

View File

@ -827,7 +827,7 @@ MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTMU*:");
MODULE_ALIAS("dmi:*:*Packard*Bell*:pnENBFT*:"); MODULE_ALIAS("dmi:*:*Packard*Bell*:pnENBFT*:");
MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTMA*:"); MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTMA*:");
MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTVR46*:"); MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTVR46*:");
MODULE_ALIAS("dmi:*:*Acer*:pnExtensa 5420*:"); MODULE_ALIAS("dmi:*:*Acer*:pnExtensa*5420*:");
module_init(acerhdf_init); module_init(acerhdf_init);
module_exit(acerhdf_exit); module_exit(acerhdf_exit);

View File

@ -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) static void asus_led_exit(struct asus_laptop *asus)
{ {
if (!IS_ERR_OR_NULL(asus->wled.led.dev)) led_classdev_unregister(&asus->wled.led);
led_classdev_unregister(&asus->wled.led); led_classdev_unregister(&asus->bled.led);
if (!IS_ERR_OR_NULL(asus->bled.led.dev)) led_classdev_unregister(&asus->mled.led);
led_classdev_unregister(&asus->bled.led); led_classdev_unregister(&asus->tled.led);
if (!IS_ERR_OR_NULL(asus->mled.led.dev)) led_classdev_unregister(&asus->pled.led);
led_classdev_unregister(&asus->mled.led); led_classdev_unregister(&asus->rled.led);
if (!IS_ERR_OR_NULL(asus->tled.led.dev)) led_classdev_unregister(&asus->gled.led);
led_classdev_unregister(&asus->tled.led); led_classdev_unregister(&asus->kled.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);
if (asus->led_workqueue) { if (asus->led_workqueue) {
destroy_workqueue(asus->led_workqueue); destroy_workqueue(asus->led_workqueue);
asus->led_workqueue = NULL; asus->led_workqueue = NULL;

View File

@ -472,6 +472,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
{ KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } }, { KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } },
{ KE_IGNORE, 0x6E, }, /* Low Battery notification */ { KE_IGNORE, 0x6E, }, /* Low Battery notification */
{ KE_KEY, 0x71, { KEY_F13 } }, /* General-purpose button */ { 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, 0x7a, { KEY_ALS_TOGGLE } }, /* Ambient Light Sensor Toggle */
{ KE_KEY, 0x7c, { KEY_MICMUTE } }, { KE_KEY, 0x7c, { KEY_MICMUTE } },
{ KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */ { KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */

View File

@ -57,6 +57,7 @@ MODULE_LICENSE("GPL");
#define NOTIFY_BRNDOWN_MIN 0x20 #define NOTIFY_BRNDOWN_MIN 0x20
#define NOTIFY_BRNDOWN_MAX 0x2e #define NOTIFY_BRNDOWN_MAX 0x2e
#define NOTIFY_FNLOCK_TOGGLE 0x4e #define NOTIFY_FNLOCK_TOGGLE 0x4e
#define NOTIFY_KBD_DOCK_CHANGE 0x75
#define NOTIFY_KBD_BRTUP 0xc4 #define NOTIFY_KBD_BRTUP 0xc4
#define NOTIFY_KBD_BRTDWN 0xc5 #define NOTIFY_KBD_BRTDWN 0xc5
#define NOTIFY_KBD_BRTTOGGLE 0xc7 #define NOTIFY_KBD_BRTTOGGLE 0xc7
@ -116,6 +117,8 @@ struct bios_args {
u32 arg0; u32 arg0;
u32 arg1; u32 arg1;
u32 arg2; /* At least TUF Gaming series uses 3 dword input buffer. */ u32 arg2; /* At least TUF Gaming series uses 3 dword input buffer. */
u32 arg4;
u32 arg5;
} __packed; } __packed;
/* /*
@ -222,45 +225,6 @@ struct asus_wmi {
struct asus_wmi_driver *driver; 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 ************************************************************************/ /* WMI ************************************************************************/
static int asus_wmi_evaluate_method3(u32 method_id, 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; struct acpi_buffer input;
u64 phys_addr; u64 phys_addr;
u32 retval; u32 retval;
u32 status = -1; u32 status;
/* /*
* Copy to dma capable address otherwise memory corruption occurs as * 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); 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 ********************************************************************/ /* Battery ********************************************************************/
/* The battery maximum charging percentage */ /* 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) 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);
led_classdev_unregister(&asus->kbd_led); led_classdev_unregister(&asus->tpd_led);
if (!IS_ERR_OR_NULL(asus->tpd_led.dev)) led_classdev_unregister(&asus->wlan_led);
led_classdev_unregister(&asus->tpd_led); led_classdev_unregister(&asus->lightbar_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);
if (asus->led_workqueue) if (asus->led_workqueue)
destroy_workqueue(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) static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
{ {
int orig_code;
unsigned int key_value = 1; unsigned int key_value = 1;
bool autorelease = 1; bool autorelease = 1;
int result, orig_code;
orig_code = code; orig_code = code;
@ -2105,6 +2113,17 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
return; 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) { if (asus->fan_boost_mode_available && code == NOTIFY_KBD_FBM) {
fan_boost_mode_switch_next(asus); fan_boost_mode_switch_next(asus);
return; return;

View File

@ -15,6 +15,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/dmi.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/gfp.h> #include <linux/gfp.h>
@ -34,7 +35,7 @@
#include "dcdbas.h" #include "dcdbas.h"
#define DRIVER_NAME "dcdbas" #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" #define DRIVER_DESCRIPTION "Dell Systems Management Base Driver"
static struct platform_device *dcdbas_pdev; 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 unsigned long max_smi_data_buf_size = MAX_SMI_DATA_BUF_SIZE;
static u32 smi_data_buf_phys_addr; static u32 smi_data_buf_phys_addr;
static DEFINE_MUTEX(smi_data_lock); 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_action;
static unsigned int host_control_smi_type; 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) static int dcdbas_check_wsmt(void)
{ {
const struct dmi_device *dev = NULL;
struct acpi_table_wsmt *wsmt = NULL; struct acpi_table_wsmt *wsmt = NULL;
struct smm_eps_table *eps = NULL; struct smm_eps_table *eps = NULL;
u64 bios_buf_paddr;
u64 remap_size; u64 remap_size;
u8 *addr; u8 *addr;
@ -532,6 +535,17 @@ static int dcdbas_check_wsmt(void)
!(wsmt->protection_flags & ACPI_WSMT_COMM_BUFFER_NESTED_PTR_PROTECTION)) !(wsmt->protection_flags & ACPI_WSMT_COMM_BUFFER_NESTED_PTR_PROTECTION))
return 0; 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) */ /* Scan for EPS (entry point structure) */
for (addr = (u8 *)__va(0xf0000); for (addr = (u8 *)__va(0xf0000);
addr < (u8 *)__va(0x100000 - sizeof(struct smm_eps_table)); addr < (u8 *)__va(0x100000 - sizeof(struct smm_eps_table));
@ -542,34 +556,37 @@ static int dcdbas_check_wsmt(void)
} }
if (!eps) { 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; 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. * Get physical address of buffer and map to virtual address.
* Table gives size in 4K pages, regardless of actual system page size. * Table gives size in 4K pages, regardless of actual system page size.
*/ */
if (upper_32_bits(eps->smm_comm_buff_addr + 8)) { if (upper_32_bits(bios_buf_paddr + 8)) {
dev_warn(&dcdbas_pdev->dev, "found WSMT, but EPS buffer address is above 4GB\n"); dev_warn(&dcdbas_pdev->dev, "found WSMT, but buffer address is above 4GB\n");
return -EINVAL; return -EINVAL;
} }
/* /*
* Limit remap size to MAX_SMI_DATA_BUF_SIZE + 8 (since the first 8 * 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). * 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) if (remap_size > MAX_SMI_DATA_BUF_SIZE + 8)
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) { bios_buffer = memremap(bios_buf_paddr, remap_size, MEMREMAP_WB);
dev_warn(&dcdbas_pdev->dev, "found WSMT, but failed to map EPS buffer\n"); if (!bios_buffer) {
dev_warn(&dcdbas_pdev->dev, "found WSMT, but failed to map buffer\n");
return -ENOMEM; return -ENOMEM;
} }
/* First 8 bytes is for a semaphore, not part of the smi_data_buf */ /* 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_phys_addr = bios_buf_paddr + 8;
smi_data_buf = eps_buffer + 8; smi_data_buf = bios_buffer + 8;
smi_data_buf_size = remap_size - 8; smi_data_buf_size = remap_size - 8;
max_smi_data_buf_size = smi_data_buf_size; max_smi_data_buf_size = smi_data_buf_size;
wsmt_enabled = true; wsmt_enabled = true;
@ -736,8 +753,8 @@ static void __exit dcdbas_exit(void)
*/ */
if (dcdbas_pdev) if (dcdbas_pdev)
smi_data_buf_free(); smi_data_buf_free();
if (eps_buffer) if (bios_buffer)
memunmap(eps_buffer); memunmap(bios_buffer);
platform_device_unregister(dcdbas_pdev_reg); platform_device_unregister(dcdbas_pdev_reg);
platform_driver_unregister(&dcdbas_driver); platform_driver_unregister(&dcdbas_driver);
} }

View File

@ -2204,10 +2204,13 @@ static int __init dell_init(void)
dell_laptop_register_notifier(&dell_laptop_notifier); dell_laptop_register_notifier(&dell_laptop_notifier);
micmute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE); if (dell_smbios_find_token(GLOBAL_MIC_MUTE_DISABLE) &&
ret = led_classdev_register(&platform_device->dev, &micmute_led_cdev); dell_smbios_find_token(GLOBAL_MIC_MUTE_ENABLE)) {
if (ret < 0) micmute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
goto fail_led; 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) if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
return 0; return 0;

View File

@ -310,6 +310,16 @@ static const struct key_entry dell_wmi_keymap_type_0011[] = {
/* Battery inserted */ /* Battery inserted */
{ KE_IGNORE, 0xfff1, { KEY_RESERVED } }, { 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 */ /* Keyboard backlight level changed */
{ KE_IGNORE, KBD_LED_OFF_TOKEN, { KEY_RESERVED } }, { KE_IGNORE, KBD_LED_OFF_TOKEN, { KEY_RESERVED } },
{ KE_IGNORE, KBD_LED_ON_TOKEN, { KEY_RESERVED } }, { KE_IGNORE, KBD_LED_ON_TOKEN, { KEY_RESERVED } },

View File

@ -541,13 +541,11 @@ static int eeepc_led_init(struct eeepc_laptop *eeepc)
static void eeepc_led_exit(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) if (eeepc->led_workqueue)
destroy_workqueue(eeepc->led_workqueue); destroy_workqueue(eeepc->led_workqueue);
} }
/* /*
* PCI hotplug (for wlan rfkill) * PCI hotplug (for wlan rfkill)
*/ */

View File

@ -111,10 +111,10 @@ enum hp_wireless2_bits {
HPWMI_POWER_SOFT = 0x02, HPWMI_POWER_SOFT = 0x02,
HPWMI_POWER_BIOS = 0x04, HPWMI_POWER_BIOS = 0x04,
HPWMI_POWER_HARD = 0x08, 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)) \ #define IS_HWBLOCKED(x) ((x & HPWMI_POWER_FW_OR_HW) != HPWMI_POWER_FW_OR_HW)
!= (HPWMI_POWER_BIOS | HPWMI_POWER_HARD))
#define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT) #define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT)
struct bios_rfkill2_device_state { 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, static ssize_t als_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
u32 tmp = simple_strtoul(buf, NULL, 10); u32 tmp;
int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, HPWMI_WRITE, &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)); sizeof(tmp), sizeof(tmp));
if (ret) if (ret)
return ret < 0 ? ret : -EINVAL; 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, static ssize_t postcode_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
long unsigned int tmp2; u32 tmp = 1;
bool clear;
int ret; int ret;
u32 tmp;
ret = kstrtoul(buf, 10, &tmp2); ret = kstrtobool(buf, &clear);
if (!ret && tmp2 != 1)
ret = -EINVAL;
if (ret) if (ret)
goto out; return ret;
if (clear == false)
return -EINVAL;
/* Clear the POST error code. It is kept until until cleared. */ /* 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, ret = hp_wmi_perform_query(HPWMI_POSTCODEERROR_QUERY, HPWMI_WRITE, &tmp,
sizeof(tmp), sizeof(tmp)); sizeof(tmp), sizeof(tmp));
out:
if (ret) if (ret)
return ret < 0 ? ret : -EINVAL; return ret < 0 ? ret : -EINVAL;

View File

@ -79,6 +79,13 @@ static const struct dmi_system_id button_array_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Wacom MobileStudio Pro 16"), 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"),
},
},
{ } { }
}; };

View File

@ -40,28 +40,70 @@ static const struct key_entry intel_vbtn_keymap[] = {
{ KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } }, /* volume-down key release */ { KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } }, /* volume-down key release */
{ KE_KEY, 0xC8, { KEY_ROTATE_LOCK_TOGGLE } }, /* rotate-lock key press */ { KE_KEY, 0xC8, { KEY_ROTATE_LOCK_TOGGLE } }, /* rotate-lock key press */
{ KE_KEY, 0xC9, { KEY_ROTATE_LOCK_TOGGLE } }, /* rotate-lock key release */ { 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, 0xCA, { .sw = { SW_DOCK, 1 } } }, /* Docked */
{ KE_SW, 0xCB, { .sw = { SW_DOCK, 0 } } }, /* Undocked */ { KE_SW, 0xCB, { .sw = { SW_DOCK, 0 } } }, /* Undocked */
{ KE_SW, 0xCC, { .sw = { SW_TABLET_MODE, 1 } } }, /* Tablet */ { KE_SW, 0xCC, { .sw = { SW_TABLET_MODE, 1 } } }, /* Tablet */
{ KE_SW, 0xCD, { .sw = { SW_TABLET_MODE, 0 } } }, /* Laptop */ { 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 intel_vbtn_priv {
struct key_entry keymap[KEYMAP_LEN];
struct input_dev *input_dev; struct input_dev *input_dev;
bool has_buttons;
bool has_switches;
bool wakeup_mode; 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) static int intel_vbtn_input_setup(struct platform_device *device)
{ {
struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev); 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); priv->input_dev = devm_input_allocate_device(&device->dev);
if (!priv->input_dev) if (!priv->input_dev)
return -ENOMEM; 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) if (ret)
return 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->name = "Intel Virtual Button driver";
priv->input_dev->id.bustype = BUS_HOST; priv->input_dev->id.bustype = BUS_HOST;
if (priv->has_switches)
detect_tablet_mode(device);
return input_register_device(priv->input_dev); return input_register_device(priv->input_dev);
} }
@ -114,44 +159,46 @@ out_unknown:
dev_dbg(&device->dev, "unknown event index 0x%x\n", event); 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); const char *chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE);
struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev); unsigned long long vgbs;
acpi_handle handle = ACPI_HANDLE(&device->dev);
struct acpi_buffer vgbs_output = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
acpi_status status; 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); status = acpi_evaluate_integer(handle, "VGBS", NULL, &vgbs);
if (ACPI_FAILURE(status)) return ACPI_SUCCESS(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);
} }
static int intel_vbtn_probe(struct platform_device *device) static int intel_vbtn_probe(struct platform_device *device)
{ {
acpi_handle handle = ACPI_HANDLE(&device->dev); acpi_handle handle = ACPI_HANDLE(&device->dev);
bool has_buttons, has_switches;
struct intel_vbtn_priv *priv; struct intel_vbtn_priv *priv;
acpi_status status; acpi_status status;
int err; int err;
status = acpi_evaluate_object(handle, "VBDL", NULL, NULL); has_buttons = intel_vbtn_has_buttons(handle);
if (ACPI_FAILURE(status)) { has_switches = intel_vbtn_has_switches(handle);
if (!has_buttons && !has_switches) {
dev_warn(&device->dev, "failed to read Intel Virtual Button driver\n"); dev_warn(&device->dev, "failed to read Intel Virtual Button driver\n");
return -ENODEV; return -ENODEV;
} }
@ -161,14 +208,15 @@ static int intel_vbtn_probe(struct platform_device *device)
return -ENOMEM; return -ENOMEM;
dev_set_drvdata(&device->dev, priv); dev_set_drvdata(&device->dev, priv);
priv->has_buttons = has_buttons;
priv->has_switches = has_switches;
err = intel_vbtn_input_setup(device); err = intel_vbtn_input_setup(device);
if (err) { if (err) {
pr_err("Failed to setup Intel Virtual Button\n"); pr_err("Failed to setup Intel Virtual Button\n");
return err; return err;
} }
detect_tablet_mode(device);
status = acpi_install_notify_handler(handle, status = acpi_install_notify_handler(handle,
ACPI_DEVICE_NOTIFY, ACPI_DEVICE_NOTIFY,
notify_handler, notify_handler,

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

View File

@ -6,14 +6,14 @@
* *
* Some Intel Cherry Trail based device which ship with Windows 10, have * Some Intel Cherry Trail based device which ship with Windows 10, have
* this weird INT33FE ACPI device with a CRS table with 4 I2cSerialBusV2 * this weird INT33FE ACPI device with a CRS table with 4 I2cSerialBusV2
* resources, for 4 different chips attached to various i2c busses: * 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 * 1. The Whiskey Cove PMIC, which is also described by the INT34D3 ACPI device
* 2. Maxim MAX17047 Fuel Gauge Controller * 2. Maxim MAX17047 Fuel Gauge Controller
* 3. FUSB302 USB Type-C Controller * 3. FUSB302 USB Type-C Controller
* 4. PI3USB30532 USB switch * 4. PI3USB30532 USB switch
* *
* So this driver is a stub / pseudo driver whose only purpose is to * 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. * for these chips can bind to the them.
*/ */
@ -21,43 +21,32 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/usb/pd.h> #include <linux/usb/pd.h>
#include "intel_cht_int33fe_common.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 * the max17047 both through the INT33FE ACPI device (it is right there
* in the resources table) as well as through a separate MAX17047 device. * 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. * for the max17047 has already been registered.
*/ */
static int cht_int33fe_check_for_max17047(struct device *dev, void *data) static int cht_int33fe_check_for_max17047(struct device *dev, void *data)
{ {
struct i2c_client **max17047 = data; struct i2c_client **max17047 = data;
struct acpi_device *adev; struct acpi_device *adev;
const char *hid;
adev = ACPI_COMPANION(dev); adev = ACPI_COMPANION(dev);
if (!adev) if (!adev)
return 0; return 0;
hid = acpi_device_hid(adev);
/* The MAX17047 ACPI node doesn't have an UID, so we don't check that */ /* 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; return 0;
*max17047 = to_i2c_client(dev); *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 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), 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, * We are not using inline property here because those are constant,
* and we need to adjust this one at runtime to point to real * 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 }, { .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_STRING("linux,extcon-name", "cht_wcove_pwrsrc"),
PROPERTY_ENTRY_REF_ARRAY("usb-role-switch", fusb302_mux_refs), 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 \ #define PDO_FIXED_FLAGS \
(PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP | PDO_FIXED_USB_COMM) (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), 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("data-role", "dual"),
PROPERTY_ENTRY_STRING("power-role", "dual"), PROPERTY_ENTRY_STRING("power-role", "dual"),
PROPERTY_ENTRY_STRING("try-power-role", "sink"), PROPERTY_ENTRY_STRING("try-power-role", "sink"),
PROPERTY_ENTRY_U32_ARRAY("source-pdos", src_pdo), PROPERTY_ENTRY_U32_ARRAY("source-pdos", src_pdo),
PROPERTY_ENTRY_U32_ARRAY("sink-pdos", snk_pdo), PROPERTY_ENTRY_U32_ARRAY("sink-pdos", snk_pdo),
PROPERTY_ENTRY_U32("op-sink-microwatt", 2500000), PROPERTY_ENTRY_U32("op-sink-microwatt", 2500000),
PROPERTY_ENTRY_REF("orientation-switch", PROPERTY_ENTRY_REF("orientation-switch", &pi3usb30532_node),
&nodes[INT33FE_NODE_PI3USB30532]), PROPERTY_ENTRY_REF("mode-switch", &pi3usb30532_node),
PROPERTY_ENTRY_REF("mode-switch", PROPERTY_ENTRY_REF("displayport", &displayport_node),
&nodes[INT33FE_NODE_PI3USB30532]),
PROPERTY_ENTRY_REF("displayport",
&nodes[INT33FE_NODE_DISPLAYPORT]),
{ } { }
}; };
static const struct software_node nodes[] = { static const struct software_node usb_connector_node = {
{ "fusb302", NULL, fusb302_props }, .name = "connector",
{ "max17047", NULL, max17047_props }, .parent = &fusb302_node,
{ "pi3usb30532" }, .properties = usb_connector_properties,
{ "displayport" }, };
{ "connector", &nodes[0], usb_connector_props },
{ } 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) 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 fwnode_handle *fwnode;
struct pci_dev *pdev; struct pci_dev *pdev;
fwnode = software_node_fwnode(&nodes[INT33FE_NODE_DISPLAYPORT]); fwnode = software_node_fwnode(&displayport_node);
if (!fwnode) if (!fwnode)
return -ENODEV; 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) 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) { if (fusb302_mux_refs[0].node) {
fwnode_handle_put( fwnode_handle_put(software_node_fwnode(fusb302_mux_refs[0].node));
software_node_fwnode(fusb302_mux_refs[0].node));
fusb302_mux_refs[0].node = NULL; 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; fusb302_mux_refs[0].node = mux_ref_node;
ret = software_node_register_nodes(nodes); ret = software_node_register_node_group(node_group);
if (ret) if (ret)
return ret; return ret;
@ -222,16 +229,15 @@ cht_int33fe_register_max17047(struct device *dev, struct cht_int33fe_data *data)
struct fwnode_handle *fwnode; struct fwnode_handle *fwnode;
int ret; int ret;
fwnode = software_node_fwnode(&nodes[INT33FE_NODE_MAX17047]); fwnode = software_node_fwnode(&max17047_node);
if (!fwnode) if (!fwnode)
return -ENODEV; return -ENODEV;
i2c_for_each_dev(&max17047, cht_int33fe_check_for_max17047); i2c_for_each_dev(&max17047, cht_int33fe_check_for_max17047);
if (max17047) { if (max17047) {
/* Pre-existing i2c-client for the max17047, add device-props */ /* Pre-existing I²C client for the max17047, add device properties */
fwnode->secondary = ERR_PTR(-ENODEV); set_secondary_fwnode(&max17047->dev, fwnode);
max17047->dev.fwnode->secondary = fwnode; /* And re-probe to get the new device properties applied */
/* And re-probe to get the new device-props applied. */
ret = device_reprobe(&max17047->dev); ret = device_reprobe(&max17047->dev);
if (ret) if (ret)
dev_warn(dev, "Reprobing max17047 error: %d\n", 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 * must be registered before the fusb302 is instantiated, otherwise
* it will end up with a dummy-regulator. * it will end up with a dummy-regulator.
* Note "cht_wc_usb_typec_vbus" comes from the regulator_init_data * 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 * gets instantiated. We use regulator_get_optional here so that we
* don't end up getting a dummy-regulator ourselves. * 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); 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); fusb302_irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 1);
if (fusb302_irq < 0) { if (fusb302_irq < 0) {
if (fusb302_irq != -EPROBE_DEFER) if (fusb302_irq != -EPROBE_DEFER)
@ -289,12 +295,12 @@ int cht_int33fe_typec_probe(struct cht_int33fe_data *data)
if (ret) if (ret)
return 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); ret = cht_int33fe_register_max17047(dev, data);
if (ret) if (ret)
goto out_remove_nodes; goto out_remove_nodes;
fwnode = software_node_fwnode(&nodes[INT33FE_NODE_FUSB302]); fwnode = software_node_fwnode(&fusb302_node);
if (!fwnode) { if (!fwnode) {
ret = -ENODEV; ret = -ENODEV;
goto out_unregister_max17047; goto out_unregister_max17047;
@ -312,7 +318,7 @@ int cht_int33fe_typec_probe(struct cht_int33fe_data *data)
goto out_unregister_max17047; goto out_unregister_max17047;
} }
fwnode = software_node_fwnode(&nodes[INT33FE_NODE_PI3USB30532]); fwnode = software_node_fwnode(&pi3usb30532_node);
if (!fwnode) { if (!fwnode) {
ret = -ENODEV; ret = -ENODEV;
goto out_unregister_fusb302; goto out_unregister_fusb302;

View File

@ -46,6 +46,7 @@ struct mid_pb_ddata {
unsigned short mirqlvl1_addr; unsigned short mirqlvl1_addr;
unsigned short pbstat_addr; unsigned short pbstat_addr;
u8 pbstat_mask; u8 pbstat_mask;
struct intel_scu_ipc_dev *scu;
int (*setup)(struct mid_pb_ddata *ddata); int (*setup)(struct mid_pb_ddata *ddata);
}; };
@ -55,7 +56,8 @@ static int mid_pbstat(struct mid_pb_ddata *ddata, int *value)
int ret; int ret;
u8 pbstat; 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) if (ret)
return 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) 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) static int mrfld_setup(struct mid_pb_ddata *ddata)
{ {
/* Unmask the PBIRQ and MPBIRQ on Tangier */ /* Unmask the PBIRQ and MPBIRQ on Tangier */
intel_scu_ipc_update_register(BCOVE_PBIRQ, 0, MSIC_PWRBTNM); intel_scu_ipc_dev_update(ddata->scu, BCOVE_PBIRQ, 0, MSIC_PWRBTNM);
intel_scu_ipc_update_register(BCOVE_PBIRQMASK, 0, MSIC_PWRBTNM); intel_scu_ipc_dev_update(ddata->scu, BCOVE_PBIRQMASK, 0, MSIC_PWRBTNM);
return 0; return 0;
} }
@ -161,6 +164,10 @@ static int mid_pb_probe(struct platform_device *pdev)
return error; 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, error = devm_request_threaded_irq(&pdev->dev, irq, NULL, mid_pb_isr,
IRQF_ONESHOT, DRIVER_NAME, ddata); IRQF_ONESHOT, DRIVER_NAME, ddata);
if (error) { if (error) {

View File

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

View File

@ -18,11 +18,10 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/pci.h> #include <linux/io.h>
#include <linux/pm.h> #include <linux/module.h>
#include <linux/sfi.h> #include <linux/slab.h>
#include <asm/intel-mid.h>
#include <asm/intel_scu_ipc.h> #include <asm/intel_scu_ipc.h>
/* IPC defines the following message types */ /* IPC defines the following message types */
@ -55,14 +54,14 @@
#define IPC_IOC 0x100 /* IPC command register IOC bit */ #define IPC_IOC 0x100 /* IPC command register IOC bit */
struct intel_scu_ipc_dev { struct intel_scu_ipc_dev {
struct device *dev; struct device dev;
struct resource mem;
struct module *owner;
int irq;
void __iomem *ipc_base; void __iomem *ipc_base;
struct completion cmd_complete; 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 0x04
#define IPC_STATUS_IRQ BIT(2) #define IPC_STATUS_IRQ BIT(2)
#define IPC_STATUS_ERR BIT(1) #define IPC_STATUS_ERR BIT(1)
@ -78,8 +77,110 @@ static struct intel_scu_ipc_dev ipcdev; /* Only one for now */
/* Timeout in jiffies */ /* Timeout in jiffies */
#define IPC_TIMEOUT (3 * HZ) #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 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 * Send ipc command
* Command Register (Write Only): * Command Register (Write Only):
@ -143,7 +244,6 @@ static inline int busy_loop(struct intel_scu_ipc_dev *scu)
usleep_range(50, 100); usleep_range(50, 100);
} while (time_before(jiffies, end)); } while (time_before(jiffies, end));
dev_err(scu->dev, "IPC timed out");
return -ETIMEDOUT; return -ETIMEDOUT;
} }
@ -152,10 +252,8 @@ static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu)
{ {
int status; int status;
if (!wait_for_completion_timeout(&scu->cmd_complete, IPC_TIMEOUT)) { if (!wait_for_completion_timeout(&scu->cmd_complete, IPC_TIMEOUT))
dev_err(scu->dev, "IPC timed out\n");
return -ETIMEDOUT; return -ETIMEDOUT;
}
status = ipc_read_status(scu); status = ipc_read_status(scu);
if (status & IPC_STATUS_ERR) 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) 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 */ /* 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; int nc;
u32 offset = 0; u32 offset = 0;
int err; 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)); memset(cbuf, 0, sizeof(cbuf));
mutex_lock(&ipclock); mutex_lock(&ipclock);
if (!scu)
if (scu->dev == NULL) { scu = ipcdev;
if (!scu) {
mutex_unlock(&ipclock); mutex_unlock(&ipclock);
return -ENODEV; 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 * @addr: Register on SCU
* @data: Return pointer for read byte * @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. * 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 * @addr: Register on SCU
* @data: Byte to write * @data: Byte to write
* *
@ -247,14 +348,15 @@ EXPORT_SYMBOL(intel_scu_ipc_ioread8);
* *
* This function may sleep. * 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 * @addr: Register list
* @data: Bytes to return * @data: Bytes to return
* @len: Length of array * @len: Length of array
@ -266,14 +368,16 @@ EXPORT_SYMBOL(intel_scu_ipc_iowrite8);
* *
* This function may sleep. * 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 * @addr: Register list
* @data: Bytes to write * @data: Bytes to write
* @len: Length of array * @len: Length of array
@ -285,16 +389,18 @@ EXPORT_SYMBOL(intel_scu_ipc_readv);
* *
* This function may sleep. * 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 * @addr: Register address
* @bits: Bits to update * @data: Bits to update
* @mask: Mask of bits to update * @mask: Mask of bits to update
* *
* Read-modify-write power control unit register. The first data argument * 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 * This function may sleep. Locking between SCU accesses is handled
* for the caller. * 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 }; u8 tmp[2] = { data, mask };
return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_M); 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 * @cmd: Command
* @sub: Sub type * @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 * This function may sleep. Locking for SCU accesses is handled for the
* caller. * 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; int err;
mutex_lock(&ipclock); mutex_lock(&ipclock);
if (scu->dev == NULL) { if (!scu)
scu = ipcdev;
if (!scu) {
mutex_unlock(&ipclock); mutex_unlock(&ipclock);
return -ENODEV; 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); err = intel_scu_ipc_check_status(scu);
mutex_unlock(&ipclock); mutex_unlock(&ipclock);
if (err)
dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err);
return 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 * @cmd: Command
* @sub: Sub type * @sub: Sub type
* @in: Input data * @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 * @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 * 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. * 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, int intel_scu_ipc_dev_command_with_size(struct intel_scu_ipc_dev *scu, int cmd,
u32 *out, int outlen) 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; int i, err;
if (inbuflen > 4 || outbuflen > 4)
return -EINVAL;
mutex_lock(&ipclock); mutex_lock(&ipclock);
if (scu->dev == NULL) { if (!scu)
scu = ipcdev;
if (!scu) {
mutex_unlock(&ipclock); mutex_unlock(&ipclock);
return -ENODEV; return -ENODEV;
} }
for (i = 0; i < inlen; i++) memcpy(inbuf, in, inlen);
ipc_data_writel(scu, *in++, 4 * i); 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); err = intel_scu_ipc_check_status(scu);
if (!err) { if (!err) {
for (i = 0; i < outlen; i++) u32 outbuf[4] = {};
*out++ = ipc_data_readl(scu, 4 * i);
for (i = 0; i < outbuflen; i++)
outbuf[i] = ipc_data_readl(scu, 4 * i);
memcpy(out, outbuf, outlen);
} }
mutex_unlock(&ipclock); mutex_unlock(&ipclock);
if (err)
dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err);
return 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 * 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; return IRQ_HANDLED;
} }
/** static void intel_scu_ipc_release(struct device *dev)
* 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)
{ {
int err; struct intel_scu_ipc_dev *scu;
struct intel_scu_ipc_dev *scu = &ipcdev;
if (scu->dev) /* We support only one SCU */ scu = container_of(dev, struct intel_scu_ipc_dev, dev);
return -EBUSY; if (scu->irq > 0)
free_irq(scu->irq, scu);
err = pcim_enable_device(pdev); iounmap(scu->ipc_base);
if (err) release_mem_region(scu->mem.start, resource_size(&scu->mem));
return err; kfree(scu);
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;
} }
static const struct pci_device_id pci_ids[] = { /**
{ PCI_VDEVICE(INTEL, 0x080e) }, * __intel_scu_ipc_register() - Register SCU IPC device
{ PCI_VDEVICE(INTEL, 0x08ea) }, * @parent: Parent device
{ PCI_VDEVICE(INTEL, 0x11a0) }, * @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 = { mutex_lock(&ipclock);
.driver = { /* We support only one IPC */
.suppress_bind_attrs = true, if (ipcdev) {
}, err = -EBUSY;
.name = "intel_scu_ipc", goto err_unlock;
.id_table = pci_ids, }
.probe = ipc_probe,
}; scu = kzalloc(sizeof(*scu), GFP_KERNEL);
builtin_pci_driver(ipc_driver); 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);

View File

@ -22,6 +22,9 @@
static int major; static int major;
struct intel_scu_ipc_dev *scu;
static DEFINE_MUTEX(scu_lock);
/* IOCTL commands */ /* IOCTL commands */
#define INTE_SCU_IPC_REGISTER_READ 0 #define INTE_SCU_IPC_REGISTER_READ 0
#define INTE_SCU_IPC_REGISTER_WRITE 1 #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) { switch (cmd) {
case INTE_SCU_IPC_REGISTER_READ: 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: 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: case INTE_SCU_IPC_REGISTER_UPDATE:
return intel_scu_ipc_update_register(data->addr[0], return intel_scu_ipc_dev_update(scu, data->addr[0], data->data[0],
data->data[0], data->mask); data->mask);
default: default:
return -ENOTTY; return -ENOTTY;
} }
@ -91,8 +94,40 @@ static long scu_ipc_ioctl(struct file *fp, unsigned int cmd,
return 0; 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 = { static const struct file_operations scu_ipc_fops = {
.unlocked_ioctl = scu_ipc_ioctl, .unlocked_ioctl = scu_ipc_ioctl,
.open = scu_ipc_open,
.release = scu_ipc_release,
}; };
static int __init ipc_module_init(void) static int __init ipc_module_init(void)

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

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

View File

@ -21,13 +21,12 @@
#define PUNIT_MAILBOX_BUSY_BIT 31 #define PUNIT_MAILBOX_BUSY_BIT 31
/* /*
* Commands has variable amount of processing time. Most of the commands will * The average time to complete some commands is about 40us. The current
* be done in 0-3 tries, but some takes up to 50. * count is enough to satisfy 40us. But when the firmware is very busy, this
* The real processing time was observed as 25us for the most of the commands * causes timeout occasionally. So increase to deal with some worst case
* at 2GHz. It is possible to optimize this count taking samples on customer * scenarios. Most of the command still complete in few us.
* systems.
*/ */
#define OS_MAILBOX_RETRY_COUNT 50 #define OS_MAILBOX_RETRY_COUNT 100
struct isst_if_device { struct isst_if_device {
struct mutex mutex; struct mutex mutex;

View File

@ -353,21 +353,16 @@ int telemetry_clear_pltdata(void)
EXPORT_SYMBOL_GPL(telemetry_clear_pltdata); 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 * May be used by other telemetry modules to get platform specific
* * configuration.
* Return: 0 success, < 0 for failure
*/ */
int telemetry_pltconfig_valid(void) struct telemetry_plt_config *telemetry_get_pltdata(void)
{ {
if (telm_core_conf.plt_config) return telm_core_conf.plt_config;
return 0;
else
return -EINVAL;
} }
EXPORT_SYMBOL_GPL(telemetry_pltconfig_valid); EXPORT_SYMBOL_GPL(telemetry_get_pltdata);
static inline int telemetry_get_pssevtname(enum telemetry_unit telem_unit, static inline int telemetry_get_pssevtname(enum telemetry_unit telem_unit,
const char **name, int len) const char **name, int len)

View File

@ -15,6 +15,7 @@
*/ */
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/mfd/intel_pmc_bxt.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
@ -22,7 +23,6 @@
#include <asm/cpu_device_id.h> #include <asm/cpu_device_id.h>
#include <asm/intel-family.h> #include <asm/intel-family.h>
#include <asm/intel_pmc_ipc.h>
#include <asm/intel_telemetry.h> #include <asm/intel_telemetry.h>
#define DRIVER_NAME "telemetry_soc_debugfs" #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) static int telem_s0ix_res_get(void *data, u64 *val)
{ {
struct telemetry_plt_config *plt_config = telemetry_get_pltdata();
u64 s0ix_total_res; u64 s0ix_total_res;
int ret; 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) { if (ret) {
pr_err("Failed to read S0ix residency"); pr_err("Failed to read S0ix residency");
return ret; return ret;
@ -837,12 +838,15 @@ static int pm_suspend_exit_cb(void)
*/ */
if (suspend_shlw_ctr_exit == suspend_shlw_ctr_temp && if (suspend_shlw_ctr_exit == suspend_shlw_ctr_temp &&
suspend_deep_ctr_exit == suspend_deep_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); &suspend_shlw_res_exit);
if (ret < 0) if (ret < 0)
goto out; 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); &suspend_deep_res_exit);
if (ret < 0) if (ret < 0)
goto out; goto out;
@ -910,8 +914,7 @@ static int __init telemetry_debugfs_init(void)
debugfs_conf = (struct telemetry_debugfs_conf *)id->driver_data; debugfs_conf = (struct telemetry_debugfs_conf *)id->driver_data;
err = telemetry_pltconfig_valid(); if (!telemetry_get_pltdata()) {
if (err < 0) {
pr_info("Invalid pltconfig, ensure IPC1 device is enabled in BIOS\n"); pr_info("Invalid pltconfig, ensure IPC1 device is enabled in BIOS\n");
return -ENODEV; return -ENODEV;
} }

View File

@ -15,7 +15,6 @@
#include <asm/cpu_device_id.h> #include <asm/cpu_device_id.h>
#include <asm/intel-family.h> #include <asm/intel-family.h>
#include <asm/intel_pmc_ipc.h>
#include <asm/intel_punit_ipc.h> #include <asm/intel_punit_ipc.h>
#include <asm/intel_telemetry.h> #include <asm/intel_telemetry.h>
@ -35,6 +34,7 @@
#define TELEM_SSRAM_STARTTIME_OFFSET 8 #define TELEM_SSRAM_STARTTIME_OFFSET 8
#define TELEM_SSRAM_EVTLOG_OFFSET 16 #define TELEM_SSRAM_EVTLOG_OFFSET 16
#define IOSS_TELEM 0xeb
#define IOSS_TELEM_EVENT_READ 0x0 #define IOSS_TELEM_EVENT_READ 0x0
#define IOSS_TELEM_EVENT_WRITE 0x1 #define IOSS_TELEM_EVENT_WRITE 0x1
#define IOSS_TELEM_INFO_READ 0x2 #define IOSS_TELEM_INFO_READ 0x2
@ -42,9 +42,6 @@
#define IOSS_TELEM_TRACE_CTL_WRITE 0x6 #define IOSS_TELEM_TRACE_CTL_WRITE 0x6
#define IOSS_TELEM_EVENT_CTL_READ 0x7 #define IOSS_TELEM_EVENT_CTL_READ 0x7
#define IOSS_TELEM_EVENT_CTL_WRITE 0x8 #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 IOSS_TELEM_EVT_WRITE_SIZE 0x3
#define TELEM_INFO_SRAMEVTS_MASK 0xFF00 #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) static inline int telemetry_plt_config_ioss_event(u32 evt_id, int index)
{ {
u32 write_buf; u32 write_buf;
int ret;
write_buf = evt_id | TELEM_EVENT_ENABLE; write_buf = evt_id | TELEM_EVENT_ENABLE;
write_buf <<= BITS_PER_BYTE; write_buf <<= BITS_PER_BYTE;
write_buf |= index; write_buf |= index;
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, return intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM,
IOSS_TELEM_EVENT_WRITE, (u8 *)&write_buf, IOSS_TELEM_EVENT_WRITE, &write_buf,
IOSS_TELEM_EVT_WRITE_SIZE, NULL, 0); IOSS_TELEM_EVT_WRITE_SIZE, NULL, 0);
return ret;
} }
static inline int telemetry_plt_config_pss_event(u32 evt_id, int index) 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, static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
enum telemetry_action action) enum telemetry_action action)
{ {
struct intel_scu_ipc_dev *scu = telm_conf->scu;
u8 num_ioss_evts, ioss_period; u8 num_ioss_evts, ioss_period;
int ret, index, idx; int ret, index, idx;
u32 *ioss_evtmap; u32 *ioss_evtmap;
@ -288,9 +283,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
ioss_evtmap = evtconfig.evtmap; ioss_evtmap = evtconfig.evtmap;
/* Get telemetry EVENT CTL */ /* 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, IOSS_TELEM_EVENT_CTL_READ, NULL, 0,
&telem_ctrl, IOSS_TELEM_READ_WORD); &telem_ctrl, sizeof(telem_ctrl));
if (ret) { if (ret) {
pr_err("IOSS TELEM_CTRL Read Failed\n"); pr_err("IOSS TELEM_CTRL Read Failed\n");
return ret; return ret;
@ -299,11 +294,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
/* Disable Telemetry */ /* Disable Telemetry */
TELEM_DISABLE(telem_ctrl); TELEM_DISABLE(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, IOSS_TELEM_EVENT_CTL_WRITE, &telem_ctrl,
(u8 *)&telem_ctrl, sizeof(telem_ctrl), NULL, 0);
IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
NULL, 0);
if (ret) { if (ret) {
pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n"); pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
return ret; return ret;
@ -315,10 +308,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
/* Clear All Events */ /* Clear All Events */
TELEM_CLEAR_EVENTS(telem_ctrl); 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, IOSS_TELEM_EVENT_CTL_WRITE,
(u8 *)&telem_ctrl, &telem_ctrl, sizeof(telem_ctrl),
IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
NULL, 0); NULL, 0);
if (ret) { if (ret) {
pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n"); 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 */ /* Clear All Events */
TELEM_CLEAR_EVENTS(telem_ctrl); 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, IOSS_TELEM_EVENT_CTL_WRITE,
(u8 *)&telem_ctrl, &telem_ctrl, sizeof(telem_ctrl),
IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
NULL, 0); NULL, 0);
if (ret) { if (ret) {
pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n"); 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_ENABLE_PERIODIC(telem_ctrl);
telem_ctrl |= ioss_period; 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, IOSS_TELEM_EVENT_CTL_WRITE,
(u8 *)&telem_ctrl, &telem_ctrl, sizeof(telem_ctrl), NULL, 0);
IOSS_TELEM_EVT_CTRL_WRITE_SIZE, NULL, 0);
if (ret) { if (ret) {
pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n"); pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n");
return ret; return ret;
@ -586,8 +576,9 @@ static int telemetry_setup(struct platform_device *pdev)
u32 read_buf, events, event_regs; u32 read_buf, events, event_regs;
int ret; int ret;
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, IOSS_TELEM_INFO_READ, ret = intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM,
NULL, 0, &read_buf, IOSS_TELEM_READ_WORD); IOSS_TELEM_INFO_READ, NULL, 0,
&read_buf, sizeof(read_buf));
if (ret) { if (ret) {
dev_err(&pdev->dev, "IOSS TELEM_INFO Read Failed\n"); dev_err(&pdev->dev, "IOSS TELEM_INFO Read Failed\n");
return ret; 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)); mutex_lock(&(telm_conf->telem_lock));
if (ioss_period) { if (ioss_period) {
struct intel_scu_ipc_dev *scu = telm_conf->scu;
if (TELEM_SAMPLE_PERIOD_INVALID(ioss_period)) { if (TELEM_SAMPLE_PERIOD_INVALID(ioss_period)) {
pr_err("IOSS Sampling Period Out of Range\n"); pr_err("IOSS Sampling Period Out of Range\n");
ret = -EINVAL; ret = -EINVAL;
@ -688,9 +681,9 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
} }
/* Get telemetry EVENT CTL */ /* 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, IOSS_TELEM_EVENT_CTL_READ, NULL, 0,
&telem_ctrl, IOSS_TELEM_READ_WORD); &telem_ctrl, sizeof(telem_ctrl));
if (ret) { if (ret) {
pr_err("IOSS TELEM_CTRL Read Failed\n"); pr_err("IOSS TELEM_CTRL Read Failed\n");
goto out; goto out;
@ -699,11 +692,10 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
/* Disable Telemetry */ /* Disable Telemetry */
TELEM_DISABLE(telem_ctrl); TELEM_DISABLE(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, IOSS_TELEM_EVENT_CTL_WRITE,
(u8 *)&telem_ctrl, &telem_ctrl, sizeof(telem_ctrl),
IOSS_TELEM_EVT_CTRL_WRITE_SIZE, NULL, 0);
NULL, 0);
if (ret) { if (ret) {
pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n"); pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
goto out; 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_ENABLE_PERIODIC(telem_ctrl);
telem_ctrl |= ioss_period; 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, IOSS_TELEM_EVENT_CTL_WRITE,
(u8 *)&telem_ctrl, &telem_ctrl, sizeof(telem_ctrl),
IOSS_TELEM_EVT_CTRL_WRITE_SIZE, NULL, 0);
NULL, 0);
if (ret) { if (ret) {
pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n"); pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n");
goto out; goto out;
@ -1014,9 +1005,9 @@ static int telemetry_plt_get_trace_verbosity(enum telemetry_unit telem_unit,
break; break;
case TELEM_IOSS: case TELEM_IOSS:
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, ret = intel_scu_ipc_dev_command(telm_conf->scu,
IOSS_TELEM_TRACE_CTL_READ, NULL, 0, &temp, IOSS_TELEM, IOSS_TELEM_TRACE_CTL_READ,
IOSS_TELEM_READ_WORD); NULL, 0, &temp, sizeof(temp));
if (ret) { if (ret) {
pr_err("IOSS TRACE_CTL Read Failed\n"); pr_err("IOSS TRACE_CTL Read Failed\n");
goto out; goto out;
@ -1068,9 +1059,9 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
break; break;
case TELEM_IOSS: case TELEM_IOSS:
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, ret = intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM,
IOSS_TELEM_TRACE_CTL_READ, NULL, 0, &temp, IOSS_TELEM_TRACE_CTL_READ,
IOSS_TELEM_READ_WORD); NULL, 0, &temp, sizeof(temp));
if (ret) { if (ret) {
pr_err("IOSS TRACE_CTL Read Failed\n"); pr_err("IOSS TRACE_CTL Read Failed\n");
goto out; goto out;
@ -1079,9 +1070,9 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
TELEM_CLEAR_VERBOSITY_BITS(temp); TELEM_CLEAR_VERBOSITY_BITS(temp);
TELEM_SET_VERBOSITY_BITS(temp, verbosity); TELEM_SET_VERBOSITY_BITS(temp, verbosity);
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, ret = intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM,
IOSS_TELEM_TRACE_CTL_WRITE, (u8 *)&temp, IOSS_TELEM_TRACE_CTL_WRITE,
IOSS_TELEM_WRITE_FOURBYTES, NULL, 0); &temp, sizeof(temp), NULL, 0);
if (ret) { if (ret) {
pr_err("IOSS TRACE_CTL Verbosity Set Failed\n"); pr_err("IOSS TRACE_CTL Verbosity Set Failed\n");
goto out; 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 = (struct telemetry_plt_config *)id->driver_data;
telm_conf->pmc = dev_get_drvdata(pdev->dev.parent);
mem = devm_platform_ioremap_resource(pdev, 0); mem = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mem)) if (IS_ERR(mem))
return PTR_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->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_lock);
mutex_init(&telm_conf->telem_trace_lock); mutex_init(&telm_conf->telem_trace_lock);

View File

@ -67,9 +67,7 @@ static u32 inited;
#define INIT_INPUT_WMI_0 0x01 #define INIT_INPUT_WMI_0 0x01
#define INIT_INPUT_WMI_2 0x02 #define INIT_INPUT_WMI_2 0x02
#define INIT_INPUT_ACPI 0x04 #define INIT_INPUT_ACPI 0x04
#define INIT_TPAD_LED 0x08 #define INIT_SPARSE_KEYMAP 0x80
#define INIT_KBD_LED 0x10
#define INIT_SPARSE_KEYMAP 0x80
static const struct key_entry wmi_keymap[] = { static const struct key_entry wmi_keymap[] = {
{KE_KEY, 0x70, {KEY_F15} }, /* LG control panel (F1) */ {KE_KEY, 0x70, {KEY_F15} }, /* LG control panel (F1) */
@ -626,11 +624,9 @@ static int acpi_add(struct acpi_device *device)
if (ret) if (ret)
goto out_platform_device; goto out_platform_device;
if (!led_classdev_register(&pf_device->dev, &kbd_backlight)) /* LEDs are optional */
inited |= INIT_KBD_LED; led_classdev_register(&pf_device->dev, &kbd_backlight);
led_classdev_register(&pf_device->dev, &tpad_led);
if (!led_classdev_register(&pf_device->dev, &tpad_led))
inited |= INIT_TPAD_LED;
wmi_input_setup(); wmi_input_setup();
@ -646,11 +642,9 @@ out_platform_registered:
static int acpi_remove(struct acpi_device *device) static int acpi_remove(struct acpi_device *device)
{ {
sysfs_remove_group(&pf_device->dev.kobj, &dev_attribute_group); 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(); wmi_input_destroy();
platform_device_unregister(pf_device); platform_device_unregister(pf_device);

View File

@ -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) 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) if (samsung->led_workqueue)
destroy_workqueue(samsung->led_workqueue); destroy_workqueue(samsung->led_workqueue);
} }

View File

@ -757,33 +757,6 @@ static union acpi_object *__call_snc_method(acpi_handle handle, char *method,
return result; 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) #define MIN(a, b) (a > b ? b : a)
static int sony_nc_buffer_call(acpi_handle handle, char *name, u64 *value, static int sony_nc_buffer_call(acpi_handle handle, char *name, u64 *value,
void *buffer, size_t buflen) void *buffer, size_t buflen)
@ -795,17 +768,20 @@ static int sony_nc_buffer_call(acpi_handle handle, char *name, u64 *value,
if (!object) if (!object)
return -EINVAL; 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); len = MIN(buflen, object->buffer.length);
memset(buffer, 0, buflen);
memcpy(buffer, object->buffer.pointer, len); memcpy(buffer, object->buffer.pointer, len);
} else if (object->type == ACPI_TYPE_INTEGER) { } else if (object->type == ACPI_TYPE_INTEGER) {
len = MIN(buflen, sizeof(object->integer.value)); len = MIN(buflen, sizeof(object->integer.value));
memset(buffer, 0, buflen);
memcpy(buffer, &object->integer.value, len); memcpy(buffer, &object->integer.value, len);
} else { } else {
pr_warn("Invalid acpi_object: expected 0x%x got 0x%x\n", pr_warn("Unexpected acpi_object: 0x%x\n", object->type);
ACPI_TYPE_BUFFER, object->type);
ret = -EINVAL; ret = -EINVAL;
} }
@ -813,6 +789,23 @@ static int sony_nc_buffer_call(acpi_handle handle, char *name, u64 *value,
return ret; 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 { struct sony_nc_handles {
u16 cap[0x10]; u16 cap[0x10];
struct device_attribute devattr; struct device_attribute devattr;
@ -2295,7 +2288,12 @@ static void sony_nc_thermal_cleanup(struct platform_device *pd)
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static void sony_nc_thermal_resume(void) 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) if (status != th_handle->mode)
sony_nc_thermal_mode_set(th_handle->mode); sony_nc_thermal_mode_set(th_handle->mode);

View File

@ -318,6 +318,7 @@ static struct {
u32 uwb:1; u32 uwb:1;
u32 fan_ctrl_status_undef:1; u32 fan_ctrl_status_undef:1;
u32 second_fan:1; u32 second_fan:1;
u32 second_fan_ctl:1;
u32 beep_needs_two_args:1; u32 beep_needs_two_args:1;
u32 mixer_no_level_control:1; u32 mixer_no_level_control:1;
u32 battery_force_primary:1; u32 battery_force_primary:1;
@ -884,20 +885,11 @@ static ssize_t dispatch_proc_write(struct file *file,
if (!ibm || !ibm->write) if (!ibm || !ibm->write)
return -EINVAL; return -EINVAL;
if (count > PAGE_SIZE - 2)
return -EINVAL;
kernbuf = kmalloc(count + 2, GFP_KERNEL); kernbuf = strndup_user(userbuf, PAGE_SIZE);
if (!kernbuf) if (IS_ERR(kernbuf))
return -ENOMEM; return PTR_ERR(kernbuf);
if (copy_from_user(kernbuf, userbuf, count)) {
kfree(kernbuf);
return -EFAULT;
}
kernbuf[count] = 0;
strcat(kernbuf, ",");
ret = ibm->write(kernbuf); ret = ibm->write(kernbuf);
if (ret == 0) if (ret == 0)
ret = count; ret = count;
@ -915,23 +907,6 @@ static const struct proc_ops dispatch_proc_ops = {
.proc_write = dispatch_proc_write, .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) if (id >= TPACPI_RFK_SW_MAX)
return -ENODEV; return -ENODEV;
while ((cmd = next_cmd(&buf))) { while ((cmd = strsep(&buf, ","))) {
if (strlencmp(cmd, "enable") == 0) if (strlencmp(cmd, "enable") == 0)
status = TPACPI_RFK_RADIO_ON; status = TPACPI_RFK_RADIO_ON;
else if (strlencmp(cmd, "disable") == 0) else if (strlencmp(cmd, "disable") == 0)
@ -4305,7 +4280,7 @@ static int hotkey_write(char *buf)
mask = hotkey_user_mask; mask = hotkey_user_mask;
res = 0; res = 0;
while ((cmd = next_cmd(&buf))) { while ((cmd = strsep(&buf, ","))) {
if (strlencmp(cmd, "enable") == 0) { if (strlencmp(cmd, "enable") == 0) {
hotkey_enabledisable_warn(1); hotkey_enabledisable_warn(1);
} else if (strlencmp(cmd, "disable") == 0) { } else if (strlencmp(cmd, "disable") == 0) {
@ -5232,7 +5207,7 @@ static int video_write(char *buf)
enable = 0; enable = 0;
disable = 0; disable = 0;
while ((cmd = next_cmd(&buf))) { while ((cmd = strsep(&buf, ","))) {
if (strlencmp(cmd, "lcd_enable") == 0) { if (strlencmp(cmd, "lcd_enable") == 0) {
enable |= TP_ACPI_VIDEO_S_LCD; enable |= TP_ACPI_VIDEO_S_LCD;
} else if (strlencmp(cmd, "lcd_disable") == 0) { } 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) 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) 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) static int kbdlight_write(char *buf)
{ {
char *cmd; char *cmd;
int level = -1; int res, level = -EINVAL;
if (!tp_features.kbdlight) if (!tp_features.kbdlight)
return -ENODEV; return -ENODEV;
while ((cmd = next_cmd(&buf))) { while ((cmd = strsep(&buf, ","))) {
if (strlencmp(cmd, "0") == 0) res = kstrtoint(cmd, 10, &level);
level = 0; if (res < 0)
else if (strlencmp(cmd, "1") == 0) return res;
level = 1;
else if (strlencmp(cmd, "2") == 0)
level = 2;
else
return -EINVAL;
} }
if (level == -1) if (level >= 3 || level < 0)
return -EINVAL; return -EINVAL;
return kbdlight_set_level_and_update(level); return kbdlight_set_level_and_update(level);
@ -5657,7 +5626,7 @@ static int light_write(char *buf)
if (!tp_features.light) if (!tp_features.light)
return -ENODEV; return -ENODEV;
while ((cmd = next_cmd(&buf))) { while ((cmd = strsep(&buf, ","))) {
if (strlencmp(cmd, "on") == 0) { if (strlencmp(cmd, "on") == 0) {
newstatus = 1; newstatus = 1;
} else if (strlencmp(cmd, "off") == 0) { } else if (strlencmp(cmd, "off") == 0) {
@ -5742,7 +5711,7 @@ static int cmos_write(char *buf)
char *cmd; char *cmd;
int cmos_cmd, res; int cmos_cmd, res;
while ((cmd = next_cmd(&buf))) { while ((cmd = strsep(&buf, ","))) {
if (sscanf(cmd, "%u", &cmos_cmd) == 1 && if (sscanf(cmd, "%u", &cmos_cmd) == 1 &&
cmos_cmd >= 0 && cmos_cmd <= 21) { cmos_cmd >= 0 && cmos_cmd <= 21) {
/* cmos_cmd set */ /* cmos_cmd set */
@ -5948,20 +5917,14 @@ static void led_exit(void)
{ {
unsigned int i; unsigned int i;
for (i = 0; i < TPACPI_LED_NUMLEDS; i++) { for (i = 0; i < TPACPI_LED_NUMLEDS; i++)
if (tpacpi_leds[i].led_classdev.name) led_classdev_unregister(&tpacpi_leds[i].led_classdev);
led_classdev_unregister(&tpacpi_leds[i].led_classdev);
}
kfree(tpacpi_leds); kfree(tpacpi_leds);
} }
static int __init tpacpi_init_led(unsigned int led) static int __init tpacpi_init_led(unsigned int led)
{ {
int rc;
tpacpi_leds[led].led = led;
/* LEDs with no name don't get registered */ /* LEDs with no name don't get registered */
if (!tpacpi_led_names[led]) if (!tpacpi_led_names[led])
return 0; 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.brightness_set_blocking = &led_sysfs_set;
tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set; tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set;
if (led_supported == TPACPI_LED_570) if (led_supported == TPACPI_LED_570)
tpacpi_leds[led].led_classdev.brightness_get = tpacpi_leds[led].led_classdev.brightness_get = &led_sysfs_get;
&led_sysfs_get;
tpacpi_leds[led].led_classdev.name = tpacpi_led_names[led]; tpacpi_leds[led].led_classdev.name = tpacpi_led_names[led];
tpacpi_leds[led].led = led;
rc = led_classdev_register(&tpacpi_pdev->dev, return led_classdev_register(&tpacpi_pdev->dev, &tpacpi_leds[led].led_classdev);
&tpacpi_leds[led].led_classdev);
if (rc < 0)
tpacpi_leds[led].led_classdev.name = NULL;
return rc;
} }
static const struct tpacpi_quirk led_useful_qtable[] __initconst = { 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++) { for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
tpacpi_leds[i].led = -1; tpacpi_leds[i].led = -1;
if (!tpacpi_is_led_restricted(i) && if (!tpacpi_is_led_restricted(i) && test_bit(i, &useful_leds)) {
test_bit(i, &useful_leds)) {
rc = tpacpi_init_led(i); rc = tpacpi_init_led(i);
if (rc < 0) { if (rc < 0) {
led_exit(); led_exit();
@ -6143,12 +6100,14 @@ static int led_write(char *buf)
if (!led_supported) if (!led_supported)
return -ENODEV; return -ENODEV;
while ((cmd = next_cmd(&buf))) { while ((cmd = strsep(&buf, ","))) {
if (sscanf(cmd, "%d", &led) != 1) if (sscanf(cmd, "%d", &led) != 1)
return -EINVAL; return -EINVAL;
if (led < 0 || led > (TPACPI_LED_NUMLEDS - 1) || if (led < 0 || led > (TPACPI_LED_NUMLEDS - 1))
tpacpi_leds[led].led < 0) return -ENODEV;
if (tpacpi_leds[led].led < 0)
return -ENODEV; return -ENODEV;
if (strstr(cmd, "off")) { if (strstr(cmd, "off")) {
@ -6228,7 +6187,7 @@ static int beep_write(char *buf)
if (!beep_handle) if (!beep_handle)
return -ENODEV; return -ENODEV;
while ((cmd = next_cmd(&buf))) { while ((cmd = strsep(&buf, ","))) {
if (sscanf(cmd, "%u", &beep_cmd) == 1 && if (sscanf(cmd, "%u", &beep_cmd) == 1 &&
beep_cmd >= 0 && beep_cmd <= 17) { beep_cmd >= 0 && beep_cmd <= 17) {
/* beep_cmd set */ /* beep_cmd set */
@ -7116,7 +7075,7 @@ static int brightness_write(char *buf)
if (level < 0) if (level < 0)
return level; return level;
while ((cmd = next_cmd(&buf))) { while ((cmd = strsep(&buf, ","))) {
if (strlencmp(cmd, "up") == 0) { if (strlencmp(cmd, "up") == 0) {
if (level < bright_maxlvl) if (level < bright_maxlvl)
level++; level++;
@ -7868,7 +7827,7 @@ static int volume_write(char *buf)
new_level = s & TP_EC_AUDIO_LVL_MSK; new_level = s & TP_EC_AUDIO_LVL_MSK;
new_mute = s & TP_EC_AUDIO_MUTESW_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 (!tp_features.mixer_no_level_control) {
if (strlencmp(cmd, "up") == 0) { if (strlencmp(cmd, "up") == 0) {
if (new_mute) if (new_mute)
@ -8324,11 +8283,19 @@ static int fan_set_level(int level)
switch (fan_control_access_mode) { switch (fan_control_access_mode) {
case TPACPI_FAN_WR_ACPI_SFAN: case TPACPI_FAN_WR_ACPI_SFAN:
if (level >= 0 && level <= 7) { if ((level < 0) || (level > 7))
if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))
return -EIO;
} else
return -EINVAL; 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; break;
case TPACPI_FAN_WR_ACPI_FANS: case TPACPI_FAN_WR_ACPI_FANS:
@ -8345,6 +8312,15 @@ static int fan_set_level(int level)
else if (level & TP_EC_FAN_AUTO) else if (level & TP_EC_FAN_AUTO)
level |= 4; /* safety min speed 4 */ 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)) if (!acpi_ec_write(fan_status_offset, level))
return -EIO; return -EIO;
else else
@ -8763,6 +8739,7 @@ static const struct attribute_group fan_attr_group = {
#define TPACPI_FAN_Q1 0x0001 /* Unitialized HFSP */ #define TPACPI_FAN_Q1 0x0001 /* Unitialized HFSP */
#define TPACPI_FAN_2FAN 0x0002 /* EC 0x31 bit 0 selects fan2 */ #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 = { static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
TPACPI_QEC_IBM('1', 'Y', TPACPI_FAN_Q1), 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_IBM('7', '0', TPACPI_FAN_Q1),
TPACPI_QEC_LNV('7', 'M', TPACPI_FAN_2FAN), TPACPI_QEC_LNV('7', 'M', TPACPI_FAN_2FAN),
TPACPI_Q_LNV('N', '1', 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) 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; fan_watchdog_maxinterval = 0;
tp_features.fan_ctrl_status_undef = 0; tp_features.fan_ctrl_status_undef = 0;
tp_features.second_fan = 0; tp_features.second_fan = 0;
tp_features.second_fan_ctl = 0;
fan_control_desired_level = 7; fan_control_desired_level = 7;
if (tpacpi_is_ibm()) { if (tpacpi_is_ibm()) {
@ -8812,8 +8797,12 @@ static int __init fan_init(struct ibm_init_struct *iibm)
fan_quirk1_setup(); fan_quirk1_setup();
if (quirks & TPACPI_FAN_2FAN) { if (quirks & TPACPI_FAN_2FAN) {
tp_features.second_fan = 1; tp_features.second_fan = 1;
dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN, pr_info("secondary fan support enabled\n");
"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 { } else {
pr_err("ThinkPad ACPI EC access misbehaving, fan status and control unavailable\n"); 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; char *cmd;
int rc = 0; int rc = 0;
while (!rc && (cmd = next_cmd(&buf))) { while (!rc && (cmd = strsep(&buf, ","))) {
if (!((fan_control_commands & TPACPI_FAN_CMD_LEVEL) && if (!((fan_control_commands & TPACPI_FAN_CMD_LEVEL) &&
fan_write_cmd_level(cmd, &rc)) && fan_write_cmd_level(cmd, &rc)) &&
!((fan_control_commands & TPACPI_FAN_CMD_ENABLE) && !((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); mute_led_cdev[i].brightness = ledtrig_audio_get(i);
err = led_classdev_register(&tpacpi_pdev->dev, &mute_led_cdev[i]); err = led_classdev_register(&tpacpi_pdev->dev, &mute_led_cdev[i]);
if (err < 0) { if (err < 0) {
while (i--) { while (i--)
if (led_tables[i].state >= 0) led_classdev_unregister(&mute_led_cdev[i]);
led_classdev_unregister(&mute_led_cdev[i]);
}
return err; return err;
} }
} }
@ -9286,10 +9273,8 @@ static void mute_led_exit(void)
int i; int i;
for (i = 0; i < TPACPI_LED_MAX; i++) { for (i = 0; i < TPACPI_LED_MAX; i++) {
if (led_tables[i].state >= 0) { led_classdev_unregister(&mute_led_cdev[i]);
led_classdev_unregister(&mute_led_cdev[i]); tpacpi_led_set(i, false);
tpacpi_led_set(i, false);
}
} }
} }
@ -9786,19 +9771,18 @@ static int lcdshadow_read(struct seq_file *m)
static int lcdshadow_write(char *buf) static int lcdshadow_write(char *buf)
{ {
char *cmd; char *cmd;
int state = -1; int res, state = -EINVAL;
if (lcdshadow_state < 0) if (lcdshadow_state < 0)
return -ENODEV; return -ENODEV;
while ((cmd = next_cmd(&buf))) { while ((cmd = strsep(&buf, ","))) {
if (strlencmp(cmd, "0") == 0) res = kstrtoint(cmd, 10, &state);
state = 0; if (res < 0)
else if (strlencmp(cmd, "1") == 0) return res;
state = 1;
} }
if (state == -1) if (state >= 2 || state < 0)
return -EINVAL; return -EINVAL;
return lcdshadow_set(state); return lcdshadow_set(state);
@ -10314,10 +10298,9 @@ static int __init set_ibm_param(const char *val, const struct kernel_param *kp)
continue; continue;
if (strcmp(ibm->name, kp->name) == 0 && ibm->write) { 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; return -ENOSPC;
strcpy(ibms_init[i].param, val); strcpy(ibms_init[i].param, val);
strcat(ibms_init[i].param, ",");
return 0; return 0;
} }
} }

View File

@ -205,9 +205,6 @@ struct toshiba_acpi_dev {
unsigned int special_functions; unsigned int special_functions;
bool kbd_event_generated; bool kbd_event_generated;
bool kbd_led_registered;
bool illumination_led_registered;
bool eco_led_registered;
bool killswitch; bool killswitch;
}; };
@ -458,7 +455,6 @@ static void toshiba_illumination_available(struct toshiba_acpi_dev *dev)
acpi_status status; acpi_status status;
dev->illumination_supported = 0; dev->illumination_supported = 0;
dev->illumination_led_registered = false;
if (!sci_open(dev)) if (!sci_open(dev))
return; return;
@ -528,7 +524,6 @@ static void toshiba_kbd_illum_available(struct toshiba_acpi_dev *dev)
acpi_status status; acpi_status status;
dev->kbd_illum_supported = 0; dev->kbd_illum_supported = 0;
dev->kbd_led_registered = false;
dev->kbd_event_generated = false; dev->kbd_event_generated = false;
if (!sci_open(dev)) if (!sci_open(dev))
@ -673,7 +668,6 @@ static void toshiba_eco_mode_available(struct toshiba_acpi_dev *dev)
acpi_status status; acpi_status status;
dev->eco_supported = 0; dev->eco_supported = 0;
dev->eco_led_registered = false;
status = tci_raw(dev, in, out); status = tci_raw(dev, in, out);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
@ -2993,14 +2987,9 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev)
backlight_device_unregister(dev->backlight_dev); backlight_device_unregister(dev->backlight_dev);
if (dev->illumination_led_registered) led_classdev_unregister(&dev->led_dev);
led_classdev_unregister(&dev->led_dev); led_classdev_unregister(&dev->kbd_led);
led_classdev_unregister(&dev->eco_led);
if (dev->kbd_led_registered)
led_classdev_unregister(&dev->kbd_led);
if (dev->eco_led_registered)
led_classdev_unregister(&dev->eco_led);
if (dev->wwan_rfk) { if (dev->wwan_rfk) {
rfkill_unregister(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.max_brightness = 1;
dev->led_dev.brightness_set = toshiba_illumination_set; dev->led_dev.brightness_set = toshiba_illumination_set;
dev->led_dev.brightness_get = toshiba_illumination_get; dev->led_dev.brightness_get = toshiba_illumination_get;
if (!led_classdev_register(&acpi_dev->dev, &dev->led_dev)) led_classdev_register(&acpi_dev->dev, &dev->led_dev);
dev->illumination_led_registered = true;
} }
toshiba_eco_mode_available(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.max_brightness = 1;
dev->eco_led.brightness_set = toshiba_eco_mode_set_status; dev->eco_led.brightness_set = toshiba_eco_mode_set_status;
dev->eco_led.brightness_get = toshiba_eco_mode_get_status; dev->eco_led.brightness_get = toshiba_eco_mode_get_status;
if (!led_classdev_register(&dev->acpi_dev->dev, &dev->eco_led)) led_classdev_register(&dev->acpi_dev->dev, &dev->eco_led);
dev->eco_led_registered = true;
} }
toshiba_kbd_illum_available(dev); 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.max_brightness = 1;
dev->kbd_led.brightness_set = toshiba_kbd_backlight_set; dev->kbd_led.brightness_set = toshiba_kbd_backlight_set;
dev->kbd_led.brightness_get = toshiba_kbd_backlight_get; dev->kbd_led.brightness_get = toshiba_kbd_backlight_get;
if (!led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led)) led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led);
dev->kbd_led_registered = true;
} }
ret = toshiba_touchpad_get(dev, &dummy); ret = toshiba_touchpad_get(dev, &dummy);

View File

@ -373,6 +373,23 @@ static const struct ts_dmi_data jumper_ezpad_mini3_data = {
.properties = jumper_ezpad_mini3_props, .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[] = { static const struct property_entry myria_my8307_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1720), PROPERTY_ENTRY_U32("touchscreen-size-x", 1720),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), 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, .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[] = { static const struct property_entry onda_v891w_v1_props[] = {
PROPERTY_ENTRY_U32("touchscreen-min-x", 46), PROPERTY_ENTRY_U32("touchscreen-min-x", 46),
PROPERTY_ENTRY_U32("touchscreen-min-y", 8), PROPERTY_ENTRY_U32("touchscreen-min-y", 8),
@ -588,6 +623,22 @@ static const struct ts_dmi_data schneider_sct101ctm_data = {
.properties = schneider_sct101ctm_props, .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[] = { static const struct property_entry teclast_x3_plus_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1980), PROPERTY_ENTRY_U32("touchscreen-size-x", 1980),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1500), 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[] = { 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-size-y", 1280),
PROPERTY_ENTRY_U32("touchscreen-inverted-y", 1), PROPERTY_ENTRY_U32("touchscreen-inverted-y", 1),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-surftab-twin-10-1-st10432-8.fw"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-surftab-twin-10-1-st10432-8.fw"),
PROPERTY_ENTRY_U32("silead,max-fingers", 10), 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, .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 */ /* NOTE: Please keep this table sorted alphabetically */
const struct dmi_system_id touchscreen_dmi_table[] = { 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"), 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 */ /* Myria MY8307 */
.driver_data = (void *)&myria_my8307_data, .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") 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 */ /* ONDA V891w revision P891WBEBV1B00 aka v1 */
.driver_data = (void *)&onda_v891w_v1_data, .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"), 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 */ /* Teclast X3 Plus */
.driver_data = (void *)&teclast_x3_plus_data, .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"), 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 */ /* Yours Y8W81, same case and touchscreen as Chuwi Vi8 */
.driver_data = (void *)&chuwi_vi8_data, .driver_data = (void *)&chuwi_vi8_data,
@ -1114,7 +1223,7 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Y8W81"), DMI_MATCH(DMI_PRODUCT_NAME, "Y8W81"),
}, },
}, },
{ }, { }
}; };
static const struct ts_dmi_data *ts_data; static const struct ts_dmi_data *ts_data;

View File

@ -111,11 +111,11 @@ static struct platform_driver acpi_wmi_driver = {
static bool find_guid(const char *guid_string, struct wmi_block **out) 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 wmi_block *wblock;
struct guid_block *block; struct guid_block *block;
if (uuid_le_to_bin(guid_string, &guid_input)) if (guid_parse(guid_string, &guid_input))
return false; return false;
list_for_each_entry(wblock, &wmi_block_list, list) { 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) struct wmi_driver *wdriver)
{ {
const struct wmi_device_id *id; const struct wmi_device_id *id;
uuid_le guid_input; guid_t guid_input;
if (wblock == NULL || wdriver == NULL) if (wblock == NULL || wdriver == NULL)
return NULL; return NULL;
@ -143,7 +143,7 @@ static const void *find_guid_context(struct wmi_block *wblock,
id = wdriver->id_table; id = wdriver->id_table;
while (*id->guid_string) { while (*id->guid_string) {
if (uuid_le_to_bin(id->guid_string, &guid_input)) if (guid_parse(id->guid_string, &guid_input))
continue; continue;
if (!memcmp(wblock->gblock.guid, &guid_input, 16)) if (!memcmp(wblock->gblock.guid, &guid_input, 16))
return id->context; 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 * set_required_buffer_size - Sets the buffer size needed for performing IOCTL
* @wdev: A wmi bus device from a driver * @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 * 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 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
* @instance: Instance index * @instance: Instance index
* @method_id: Method ID to call * @method_id: Method ID to call
* &in: Buffer containing input for the method call * @in: Buffer containing input for the method call
* &out: Empty buffer to return the method results * @out: Empty buffer to return the method results
* *
* Call an ACPI-WMI method * Call an ACPI-WMI method
*/ */
@ -244,8 +244,8 @@ EXPORT_SYMBOL_GPL(wmi_evaluate_method);
* @wdev: A wmi bus device from a driver * @wdev: A wmi bus device from a driver
* @instance: Instance index * @instance: Instance index
* @method_id: Method ID to call * @method_id: Method ID to call
* &in: Buffer containing input for the method call * @in: Buffer containing input for the method call
* &out: Empty buffer to return the method results * @out: Empty buffer to return the method results
* *
* Call an ACPI-WMI method * 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) * wmi_query_block - Return contents of a WMI block (deprecated)
* @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
* @instance: Instance index * @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 * 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 * wmi_set_block - Write to a WMI block
* @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
* @instance: Instance index * @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 * 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 * 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 * @handler: Function to handle notifications
* @data: Data to be returned to handler when event is fired * @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; struct wmi_block *block;
acpi_status status = AE_NOT_EXIST; acpi_status status = AE_NOT_EXIST;
uuid_le guid_input; guid_t guid_input;
if (!guid || !handler) if (!guid || !handler)
return AE_BAD_PARAMETER; return AE_BAD_PARAMETER;
if (uuid_le_to_bin(guid, &guid_input)) if (guid_parse(guid, &guid_input))
return AE_BAD_PARAMETER; return AE_BAD_PARAMETER;
list_for_each_entry(block, &wmi_block_list, list) { 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 * 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. * 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; struct wmi_block *block;
acpi_status status = AE_NOT_EXIST; acpi_status status = AE_NOT_EXIST;
uuid_le guid_input; guid_t guid_input;
if (!guid) if (!guid)
return AE_BAD_PARAMETER; return AE_BAD_PARAMETER;
if (uuid_le_to_bin(guid, &guid_input)) if (guid_parse(guid, &guid_input))
return AE_BAD_PARAMETER; return AE_BAD_PARAMETER;
list_for_each_entry(block, &wmi_block_list, list) { 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; return 0;
while (*id->guid_string) { 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; continue;
if (!memcmp(&driver_guid, wblock->gblock.guid, 16)) if (!memcmp(&driver_guid, wblock->gblock.guid, 16))
return 1; return 1;
@ -1116,8 +1118,7 @@ static void wmi_free_devices(struct acpi_device *device)
} }
} }
static bool guid_already_parsed(struct acpi_device *device, static bool guid_already_parsed(struct acpi_device *device, const u8 *guid)
const u8 *guid)
{ {
struct wmi_block *wblock; 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); wblock->handler(event, wblock->handler_data);
} }
if (debug_event) { if (debug_event)
pr_info("DEBUG Event GUID: %pUL\n", pr_info("DEBUG Event GUID: %pUL\n", wblock->gblock.guid);
wblock->gblock.guid);
}
acpi_bus_generate_netlink_event( acpi_bus_generate_netlink_event(
wblock->acpi_device->pnp.device_class, wblock->acpi_device->pnp.device_class,

View File

@ -11,7 +11,7 @@ config TYPEC_MUX_PI3USB30532
config TYPEC_MUX_INTEL_PMC config TYPEC_MUX_INTEL_PMC
tristate "Intel PMC mux control" tristate "Intel PMC mux control"
depends on INTEL_PMC_IPC depends on INTEL_SCU_IPC
select USB_ROLE_SWITCH select USB_ROLE_SWITCH
help help
Driver for USB muxes controlled by Intel PMC FW. Intel PMC FW can Driver for USB muxes controlled by Intel PMC FW. Intel PMC FW can

View File

@ -15,7 +15,7 @@
#include <linux/usb/typec_dp.h> #include <linux/usb/typec_dp.h>
#include <linux/usb/typec_tbt.h> #include <linux/usb/typec_tbt.h>
#include <asm/intel_pmc_ipc.h> #include <asm/intel_scu_ipc.h>
#define PMC_USBC_CMD 0xa7 #define PMC_USBC_CMD 0xa7
@ -97,6 +97,7 @@ struct pmc_usb_port {
struct pmc_usb { struct pmc_usb {
u8 num_ports; u8 num_ports;
struct device *dev; struct device *dev;
struct intel_scu_ipc_dev *ipc;
struct pmc_usb_port *port; 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. * Error bit will always be 0 with the USBC command.
* Status can be checked from the response message. * Status can be checked from the response message.
*/ */
intel_pmc_ipc_command(PMC_USBC_CMD, 0, msg, len, intel_scu_ipc_dev_command(port->pmc->ipc, PMC_USBC_CMD, 0, msg, len,
(void *)response, 1); response, sizeof(response));
if (response[2]) { if (response[2]) {
if (response[2] & BIT(1)) if (response[2] & BIT(1))
return -EIO; return -EIO;
@ -374,6 +374,10 @@ static int pmc_usb_probe(struct platform_device *pdev)
if (!pmc->port) if (!pmc->port)
return -ENOMEM; return -ENOMEM;
pmc->ipc = devm_intel_scu_ipc_dev_get(&pdev->dev);
if (!pmc->ipc)
return -ENODEV;
pmc->dev = &pdev->dev; pmc->dev = &pdev->dev;
/* /*

View File

@ -41,8 +41,8 @@ config TYPEC_FUSB302
config TYPEC_WCOVE config TYPEC_WCOVE
tristate "Intel WhiskeyCove PMIC USB Type-C PHY driver" tristate "Intel WhiskeyCove PMIC USB Type-C PHY driver"
depends on ACPI depends on ACPI
depends on MFD_INTEL_PMC_BXT
depends on INTEL_SOC_PMIC depends on INTEL_SOC_PMIC
depends on INTEL_PMC_IPC
depends on BXT_WC_PMIC_OPREGION depends on BXT_WC_PMIC_OPREGION
help help
This driver adds support for USB Type-C on Intel Broxton platforms This driver adds support for USB Type-C on Intel Broxton platforms

View File

@ -1217,6 +1217,7 @@ config ITCO_WDT
depends on (X86 || IA64) && PCI depends on (X86 || IA64) && PCI
select WATCHDOG_CORE select WATCHDOG_CORE
depends on I2C || I2C=n depends on I2C || I2C=n
depends on MFD_INTEL_PMC_BXT || !MFD_INTEL_PMC_BXT
select LPC_ICH if !EXPERT select LPC_ICH if !EXPERT
select I2C_I801 if !EXPERT && I2C select I2C_I801 if !EXPERT && I2C
---help--- ---help---

View File

@ -64,6 +64,7 @@
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */ #include <linux/uaccess.h> /* For copy_to_user/put_user/... */
#include <linux/io.h> /* For inb/outb/... */ #include <linux/io.h> /* For inb/outb/... */
#include <linux/platform_data/itco_wdt.h> #include <linux/platform_data/itco_wdt.h>
#include <linux/mfd/intel_pmc_bxt.h>
#include "iTCO_vendor.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; return val != newval ? -EIO : 0;
} }
static void iTCO_wdt_no_reboot_bit_setup(struct iTCO_wdt_private *p, static int update_no_reboot_bit_pmc(void *priv, bool set)
struct itco_wdt_platform_data *pdata)
{ {
if (pdata->update_no_reboot_bit) { struct intel_pmc_dev *pmc = priv;
p->update_no_reboot_bit = pdata->update_no_reboot_bit; u32 bits = PMC_CFG_NO_REBOOT_EN;
p->no_reboot_priv = pdata->no_reboot_priv; 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; return;
} }
@ -478,14 +491,14 @@ static int iTCO_wdt_probe(struct platform_device *pdev)
return -ENODEV; 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 * Get the Memory-Mapped GCS or PMC register, we need it for the
* NO_REBOOT flag (TCO v2 and v3). * NO_REBOOT flag (TCO v2 and v3).
*/ */
if (p->iTCO_version >= 2 && p->iTCO_version < 6 && 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, p->gcs_pmc_res = platform_get_resource(pdev,
IORESOURCE_MEM, IORESOURCE_MEM,
ICH_RES_MEM_GCS_PMC); ICH_RES_MEM_GCS_PMC);

View File

@ -33,14 +33,24 @@ enum {
SCU_WATCHDOG_KEEPALIVE, 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) 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 ret, in_size;
int timeout = wd->timeout; int timeout = wd->timeout;
struct ipc_wd_start { struct ipc_wd_start {
@ -49,38 +59,41 @@ static int wdt_start(struct watchdog_device *wd)
} ipc_wd_start = { timeout - MID_WDT_PRETIMEOUT, timeout }; } ipc_wd_start = { timeout - MID_WDT_PRETIMEOUT, timeout };
/* /*
* SCU expects the input size for watchdog IPC to * SCU expects the input size for watchdog IPC to be 2 which is the
* be based on 4 bytes * 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); 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) if (ret)
dev_crit(dev, "error starting watchdog: %d\n", ret); dev_crit(mid->dev, "error starting watchdog: %d\n", ret);
return ret; return ret;
} }
static int wdt_ping(struct watchdog_device *wd) 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; int ret;
ret = wdt_command(SCU_WATCHDOG_KEEPALIVE, NULL, 0); ret = wdt_command(mid, SCU_WATCHDOG_KEEPALIVE, NULL, 0, 0);
if (ret) if (ret)
dev_crit(dev, "Error executing keepalive: %d\n", ret); dev_crit(mid->dev, "Error executing keepalive: %d\n", ret);
return ret; return ret;
} }
static int wdt_stop(struct watchdog_device *wd) 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; int ret;
ret = wdt_command(SCU_WATCHDOG_STOP, NULL, 0); ret = wdt_command(mid, SCU_WATCHDOG_STOP, NULL, 0, 0);
if (ret) if (ret)
dev_crit(dev, "Error stopping watchdog: %d\n", ret); dev_crit(mid->dev, "Error stopping watchdog: %d\n", ret);
return ret; return ret;
} }
@ -110,6 +123,7 @@ static int mid_wdt_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct watchdog_device *wdt_dev; struct watchdog_device *wdt_dev;
struct intel_mid_wdt_pdata *pdata = dev->platform_data; struct intel_mid_wdt_pdata *pdata = dev->platform_data;
struct mid_wdt *mid;
int ret; int ret;
if (!pdata) { if (!pdata) {
@ -123,10 +137,13 @@ static int mid_wdt_probe(struct platform_device *pdev)
return ret; return ret;
} }
wdt_dev = devm_kzalloc(dev, sizeof(*wdt_dev), GFP_KERNEL); mid = devm_kzalloc(dev, sizeof(*mid), GFP_KERNEL);
if (!wdt_dev) if (!mid)
return -ENOMEM; return -ENOMEM;
mid->dev = dev;
wdt_dev = &mid->wd;
wdt_dev->info = &mid_wdt_info; wdt_dev->info = &mid_wdt_info;
wdt_dev->ops = &mid_wdt_ops; wdt_dev->ops = &mid_wdt_ops;
wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN; 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; wdt_dev->parent = dev;
watchdog_set_nowayout(wdt_dev, WATCHDOG_NOWAYOUT); 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, ret = devm_request_irq(dev, pdata->irq, mid_wdt_irq,
IRQF_SHARED | IRQF_NO_SUSPEND, "watchdog", IRQF_SHARED | IRQF_NO_SUSPEND, "watchdog",
@ -145,6 +162,10 @@ static int mid_wdt_probe(struct platform_device *pdev)
return ret; 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 * The firmware followed by U-Boot leaves the watchdog running
* with the default threshold which may vary. When we get here * with the default threshold which may vary. When we get here

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

View File

@ -13,6 +13,20 @@
#include <linux/regmap.h> #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 { struct intel_soc_pmic {
int irq; int irq;
struct regmap *regmap; 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_chgr;
struct regmap_irq_chip_data *irq_chip_data_crit; struct regmap_irq_chip_data *irq_chip_data_crit;
struct device *dev; struct device *dev;
struct intel_scu_ipc_dev *scu;
}; };
int intel_soc_pmic_exec_mipi_pmic_seq_element(u16 i2c_address, u32 reg_address, int intel_soc_pmic_exec_mipi_pmic_seq_element(u16 i2c_address, u32 reg_address,

View File

@ -12,13 +12,16 @@
#define ICH_RES_MEM_OFF 2 #define ICH_RES_MEM_OFF 2
#define ICH_RES_MEM_GCS_PMC 0 #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 { struct itco_wdt_platform_data {
char name[32]; char name[32];
unsigned int version; unsigned int version;
/* private data to be passed to update_no_reboot_bit API */ bool no_reboot_use_pmc;
void *no_reboot_priv;
/* pointer for platform specific no reboot update function */
int (*update_no_reboot_bit)(void *priv, bool set);
}; };
#endif /* _ITCO_WDT_H_ */ #endif /* _ITCO_WDT_H_ */

View File

@ -85,6 +85,9 @@
/* Maximum charging percentage */ /* Maximum charging percentage */
#define ASUS_WMI_DEVID_RSOC 0x00120057 #define ASUS_WMI_DEVID_RSOC 0x00120057
/* Keyboard dock */
#define ASUS_WMI_DEVID_KBD_DOCK 0x00120063
/* DSTS masks */ /* DSTS masks */
#define ASUS_WMI_DSTS_STATUS_BIT 0x00000001 #define ASUS_WMI_DSTS_STATUS_BIT 0x00000001
#define ASUS_WMI_DSTS_UNKNOWN_BIT 0x00000002 #define ASUS_WMI_DSTS_UNKNOWN_BIT 0x00000002

View File

@ -440,6 +440,9 @@ software_node_find_by_name(const struct software_node *parent,
int software_node_register_nodes(const struct software_node *nodes); int software_node_register_nodes(const struct software_node *nodes);
void software_node_unregister_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_register(const struct software_node *node);
int software_node_notify(struct device *dev, unsigned long action); int software_node_notify(struct device *dev, unsigned long action);

View File

@ -15,7 +15,7 @@ struct process_cmd_struct {
int arg; int arg;
}; };
static const char *version_str = "v1.3"; static const char *version_str = "v1.4";
static const int supported_api_ver = 1; static const int supported_api_ver = 1;
static struct isst_if_platform_info isst_platform_info; static struct isst_if_platform_info isst_platform_info;
static char *progname; static char *progname;
@ -25,7 +25,7 @@ static FILE *outf;
static int cpu_model; static int cpu_model;
static int cpu_stepping; static int cpu_stepping;
#define MAX_CPUS_IN_ONE_REQ 64 #define MAX_CPUS_IN_ONE_REQ 256
static short max_target_cpus; static short max_target_cpus;
static unsigned short target_cpus[MAX_CPUS_IN_ONE_REQ]; 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); pkg_id = get_physical_package_id(cpu);
for (i = 0; i < 64; ++i) { for (i = 0; i < 64; ++i) {
if (core_mask & BIT(i)) { if (core_mask & BIT_ULL(i)) {
int j; int j;
for (j = 0; j < topo_max_cpus; ++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]; ctdp_level = &clx_n_pkg_dev.ctdp_level[0];
pbf_info = &ctdp_level->pbf_info; pbf_info = &ctdp_level->pbf_info;
clx_n_pkg_dev.processed = 1;
isst_ctdp_display_information(cpu, outf, tdp_level, &clx_n_pkg_dev); isst_ctdp_display_information(cpu, outf, tdp_level, &clx_n_pkg_dev);
free_cpu_set(ctdp_level->core_cpumask); free_cpu_set(ctdp_level->core_cpumask);
free_cpu_set(pbf_info->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, static void set_pbf_for_cpu(int cpu, void *arg1, void *arg2, void *arg3,
void *arg4) void *arg4)
{ {
struct isst_pkg_ctdp_level_info ctdp_level;
struct isst_pkg_ctdp pkg_dev;
int ret; int ret;
int status = *(int *)arg4; 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; 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) { if (auto_mode && status) {
ret = set_pbf_core_power(cpu); ret = set_pbf_core_power(cpu);
if (ret) 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, static void set_fact_for_cpu(int cpu, void *arg1, void *arg2, void *arg3,
void *arg4) void *arg4)
{ {
struct isst_pkg_ctdp_level_info ctdp_level;
struct isst_pkg_ctdp pkg_dev;
int ret; int ret;
int status = *(int *)arg4; 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); ret = isst_pm_qos_config(cpu, 1, 1);
if (ret) if (ret)
goto disp_results; goto disp_results;

View File

@ -912,16 +912,16 @@ int isst_pm_qos_config(int cpu, int enable_clos, int priority_type)
return ret; return ret;
if (ctdp_level.fact_enabled) { 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; return -EINVAL;
} }
ret = isst_write_pm_config(cpu, 0); ret = isst_write_pm_config(cpu, 0);
if (ret) 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 { } else {
ret = isst_write_pm_config(cpu, 1); ret = isst_write_pm_config(cpu, 1);
if (ret) 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, ret = isst_send_mbox_command(cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0, 0,

View File

@ -316,21 +316,31 @@ void isst_ctdp_display_core_info(int cpu, FILE *outf, char *prefix,
{ {
char header[256]; char header[256];
char value[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) if (str0 && !val)
snprintf(value, sizeof(value), "%s", str0); snprintf(value, sizeof(value), "%s", str0);
else if (str1 && val) else if (str1 && val)
snprintf(value, sizeof(value), "%s", str1); snprintf(value, sizeof(value), "%s", str1);
else else
snprintf(value, sizeof(value), "%u", val); 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); 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, _isst_pbf_display_information(cpu, outf,
tdp_level, tdp_level,
&ctdp_level->pbf_info, &ctdp_level->pbf_info,
level + 1); level + 2);
continue; continue;
} }

View File

@ -29,6 +29,7 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#define BIT(x) (1 << (x)) #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(h, l) (((~0UL) << (l)) & (~0UL >> (sizeof(long) * 8 - 1 - (h))))
#define GENMASK_ULL(h, l) \ #define GENMASK_ULL(h, l) \
(((~0ULL) << (l)) & (~0ULL >> (sizeof(long long) * 8 - 1 - (h)))) (((~0ULL) << (l)) & (~0ULL >> (sizeof(long long) * 8 - 1 - (h))))