Commit 088e0c18 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'platform-drivers-x86-v6.4-1' of...

Merge tag 'platform-drivers-x86-v6.4-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86

Pull x86 platform driver updates from Hans de Goede:

 -  AMD PMC and PMF drivers:
    - Numerous bugfixes

 -  Intel Speed Select Technology (ISST):
    - TPMI (Topology Aware Register and PM Capsule Interface) support
      for ISST support on upcoming processor models
    - Various other improvements / new hw support
    - tools/intel-speed-select: TPMI support + other improvements

 -  Intel In Field Scan (IFS):
    - Add Array Bist test support

 -  New drivers:
    - intel_bytcrc_pwrsrc Crystal Cove PMIC pwrsrc / reset-reason driver
    - lenovo-ymc Yoga Mode Control driver for reporting SW_TABLET_MODE
    - msi-ec Driver for MSI laptop EC features like battery charging limits

 -  apple-gmux:
    - Support for new MMIO based models (T2 Macs)
    - Honor acpi_backlight= auto-detect-code + kernel cmdline option
      to switch between gmux and apple_bl backlight drivers and remove
      own custom handling for this

 -  x86-android-tablets: Refactor / cleanup + new hw support

 -  Miscellaneous other cleanups / fixes

* tag 'platform-drivers-x86-v6.4-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (178 commits)
  platform/x86: x86-android-tablets: Add accelerometer support for Yoga Tablet 2 1050/830 series
  platform/x86: x86-android-tablets: Add "yogabook-touch-kbd-digitizer-switch" pdev for Lenovo Yoga Book
  platform/x86: x86-android-tablets: Add Wacom digitizer info for Lenovo Yoga Book
  platform/x86: x86-android-tablets: Update Yoga Book HiDeep touchscreen comment
  platform/x86: thinkpad_acpi: Fix Embedded Controller access on X380 Yoga
  platform/x86/intel/sdsi: Change mailbox timeout
  platform/x86/intel/pmt: Ignore uninitialized entries
  platform/x86: amd: pmc: provide user message where s0ix is not supported
  platform/x86/amd: pmc: Fix memory leak in amd_pmc_stb_debugfs_open_v2()
  mlxbf-bootctl: Add sysfs file for BlueField boot fifo
  platform/x86: amd: pmc: Remove __maybe_unused from amd_pmc_suspend_handler()
  platform/x86/intel/pmc/mtl: Put GNA/IPU/VPU devices in D3
  platform/x86/amd: pmc: Move out of BIOS SMN pair for STB init
  platform/x86/amd: pmc: Utilize SMN index 0 for driver probe
  platform/x86/amd: pmc: Move idlemask check into `amd_pmc_idlemask_read`
  platform/x86/amd: pmc: Don't dump data after resume from s0i3 on picasso
  platform/x86/amd: pmc: Hide SMU version and program attributes for Picasso
  platform/x86/amd: pmc: Don't try to read SMU version on Picasso
  platform/x86/amd/pmf: Move out of BIOS SMN pair for driver probe
  platform/x86: intel-uncore-freq: Add client processors
  ...
parents 07d971ab e578c943
Device instance to test mapping
intel_ifs_0 -> Scan Test
intel_ifs_1 -> Array BIST test
What: /sys/devices/virtual/misc/intel_ifs_<N>/run_test What: /sys/devices/virtual/misc/intel_ifs_<N>/run_test
Date: Nov 16 2022 Date: Nov 16 2022
KernelVersion: 6.2 KernelVersion: 6.2
...@@ -8,6 +12,7 @@ Description: Write <cpu#> to trigger IFS test for one online core. ...@@ -8,6 +12,7 @@ Description: Write <cpu#> to trigger IFS test for one online core.
completes the test for the core containing that thread. completes the test for the core containing that thread.
Example: to test the core containing cpu5: echo 5 > Example: to test the core containing cpu5: echo 5 >
/sys/devices/virtual/misc/intel_ifs_<N>/run_test /sys/devices/virtual/misc/intel_ifs_<N>/run_test
Devices: all
What: /sys/devices/virtual/misc/intel_ifs_<N>/status What: /sys/devices/virtual/misc/intel_ifs_<N>/status
Date: Nov 16 2022 Date: Nov 16 2022
...@@ -15,21 +20,25 @@ KernelVersion: 6.2 ...@@ -15,21 +20,25 @@ KernelVersion: 6.2
Contact: "Jithu Joseph" <jithu.joseph@intel.com> Contact: "Jithu Joseph" <jithu.joseph@intel.com>
Description: The status of the last test. It can be one of "pass", "fail" Description: The status of the last test. It can be one of "pass", "fail"
or "untested". or "untested".
Devices: all
What: /sys/devices/virtual/misc/intel_ifs_<N>/details What: /sys/devices/virtual/misc/intel_ifs_<N>/details
Date: Nov 16 2022 Date: Nov 16 2022
KernelVersion: 6.2 KernelVersion: 6.2
Contact: "Jithu Joseph" <jithu.joseph@intel.com> Contact: "Jithu Joseph" <jithu.joseph@intel.com>
Description: Additional information regarding the last test. The details file reports Description: Additional information regarding the last test. The details file reports
the hex value of the SCAN_STATUS MSR. Note that the error_code field the hex value of the STATUS MSR for this test. Note that the error_code field
may contain driver defined software code not defined in the Intel SDM. may contain driver defined software code not defined in the Intel SDM.
Devices: all
What: /sys/devices/virtual/misc/intel_ifs_<N>/image_version What: /sys/devices/virtual/misc/intel_ifs_<N>/image_version
Date: Nov 16 2022 Date: Nov 16 2022
KernelVersion: 6.2 KernelVersion: 6.2
Contact: "Jithu Joseph" <jithu.joseph@intel.com> Contact: "Jithu Joseph" <jithu.joseph@intel.com>
Description: Version (hexadecimal) of loaded IFS binary image. If no scan image Description: Version (hexadecimal) of loaded IFS test image. If no test image
is loaded reports "none". is loaded reports "none". Only present for device instances where a test image
is applicable.
Devices: intel_ifs_0
What: /sys/devices/virtual/misc/intel_ifs_<N>/current_batch What: /sys/devices/virtual/misc/intel_ifs_<N>/current_batch
Date: Nov 16 2022 Date: Nov 16 2022
...@@ -39,3 +48,5 @@ Description: Write a number less than or equal to 0xff to load an IFS test image ...@@ -39,3 +48,5 @@ Description: Write a number less than or equal to 0xff to load an IFS test image
The number written treated as the 2 digit suffix in the following file name: The number written treated as the 2 digit suffix in the following file name:
/lib/firmware/intel/ifs_<N>/ff-mm-ss-02x.scan /lib/firmware/intel/ifs_<N>/ff-mm-ss-02x.scan
Reading the file will provide the suffix of the currently loaded IFS test image. Reading the file will provide the suffix of the currently loaded IFS test image.
This file is present only for device instances where a test image is applicable.
Devices: intel_ifs_0
...@@ -68,3 +68,10 @@ Description: ...@@ -68,3 +68,10 @@ Description:
Wasted burnt and invalid Wasted burnt and invalid
Invalid not burnt but marked as valid (error state). Invalid not burnt but marked as valid (error state).
======= =============================================== ======= ===============================================
What: /sys/bus/platform/devices/MLNXBF04:00/bootfifo
Date: Apr 2023
KernelVersion: 6.4
Contact: "Liming Sun <limings@nvidia.com>"
Description:
The file used to access the BlueField boot fifo.
...@@ -14148,6 +14148,13 @@ S: Odd Fixes ...@@ -14148,6 +14148,13 @@ S: Odd Fixes
F: Documentation/devicetree/bindings/net/ieee802154/mrf24j40.txt F: Documentation/devicetree/bindings/net/ieee802154/mrf24j40.txt
F: drivers/net/ieee802154/mrf24j40.c F: drivers/net/ieee802154/mrf24j40.c
MSI EC DRIVER
M: Nikita Kravets <teackot@gmail.com>
L: platform-driver-x86@vger.kernel.org
S: Maintained
W: https://github.com/BeardOverflow/msi-ec
F: drivers/platform/x86/msi-ec.*
MSI LAPTOP SUPPORT MSI LAPTOP SUPPORT
M: "Lee, Chun-Yi" <jlee@suse.com> M: "Lee, Chun-Yi" <jlee@suse.com>
L: platform-driver-x86@vger.kernel.org L: platform-driver-x86@vger.kernel.org
...@@ -16330,12 +16337,6 @@ S: Maintained ...@@ -16330,12 +16337,6 @@ S: Maintained
F: crypto/pcrypt.c F: crypto/pcrypt.c
F: include/crypto/pcrypt.h F: include/crypto/pcrypt.h
PEAQ WMI HOTKEYS DRIVER
M: Hans de Goede <hdegoede@redhat.com>
L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/peaq-wmi.c
PECI HARDWARE MONITORING DRIVERS PECI HARDWARE MONITORING DRIVERS
M: Iwona Winiarska <iwona.winiarska@intel.com> M: Iwona Winiarska <iwona.winiarska@intel.com>
L: linux-hwmon@vger.kernel.org L: linux-hwmon@vger.kernel.org
...@@ -22725,7 +22726,7 @@ M: Hans de Goede <hdegoede@redhat.com> ...@@ -22725,7 +22726,7 @@ M: Hans de Goede <hdegoede@redhat.com>
L: platform-driver-x86@vger.kernel.org L: platform-driver-x86@vger.kernel.org
S: Maintained S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git
F: drivers/platform/x86/x86-android-tablets.c F: drivers/platform/x86/x86-android-tablets/
X86 PLATFORM DRIVERS X86 PLATFORM DRIVERS
M: Hans de Goede <hdegoede@redhat.com> M: Hans de Goede <hdegoede@redhat.com>
......
...@@ -206,6 +206,8 @@ ...@@ -206,6 +206,8 @@
/* Abbreviated from Intel SDM name IA32_INTEGRITY_CAPABILITIES */ /* Abbreviated from Intel SDM name IA32_INTEGRITY_CAPABILITIES */
#define MSR_INTEGRITY_CAPS 0x000002d9 #define MSR_INTEGRITY_CAPS 0x000002d9
#define MSR_INTEGRITY_CAPS_ARRAY_BIST_BIT 2
#define MSR_INTEGRITY_CAPS_ARRAY_BIST BIT(MSR_INTEGRITY_CAPS_ARRAY_BIST_BIT)
#define MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT 4 #define MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT 4
#define MSR_INTEGRITY_CAPS_PERIODIC_BIST BIT(MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT) #define MSR_INTEGRITY_CAPS_PERIODIC_BIST BIT(MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT)
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/arm-smccc.h> #include <linux/arm-smccc.h>
#include <linux/delay.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
...@@ -44,6 +45,10 @@ static const char * const mlxbf_bootctl_lifecycle_states[] = { ...@@ -44,6 +45,10 @@ static const char * const mlxbf_bootctl_lifecycle_states[] = {
[3] = "RMA", [3] = "RMA",
}; };
/* Mapped pointer for RSH_BOOT_FIFO_DATA and RSH_BOOT_FIFO_COUNT register. */
static void __iomem *mlxbf_rsh_boot_data;
static void __iomem *mlxbf_rsh_boot_cnt;
/* ARM SMC call which is atomic and no need for lock. */ /* ARM SMC call which is atomic and no need for lock. */
static int mlxbf_bootctl_smc(unsigned int smc_op, int smc_arg) static int mlxbf_bootctl_smc(unsigned int smc_op, int smc_arg)
{ {
...@@ -244,11 +249,29 @@ static ssize_t secure_boot_fuse_state_show(struct device *dev, ...@@ -244,11 +249,29 @@ static ssize_t secure_boot_fuse_state_show(struct device *dev,
return buf_len; return buf_len;
} }
static ssize_t fw_reset_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long key;
int err;
err = kstrtoul(buf, 16, &key);
if (err)
return err;
if (mlxbf_bootctl_smc(MLXBF_BOOTCTL_FW_RESET, key) < 0)
return -EINVAL;
return count;
}
static DEVICE_ATTR_RW(post_reset_wdog); static DEVICE_ATTR_RW(post_reset_wdog);
static DEVICE_ATTR_RW(reset_action); static DEVICE_ATTR_RW(reset_action);
static DEVICE_ATTR_RW(second_reset_action); static DEVICE_ATTR_RW(second_reset_action);
static DEVICE_ATTR_RO(lifecycle_state); static DEVICE_ATTR_RO(lifecycle_state);
static DEVICE_ATTR_RO(secure_boot_fuse_state); static DEVICE_ATTR_RO(secure_boot_fuse_state);
static DEVICE_ATTR_WO(fw_reset);
static struct attribute *mlxbf_bootctl_attrs[] = { static struct attribute *mlxbf_bootctl_attrs[] = {
&dev_attr_post_reset_wdog.attr, &dev_attr_post_reset_wdog.attr,
...@@ -256,6 +279,7 @@ static struct attribute *mlxbf_bootctl_attrs[] = { ...@@ -256,6 +279,7 @@ static struct attribute *mlxbf_bootctl_attrs[] = {
&dev_attr_second_reset_action.attr, &dev_attr_second_reset_action.attr,
&dev_attr_lifecycle_state.attr, &dev_attr_lifecycle_state.attr,
&dev_attr_secure_boot_fuse_state.attr, &dev_attr_secure_boot_fuse_state.attr,
&dev_attr_fw_reset.attr,
NULL NULL
}; };
...@@ -268,6 +292,45 @@ static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = { ...@@ -268,6 +292,45 @@ static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = {
MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids); MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids);
static ssize_t mlxbf_bootctl_bootfifo_read(struct file *filp,
struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t pos,
size_t count)
{
unsigned long timeout = msecs_to_jiffies(500);
unsigned long expire = jiffies + timeout;
u64 data, cnt = 0;
char *p = buf;
while (count >= sizeof(data)) {
/* Give up reading if no more data within 500ms. */
if (!cnt) {
cnt = readq(mlxbf_rsh_boot_cnt);
if (!cnt) {
if (time_after(jiffies, expire))
break;
usleep_range(10, 50);
continue;
}
}
data = readq(mlxbf_rsh_boot_data);
memcpy(p, &data, sizeof(data));
count -= sizeof(data);
p += sizeof(data);
cnt--;
expire = jiffies + timeout;
}
return p - buf;
}
static struct bin_attribute mlxbf_bootctl_bootfifo_sysfs_attr = {
.attr = { .name = "bootfifo", .mode = 0400 },
.read = mlxbf_bootctl_bootfifo_read,
};
static bool mlxbf_bootctl_guid_match(const guid_t *guid, static bool mlxbf_bootctl_guid_match(const guid_t *guid,
const struct arm_smccc_res *res) const struct arm_smccc_res *res)
{ {
...@@ -285,6 +348,16 @@ static int mlxbf_bootctl_probe(struct platform_device *pdev) ...@@ -285,6 +348,16 @@ static int mlxbf_bootctl_probe(struct platform_device *pdev)
guid_t guid; guid_t guid;
int ret; int ret;
/* Get the resource of the bootfifo data register. */
mlxbf_rsh_boot_data = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mlxbf_rsh_boot_data))
return PTR_ERR(mlxbf_rsh_boot_data);
/* Get the resource of the bootfifo counter register. */
mlxbf_rsh_boot_cnt = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(mlxbf_rsh_boot_cnt))
return PTR_ERR(mlxbf_rsh_boot_cnt);
/* Ensure we have the UUID we expect for this service. */ /* Ensure we have the UUID we expect for this service. */
arm_smccc_smc(MLXBF_BOOTCTL_SIP_SVC_UID, 0, 0, 0, 0, 0, 0, 0, &res); arm_smccc_smc(MLXBF_BOOTCTL_SIP_SVC_UID, 0, 0, 0, 0, 0, 0, 0, &res);
guid_parse(mlxbf_bootctl_svc_uuid_str, &guid); guid_parse(mlxbf_bootctl_svc_uuid_str, &guid);
...@@ -302,11 +375,25 @@ static int mlxbf_bootctl_probe(struct platform_device *pdev) ...@@ -302,11 +375,25 @@ static int mlxbf_bootctl_probe(struct platform_device *pdev)
if (ret < 0) if (ret < 0)
dev_warn(&pdev->dev, "Unable to reset the EMMC boot mode\n"); dev_warn(&pdev->dev, "Unable to reset the EMMC boot mode\n");
ret = sysfs_create_bin_file(&pdev->dev.kobj,
&mlxbf_bootctl_bootfifo_sysfs_attr);
if (ret)
pr_err("Unable to create bootfifo sysfs file, error %d\n", ret);
return ret;
}
static int mlxbf_bootctl_remove(struct platform_device *pdev)
{
sysfs_remove_bin_file(&pdev->dev.kobj,
&mlxbf_bootctl_bootfifo_sysfs_attr);
return 0; return 0;
} }
static struct platform_driver mlxbf_bootctl_driver = { static struct platform_driver mlxbf_bootctl_driver = {
.probe = mlxbf_bootctl_probe, .probe = mlxbf_bootctl_probe,
.remove = mlxbf_bootctl_remove,
.driver = { .driver = {
.name = "mlxbf-bootctl", .name = "mlxbf-bootctl",
.dev_groups = mlxbf_bootctl_groups, .dev_groups = mlxbf_bootctl_groups,
......
...@@ -75,6 +75,12 @@ ...@@ -75,6 +75,12 @@
#define MLXBF_BOOTCTL_GET_DIMM_INFO 0x82000008 #define MLXBF_BOOTCTL_GET_DIMM_INFO 0x82000008
/*
* Initiate Firmware Reset via TYU. This might be invoked during the reset
* flow in isolation mode.
*/
#define MLXBF_BOOTCTL_FW_RESET 0x8200000D
/* SMC function IDs for SiP Service queries */ /* SMC function IDs for SiP Service queries */
#define MLXBF_BOOTCTL_SIP_SVC_CALL_COUNT 0x8200ff00 #define MLXBF_BOOTCTL_SIP_SVC_CALL_COUNT 0x8200ff00
#define MLXBF_BOOTCTL_SIP_SVC_UID 0x8200ff01 #define MLXBF_BOOTCTL_SIP_SVC_UID 0x8200ff01
......
...@@ -746,6 +746,7 @@ static struct spi_driver olpc_xo175_ec_spi_driver = { ...@@ -746,6 +746,7 @@ static struct spi_driver olpc_xo175_ec_spi_driver = {
.of_match_table = olpc_xo175_ec_of_match, .of_match_table = olpc_xo175_ec_of_match,
.pm = &olpc_xo175_ec_pm_ops, .pm = &olpc_xo175_ec_pm_ops,
}, },
.id_table = olpc_xo175_ec_id_table,
.probe = olpc_xo175_ec_probe, .probe = olpc_xo175_ec_probe,
.remove = olpc_xo175_ec_remove, .remove = olpc_xo175_ec_remove,
}; };
......
...@@ -305,7 +305,7 @@ static const struct software_node *ssam_node_group_sp9[] = { ...@@ -305,7 +305,7 @@ static const struct software_node *ssam_node_group_sp9[] = {
&ssam_node_bat_ac, &ssam_node_bat_ac,
&ssam_node_bat_main, &ssam_node_bat_main,
&ssam_node_tmp_pprof, &ssam_node_tmp_pprof,
/* TODO: Tablet mode switch (via POS subsystem) */ &ssam_node_pos_tablet_switch,
&ssam_node_hid_kip_keyboard, &ssam_node_hid_kip_keyboard,
&ssam_node_hid_kip_penstash, &ssam_node_hid_kip_penstash,
&ssam_node_hid_kip_touchpad, &ssam_node_hid_kip_touchpad,
......
...@@ -20,16 +20,23 @@ ...@@ -20,16 +20,23 @@
struct ssam_tablet_sw; struct ssam_tablet_sw;
struct ssam_tablet_sw_state {
u32 source;
u32 state;
};
struct ssam_tablet_sw_ops { struct ssam_tablet_sw_ops {
int (*get_state)(struct ssam_tablet_sw *sw, u32 *state); int (*get_state)(struct ssam_tablet_sw *sw, struct ssam_tablet_sw_state *state);
const char *(*state_name)(struct ssam_tablet_sw *sw, u32 state); const char *(*state_name)(struct ssam_tablet_sw *sw,
bool (*state_is_tablet_mode)(struct ssam_tablet_sw *sw, u32 state); const struct ssam_tablet_sw_state *state);
bool (*state_is_tablet_mode)(struct ssam_tablet_sw *sw,
const struct ssam_tablet_sw_state *state);
}; };
struct ssam_tablet_sw { struct ssam_tablet_sw {
struct ssam_device *sdev; struct ssam_device *sdev;
u32 state; struct ssam_tablet_sw_state state;
struct work_struct update_work; struct work_struct update_work;
struct input_dev *mode_switch; struct input_dev *mode_switch;
...@@ -45,9 +52,11 @@ struct ssam_tablet_sw_desc { ...@@ -45,9 +52,11 @@ struct ssam_tablet_sw_desc {
struct { struct {
u32 (*notify)(struct ssam_event_notifier *nf, const struct ssam_event *event); u32 (*notify)(struct ssam_event_notifier *nf, const struct ssam_event *event);
int (*get_state)(struct ssam_tablet_sw *sw, u32 *state); int (*get_state)(struct ssam_tablet_sw *sw, struct ssam_tablet_sw_state *state);
const char *(*state_name)(struct ssam_tablet_sw *sw, u32 state); const char *(*state_name)(struct ssam_tablet_sw *sw,
bool (*state_is_tablet_mode)(struct ssam_tablet_sw *sw, u32 state); const struct ssam_tablet_sw_state *state);
bool (*state_is_tablet_mode)(struct ssam_tablet_sw *sw,
const struct ssam_tablet_sw_state *state);
} ops; } ops;
struct { struct {
...@@ -61,7 +70,7 @@ struct ssam_tablet_sw_desc { ...@@ -61,7 +70,7 @@ struct ssam_tablet_sw_desc {
static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf)
{ {
struct ssam_tablet_sw *sw = dev_get_drvdata(dev); struct ssam_tablet_sw *sw = dev_get_drvdata(dev);
const char *state = sw->ops.state_name(sw, sw->state); const char *state = sw->ops.state_name(sw, &sw->state);
return sysfs_emit(buf, "%s\n", state); return sysfs_emit(buf, "%s\n", state);
} }
...@@ -79,19 +88,19 @@ static const struct attribute_group ssam_tablet_sw_group = { ...@@ -79,19 +88,19 @@ static const struct attribute_group ssam_tablet_sw_group = {
static void ssam_tablet_sw_update_workfn(struct work_struct *work) static void ssam_tablet_sw_update_workfn(struct work_struct *work)
{ {
struct ssam_tablet_sw *sw = container_of(work, struct ssam_tablet_sw, update_work); struct ssam_tablet_sw *sw = container_of(work, struct ssam_tablet_sw, update_work);
struct ssam_tablet_sw_state state;
int tablet, status; int tablet, status;
u32 state;
status = sw->ops.get_state(sw, &state); status = sw->ops.get_state(sw, &state);
if (status) if (status)
return; return;
if (sw->state == state) if (sw->state.source == state.source && sw->state.state == state.state)
return; return;
sw->state = state; sw->state = state;
/* Send SW_TABLET_MODE event. */ /* Send SW_TABLET_MODE event. */
tablet = sw->ops.state_is_tablet_mode(sw, state); tablet = sw->ops.state_is_tablet_mode(sw, &state);
input_report_switch(sw->mode_switch, SW_TABLET_MODE, tablet); input_report_switch(sw->mode_switch, SW_TABLET_MODE, tablet);
input_sync(sw->mode_switch); input_sync(sw->mode_switch);
} }
...@@ -146,7 +155,7 @@ static int ssam_tablet_sw_probe(struct ssam_device *sdev) ...@@ -146,7 +155,7 @@ static int ssam_tablet_sw_probe(struct ssam_device *sdev)
sw->mode_switch->id.bustype = BUS_HOST; sw->mode_switch->id.bustype = BUS_HOST;
sw->mode_switch->dev.parent = &sdev->dev; sw->mode_switch->dev.parent = &sdev->dev;
tablet = sw->ops.state_is_tablet_mode(sw, sw->state); tablet = sw->ops.state_is_tablet_mode(sw, &sw->state);
input_set_capability(sw->mode_switch, EV_SW, SW_TABLET_MODE); input_set_capability(sw->mode_switch, EV_SW, SW_TABLET_MODE);
input_report_switch(sw->mode_switch, SW_TABLET_MODE, tablet); input_report_switch(sw->mode_switch, SW_TABLET_MODE, tablet);
...@@ -203,9 +212,10 @@ enum ssam_kip_cover_state { ...@@ -203,9 +212,10 @@ enum ssam_kip_cover_state {
SSAM_KIP_COVER_STATE_FOLDED_BACK = 0x05, SSAM_KIP_COVER_STATE_FOLDED_BACK = 0x05,
}; };
static const char *ssam_kip_cover_state_name(struct ssam_tablet_sw *sw, u32 state) static const char *ssam_kip_cover_state_name(struct ssam_tablet_sw *sw,
const struct ssam_tablet_sw_state *state)
{ {
switch (state) { switch (state->state) {
case SSAM_KIP_COVER_STATE_DISCONNECTED: case SSAM_KIP_COVER_STATE_DISCONNECTED:
return "disconnected"; return "disconnected";
...@@ -222,14 +232,15 @@ static const char *ssam_kip_cover_state_name(struct ssam_tablet_sw *sw, u32 stat ...@@ -222,14 +232,15 @@ static const char *ssam_kip_cover_state_name(struct ssam_tablet_sw *sw, u32 stat
return "folded-back"; return "folded-back";
default: default:
dev_warn(&sw->sdev->dev, "unknown KIP cover state: %u\n", state); dev_warn(&sw->sdev->dev, "unknown KIP cover state: %u\n", state->state);
return "<unknown>"; return "<unknown>";
} }
} }
static bool ssam_kip_cover_state_is_tablet_mode(struct ssam_tablet_sw *sw, u32 state) static bool ssam_kip_cover_state_is_tablet_mode(struct ssam_tablet_sw *sw,
const struct ssam_tablet_sw_state *state)
{ {
switch (state) { switch (state->state) {
case SSAM_KIP_COVER_STATE_DISCONNECTED: case SSAM_KIP_COVER_STATE_DISCONNECTED:
case SSAM_KIP_COVER_STATE_FOLDED_CANVAS: case SSAM_KIP_COVER_STATE_FOLDED_CANVAS:
case SSAM_KIP_COVER_STATE_FOLDED_BACK: case SSAM_KIP_COVER_STATE_FOLDED_BACK:
...@@ -240,7 +251,7 @@ static bool ssam_kip_cover_state_is_tablet_mode(struct ssam_tablet_sw *sw, u32 s ...@@ -240,7 +251,7 @@ static bool ssam_kip_cover_state_is_tablet_mode(struct ssam_tablet_sw *sw, u32 s
return false; return false;
default: default:
dev_warn(&sw->sdev->dev, "unknown KIP cover state: %d\n", sw->state); dev_warn(&sw->sdev->dev, "unknown KIP cover state: %d\n", state->state);
return true; return true;
} }
} }
...@@ -252,7 +263,7 @@ SSAM_DEFINE_SYNC_REQUEST_R(__ssam_kip_get_cover_state, u8, { ...@@ -252,7 +263,7 @@ SSAM_DEFINE_SYNC_REQUEST_R(__ssam_kip_get_cover_state, u8, {
.instance_id = 0x00, .instance_id = 0x00,
}); });
static int ssam_kip_get_cover_state(struct ssam_tablet_sw *sw, u32 *state) static int ssam_kip_get_cover_state(struct ssam_tablet_sw *sw, struct ssam_tablet_sw_state *state)
{ {
int status; int status;
u8 raw; u8 raw;
...@@ -263,7 +274,8 @@ static int ssam_kip_get_cover_state(struct ssam_tablet_sw *sw, u32 *state) ...@@ -263,7 +274,8 @@ static int ssam_kip_get_cover_state(struct ssam_tablet_sw *sw, u32 *state)
return status; return status;
} }
*state = raw; state->source = 0; /* Unused for KIP switch. */
state->state = raw;
return 0; return 0;
} }
...@@ -312,11 +324,24 @@ MODULE_PARM_DESC(tablet_mode_in_slate_state, "Enable tablet mode in slate device ...@@ -312,11 +324,24 @@ MODULE_PARM_DESC(tablet_mode_in_slate_state, "Enable tablet mode in slate device
#define SSAM_EVENT_POS_CID_POSTURE_CHANGED 0x03 #define SSAM_EVENT_POS_CID_POSTURE_CHANGED 0x03
#define SSAM_POS_MAX_SOURCES 4 #define SSAM_POS_MAX_SOURCES 4
enum ssam_pos_state { enum ssam_pos_source_id {
SSAM_POS_POSTURE_LID_CLOSED = 0x00, SSAM_POS_SOURCE_COVER = 0x00,
SSAM_POS_POSTURE_LAPTOP = 0x01, SSAM_POS_SOURCE_SLS = 0x03,
SSAM_POS_POSTURE_SLATE = 0x02, };
SSAM_POS_POSTURE_TABLET = 0x03,
enum ssam_pos_state_cover {
SSAM_POS_COVER_DISCONNECTED = 0x01,
SSAM_POS_COVER_CLOSED = 0x02,
SSAM_POS_COVER_LAPTOP = 0x03,
SSAM_POS_COVER_FOLDED_CANVAS = 0x04,
SSAM_POS_COVER_FOLDED_BACK = 0x05,
};
enum ssam_pos_state_sls {
SSAM_POS_SLS_LID_CLOSED = 0x00,
SSAM_POS_SLS_LAPTOP = 0x01,
SSAM_POS_SLS_SLATE = 0x02,
SSAM_POS_SLS_TABLET = 0x03,
}; };
struct ssam_sources_list { struct ssam_sources_list {
...@@ -324,42 +349,116 @@ struct ssam_sources_list { ...@@ -324,42 +349,116 @@ struct ssam_sources_list {
__le32 id[SSAM_POS_MAX_SOURCES]; __le32 id[SSAM_POS_MAX_SOURCES];
} __packed; } __packed;
static const char *ssam_pos_state_name(struct ssam_tablet_sw *sw, u32 state) static const char *ssam_pos_state_name_cover(struct ssam_tablet_sw *sw, u32 state)
{
switch (state) {
case SSAM_POS_COVER_DISCONNECTED:
return "disconnected";
case SSAM_POS_COVER_CLOSED:
return "closed";
case SSAM_POS_COVER_LAPTOP:
return "laptop";
case SSAM_POS_COVER_FOLDED_CANVAS:
return "folded-canvas";
case SSAM_POS_COVER_FOLDED_BACK:
return "folded-back";
default:
dev_warn(&sw->sdev->dev, "unknown device posture for type-cover: %u\n", state);
return "<unknown>";
}
}
static const char *ssam_pos_state_name_sls(struct ssam_tablet_sw *sw, u32 state)
{ {
switch (state) { switch (state) {
case SSAM_POS_POSTURE_LID_CLOSED: case SSAM_POS_SLS_LID_CLOSED:
return "closed"; return "closed";
case SSAM_POS_POSTURE_LAPTOP: case SSAM_POS_SLS_LAPTOP:
return "laptop"; return "laptop";
case SSAM_POS_POSTURE_SLATE: case SSAM_POS_SLS_SLATE:
return "slate"; return "slate";
case SSAM_POS_POSTURE_TABLET: case SSAM_POS_SLS_TABLET:
return "tablet"; return "tablet";
default: default:
dev_warn(&sw->sdev->dev, "unknown device posture: %u\n", state); dev_warn(&sw->sdev->dev, "unknown device posture for SLS: %u\n", state);
return "<unknown>"; return "<unknown>";
} }
} }
static bool ssam_pos_state_is_tablet_mode(struct ssam_tablet_sw *sw, u32 state) static const char *ssam_pos_state_name(struct ssam_tablet_sw *sw,
const struct ssam_tablet_sw_state *state)
{
switch (state->source) {
case SSAM_POS_SOURCE_COVER:
return ssam_pos_state_name_cover(sw, state->state);
case SSAM_POS_SOURCE_SLS:
return ssam_pos_state_name_sls(sw, state->state);
default:
dev_warn(&sw->sdev->dev, "unknown device posture source: %u\n", state->source);
return "<unknown>";
}
}
static bool ssam_pos_state_is_tablet_mode_cover(struct ssam_tablet_sw *sw, u32 state)
{ {
switch (state) { switch (state) {
case SSAM_POS_POSTURE_LAPTOP: case SSAM_POS_COVER_DISCONNECTED:
case SSAM_POS_POSTURE_LID_CLOSED: case SSAM_POS_COVER_FOLDED_CANVAS:
case SSAM_POS_COVER_FOLDED_BACK:
return true;
case SSAM_POS_COVER_CLOSED:
case SSAM_POS_COVER_LAPTOP:
return false; return false;
case SSAM_POS_POSTURE_SLATE: default:
dev_warn(&sw->sdev->dev, "unknown device posture for type-cover: %u\n", state);
return true;
}
}
static bool ssam_pos_state_is_tablet_mode_sls(struct ssam_tablet_sw *sw, u32 state)
{
switch (state) {
case SSAM_POS_SLS_LAPTOP:
case SSAM_POS_SLS_LID_CLOSED:
return false;
case SSAM_POS_SLS_SLATE:
return tablet_mode_in_slate_state; return tablet_mode_in_slate_state;
case SSAM_POS_POSTURE_TABLET: case SSAM_POS_SLS_TABLET:
return true; return true;
default: default:
dev_warn(&sw->sdev->dev, "unknown device posture: %u\n", state); dev_warn(&sw->sdev->dev, "unknown device posture for SLS: %u\n", state);
return true;
}
}
static bool ssam_pos_state_is_tablet_mode(struct ssam_tablet_sw *sw,
const struct ssam_tablet_sw_state *state)
{
switch (state->source) {
case SSAM_POS_SOURCE_COVER:
return ssam_pos_state_is_tablet_mode_cover(sw, state->state);
case SSAM_POS_SOURCE_SLS:
return ssam_pos_state_is_tablet_mode_sls(sw, state->state);
default:
dev_warn(&sw->sdev->dev, "unknown device posture source: %u\n", state->source);
return true; return true;
} }
} }
...@@ -450,9 +549,10 @@ static int ssam_pos_get_posture_for_source(struct ssam_tablet_sw *sw, u32 source ...@@ -450,9 +549,10 @@ static int ssam_pos_get_posture_for_source(struct ssam_tablet_sw *sw, u32 source
return 0; return 0;
} }
static int ssam_pos_get_posture(struct ssam_tablet_sw *sw, u32 *state) static int ssam_pos_get_posture(struct ssam_tablet_sw *sw, struct ssam_tablet_sw_state *state)
{ {
u32 source_id; u32 source_id;
u32 source_state;
int status; int status;
status = ssam_pos_get_source(sw, &source_id); status = ssam_pos_get_source(sw, &source_id);
...@@ -461,13 +561,15 @@ static int ssam_pos_get_posture(struct ssam_tablet_sw *sw, u32 *state) ...@@ -461,13 +561,15 @@ static int ssam_pos_get_posture(struct ssam_tablet_sw *sw, u32 *state)
return status; return status;
} }
status = ssam_pos_get_posture_for_source(sw, source_id, state); status = ssam_pos_get_posture_for_source(sw, source_id, &source_state);
if (status) { if (status) {
dev_err(&sw->sdev->dev, "failed to get posture value for source %u: %d\n", dev_err(&sw->sdev->dev, "failed to get posture value for source %u: %d\n",
source_id, status); source_id, status);
return status; return status;
} }
state->source = source_id;
state->state = source_state;
return 0; return 0;
} }
......
...@@ -84,13 +84,6 @@ config MXM_WMI ...@@ -84,13 +84,6 @@ config MXM_WMI
MXM is a standard for laptop graphics cards, the WMI interface MXM is a standard for laptop graphics cards, the WMI interface
is required for switchable nvidia graphics machines is required for switchable nvidia graphics machines
config PEAQ_WMI
tristate "PEAQ 2-in-1 WMI hotkey driver"
depends on ACPI_WMI
depends on INPUT
help
Say Y here if you want to support WMI-based hotkeys on PEAQ 2-in-1s.
config NVIDIA_WMI_EC_BACKLIGHT config NVIDIA_WMI_EC_BACKLIGHT
tristate "EC Backlight Driver for Hybrid Graphics Notebook Systems" tristate "EC Backlight Driver for Hybrid Graphics Notebook Systems"
depends on ACPI_VIDEO depends on ACPI_VIDEO
...@@ -213,7 +206,6 @@ config APPLE_GMUX ...@@ -213,7 +206,6 @@ config APPLE_GMUX
depends on ACPI && PCI depends on ACPI && PCI
depends on PNP depends on PNP
depends on BACKLIGHT_CLASS_DEVICE depends on BACKLIGHT_CLASS_DEVICE
depends on BACKLIGHT_APPLE=n || BACKLIGHT_APPLE
help help
This driver provides support for the gmux device found on many This driver provides support for the gmux device found on many
Apple laptops, which controls the display mux for the hybrid Apple laptops, which controls the display mux for the hybrid
...@@ -469,6 +461,15 @@ config IDEAPAD_LAPTOP ...@@ -469,6 +461,15 @@ config IDEAPAD_LAPTOP
This is a driver for Lenovo IdeaPad netbooks contains drivers for This is a driver for Lenovo IdeaPad netbooks contains drivers for
rfkill switch, hotkey, fan control and backlight control. rfkill switch, hotkey, fan control and backlight control.
config LENOVO_YMC
tristate "Lenovo Yoga Tablet Mode Control"
depends on ACPI_WMI
depends on INPUT
select INPUT_SPARSEKMAP
help
This driver maps the Tablet Mode Control switch to SW_TABLET_MODE input
events for Lenovo Yoga notebooks.
config SENSORS_HDAPS config SENSORS_HDAPS
tristate "Thinkpad Hard Drive Active Protection System (hdaps)" tristate "Thinkpad Hard Drive Active Protection System (hdaps)"
depends on INPUT depends on INPUT
...@@ -643,6 +644,14 @@ config THINKPAD_LMI ...@@ -643,6 +644,14 @@ config THINKPAD_LMI
source "drivers/platform/x86/intel/Kconfig" source "drivers/platform/x86/intel/Kconfig"
config MSI_EC
tristate "MSI EC Extras"
depends on ACPI
depends on ACPI_BATTERY
help
This driver allows various MSI laptops' functionalities to be
controlled from userspace, including battery charge threshold.
config MSI_LAPTOP config MSI_LAPTOP
tristate "MSI Laptop Extras" tristate "MSI Laptop Extras"
depends on ACPI depends on ACPI
...@@ -978,22 +987,7 @@ config TOUCHSCREEN_DMI ...@@ -978,22 +987,7 @@ config TOUCHSCREEN_DMI
the OS-image for the device. This option supplies the missing info. the OS-image for the device. This option supplies the missing info.
Enable this for x86 tablets with Silead or Chipone touchscreens. Enable this for x86 tablets with Silead or Chipone touchscreens.
config X86_ANDROID_TABLETS source "drivers/platform/x86/x86-android-tablets/Kconfig"
tristate "X86 Android tablet support"
depends on I2C && SPI && SERIAL_DEV_BUS && ACPI && EFI && GPIOLIB
help
X86 tablets which ship with Android as (part of) the factory image
typically have various problems with their DSDTs. The factory kernels
shipped on these devices typically have device addresses and GPIOs
hardcoded in the kernel, rather than specified in their DSDT.
With the DSDT containing a random collection of devices which may or
may not actually be present. This driver contains various fixes for
such tablets, including instantiating kernel devices for devices which
are missing from the DSDT.
If you have a x86 Android tablet say Y or M here, for a generic x86
distro config say M here.
config FW_ATTR_CLASS config FW_ATTR_CLASS
tristate tristate
......
...@@ -12,7 +12,6 @@ obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o ...@@ -12,7 +12,6 @@ obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o
obj-$(CONFIG_HUAWEI_WMI) += huawei-wmi.o obj-$(CONFIG_HUAWEI_WMI) += huawei-wmi.o
obj-$(CONFIG_MXM_WMI) += mxm-wmi.o obj-$(CONFIG_MXM_WMI) += mxm-wmi.o
obj-$(CONFIG_NVIDIA_WMI_EC_BACKLIGHT) += nvidia-wmi-ec-backlight.o obj-$(CONFIG_NVIDIA_WMI_EC_BACKLIGHT) += nvidia-wmi-ec-backlight.o
obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o
obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o
obj-$(CONFIG_GIGABYTE_WMI) += gigabyte-wmi.o obj-$(CONFIG_GIGABYTE_WMI) += gigabyte-wmi.o
obj-$(CONFIG_YOGABOOK_WMI) += lenovo-yogabook-wmi.o obj-$(CONFIG_YOGABOOK_WMI) += lenovo-yogabook-wmi.o
...@@ -63,6 +62,7 @@ obj-$(CONFIG_UV_SYSFS) += uv_sysfs.o ...@@ -63,6 +62,7 @@ obj-$(CONFIG_UV_SYSFS) += uv_sysfs.o
# IBM Thinkpad and Lenovo # IBM Thinkpad and Lenovo
obj-$(CONFIG_IBM_RTL) += ibm_rtl.o obj-$(CONFIG_IBM_RTL) += ibm_rtl.o
obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o
obj-$(CONFIG_LENOVO_YMC) += lenovo-ymc.o
obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o
...@@ -71,6 +71,7 @@ obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o ...@@ -71,6 +71,7 @@ obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o
obj-y += intel/ obj-y += intel/
# MSI # MSI
obj-$(CONFIG_MSI_EC) += msi-ec.o
obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
obj-$(CONFIG_MSI_WMI) += msi-wmi.o obj-$(CONFIG_MSI_WMI) += msi-wmi.o
...@@ -112,7 +113,7 @@ obj-$(CONFIG_SERIAL_MULTI_INSTANTIATE) += serial-multi-instantiate.o ...@@ -112,7 +113,7 @@ obj-$(CONFIG_SERIAL_MULTI_INSTANTIATE) += serial-multi-instantiate.o
obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o
obj-$(CONFIG_TOUCHSCREEN_DMI) += touchscreen_dmi.o obj-$(CONFIG_TOUCHSCREEN_DMI) += touchscreen_dmi.o
obj-$(CONFIG_WIRELESS_HOTKEY) += wireless-hotkey.o obj-$(CONFIG_WIRELESS_HOTKEY) += wireless-hotkey.o
obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets/
# Intel uncore drivers # Intel uncore drivers
obj-$(CONFIG_INTEL_IPS) += intel_ips.o obj-$(CONFIG_INTEL_IPS) += intel_ips.o
......
...@@ -2258,7 +2258,7 @@ static int acer_platform_probe(struct platform_device *device) ...@@ -2258,7 +2258,7 @@ static int acer_platform_probe(struct platform_device *device)
return err; return err;
} }
static int acer_platform_remove(struct platform_device *device) static void acer_platform_remove(struct platform_device *device)
{ {
if (has_cap(ACER_CAP_MAILLED)) if (has_cap(ACER_CAP_MAILLED))
acer_led_exit(); acer_led_exit();
...@@ -2266,7 +2266,6 @@ static int acer_platform_remove(struct platform_device *device) ...@@ -2266,7 +2266,6 @@ static int acer_platform_remove(struct platform_device *device)
acer_backlight_exit(); acer_backlight_exit();
acer_rfkill_exit(); acer_rfkill_exit();
return 0;
} }
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
...@@ -2334,7 +2333,7 @@ static struct platform_driver acer_platform_driver = { ...@@ -2334,7 +2333,7 @@ static struct platform_driver acer_platform_driver = {
.pm = &acer_pm, .pm = &acer_pm,
}, },
.probe = acer_platform_probe, .probe = acer_platform_probe,
.remove = acer_platform_remove, .remove_new = acer_platform_remove,
.shutdown = acer_platform_shutdown, .shutdown = acer_platform_shutdown,
}; };
......
...@@ -341,7 +341,7 @@ static void acerhdf_check_param(struct thermal_zone_device *thermal) ...@@ -341,7 +341,7 @@ static void acerhdf_check_param(struct thermal_zone_device *thermal)
pr_err("fanoff temperature (%d) is above fanon temperature (%d), clamping to %d\n", pr_err("fanoff temperature (%d) is above fanon temperature (%d), clamping to %d\n",
fanoff, fanon, fanon); fanoff, fanon, fanon);
fanoff = fanon; fanoff = fanon;
}; }
trips[0].temperature = fanon; trips[0].temperature = fanon;
trips[0].hysteresis = fanon - fanoff; trips[0].hysteresis = fanon - fanoff;
......
...@@ -90,14 +90,12 @@ static int adv_swbutton_probe(struct platform_device *device) ...@@ -90,14 +90,12 @@ static int adv_swbutton_probe(struct platform_device *device)
return 0; return 0;
} }
static int adv_swbutton_remove(struct platform_device *device) static void adv_swbutton_remove(struct platform_device *device)
{ {
acpi_handle handle = ACPI_HANDLE(&device->dev); acpi_handle handle = ACPI_HANDLE(&device->dev);
acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY,
adv_swbutton_notify); adv_swbutton_notify);
return 0;
} }
static const struct acpi_device_id button_device_ids[] = { static const struct acpi_device_id button_device_ids[] = {
...@@ -112,7 +110,7 @@ static struct platform_driver adv_swbutton_driver = { ...@@ -112,7 +110,7 @@ static struct platform_driver adv_swbutton_driver = {
.acpi_match_table = button_device_ids, .acpi_match_table = button_device_ids,
}, },
.probe = adv_swbutton_probe, .probe = adv_swbutton_probe,
.remove = adv_swbutton_remove, .remove_new = adv_swbutton_remove,
}; };
module_platform_driver(adv_swbutton_driver); module_platform_driver(adv_swbutton_driver);
......
...@@ -7,7 +7,7 @@ source "drivers/platform/x86/amd/pmf/Kconfig" ...@@ -7,7 +7,7 @@ source "drivers/platform/x86/amd/pmf/Kconfig"
config AMD_PMC config AMD_PMC
tristate "AMD SoC PMC driver" tristate "AMD SoC PMC driver"
depends on ACPI && PCI && RTC_CLASS depends on ACPI && PCI && RTC_CLASS && AMD_NB
select SERIO select SERIO
help help
The driver provides support for AMD Power Management Controller The driver provides support for AMD Power Management Controller
......
...@@ -340,16 +340,14 @@ static int hsmp_pltdrv_probe(struct platform_device *pdev) ...@@ -340,16 +340,14 @@ static int hsmp_pltdrv_probe(struct platform_device *pdev)
return misc_register(&hsmp_device); return misc_register(&hsmp_device);
} }
static int hsmp_pltdrv_remove(struct platform_device *pdev) static void hsmp_pltdrv_remove(struct platform_device *pdev)
{ {
misc_deregister(&hsmp_device); misc_deregister(&hsmp_device);
return 0;
} }
static struct platform_driver amd_hsmp_driver = { static struct platform_driver amd_hsmp_driver = {
.probe = hsmp_pltdrv_probe, .probe = hsmp_pltdrv_probe,
.remove = hsmp_pltdrv_remove, .remove_new = hsmp_pltdrv_remove,
.driver = { .driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
}, },
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <asm/amd_nb.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/bitfield.h> #include <linux/bitfield.h>
#include <linux/bits.h> #include <linux/bits.h>
...@@ -37,8 +38,6 @@ ...@@ -37,8 +38,6 @@
#define AMD_PMC_SCRATCH_REG_YC 0xD14 #define AMD_PMC_SCRATCH_REG_YC 0xD14
/* STB Registers */ /* STB Registers */
#define AMD_PMC_STB_INDEX_ADDRESS 0xF8
#define AMD_PMC_STB_INDEX_DATA 0xFC
#define AMD_PMC_STB_PMI_0 0x03E30600 #define AMD_PMC_STB_PMI_0 0x03E30600
#define AMD_PMC_STB_S2IDLE_PREPARE 0xC6000001 #define AMD_PMC_STB_S2IDLE_PREPARE 0xC6000001
#define AMD_PMC_STB_S2IDLE_RESTORE 0xC6000002 #define AMD_PMC_STB_S2IDLE_RESTORE 0xC6000002
...@@ -56,8 +55,6 @@ ...@@ -56,8 +55,6 @@
#define S2D_TELEMETRY_DRAMBYTES_MAX 0x1000000 #define S2D_TELEMETRY_DRAMBYTES_MAX 0x1000000
/* Base address of SMU for mapping physical address to virtual address */ /* Base address of SMU for mapping physical address to virtual address */
#define AMD_PMC_SMU_INDEX_ADDRESS 0xB8
#define AMD_PMC_SMU_INDEX_DATA 0xBC
#define AMD_PMC_MAPPING_SIZE 0x01000 #define AMD_PMC_MAPPING_SIZE 0x01000
#define AMD_PMC_BASE_ADDR_OFFSET 0x10000 #define AMD_PMC_BASE_ADDR_OFFSET 0x10000
#define AMD_PMC_BASE_ADDR_LO 0x13B102E8 #define AMD_PMC_BASE_ADDR_LO 0x13B102E8
...@@ -97,6 +94,7 @@ ...@@ -97,6 +94,7 @@
#define AMD_CPU_ID_YC 0x14B5 #define AMD_CPU_ID_YC 0x14B5
#define AMD_CPU_ID_CB 0x14D8 #define AMD_CPU_ID_CB 0x14D8
#define AMD_CPU_ID_PS 0x14E8 #define AMD_CPU_ID_PS 0x14E8
#define AMD_CPU_ID_SP 0x14A4
#define PMC_MSG_DELAY_MIN_US 50 #define PMC_MSG_DELAY_MIN_US 50
#define RESPONSE_REGISTER_LOOP_MAX 20000 #define RESPONSE_REGISTER_LOOP_MAX 20000
...@@ -268,6 +266,7 @@ static int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp) ...@@ -268,6 +266,7 @@ static int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp)
dev->msg_port = 0; dev->msg_port = 0;
if (ret) { if (ret) {
dev_err(dev->dev, "error: S2D_NUM_SAMPLES not supported : %d\n", ret); dev_err(dev->dev, "error: S2D_NUM_SAMPLES not supported : %d\n", ret);
kfree(buf);
return ret; return ret;
} }
...@@ -342,33 +341,6 @@ static int amd_pmc_setup_smu_logging(struct amd_pmc_dev *dev) ...@@ -342,33 +341,6 @@ static int amd_pmc_setup_smu_logging(struct amd_pmc_dev *dev)
return 0; return 0;
} }
static int amd_pmc_idlemask_read(struct amd_pmc_dev *pdev, struct device *dev,
struct seq_file *s)
{
u32 val;
switch (pdev->cpu_id) {
case AMD_CPU_ID_CZN:
val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_CZN);
break;
case AMD_CPU_ID_YC:
case AMD_CPU_ID_CB:
case AMD_CPU_ID_PS:
val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_YC);
break;
default:
return -EINVAL;
}
if (dev)
dev_dbg(pdev->dev, "SMU idlemask s0i3: 0x%x\n", val);
if (s)
seq_printf(s, "SMU idlemask : 0x%x\n", val);
return 0;
}
static int get_metrics_table(struct amd_pmc_dev *pdev, struct smu_metrics *table) static int get_metrics_table(struct amd_pmc_dev *pdev, struct smu_metrics *table)
{ {
if (!pdev->smu_virt_addr) { if (!pdev->smu_virt_addr) {
...@@ -403,6 +375,9 @@ static int amd_pmc_get_smu_version(struct amd_pmc_dev *dev) ...@@ -403,6 +375,9 @@ static int amd_pmc_get_smu_version(struct amd_pmc_dev *dev)
int rc; int rc;
u32 val; u32 val;
if (dev->cpu_id == AMD_CPU_ID_PCO)
return -ENODEV;
rc = amd_pmc_send_cmd(dev, 0, &val, SMU_MSG_GETSMUVERSION, 1); rc = amd_pmc_send_cmd(dev, 0, &val, SMU_MSG_GETSMUVERSION, 1);
if (rc) if (rc)
return rc; return rc;
...@@ -449,12 +424,31 @@ static ssize_t smu_program_show(struct device *d, struct device_attribute *attr, ...@@ -449,12 +424,31 @@ static ssize_t smu_program_show(struct device *d, struct device_attribute *attr,
static DEVICE_ATTR_RO(smu_fw_version); static DEVICE_ATTR_RO(smu_fw_version);
static DEVICE_ATTR_RO(smu_program); static DEVICE_ATTR_RO(smu_program);
static umode_t pmc_attr_is_visible(struct kobject *kobj, struct attribute *attr, int idx)
{
struct device *dev = kobj_to_dev(kobj);
struct amd_pmc_dev *pdev = dev_get_drvdata(dev);
if (pdev->cpu_id == AMD_CPU_ID_PCO)
return 0;
return 0444;
}
static struct attribute *pmc_attrs[] = { static struct attribute *pmc_attrs[] = {
&dev_attr_smu_fw_version.attr, &dev_attr_smu_fw_version.attr,
&dev_attr_smu_program.attr, &dev_attr_smu_program.attr,
NULL, NULL,
}; };
ATTRIBUTE_GROUPS(pmc);
static struct attribute_group pmc_attr_group = {
.attrs = pmc_attrs,
.is_visible = pmc_attr_is_visible,
};
static const struct attribute_group *pmc_groups[] = {
&pmc_attr_group,
NULL,
};
static int smu_fw_info_show(struct seq_file *s, void *unused) static int smu_fw_info_show(struct seq_file *s, void *unused)
{ {
...@@ -521,28 +515,47 @@ static int s0ix_stats_show(struct seq_file *s, void *unused) ...@@ -521,28 +515,47 @@ static int s0ix_stats_show(struct seq_file *s, void *unused)
} }
DEFINE_SHOW_ATTRIBUTE(s0ix_stats); DEFINE_SHOW_ATTRIBUTE(s0ix_stats);
static int amd_pmc_idlemask_show(struct seq_file *s, void *unused) static int amd_pmc_idlemask_read(struct amd_pmc_dev *pdev, struct device *dev,
struct seq_file *s)
{ {
struct amd_pmc_dev *dev = s->private; u32 val;
int rc; int rc;
/* we haven't yet read SMU version */ switch (pdev->cpu_id) {
if (!dev->major) { case AMD_CPU_ID_CZN:
rc = amd_pmc_get_smu_version(dev); /* we haven't yet read SMU version */
if (rc) if (!pdev->major) {
return rc; rc = amd_pmc_get_smu_version(pdev);
if (rc)
return rc;
}
if (pdev->major > 56 || (pdev->major >= 55 && pdev->minor >= 37))
val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_CZN);
else
return -EINVAL;
break;
case AMD_CPU_ID_YC:
case AMD_CPU_ID_CB:
case AMD_CPU_ID_PS:
val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_YC);
break;
default:
return -EINVAL;
} }
if (dev->major > 56 || (dev->major >= 55 && dev->minor >= 37)) { if (dev)
rc = amd_pmc_idlemask_read(dev, NULL, s); dev_dbg(pdev->dev, "SMU idlemask s0i3: 0x%x\n", val);
if (rc)
return rc; if (s)
} else { seq_printf(s, "SMU idlemask : 0x%x\n", val);
seq_puts(s, "Unsupported SMU version for Idlemask\n");
}
return 0; return 0;
} }
static int amd_pmc_idlemask_show(struct seq_file *s, void *unused)
{
return amd_pmc_idlemask_read(s->private, NULL, s);
}
DEFINE_SHOW_ATTRIBUTE(amd_pmc_idlemask); DEFINE_SHOW_ATTRIBUTE(amd_pmc_idlemask);
static void amd_pmc_dbgfs_unregister(struct amd_pmc_dev *dev) static void amd_pmc_dbgfs_unregister(struct amd_pmc_dev *dev)
...@@ -812,6 +825,14 @@ static void amd_pmc_s2idle_check(void) ...@@ -812,6 +825,14 @@ static void amd_pmc_s2idle_check(void)
dev_err(pdev->dev, "error writing to STB: %d\n", rc); dev_err(pdev->dev, "error writing to STB: %d\n", rc);
} }
static int amd_pmc_dump_data(struct amd_pmc_dev *pdev)
{
if (pdev->cpu_id == AMD_CPU_ID_PCO)
return -ENODEV;
return amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_DUMP_DATA, 0);
}
static void amd_pmc_s2idle_restore(void) static void amd_pmc_s2idle_restore(void)
{ {
struct amd_pmc_dev *pdev = &pmc; struct amd_pmc_dev *pdev = &pmc;
...@@ -824,7 +845,7 @@ static void amd_pmc_s2idle_restore(void) ...@@ -824,7 +845,7 @@ static void amd_pmc_s2idle_restore(void)
dev_err(pdev->dev, "resume failed: %d\n", rc); dev_err(pdev->dev, "resume failed: %d\n", rc);
/* Let SMU know that we are looking for stats */ /* Let SMU know that we are looking for stats */
amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_DUMP_DATA, 0); amd_pmc_dump_data(pdev);
rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_S2IDLE_RESTORE); rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_S2IDLE_RESTORE);
if (rc) if (rc)
...@@ -840,7 +861,7 @@ static struct acpi_s2idle_dev_ops amd_pmc_s2idle_dev_ops = { ...@@ -840,7 +861,7 @@ static struct acpi_s2idle_dev_ops amd_pmc_s2idle_dev_ops = {
.restore = amd_pmc_s2idle_restore, .restore = amd_pmc_s2idle_restore,
}; };
static int __maybe_unused amd_pmc_suspend_handler(struct device *dev) static int amd_pmc_suspend_handler(struct device *dev)
{ {
struct amd_pmc_dev *pdev = dev_get_drvdata(dev); struct amd_pmc_dev *pdev = dev_get_drvdata(dev);
...@@ -866,6 +887,7 @@ static const struct pci_device_id pmc_pci_ids[] = { ...@@ -866,6 +887,7 @@ static const struct pci_device_id pmc_pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RN) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RN) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PCO) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PCO) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RV) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RV) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_SP) },
{ } { }
}; };
...@@ -902,17 +924,9 @@ static int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data) ...@@ -902,17 +924,9 @@ static int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data)
{ {
int err; int err;
err = pci_write_config_dword(dev->rdev, AMD_PMC_STB_INDEX_ADDRESS, AMD_PMC_STB_PMI_0); err = amd_smn_write(0, AMD_PMC_STB_PMI_0, data);
if (err) { if (err) {
dev_err(dev->dev, "failed to write addr in stb: 0x%X\n", dev_err(dev->dev, "failed to write data in stb: 0x%X\n", AMD_PMC_STB_PMI_0);
AMD_PMC_STB_INDEX_ADDRESS);
return pcibios_err_to_errno(err);
}
err = pci_write_config_dword(dev->rdev, AMD_PMC_STB_INDEX_DATA, data);
if (err) {
dev_err(dev->dev, "failed to write data in stb: 0x%X\n",
AMD_PMC_STB_INDEX_DATA);
return pcibios_err_to_errno(err); return pcibios_err_to_errno(err);
} }
...@@ -923,18 +937,10 @@ static int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf) ...@@ -923,18 +937,10 @@ static int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf)
{ {
int i, err; int i, err;
err = pci_write_config_dword(dev->rdev, AMD_PMC_STB_INDEX_ADDRESS, AMD_PMC_STB_PMI_0);
if (err) {
dev_err(dev->dev, "error writing addr to stb: 0x%X\n",
AMD_PMC_STB_INDEX_ADDRESS);
return pcibios_err_to_errno(err);
}
for (i = 0; i < FIFO_SIZE; i++) { for (i = 0; i < FIFO_SIZE; i++) {
err = pci_read_config_dword(dev->rdev, AMD_PMC_STB_INDEX_DATA, buf++); err = amd_smn_read(0, AMD_PMC_STB_PMI_0, buf++);
if (err) { if (err) {
dev_err(dev->dev, "error reading data from stb: 0x%X\n", dev_err(dev->dev, "error reading data from stb: 0x%X\n", AMD_PMC_STB_PMI_0);
AMD_PMC_STB_INDEX_DATA);
return pcibios_err_to_errno(err); return pcibios_err_to_errno(err);
} }
} }
...@@ -960,31 +966,26 @@ static int amd_pmc_probe(struct platform_device *pdev) ...@@ -960,31 +966,26 @@ static int amd_pmc_probe(struct platform_device *pdev)
} }
dev->cpu_id = rdev->device; dev->cpu_id = rdev->device;
dev->rdev = rdev;
err = pci_write_config_dword(rdev, AMD_PMC_SMU_INDEX_ADDRESS, AMD_PMC_BASE_ADDR_LO); if (dev->cpu_id == AMD_CPU_ID_SP) {
if (err) { dev_warn_once(dev->dev, "S0i3 is not supported on this hardware\n");
dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMC_SMU_INDEX_ADDRESS); err = -ENODEV;
err = pcibios_err_to_errno(err);
goto err_pci_dev_put; goto err_pci_dev_put;
} }
err = pci_read_config_dword(rdev, AMD_PMC_SMU_INDEX_DATA, &val); dev->rdev = rdev;
err = amd_smn_read(0, AMD_PMC_BASE_ADDR_LO, &val);
if (err) { if (err) {
dev_err(dev->dev, "error reading 0x%x\n", AMD_PMC_BASE_ADDR_LO);
err = pcibios_err_to_errno(err); err = pcibios_err_to_errno(err);
goto err_pci_dev_put; goto err_pci_dev_put;
} }
base_addr_lo = val & AMD_PMC_BASE_ADDR_HI_MASK; base_addr_lo = val & AMD_PMC_BASE_ADDR_HI_MASK;
err = pci_write_config_dword(rdev, AMD_PMC_SMU_INDEX_ADDRESS, AMD_PMC_BASE_ADDR_HI); err = amd_smn_read(0, AMD_PMC_BASE_ADDR_HI, &val);
if (err) {
dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMC_SMU_INDEX_ADDRESS);
err = pcibios_err_to_errno(err);
goto err_pci_dev_put;
}
err = pci_read_config_dword(rdev, AMD_PMC_SMU_INDEX_DATA, &val);
if (err) { if (err) {
dev_err(dev->dev, "error reading 0x%x\n", AMD_PMC_BASE_ADDR_HI);
err = pcibios_err_to_errno(err); err = pcibios_err_to_errno(err);
goto err_pci_dev_put; goto err_pci_dev_put;
} }
...@@ -1022,7 +1023,7 @@ static int amd_pmc_probe(struct platform_device *pdev) ...@@ -1022,7 +1023,7 @@ static int amd_pmc_probe(struct platform_device *pdev)
return err; return err;
} }
static int amd_pmc_remove(struct platform_device *pdev) static void amd_pmc_remove(struct platform_device *pdev)
{ {
struct amd_pmc_dev *dev = platform_get_drvdata(pdev); struct amd_pmc_dev *dev = platform_get_drvdata(pdev);
...@@ -1031,7 +1032,6 @@ static int amd_pmc_remove(struct platform_device *pdev) ...@@ -1031,7 +1032,6 @@ static int amd_pmc_remove(struct platform_device *pdev)
amd_pmc_dbgfs_unregister(dev); amd_pmc_dbgfs_unregister(dev);
pci_dev_put(dev->rdev); pci_dev_put(dev->rdev);
mutex_destroy(&dev->lock); mutex_destroy(&dev->lock);
return 0;
} }
static const struct acpi_device_id amd_pmc_acpi_ids[] = { static const struct acpi_device_id amd_pmc_acpi_ids[] = {
...@@ -1054,7 +1054,7 @@ static struct platform_driver amd_pmc_driver = { ...@@ -1054,7 +1054,7 @@ static struct platform_driver amd_pmc_driver = {
.pm = pm_sleep_ptr(&amd_pmc_pm), .pm = pm_sleep_ptr(&amd_pmc_pm),
}, },
.probe = amd_pmc_probe, .probe = amd_pmc_probe,
.remove = amd_pmc_remove, .remove_new = amd_pmc_remove,
}; };
module_platform_driver(amd_pmc_driver); module_platform_driver(amd_pmc_driver);
......
...@@ -7,6 +7,7 @@ config AMD_PMF ...@@ -7,6 +7,7 @@ config AMD_PMF
tristate "AMD Platform Management Framework" tristate "AMD Platform Management Framework"
depends on ACPI && PCI depends on ACPI && PCI
depends on POWER_SUPPLY depends on POWER_SUPPLY
depends on AMD_NB
select ACPI_PLATFORM_PROFILE select ACPI_PLATFORM_PROFILE
help help
This driver provides support for the AMD Platform Management Framework. This driver provides support for the AMD Platform Management Framework.
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
* Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
*/ */
#include <asm/amd_nb.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -22,8 +23,6 @@ ...@@ -22,8 +23,6 @@
#define AMD_PMF_REGISTER_ARGUMENT 0xA58 #define AMD_PMF_REGISTER_ARGUMENT 0xA58
/* Base address of SMU for mapping physical address to virtual address */ /* Base address of SMU for mapping physical address to virtual address */
#define AMD_PMF_SMU_INDEX_ADDRESS 0xB8
#define AMD_PMF_SMU_INDEX_DATA 0xBC
#define AMD_PMF_MAPPING_SIZE 0x01000 #define AMD_PMF_MAPPING_SIZE 0x01000
#define AMD_PMF_BASE_ADDR_OFFSET 0x10000 #define AMD_PMF_BASE_ADDR_OFFSET 0x10000
#define AMD_PMF_BASE_ADDR_LO 0x13B102E8 #define AMD_PMF_BASE_ADDR_LO 0x13B102E8
...@@ -348,30 +347,19 @@ static int amd_pmf_probe(struct platform_device *pdev) ...@@ -348,30 +347,19 @@ static int amd_pmf_probe(struct platform_device *pdev)
} }
dev->cpu_id = rdev->device; dev->cpu_id = rdev->device;
err = pci_write_config_dword(rdev, AMD_PMF_SMU_INDEX_ADDRESS, AMD_PMF_BASE_ADDR_LO);
if (err) {
dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMF_SMU_INDEX_ADDRESS);
pci_dev_put(rdev);
return pcibios_err_to_errno(err);
}
err = pci_read_config_dword(rdev, AMD_PMF_SMU_INDEX_DATA, &val); err = amd_smn_read(0, AMD_PMF_BASE_ADDR_LO, &val);
if (err) { if (err) {
dev_err(dev->dev, "error in reading from 0x%x\n", AMD_PMF_BASE_ADDR_LO);
pci_dev_put(rdev); pci_dev_put(rdev);
return pcibios_err_to_errno(err); return pcibios_err_to_errno(err);
} }
base_addr_lo = val & AMD_PMF_BASE_ADDR_HI_MASK; base_addr_lo = val & AMD_PMF_BASE_ADDR_HI_MASK;
err = pci_write_config_dword(rdev, AMD_PMF_SMU_INDEX_ADDRESS, AMD_PMF_BASE_ADDR_HI); err = amd_smn_read(0, AMD_PMF_BASE_ADDR_HI, &val);
if (err) {
dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMF_SMU_INDEX_ADDRESS);
pci_dev_put(rdev);
return pcibios_err_to_errno(err);
}
err = pci_read_config_dword(rdev, AMD_PMF_SMU_INDEX_DATA, &val);
if (err) { if (err) {
dev_err(dev->dev, "error in reading from 0x%x\n", AMD_PMF_BASE_ADDR_HI);
pci_dev_put(rdev); pci_dev_put(rdev);
return pcibios_err_to_errno(err); return pcibios_err_to_errno(err);
} }
...@@ -402,7 +390,7 @@ static int amd_pmf_probe(struct platform_device *pdev) ...@@ -402,7 +390,7 @@ static int amd_pmf_probe(struct platform_device *pdev)
return 0; return 0;
} }
static int amd_pmf_remove(struct platform_device *pdev) static void amd_pmf_remove(struct platform_device *pdev)
{ {
struct amd_pmf_dev *dev = platform_get_drvdata(pdev); struct amd_pmf_dev *dev = platform_get_drvdata(pdev);
...@@ -413,7 +401,6 @@ static int amd_pmf_remove(struct platform_device *pdev) ...@@ -413,7 +401,6 @@ static int amd_pmf_remove(struct platform_device *pdev)
mutex_destroy(&dev->lock); mutex_destroy(&dev->lock);
mutex_destroy(&dev->update_mutex); mutex_destroy(&dev->update_mutex);
kfree(dev->buf); kfree(dev->buf);
return 0;
} }
static const struct attribute_group *amd_pmf_driver_groups[] = { static const struct attribute_group *amd_pmf_driver_groups[] = {
...@@ -428,7 +415,7 @@ static struct platform_driver amd_pmf_driver = { ...@@ -428,7 +415,7 @@ static struct platform_driver amd_pmf_driver = {
.dev_groups = amd_pmf_driver_groups, .dev_groups = amd_pmf_driver_groups,
}, },
.probe = amd_pmf_probe, .probe = amd_pmf_probe,
.remove = amd_pmf_remove, .remove_new = amd_pmf_remove,
}; };
module_platform_driver(amd_pmf_driver); module_platform_driver(amd_pmf_driver);
......
...@@ -124,11 +124,10 @@ static int amilo_rfkill_probe(struct platform_device *device) ...@@ -124,11 +124,10 @@ static int amilo_rfkill_probe(struct platform_device *device)
return rc; return rc;
} }
static int amilo_rfkill_remove(struct platform_device *device) static void amilo_rfkill_remove(struct platform_device *device)
{ {
rfkill_unregister(amilo_rfkill_dev); rfkill_unregister(amilo_rfkill_dev);
rfkill_destroy(amilo_rfkill_dev); rfkill_destroy(amilo_rfkill_dev);
return 0;
} }
static struct platform_driver amilo_rfkill_driver = { static struct platform_driver amilo_rfkill_driver = {
...@@ -136,7 +135,7 @@ static struct platform_driver amilo_rfkill_driver = { ...@@ -136,7 +135,7 @@ static struct platform_driver amilo_rfkill_driver = {
.name = KBUILD_MODNAME, .name = KBUILD_MODNAME,
}, },
.probe = amilo_rfkill_probe, .probe = amilo_rfkill_probe,
.remove = amilo_rfkill_remove, .remove_new = amilo_rfkill_remove,
}; };
static int __init amilo_rfkill_init(void) static int __init amilo_rfkill_init(void)
......
This diff is collapsed.
...@@ -370,7 +370,7 @@ static int p50_gpio_probe(struct platform_device *pdev) ...@@ -370,7 +370,7 @@ static int p50_gpio_probe(struct platform_device *pdev)
return ret; return ret;
} }
static int p50_gpio_remove(struct platform_device *pdev) static void p50_gpio_remove(struct platform_device *pdev)
{ {
struct p50_gpio *p50 = platform_get_drvdata(pdev); struct p50_gpio *p50 = platform_get_drvdata(pdev);
...@@ -378,8 +378,6 @@ static int p50_gpio_remove(struct platform_device *pdev) ...@@ -378,8 +378,6 @@ static int p50_gpio_remove(struct platform_device *pdev)
platform_device_unregister(p50->leds_pdev); platform_device_unregister(p50->leds_pdev);
gpiod_remove_lookup_table(&p50_gpio_led_table); gpiod_remove_lookup_table(&p50_gpio_led_table);
return 0;
} }
static struct platform_driver p50_gpio_driver = { static struct platform_driver p50_gpio_driver = {
...@@ -387,7 +385,7 @@ static struct platform_driver p50_gpio_driver = { ...@@ -387,7 +385,7 @@ static struct platform_driver p50_gpio_driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
}, },
.probe = p50_gpio_probe, .probe = p50_gpio_probe,
.remove = p50_gpio_remove, .remove_new = p50_gpio_remove,
}; };
/* Board setup */ /* Board setup */
......
...@@ -1134,7 +1134,7 @@ static void cmpc_exit(void) ...@@ -1134,7 +1134,7 @@ static void cmpc_exit(void)
module_init(cmpc_init); module_init(cmpc_init);
module_exit(cmpc_exit); module_exit(cmpc_exit);
static const struct acpi_device_id cmpc_device_ids[] = { static const struct acpi_device_id cmpc_device_ids[] __maybe_unused = {
{CMPC_ACCEL_HID, 0}, {CMPC_ACCEL_HID, 0},
{CMPC_ACCEL_HID_V4, 0}, {CMPC_ACCEL_HID_V4, 0},
{CMPC_TABLET_HID, 0}, {CMPC_TABLET_HID, 0},
......
...@@ -1003,12 +1003,12 @@ static int compal_probe(struct platform_device *pdev) ...@@ -1003,12 +1003,12 @@ static int compal_probe(struct platform_device *pdev)
return err; return err;
} }
static int compal_remove(struct platform_device *pdev) static void compal_remove(struct platform_device *pdev)
{ {
struct compal_data *data; struct compal_data *data;
if (!extra_features) if (!extra_features)
return 0; return;
pr_info("Unloading: resetting fan control to motherboard\n"); pr_info("Unloading: resetting fan control to motherboard\n");
pwm_disable_control(); pwm_disable_control();
...@@ -1017,8 +1017,6 @@ static int compal_remove(struct platform_device *pdev) ...@@ -1017,8 +1017,6 @@ static int compal_remove(struct platform_device *pdev)
power_supply_unregister(data->psy); power_supply_unregister(data->psy);
sysfs_remove_group(&pdev->dev.kobj, &compal_platform_attr_group); sysfs_remove_group(&pdev->dev.kobj, &compal_platform_attr_group);
return 0;
} }
static struct platform_driver compal_driver = { static struct platform_driver compal_driver = {
...@@ -1026,7 +1024,7 @@ static struct platform_driver compal_driver = { ...@@ -1026,7 +1024,7 @@ static struct platform_driver compal_driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
}, },
.probe = compal_probe, .probe = compal_probe,
.remove = compal_remove, .remove_new = compal_remove,
}; };
static int __init compal_init(void) static int __init compal_init(void)
......
...@@ -698,12 +698,10 @@ static int dcdbas_probe(struct platform_device *dev) ...@@ -698,12 +698,10 @@ static int dcdbas_probe(struct platform_device *dev)
return 0; return 0;
} }
static int dcdbas_remove(struct platform_device *dev) static void dcdbas_remove(struct platform_device *dev)
{ {
unregister_reboot_notifier(&dcdbas_reboot_nb); unregister_reboot_notifier(&dcdbas_reboot_nb);
sysfs_remove_group(&dev->dev.kobj, &dcdbas_attr_group); sysfs_remove_group(&dev->dev.kobj, &dcdbas_attr_group);
return 0;
} }
static struct platform_driver dcdbas_driver = { static struct platform_driver dcdbas_driver = {
...@@ -711,7 +709,7 @@ static struct platform_driver dcdbas_driver = { ...@@ -711,7 +709,7 @@ static struct platform_driver dcdbas_driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
}, },
.probe = dcdbas_probe, .probe = dcdbas_probe,
.remove = dcdbas_remove, .remove_new = dcdbas_remove,
}; };
static const struct platform_device_info dcdbas_dev_info __initconst = { static const struct platform_device_info dcdbas_dev_info __initconst = {
......
...@@ -97,6 +97,7 @@ static struct rfkill *bluetooth_rfkill; ...@@ -97,6 +97,7 @@ static struct rfkill *bluetooth_rfkill;
static struct rfkill *wwan_rfkill; static struct rfkill *wwan_rfkill;
static bool force_rfkill; static bool force_rfkill;
static bool micmute_led_registered; static bool micmute_led_registered;
static bool mute_led_registered;
module_param(force_rfkill, bool, 0444); module_param(force_rfkill, bool, 0444);
MODULE_PARM_DESC(force_rfkill, "enable rfkill on non whitelisted models"); MODULE_PARM_DESC(force_rfkill, "enable rfkill on non whitelisted models");
...@@ -2177,6 +2178,34 @@ static struct led_classdev micmute_led_cdev = { ...@@ -2177,6 +2178,34 @@ static struct led_classdev micmute_led_cdev = {
.default_trigger = "audio-micmute", .default_trigger = "audio-micmute",
}; };
static int mute_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct calling_interface_buffer buffer;
struct calling_interface_token *token;
int state = brightness != LED_OFF;
if (state == 0)
token = dell_smbios_find_token(GLOBAL_MUTE_DISABLE);
else
token = dell_smbios_find_token(GLOBAL_MUTE_ENABLE);
if (!token)
return -ENODEV;
dell_fill_request(&buffer, token->location, token->value, 0, 0);
dell_send_request(&buffer, CLASS_TOKEN_WRITE, SELECT_TOKEN_STD);
return 0;
}
static struct led_classdev mute_led_cdev = {
.name = "platform::mute",
.max_brightness = 1,
.brightness_set_blocking = mute_led_set,
.default_trigger = "audio-mute",
};
static int __init dell_init(void) static int __init dell_init(void)
{ {
struct calling_interface_token *token; struct calling_interface_token *token;
...@@ -2230,6 +2259,15 @@ static int __init dell_init(void) ...@@ -2230,6 +2259,15 @@ static int __init dell_init(void)
micmute_led_registered = true; micmute_led_registered = true;
} }
if (dell_smbios_find_token(GLOBAL_MUTE_DISABLE) &&
dell_smbios_find_token(GLOBAL_MUTE_ENABLE)) {
mute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MUTE);
ret = led_classdev_register(&platform_device->dev, &mute_led_cdev);
if (ret < 0)
goto fail_backlight;
mute_led_registered = true;
}
if (acpi_video_get_backlight_type() != acpi_backlight_vendor) if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
return 0; return 0;
...@@ -2277,6 +2315,8 @@ static int __init dell_init(void) ...@@ -2277,6 +2315,8 @@ static int __init dell_init(void)
fail_backlight: fail_backlight:
if (micmute_led_registered) if (micmute_led_registered)
led_classdev_unregister(&micmute_led_cdev); led_classdev_unregister(&micmute_led_cdev);
if (mute_led_registered)
led_classdev_unregister(&mute_led_cdev);
fail_led: fail_led:
dell_cleanup_rfkill(); dell_cleanup_rfkill();
fail_rfkill: fail_rfkill:
...@@ -2299,6 +2339,8 @@ static void __exit dell_exit(void) ...@@ -2299,6 +2339,8 @@ static void __exit dell_exit(void)
backlight_device_unregister(dell_backlight_device); backlight_device_unregister(dell_backlight_device);
if (micmute_led_registered) if (micmute_led_registered)
led_classdev_unregister(&micmute_led_cdev); led_classdev_unregister(&micmute_led_cdev);
if (mute_led_registered)
led_classdev_unregister(&mute_led_cdev);
dell_cleanup_rfkill(); dell_cleanup_rfkill();
if (platform_device) { if (platform_device) {
platform_device_unregister(platform_device); platform_device_unregister(platform_device);
......
...@@ -34,6 +34,8 @@ ...@@ -34,6 +34,8 @@
#define KBD_LED_AUTO_100_TOKEN 0x02F6 #define KBD_LED_AUTO_100_TOKEN 0x02F6
#define GLOBAL_MIC_MUTE_ENABLE 0x0364 #define GLOBAL_MIC_MUTE_ENABLE 0x0364
#define GLOBAL_MIC_MUTE_DISABLE 0x0365 #define GLOBAL_MIC_MUTE_DISABLE 0x0365
#define GLOBAL_MUTE_ENABLE 0x058C
#define GLOBAL_MUTE_DISABLE 0x058D
struct notifier_block; struct notifier_block;
......
...@@ -154,14 +154,13 @@ static int smo8800_probe(struct platform_device *device) ...@@ -154,14 +154,13 @@ static int smo8800_probe(struct platform_device *device)
return err; return err;
} }
static int smo8800_remove(struct platform_device *device) static void smo8800_remove(struct platform_device *device)
{ {
struct smo8800_device *smo8800 = platform_get_drvdata(device); struct smo8800_device *smo8800 = platform_get_drvdata(device);
free_irq(smo8800->irq, smo8800); free_irq(smo8800->irq, smo8800);
misc_deregister(&smo8800->miscdev); misc_deregister(&smo8800->miscdev);
dev_dbg(&device->dev, "device /dev/freefall unregistered\n"); dev_dbg(&device->dev, "device /dev/freefall unregistered\n");
return 0;
} }
/* NOTE: Keep this list in sync with drivers/i2c/busses/i2c-i801.c */ /* NOTE: Keep this list in sync with drivers/i2c/busses/i2c-i801.c */
...@@ -180,7 +179,7 @@ MODULE_DEVICE_TABLE(acpi, smo8800_ids); ...@@ -180,7 +179,7 @@ MODULE_DEVICE_TABLE(acpi, smo8800_ids);
static struct platform_driver smo8800_driver = { static struct platform_driver smo8800_driver = {
.probe = smo8800_probe, .probe = smo8800_probe,
.remove = smo8800_remove, .remove_new = smo8800_remove,
.driver = { .driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.acpi_match_table = smo8800_ids, .acpi_match_table = smo8800_ids,
......
...@@ -342,7 +342,7 @@ static int lis3lv02d_probe(struct platform_device *device) ...@@ -342,7 +342,7 @@ static int lis3lv02d_probe(struct platform_device *device)
return ret; return ret;
} }
static int lis3lv02d_remove(struct platform_device *device) static void lis3lv02d_remove(struct platform_device *device)
{ {
i8042_remove_filter(hp_accel_i8042_filter); i8042_remove_filter(hp_accel_i8042_filter);
lis3lv02d_joystick_disable(&lis3_dev); lis3lv02d_joystick_disable(&lis3_dev);
...@@ -352,7 +352,6 @@ static int lis3lv02d_remove(struct platform_device *device) ...@@ -352,7 +352,6 @@ static int lis3lv02d_remove(struct platform_device *device)
flush_work(&hpled_led.work); flush_work(&hpled_led.work);
lis3lv02d_remove_fs(&lis3_dev); lis3lv02d_remove_fs(&lis3_dev);
return 0;
} }
static int __maybe_unused lis3lv02d_suspend(struct device *dev) static int __maybe_unused lis3lv02d_suspend(struct device *dev)
...@@ -373,7 +372,7 @@ static SIMPLE_DEV_PM_OPS(hp_accel_pm, lis3lv02d_suspend, lis3lv02d_resume); ...@@ -373,7 +372,7 @@ static SIMPLE_DEV_PM_OPS(hp_accel_pm, lis3lv02d_suspend, lis3lv02d_resume);
/* For the HP MDPS aka 3D Driveguard */ /* For the HP MDPS aka 3D Driveguard */
static struct platform_driver lis3lv02d_driver = { static struct platform_driver lis3lv02d_driver = {
.probe = lis3lv02d_probe, .probe = lis3lv02d_probe,
.remove = lis3lv02d_remove, .remove_new = lis3lv02d_remove,
.driver = { .driver = {
.name = "hp_accel", .name = "hp_accel",
.pm = &hp_accel_pm, .pm = &hp_accel_pm,
......
...@@ -170,11 +170,9 @@ static int __init tc1100_probe(struct platform_device *device) ...@@ -170,11 +170,9 @@ static int __init tc1100_probe(struct platform_device *device)
} }
static int tc1100_remove(struct platform_device *device) static void tc1100_remove(struct platform_device *device)
{ {
sysfs_remove_group(&device->dev.kobj, &tc1100_attribute_group); sysfs_remove_group(&device->dev.kobj, &tc1100_attribute_group);
return 0;
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
...@@ -223,7 +221,7 @@ static struct platform_driver tc1100_driver = { ...@@ -223,7 +221,7 @@ static struct platform_driver tc1100_driver = {
.pm = &tc1100_pm_ops, .pm = &tc1100_pm_ops,
#endif #endif
}, },
.remove = tc1100_remove, .remove_new = tc1100_remove,
}; };
static int __init tc1100_init(void) static int __init tc1100_init(void)
......
...@@ -830,7 +830,7 @@ static int huawei_wmi_probe(struct platform_device *pdev) ...@@ -830,7 +830,7 @@ static int huawei_wmi_probe(struct platform_device *pdev)
return 0; return 0;
} }
static int huawei_wmi_remove(struct platform_device *pdev) static void huawei_wmi_remove(struct platform_device *pdev)
{ {
const struct wmi_device_id *guid = huawei_wmi_events_id_table; const struct wmi_device_id *guid = huawei_wmi_events_id_table;
...@@ -846,8 +846,6 @@ static int huawei_wmi_remove(struct platform_device *pdev) ...@@ -846,8 +846,6 @@ static int huawei_wmi_remove(struct platform_device *pdev)
huawei_wmi_battery_exit(&pdev->dev); huawei_wmi_battery_exit(&pdev->dev);
huawei_wmi_fn_lock_exit(&pdev->dev); huawei_wmi_fn_lock_exit(&pdev->dev);
} }
return 0;
} }
static struct platform_driver huawei_wmi_driver = { static struct platform_driver huawei_wmi_driver = {
...@@ -855,7 +853,7 @@ static struct platform_driver huawei_wmi_driver = { ...@@ -855,7 +853,7 @@ static struct platform_driver huawei_wmi_driver = {
.name = "huawei-wmi", .name = "huawei-wmi",
}, },
.probe = huawei_wmi_probe, .probe = huawei_wmi_probe,
.remove = huawei_wmi_remove, .remove_new = huawei_wmi_remove,
}; };
static __init int huawei_wmi_init(void) static __init int huawei_wmi_init(void)
......
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/input/sparse-keymap.h> #include <linux/input/sparse-keymap.h>
#include <linux/jiffies.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -31,6 +30,7 @@ ...@@ -31,6 +30,7 @@
#include <linux/sysfs.h> #include <linux/sysfs.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/wmi.h> #include <linux/wmi.h>
#include "ideapad-laptop.h"
#include <acpi/video.h> #include <acpi/video.h>
...@@ -85,33 +85,6 @@ enum { ...@@ -85,33 +85,6 @@ enum {
SALS_FNLOCK_OFF = 0xf, SALS_FNLOCK_OFF = 0xf,
}; };
enum {
VPCCMD_R_VPC1 = 0x10,
VPCCMD_R_BL_MAX,
VPCCMD_R_BL,
VPCCMD_W_BL,
VPCCMD_R_WIFI,
VPCCMD_W_WIFI,
VPCCMD_R_BT,
VPCCMD_W_BT,
VPCCMD_R_BL_POWER,
VPCCMD_R_NOVO,
VPCCMD_R_VPC2,
VPCCMD_R_TOUCHPAD,
VPCCMD_W_TOUCHPAD,
VPCCMD_R_CAMERA,
VPCCMD_W_CAMERA,
VPCCMD_R_3G,
VPCCMD_W_3G,
VPCCMD_R_ODD, /* 0x21 */
VPCCMD_W_FAN,
VPCCMD_R_RF,
VPCCMD_W_RF,
VPCCMD_R_FAN = 0x2B,
VPCCMD_R_SPECIAL_BUTTONS = 0x31,
VPCCMD_W_BL_POWER = 0x33,
};
struct ideapad_dytc_priv { struct ideapad_dytc_priv {
enum platform_profile_option current_profile; enum platform_profile_option current_profile;
struct platform_profile_handler pprof; struct platform_profile_handler pprof;
...@@ -227,7 +200,6 @@ static void ideapad_shared_exit(struct ideapad_private *priv) ...@@ -227,7 +200,6 @@ static void ideapad_shared_exit(struct ideapad_private *priv)
/* /*
* ACPI Helpers * ACPI Helpers
*/ */
#define IDEAPAD_EC_TIMEOUT 200 /* in ms */
static int eval_int(acpi_handle handle, const char *name, unsigned long *res) static int eval_int(acpi_handle handle, const char *name, unsigned long *res)
{ {
...@@ -270,116 +242,11 @@ static int exec_sals(acpi_handle handle, unsigned long arg) ...@@ -270,116 +242,11 @@ static int exec_sals(acpi_handle handle, unsigned long arg)
return exec_simple_method(handle, "SALS", arg); return exec_simple_method(handle, "SALS", arg);
} }
static int eval_int_with_arg(acpi_handle handle, const char *name, unsigned long arg, unsigned long *res)
{
struct acpi_object_list params;
unsigned long long result;
union acpi_object in_obj;
acpi_status status;
params.count = 1;
params.pointer = &in_obj;
in_obj.type = ACPI_TYPE_INTEGER;
in_obj.integer.value = arg;
status = acpi_evaluate_integer(handle, (char *)name, &params, &result);
if (ACPI_FAILURE(status))
return -EIO;
if (res)
*res = result;
return 0;
}
static int eval_dytc(acpi_handle handle, unsigned long cmd, unsigned long *res) static int eval_dytc(acpi_handle handle, unsigned long cmd, unsigned long *res)
{ {
return eval_int_with_arg(handle, "DYTC", cmd, res); return eval_int_with_arg(handle, "DYTC", cmd, res);
} }
static int eval_vpcr(acpi_handle handle, unsigned long cmd, unsigned long *res)
{
return eval_int_with_arg(handle, "VPCR", cmd, res);
}
static int eval_vpcw(acpi_handle handle, unsigned long cmd, unsigned long data)
{
struct acpi_object_list params;
union acpi_object in_obj[2];
acpi_status status;
params.count = 2;
params.pointer = in_obj;
in_obj[0].type = ACPI_TYPE_INTEGER;
in_obj[0].integer.value = cmd;
in_obj[1].type = ACPI_TYPE_INTEGER;
in_obj[1].integer.value = data;
status = acpi_evaluate_object(handle, "VPCW", &params, NULL);
if (ACPI_FAILURE(status))
return -EIO;
return 0;
}
static int read_ec_data(acpi_handle handle, unsigned long cmd, unsigned long *data)
{
unsigned long end_jiffies, val;
int err;
err = eval_vpcw(handle, 1, cmd);
if (err)
return err;
end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1;
while (time_before(jiffies, end_jiffies)) {
schedule();
err = eval_vpcr(handle, 1, &val);
if (err)
return err;
if (val == 0)
return eval_vpcr(handle, 0, data);
}
acpi_handle_err(handle, "timeout in %s\n", __func__);
return -ETIMEDOUT;
}
static int write_ec_cmd(acpi_handle handle, unsigned long cmd, unsigned long data)
{
unsigned long end_jiffies, val;
int err;
err = eval_vpcw(handle, 0, data);
if (err)
return err;
err = eval_vpcw(handle, 1, cmd);
if (err)
return err;
end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1;
while (time_before(jiffies, end_jiffies)) {
schedule();
err = eval_vpcr(handle, 1, &val);
if (err)
return err;
if (val == 0)
return 0;
}
acpi_handle_err(handle, "timeout in %s\n", __func__);
return -ETIMEDOUT;
}
/* /*
* debugfs * debugfs
*/ */
...@@ -1918,7 +1785,7 @@ static int ideapad_acpi_add(struct platform_device *pdev) ...@@ -1918,7 +1785,7 @@ static int ideapad_acpi_add(struct platform_device *pdev)
return err; return err;
} }
static int ideapad_acpi_remove(struct platform_device *pdev) static void ideapad_acpi_remove(struct platform_device *pdev)
{ {
struct ideapad_private *priv = dev_get_drvdata(&pdev->dev); struct ideapad_private *priv = dev_get_drvdata(&pdev->dev);
int i; int i;
...@@ -1939,8 +1806,6 @@ static int ideapad_acpi_remove(struct platform_device *pdev) ...@@ -1939,8 +1806,6 @@ static int ideapad_acpi_remove(struct platform_device *pdev)
ideapad_input_exit(priv); ideapad_input_exit(priv);
ideapad_debugfs_exit(priv); ideapad_debugfs_exit(priv);
ideapad_sysfs_exit(priv); ideapad_sysfs_exit(priv);
return 0;
} }
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
...@@ -1967,7 +1832,7 @@ MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); ...@@ -1967,7 +1832,7 @@ MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
static struct platform_driver ideapad_acpi_driver = { static struct platform_driver ideapad_acpi_driver = {
.probe = ideapad_acpi_add, .probe = ideapad_acpi_add,
.remove = ideapad_acpi_remove, .remove_new = ideapad_acpi_remove,
.driver = { .driver = {
.name = "ideapad_acpi", .name = "ideapad_acpi",
.pm = &ideapad_pm, .pm = &ideapad_pm,
......
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ideapad-laptop.h - Lenovo IdeaPad ACPI Extras
*
* Copyright © 2010 Intel Corporation
* Copyright © 2010 David Woodhouse <dwmw2@infradead.org>
*/
#ifndef _IDEAPAD_LAPTOP_H_
#define _IDEAPAD_LAPTOP_H_
#include <linux/acpi.h>
#include <linux/jiffies.h>
#include <linux/errno.h>
enum {
VPCCMD_R_VPC1 = 0x10,
VPCCMD_R_BL_MAX,
VPCCMD_R_BL,
VPCCMD_W_BL,
VPCCMD_R_WIFI,
VPCCMD_W_WIFI,
VPCCMD_R_BT,
VPCCMD_W_BT,
VPCCMD_R_BL_POWER,
VPCCMD_R_NOVO,
VPCCMD_R_VPC2,
VPCCMD_R_TOUCHPAD,
VPCCMD_W_TOUCHPAD,
VPCCMD_R_CAMERA,
VPCCMD_W_CAMERA,
VPCCMD_R_3G,
VPCCMD_W_3G,
VPCCMD_R_ODD, /* 0x21 */
VPCCMD_W_FAN,
VPCCMD_R_RF,
VPCCMD_W_RF,
VPCCMD_W_YMC = 0x2A,
VPCCMD_R_FAN = 0x2B,
VPCCMD_R_SPECIAL_BUTTONS = 0x31,
VPCCMD_W_BL_POWER = 0x33,
};
static inline int eval_int_with_arg(acpi_handle handle, const char *name, unsigned long arg, unsigned long *res)
{
struct acpi_object_list params;
unsigned long long result;
union acpi_object in_obj;
acpi_status status;
params.count = 1;
params.pointer = &in_obj;
in_obj.type = ACPI_TYPE_INTEGER;
in_obj.integer.value = arg;
status = acpi_evaluate_integer(handle, (char *)name, &params, &result);
if (ACPI_FAILURE(status))
return -EIO;
if (res)
*res = result;
return 0;
}
static inline int eval_vpcr(acpi_handle handle, unsigned long cmd, unsigned long *res)
{
return eval_int_with_arg(handle, "VPCR", cmd, res);
}
static inline int eval_vpcw(acpi_handle handle, unsigned long cmd, unsigned long data)
{
struct acpi_object_list params;
union acpi_object in_obj[2];
acpi_status status;
params.count = 2;
params.pointer = in_obj;
in_obj[0].type = ACPI_TYPE_INTEGER;
in_obj[0].integer.value = cmd;
in_obj[1].type = ACPI_TYPE_INTEGER;
in_obj[1].integer.value = data;
status = acpi_evaluate_object(handle, "VPCW", &params, NULL);
if (ACPI_FAILURE(status))
return -EIO;
return 0;
}
#define IDEAPAD_EC_TIMEOUT 200 /* in ms */
static inline int read_ec_data(acpi_handle handle, unsigned long cmd, unsigned long *data)
{
unsigned long end_jiffies, val;
int err;
err = eval_vpcw(handle, 1, cmd);
if (err)
return err;
end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1;
while (time_before(jiffies, end_jiffies)) {
schedule();
err = eval_vpcr(handle, 1, &val);
if (err)
return err;
if (val == 0)
return eval_vpcr(handle, 0, data);
}
acpi_handle_err(handle, "timeout in %s\n", __func__);
return -ETIMEDOUT;
}
static inline int write_ec_cmd(acpi_handle handle, unsigned long cmd, unsigned long data)
{
unsigned long end_jiffies, val;
int err;
err = eval_vpcw(handle, 0, data);
if (err)
return err;
err = eval_vpcw(handle, 1, cmd);
if (err)
return err;
end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1;
while (time_before(jiffies, end_jiffies)) {
schedule();
err = eval_vpcr(handle, 1, &val);
if (err)
return err;
if (val == 0)
return 0;
}
acpi_handle_err(handle, "timeout in %s\n", __func__);
return -ETIMEDOUT;
}
#undef IDEAPAD_EC_TIMEOUT
#endif /* !_IDEAPAD_LAPTOP_H_ */
...@@ -80,6 +80,16 @@ config INTEL_BXTWC_PMIC_TMU ...@@ -80,6 +80,16 @@ config INTEL_BXTWC_PMIC_TMU
This driver enables the alarm wakeup functionality in the TMU unit of This driver enables the alarm wakeup functionality in the TMU unit of
Whiskey Cove PMIC. Whiskey Cove PMIC.
config INTEL_BYTCRC_PWRSRC
tristate "Intel Bay Trail Crystal Cove power source driver"
depends on INTEL_SOC_PMIC
help
This option adds a power source driver for Crystal Cove PMICs
on Intel Bay Trail devices.
To compile this driver as a module, choose M here: the module
will be called intel_bytcrc_pwrsrc.
config INTEL_CHTDC_TI_PWRBTN config INTEL_CHTDC_TI_PWRBTN
tristate "Intel Cherry Trail Dollar Cove TI power button driver" tristate "Intel Cherry Trail Dollar Cove TI power button driver"
depends on INTEL_SOC_PMIC_CHTDC_TI depends on INTEL_SOC_PMIC_CHTDC_TI
......
...@@ -38,6 +38,8 @@ intel_bxtwc_tmu-y := bxtwc_tmu.o ...@@ -38,6 +38,8 @@ intel_bxtwc_tmu-y := bxtwc_tmu.o
obj-$(CONFIG_INTEL_BXTWC_PMIC_TMU) += intel_bxtwc_tmu.o obj-$(CONFIG_INTEL_BXTWC_PMIC_TMU) += intel_bxtwc_tmu.o
intel_crystal_cove_charger-y := crystal_cove_charger.o intel_crystal_cove_charger-y := crystal_cove_charger.o
obj-$(CONFIG_X86_ANDROID_TABLETS) += intel_crystal_cove_charger.o obj-$(CONFIG_X86_ANDROID_TABLETS) += intel_crystal_cove_charger.o
intel_bytcrc_pwrsrc-y := bytcrc_pwrsrc.o
obj-$(CONFIG_INTEL_BYTCRC_PWRSRC) += intel_bytcrc_pwrsrc.o
intel_chtdc_ti_pwrbtn-y := chtdc_ti_pwrbtn.o intel_chtdc_ti_pwrbtn-y := chtdc_ti_pwrbtn.o
obj-$(CONFIG_INTEL_CHTDC_TI_PWRBTN) += intel_chtdc_ti_pwrbtn.o obj-$(CONFIG_INTEL_CHTDC_TI_PWRBTN) += intel_chtdc_ti_pwrbtn.o
intel_chtwc_int33fe-y := chtwc_int33fe.o intel_chtwc_int33fe-y := chtwc_int33fe.o
......
...@@ -89,7 +89,7 @@ static int bxt_wcove_tmu_probe(struct platform_device *pdev) ...@@ -89,7 +89,7 @@ static int bxt_wcove_tmu_probe(struct platform_device *pdev)
return 0; return 0;
} }
static int bxt_wcove_tmu_remove(struct platform_device *pdev) static void bxt_wcove_tmu_remove(struct platform_device *pdev)
{ {
struct wcove_tmu *wctmu = platform_get_drvdata(pdev); struct wcove_tmu *wctmu = platform_get_drvdata(pdev);
unsigned int val; unsigned int val;
...@@ -101,7 +101,6 @@ static int bxt_wcove_tmu_remove(struct platform_device *pdev) ...@@ -101,7 +101,6 @@ static int bxt_wcove_tmu_remove(struct platform_device *pdev)
regmap_read(wctmu->regmap, BXTWC_MTMUIRQ_REG, &val); regmap_read(wctmu->regmap, BXTWC_MTMUIRQ_REG, &val);
regmap_write(wctmu->regmap, BXTWC_MTMUIRQ_REG, regmap_write(wctmu->regmap, BXTWC_MTMUIRQ_REG,
val | BXTWC_TMU_ALRM_MASK); val | BXTWC_TMU_ALRM_MASK);
return 0;
} }
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
...@@ -132,7 +131,7 @@ MODULE_DEVICE_TABLE(platform, bxt_wcove_tmu_id_table); ...@@ -132,7 +131,7 @@ MODULE_DEVICE_TABLE(platform, bxt_wcove_tmu_id_table);
static struct platform_driver bxt_wcove_tmu_driver = { static struct platform_driver bxt_wcove_tmu_driver = {
.probe = bxt_wcove_tmu_probe, .probe = bxt_wcove_tmu_probe,
.remove = bxt_wcove_tmu_remove, .remove_new = bxt_wcove_tmu_remove,
.driver = { .driver = {
.name = "bxt_wcove_tmu", .name = "bxt_wcove_tmu",
.pm = &bxtwc_tmu_pm_ops, .pm = &bxtwc_tmu_pm_ops,
......
// SPDX-License-Identifier: GPL-2.0
/*
* Power-source driver for Bay Trail Crystal Cove PMIC
*
* Copyright (c) 2023 Hans de Goede <hdegoede@redhat.com>
*
* Based on intel_crystalcove_pwrsrc.c from Android kernel sources, which is:
* Copyright (C) 2013 Intel Corporation
*/
#include <linux/debugfs.h>
#include <linux/mfd/intel_soc_pmic.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#define CRYSTALCOVE_SPWRSRC_REG 0x1E
#define CRYSTALCOVE_RESETSRC0_REG 0x20
#define CRYSTALCOVE_RESETSRC1_REG 0x21
#define CRYSTALCOVE_WAKESRC_REG 0x22
struct crc_pwrsrc_data {
struct regmap *regmap;
struct dentry *debug_dentry;
unsigned int resetsrc0;
unsigned int resetsrc1;
unsigned int wakesrc;
};
static const char * const pwrsrc_pwrsrc_info[] = {
/* bit 0 */ "USB",
/* bit 1 */ "DC in",
/* bit 2 */ "Battery",
NULL,
};
static const char * const pwrsrc_resetsrc0_info[] = {
/* bit 0 */ "SOC reporting a thermal event",
/* bit 1 */ "critical PMIC temperature",
/* bit 2 */ "critical system temperature",
/* bit 3 */ "critical battery temperature",
/* bit 4 */ "VSYS under voltage",
/* bit 5 */ "VSYS over voltage",
/* bit 6 */ "battery removal",
NULL,
};
static const char * const pwrsrc_resetsrc1_info[] = {
/* bit 0 */ "VCRIT threshold",
/* bit 1 */ "BATID reporting battery removal",
/* bit 2 */ "user pressing the power button",
NULL,
};
static const char * const pwrsrc_wakesrc_info[] = {
/* bit 0 */ "user pressing the power button",
/* bit 1 */ "a battery insertion",
/* bit 2 */ "a USB charger insertion",
/* bit 3 */ "an adapter insertion",
NULL,
};
static void crc_pwrsrc_log(struct seq_file *seq, const char *prefix,
const char * const *info, unsigned int reg_val)
{
int i;
for (i = 0; info[i]; i++) {
if (reg_val & BIT(i))
seq_printf(seq, "%s by %s\n", prefix, info[i]);
}
}
static int pwrsrc_show(struct seq_file *seq, void *unused)
{
struct crc_pwrsrc_data *data = seq->private;
unsigned int reg_val;
int ret;
ret = regmap_read(data->regmap, CRYSTALCOVE_SPWRSRC_REG, &reg_val);
if (ret)
return ret;
crc_pwrsrc_log(seq, "System powered", pwrsrc_pwrsrc_info, reg_val);
return 0;
}
static int resetsrc_show(struct seq_file *seq, void *unused)
{
struct crc_pwrsrc_data *data = seq->private;
crc_pwrsrc_log(seq, "Last shutdown caused", pwrsrc_resetsrc0_info, data->resetsrc0);
crc_pwrsrc_log(seq, "Last shutdown caused", pwrsrc_resetsrc1_info, data->resetsrc1);
return 0;
}
static int wakesrc_show(struct seq_file *seq, void *unused)
{
struct crc_pwrsrc_data *data = seq->private;
crc_pwrsrc_log(seq, "Last wake caused", pwrsrc_wakesrc_info, data->wakesrc);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(pwrsrc);
DEFINE_SHOW_ATTRIBUTE(resetsrc);
DEFINE_SHOW_ATTRIBUTE(wakesrc);
static int crc_pwrsrc_read_and_clear(struct crc_pwrsrc_data *data,
unsigned int reg, unsigned int *val)
{
int ret;
ret = regmap_read(data->regmap, reg, val);
if (ret)
return ret;
return regmap_write(data->regmap, reg, *val);
}
static int crc_pwrsrc_probe(struct platform_device *pdev)
{
struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
struct crc_pwrsrc_data *data;
int ret;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->regmap = pmic->regmap;
/*
* Read + clear resetsrc0/1 and wakesrc now, so that they get
* cleared even if the debugfs interface is never used.
*
* Properly clearing the wakesrc is important, leaving bit 0 of it
* set turns reboot into poweroff on some tablets.
*/
ret = crc_pwrsrc_read_and_clear(data, CRYSTALCOVE_RESETSRC0_REG, &data->resetsrc0);
if (ret)
return ret;
ret = crc_pwrsrc_read_and_clear(data, CRYSTALCOVE_RESETSRC1_REG, &data->resetsrc1);
if (ret)
return ret;
ret = crc_pwrsrc_read_and_clear(data, CRYSTALCOVE_WAKESRC_REG, &data->wakesrc);
if (ret)
return ret;
data->debug_dentry = debugfs_create_dir(KBUILD_MODNAME, NULL);
debugfs_create_file("pwrsrc", 0444, data->debug_dentry, data, &pwrsrc_fops);
debugfs_create_file("resetsrc", 0444, data->debug_dentry, data, &resetsrc_fops);
debugfs_create_file("wakesrc", 0444, data->debug_dentry, data, &wakesrc_fops);
platform_set_drvdata(pdev, data);
return 0;
}
static int crc_pwrsrc_remove(struct platform_device *pdev)
{
struct crc_pwrsrc_data *data = platform_get_drvdata(pdev);
debugfs_remove_recursive(data->debug_dentry);
return 0;
}
static struct platform_driver crc_pwrsrc_driver = {
.probe = crc_pwrsrc_probe,
.remove = crc_pwrsrc_remove,
.driver = {
.name = "crystal_cove_pwrsrc",
},
};
module_platform_driver(crc_pwrsrc_driver);
MODULE_ALIAS("platform:crystal_cove_pwrsrc");
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_DESCRIPTION("Power-source driver for Bay Trail Crystal Cove PMIC");
MODULE_LICENSE("GPL");
...@@ -67,11 +67,10 @@ static int chtdc_ti_pwrbtn_probe(struct platform_device *pdev) ...@@ -67,11 +67,10 @@ static int chtdc_ti_pwrbtn_probe(struct platform_device *pdev)
return 0; return 0;
} }
static int chtdc_ti_pwrbtn_remove(struct platform_device *pdev) static void chtdc_ti_pwrbtn_remove(struct platform_device *pdev)
{ {
dev_pm_clear_wake_irq(&pdev->dev); dev_pm_clear_wake_irq(&pdev->dev);
device_init_wakeup(&pdev->dev, false); device_init_wakeup(&pdev->dev, false);
return 0;
} }
static const struct platform_device_id chtdc_ti_pwrbtn_id_table[] = { static const struct platform_device_id chtdc_ti_pwrbtn_id_table[] = {
...@@ -85,7 +84,7 @@ static struct platform_driver chtdc_ti_pwrbtn_driver = { ...@@ -85,7 +84,7 @@ static struct platform_driver chtdc_ti_pwrbtn_driver = {
.name = KBUILD_MODNAME, .name = KBUILD_MODNAME,
}, },
.probe = chtdc_ti_pwrbtn_probe, .probe = chtdc_ti_pwrbtn_probe,
.remove = chtdc_ti_pwrbtn_remove, .remove_new = chtdc_ti_pwrbtn_remove,
.id_table = chtdc_ti_pwrbtn_id_table, .id_table = chtdc_ti_pwrbtn_id_table,
}; };
module_platform_driver(chtdc_ti_pwrbtn_driver); module_platform_driver(chtdc_ti_pwrbtn_driver);
......
...@@ -405,7 +405,7 @@ static int cht_int33fe_typec_probe(struct platform_device *pdev) ...@@ -405,7 +405,7 @@ static int cht_int33fe_typec_probe(struct platform_device *pdev)
return ret; return ret;
} }
static int cht_int33fe_typec_remove(struct platform_device *pdev) static void cht_int33fe_typec_remove(struct platform_device *pdev)
{ {
struct cht_int33fe_data *data = platform_get_drvdata(pdev); struct cht_int33fe_data *data = platform_get_drvdata(pdev);
...@@ -414,8 +414,6 @@ static int cht_int33fe_typec_remove(struct platform_device *pdev) ...@@ -414,8 +414,6 @@ static int cht_int33fe_typec_remove(struct platform_device *pdev)
i2c_unregister_device(data->battery_fg); i2c_unregister_device(data->battery_fg);
cht_int33fe_remove_nodes(data); cht_int33fe_remove_nodes(data);
return 0;
} }
static const struct acpi_device_id cht_int33fe_acpi_ids[] = { static const struct acpi_device_id cht_int33fe_acpi_ids[] = {
...@@ -429,7 +427,7 @@ static struct platform_driver cht_int33fe_typec_driver = { ...@@ -429,7 +427,7 @@ static struct platform_driver cht_int33fe_typec_driver = {
.acpi_match_table = ACPI_PTR(cht_int33fe_acpi_ids), .acpi_match_table = ACPI_PTR(cht_int33fe_acpi_ids),
}, },
.probe = cht_int33fe_typec_probe, .probe = cht_int33fe_typec_probe,
.remove = cht_int33fe_typec_remove, .remove_new = cht_int33fe_typec_remove,
}; };
module_platform_driver(cht_int33fe_typec_driver); module_platform_driver(cht_int33fe_typec_driver);
......
...@@ -720,7 +720,7 @@ static int intel_hid_probe(struct platform_device *device) ...@@ -720,7 +720,7 @@ static int intel_hid_probe(struct platform_device *device)
return err; return err;
} }
static int intel_hid_remove(struct platform_device *device) static void intel_hid_remove(struct platform_device *device)
{ {
acpi_handle handle = ACPI_HANDLE(&device->dev); acpi_handle handle = ACPI_HANDLE(&device->dev);
...@@ -728,12 +728,6 @@ static int intel_hid_remove(struct platform_device *device) ...@@ -728,12 +728,6 @@ static int intel_hid_remove(struct platform_device *device)
acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler); acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
intel_hid_set_enable(&device->dev, false); intel_hid_set_enable(&device->dev, false);
intel_button_array_enable(&device->dev, false); intel_button_array_enable(&device->dev, false);
/*
* Even if we failed to shut off the event stream, we can still
* safely detach from the device.
*/
return 0;
} }
static struct platform_driver intel_hid_pl_driver = { static struct platform_driver intel_hid_pl_driver = {
...@@ -743,7 +737,7 @@ static struct platform_driver intel_hid_pl_driver = { ...@@ -743,7 +737,7 @@ static struct platform_driver intel_hid_pl_driver = {
.pm = &intel_hid_pl_pm_ops, .pm = &intel_hid_pl_pm_ops,
}, },
.probe = intel_hid_probe, .probe = intel_hid_probe,
.remove = intel_hid_remove, .remove_new = intel_hid_remove,
}; };
/* /*
......
...@@ -16,27 +16,63 @@ ...@@ -16,27 +16,63 @@
static const struct x86_cpu_id ifs_cpu_ids[] __initconst = { static const struct x86_cpu_id ifs_cpu_ids[] __initconst = {
X86_MATCH(SAPPHIRERAPIDS_X), X86_MATCH(SAPPHIRERAPIDS_X),
X86_MATCH(EMERALDRAPIDS_X),
{} {}
}; };
MODULE_DEVICE_TABLE(x86cpu, ifs_cpu_ids); MODULE_DEVICE_TABLE(x86cpu, ifs_cpu_ids);
static struct ifs_device ifs_device = { ATTRIBUTE_GROUPS(plat_ifs);
.data = { ATTRIBUTE_GROUPS(plat_ifs_array);
.integrity_cap_bit = MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT,
.test_num = 0, bool *ifs_pkg_auth;
static const struct ifs_test_caps scan_test = {
.integrity_cap_bit = MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT,
.test_num = IFS_TYPE_SAF,
};
static const struct ifs_test_caps array_test = {
.integrity_cap_bit = MSR_INTEGRITY_CAPS_ARRAY_BIST_BIT,
.test_num = IFS_TYPE_ARRAY_BIST,
};
static struct ifs_device ifs_devices[] = {
[IFS_TYPE_SAF] = {
.test_caps = &scan_test,
.misc = {
.name = "intel_ifs_0",
.minor = MISC_DYNAMIC_MINOR,
.groups = plat_ifs_groups,
},
}, },
.misc = { [IFS_TYPE_ARRAY_BIST] = {
.name = "intel_ifs_0", .test_caps = &array_test,
.nodename = "intel_ifs/0", .misc = {
.minor = MISC_DYNAMIC_MINOR, .name = "intel_ifs_1",
.minor = MISC_DYNAMIC_MINOR,
.groups = plat_ifs_array_groups,
},
}, },
}; };
#define IFS_NUMTESTS ARRAY_SIZE(ifs_devices)
static void ifs_cleanup(void)
{
int i;
for (i = 0; i < IFS_NUMTESTS; i++) {
if (ifs_devices[i].misc.this_device)
misc_deregister(&ifs_devices[i].misc);
}
kfree(ifs_pkg_auth);
}
static int __init ifs_init(void) static int __init ifs_init(void)
{ {
const struct x86_cpu_id *m; const struct x86_cpu_id *m;
u64 msrval; u64 msrval;
int ret; int i, ret;
m = x86_match_cpu(ifs_cpu_ids); m = x86_match_cpu(ifs_cpu_ids);
if (!m) if (!m)
...@@ -51,28 +87,27 @@ static int __init ifs_init(void) ...@@ -51,28 +87,27 @@ static int __init ifs_init(void)
if (rdmsrl_safe(MSR_INTEGRITY_CAPS, &msrval)) if (rdmsrl_safe(MSR_INTEGRITY_CAPS, &msrval))
return -ENODEV; return -ENODEV;
ifs_device.misc.groups = ifs_get_groups(); ifs_pkg_auth = kmalloc_array(topology_max_packages(), sizeof(bool), GFP_KERNEL);
if (!ifs_pkg_auth)
if (!(msrval & BIT(ifs_device.data.integrity_cap_bit)))
return -ENODEV;
ifs_device.data.pkg_auth = kmalloc_array(topology_max_packages(), sizeof(bool), GFP_KERNEL);
if (!ifs_device.data.pkg_auth)
return -ENOMEM; return -ENOMEM;
ret = misc_register(&ifs_device.misc); for (i = 0; i < IFS_NUMTESTS; i++) {
if (ret) { if (!(msrval & BIT(ifs_devices[i].test_caps->integrity_cap_bit)))
kfree(ifs_device.data.pkg_auth); continue;
return ret; ret = misc_register(&ifs_devices[i].misc);
if (ret)
goto err_exit;
} }
return 0; return 0;
err_exit:
ifs_cleanup();
return ret;
} }
static void __exit ifs_exit(void) static void __exit ifs_exit(void)
{ {
misc_deregister(&ifs_device.misc); ifs_cleanup();
kfree(ifs_device.data.pkg_auth);
} }
module_init(ifs_init); module_init(ifs_init);
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
* In Field Scan (IFS) is a hardware feature to run circuit level tests on * In Field Scan (IFS) is a hardware feature to run circuit level tests on
* a CPU core to detect problems that are not caught by parity or ECC checks. * a CPU core to detect problems that are not caught by parity or ECC checks.
* Future CPUs will support more than one type of test which will show up * Future CPUs will support more than one type of test which will show up
* with a new platform-device instance-id, for now only .0 is exposed. * with a new platform-device instance-id.
* *
* *
* IFS Image * IFS Image
...@@ -25,7 +25,10 @@ ...@@ -25,7 +25,10 @@
* *
* Intel provides a firmware file containing the scan tests via * Intel provides a firmware file containing the scan tests via
* github [#f1]_. Similar to microcode there is a separate file for each * github [#f1]_. Similar to microcode there is a separate file for each
* family-model-stepping. * family-model-stepping. IFS Images are not applicable for some test types.
* Wherever applicable the sysfs directory would provide a "current_batch" file
* (see below) for loading the image.
*
* *
* IFS Image Loading * IFS Image Loading
* ----------------- * -----------------
...@@ -35,7 +38,7 @@ ...@@ -35,7 +38,7 @@
* SHA hashes for the test. Then the tests themselves. Status MSRs provide * SHA hashes for the test. Then the tests themselves. Status MSRs provide
* feedback on the success/failure of these steps. * feedback on the success/failure of these steps.
* *
* The test files are kept in a fixed location: /lib/firmware/intel/ifs_0/ * The test files are kept in a fixed location: /lib/firmware/intel/ifs_<n>/
* For e.g if there are 3 test files, they would be named in the following * For e.g if there are 3 test files, they would be named in the following
* fashion: * fashion:
* ff-mm-ss-01.scan * ff-mm-ss-01.scan
...@@ -47,7 +50,7 @@ ...@@ -47,7 +50,7 @@
* (e.g 1, 2 or 3 in the above scenario) into the curent_batch file. * (e.g 1, 2 or 3 in the above scenario) into the curent_batch file.
* To load ff-mm-ss-02.scan, the following command can be used:: * To load ff-mm-ss-02.scan, the following command can be used::
* *
* # echo 2 > /sys/devices/virtual/misc/intel_ifs_0/current_batch * # echo 2 > /sys/devices/virtual/misc/intel_ifs_<n>/current_batch
* *
* The above file can also be read to know the currently loaded image. * The above file can also be read to know the currently loaded image.
* *
...@@ -69,16 +72,16 @@ ...@@ -69,16 +72,16 @@
* to migrate those applications to other cores before running a core test. * to migrate those applications to other cores before running a core test.
* It may also be necessary to redirect interrupts to other CPUs. * It may also be necessary to redirect interrupts to other CPUs.
* *
* In all cases reading the SCAN_STATUS MSR provides details on what * In all cases reading the corresponding test's STATUS MSR provides details on what
* happened. The driver makes the value of this MSR visible to applications * happened. The driver makes the value of this MSR visible to applications
* via the "details" file (see below). Interrupted tests may be restarted. * via the "details" file (see below). Interrupted tests may be restarted.
* *
* The IFS driver provides sysfs interfaces via /sys/devices/virtual/misc/intel_ifs_0/ * The IFS driver provides sysfs interfaces via /sys/devices/virtual/misc/intel_ifs_<n>/
* to control execution: * to control execution:
* *
* Test a specific core:: * Test a specific core::
* *
* # echo <cpu#> > /sys/devices/virtual/misc/intel_ifs_0/run_test * # echo <cpu#> > /sys/devices/virtual/misc/intel_ifs_<n>/run_test
* *
* when HT is enabled any of the sibling cpu# can be specified to test * when HT is enabled any of the sibling cpu# can be specified to test
* its corresponding physical core. Since the tests are per physical core, * its corresponding physical core. Since the tests are per physical core,
...@@ -87,21 +90,21 @@ ...@@ -87,21 +90,21 @@
* *
* For e.g. to test core corresponding to cpu5 * For e.g. to test core corresponding to cpu5
* *
* # echo 5 > /sys/devices/virtual/misc/intel_ifs_0/run_test * # echo 5 > /sys/devices/virtual/misc/intel_ifs_<n>/run_test
* *
* Results of the last test is provided in /sys:: * Results of the last test is provided in /sys::
* *
* $ cat /sys/devices/virtual/misc/intel_ifs_0/status * $ cat /sys/devices/virtual/misc/intel_ifs_<n>/status
* pass * pass
* *
* Status can be one of pass, fail, untested * Status can be one of pass, fail, untested
* *
* Additional details of the last test is provided by the details file:: * Additional details of the last test is provided by the details file::
* *
* $ cat /sys/devices/virtual/misc/intel_ifs_0/details * $ cat /sys/devices/virtual/misc/intel_ifs_<n>/details
* 0x8081 * 0x8081
* *
* The details file reports the hex value of the SCAN_STATUS MSR. * The details file reports the hex value of the test specific status MSR.
* Hardware defined error codes are documented in volume 4 of the Intel * Hardware defined error codes are documented in volume 4 of the Intel
* Software Developer's Manual but the error_code field may contain one of * Software Developer's Manual but the error_code field may contain one of
* the following driver defined software codes: * the following driver defined software codes:
...@@ -127,6 +130,7 @@ ...@@ -127,6 +130,7 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/miscdevice.h> #include <linux/miscdevice.h>
#define MSR_ARRAY_BIST 0x00000105
#define MSR_COPY_SCAN_HASHES 0x000002c2 #define MSR_COPY_SCAN_HASHES 0x000002c2
#define MSR_SCAN_HASHES_STATUS 0x000002c3 #define MSR_SCAN_HASHES_STATUS 0x000002c3
#define MSR_AUTHENTICATE_AND_COPY_CHUNK 0x000002c4 #define MSR_AUTHENTICATE_AND_COPY_CHUNK 0x000002c4
...@@ -137,6 +141,9 @@ ...@@ -137,6 +141,9 @@
#define SCAN_TEST_PASS 1 #define SCAN_TEST_PASS 1
#define SCAN_TEST_FAIL 2 #define SCAN_TEST_FAIL 2
#define IFS_TYPE_SAF 0
#define IFS_TYPE_ARRAY_BIST 1
/* MSR_SCAN_HASHES_STATUS bit fields */ /* MSR_SCAN_HASHES_STATUS bit fields */
union ifs_scan_hashes_status { union ifs_scan_hashes_status {
u64 data; u64 data;
...@@ -189,6 +196,17 @@ union ifs_status { ...@@ -189,6 +196,17 @@ union ifs_status {
}; };
}; };
/* MSR_ARRAY_BIST bit fields */
union ifs_array {
u64 data;
struct {
u32 array_bitmask;
u16 array_bank;
u16 rsvd :15;
u16 ctrl_result :1;
};
};
/* /*
* Driver populated error-codes * Driver populated error-codes
* 0xFD: Test timed out before completing all the chunks. * 0xFD: Test timed out before completing all the chunks.
...@@ -197,22 +215,22 @@ union ifs_status { ...@@ -197,22 +215,22 @@ union ifs_status {
#define IFS_SW_TIMEOUT 0xFD #define IFS_SW_TIMEOUT 0xFD
#define IFS_SW_PARTIAL_COMPLETION 0xFE #define IFS_SW_PARTIAL_COMPLETION 0xFE
struct ifs_test_caps {
int integrity_cap_bit;
int test_num;
};
/** /**
* struct ifs_data - attributes related to intel IFS driver * struct ifs_data - attributes related to intel IFS driver
* @integrity_cap_bit: MSR_INTEGRITY_CAPS bit enumerating this test
* @loaded_version: stores the currently loaded ifs image version. * @loaded_version: stores the currently loaded ifs image version.
* @pkg_auth: array of bool storing per package auth status
* @loaded: If a valid test binary has been loaded into the memory * @loaded: If a valid test binary has been loaded into the memory
* @loading_error: Error occurred on another CPU while loading image * @loading_error: Error occurred on another CPU while loading image
* @valid_chunks: number of chunks which could be validated. * @valid_chunks: number of chunks which could be validated.
* @status: it holds simple status pass/fail/untested * @status: it holds simple status pass/fail/untested
* @scan_details: opaque scan status code from h/w * @scan_details: opaque scan status code from h/w
* @cur_batch: number indicating the currently loaded test file * @cur_batch: number indicating the currently loaded test file
* @test_num: number indicating the test type
*/ */
struct ifs_data { struct ifs_data {
int integrity_cap_bit;
bool *pkg_auth;
int loaded_version; int loaded_version;
bool loaded; bool loaded;
bool loading_error; bool loading_error;
...@@ -220,7 +238,6 @@ struct ifs_data { ...@@ -220,7 +238,6 @@ struct ifs_data {
int status; int status;
u64 scan_details; u64 scan_details;
u32 cur_batch; u32 cur_batch;
int test_num;
}; };
struct ifs_work { struct ifs_work {
...@@ -229,7 +246,8 @@ struct ifs_work { ...@@ -229,7 +246,8 @@ struct ifs_work {
}; };
struct ifs_device { struct ifs_device {
struct ifs_data data; const struct ifs_test_caps *test_caps;
struct ifs_data rw_data;
struct miscdevice misc; struct miscdevice misc;
}; };
...@@ -238,11 +256,21 @@ static inline struct ifs_data *ifs_get_data(struct device *dev) ...@@ -238,11 +256,21 @@ static inline struct ifs_data *ifs_get_data(struct device *dev)
struct miscdevice *m = dev_get_drvdata(dev); struct miscdevice *m = dev_get_drvdata(dev);
struct ifs_device *d = container_of(m, struct ifs_device, misc); struct ifs_device *d = container_of(m, struct ifs_device, misc);
return &d->data; return &d->rw_data;
}
static inline const struct ifs_test_caps *ifs_get_test_caps(struct device *dev)
{
struct miscdevice *m = dev_get_drvdata(dev);
struct ifs_device *d = container_of(m, struct ifs_device, misc);
return d->test_caps;
} }
extern bool *ifs_pkg_auth;
int ifs_load_firmware(struct device *dev); int ifs_load_firmware(struct device *dev);
int do_core_test(int cpu, struct device *dev); int do_core_test(int cpu, struct device *dev);
const struct attribute_group **ifs_get_groups(void); extern struct attribute *plat_ifs_attrs[];
extern struct attribute *plat_ifs_array_attrs[];
#endif #endif
...@@ -192,7 +192,7 @@ static int scan_chunks_sanity_check(struct device *dev) ...@@ -192,7 +192,7 @@ static int scan_chunks_sanity_check(struct device *dev)
struct ifs_work local_work; struct ifs_work local_work;
int curr_pkg, cpu, ret; int curr_pkg, cpu, ret;
memset(ifsd->pkg_auth, 0, (topology_max_packages() * sizeof(bool))); memset(ifs_pkg_auth, 0, (topology_max_packages() * sizeof(bool)));
ret = validate_ifs_metadata(dev); ret = validate_ifs_metadata(dev);
if (ret) if (ret)
return ret; return ret;
...@@ -204,7 +204,7 @@ static int scan_chunks_sanity_check(struct device *dev) ...@@ -204,7 +204,7 @@ static int scan_chunks_sanity_check(struct device *dev)
cpus_read_lock(); cpus_read_lock();
for_each_online_cpu(cpu) { for_each_online_cpu(cpu) {
curr_pkg = topology_physical_package_id(cpu); curr_pkg = topology_physical_package_id(cpu);
if (ifsd->pkg_auth[curr_pkg]) if (ifs_pkg_auth[curr_pkg])
continue; continue;
reinit_completion(&ifs_done); reinit_completion(&ifs_done);
local_work.dev = dev; local_work.dev = dev;
...@@ -215,7 +215,7 @@ static int scan_chunks_sanity_check(struct device *dev) ...@@ -215,7 +215,7 @@ static int scan_chunks_sanity_check(struct device *dev)
ret = -EIO; ret = -EIO;
goto out; goto out;
} }
ifsd->pkg_auth[curr_pkg] = 1; ifs_pkg_auth[curr_pkg] = 1;
} }
ret = 0; ret = 0;
out: out:
...@@ -257,13 +257,14 @@ static int image_sanity_check(struct device *dev, const struct microcode_header_ ...@@ -257,13 +257,14 @@ static int image_sanity_check(struct device *dev, const struct microcode_header_
*/ */
int ifs_load_firmware(struct device *dev) int ifs_load_firmware(struct device *dev)
{ {
const struct ifs_test_caps *test = ifs_get_test_caps(dev);
struct ifs_data *ifsd = ifs_get_data(dev); struct ifs_data *ifsd = ifs_get_data(dev);
const struct firmware *fw; const struct firmware *fw;
char scan_path[64]; char scan_path[64];
int ret = -EINVAL; int ret = -EINVAL;
snprintf(scan_path, sizeof(scan_path), "intel/ifs_%d/%02x-%02x-%02x-%02x.scan", snprintf(scan_path, sizeof(scan_path), "intel/ifs_%d/%02x-%02x-%02x-%02x.scan",
ifsd->test_num, boot_cpu_data.x86, boot_cpu_data.x86_model, test->test_num, boot_cpu_data.x86, boot_cpu_data.x86_model,
boot_cpu_data.x86_stepping, ifsd->cur_batch); boot_cpu_data.x86_stepping, ifsd->cur_batch);
ret = request_firmware_direct(&fw, scan_path, dev); ret = request_firmware_direct(&fw, scan_path, dev);
......
...@@ -229,6 +229,85 @@ static void ifs_test_core(int cpu, struct device *dev) ...@@ -229,6 +229,85 @@ static void ifs_test_core(int cpu, struct device *dev)
} }
} }
#define SPINUNIT 100 /* 100 nsec */
static atomic_t array_cpus_out;
/*
* Simplified cpu sibling rendezvous loop based on microcode loader __wait_for_cpus()
*/
static void wait_for_sibling_cpu(atomic_t *t, long long timeout)
{
int cpu = smp_processor_id();
const struct cpumask *smt_mask = cpu_smt_mask(cpu);
int all_cpus = cpumask_weight(smt_mask);
atomic_inc(t);
while (atomic_read(t) < all_cpus) {
if (timeout < SPINUNIT)
return;
ndelay(SPINUNIT);
timeout -= SPINUNIT;
touch_nmi_watchdog();
}
}
static int do_array_test(void *data)
{
union ifs_array *command = data;
int cpu = smp_processor_id();
int first;
/*
* Only one logical CPU on a core needs to trigger the Array test via MSR write.
*/
first = cpumask_first(cpu_smt_mask(cpu));
if (cpu == first) {
wrmsrl(MSR_ARRAY_BIST, command->data);
/* Pass back the result of the test */
rdmsrl(MSR_ARRAY_BIST, command->data);
}
/* Tests complete faster if the sibling is spinning here */
wait_for_sibling_cpu(&array_cpus_out, NSEC_PER_SEC);
return 0;
}
static void ifs_array_test_core(int cpu, struct device *dev)
{
union ifs_array command = {};
bool timed_out = false;
struct ifs_data *ifsd;
unsigned long timeout;
ifsd = ifs_get_data(dev);
command.array_bitmask = ~0U;
timeout = jiffies + HZ / 2;
do {
if (time_after(jiffies, timeout)) {
timed_out = true;
break;
}
atomic_set(&array_cpus_out, 0);
stop_core_cpuslocked(cpu, do_array_test, &command);
if (command.ctrl_result)
break;
} while (command.array_bitmask);
ifsd->scan_details = command.data;
if (command.ctrl_result)
ifsd->status = SCAN_TEST_FAIL;
else if (timed_out || command.array_bitmask)
ifsd->status = SCAN_NOT_TESTED;
else
ifsd->status = SCAN_TEST_PASS;
}
/* /*
* Initiate per core test. It wakes up work queue threads on the target cpu and * Initiate per core test. It wakes up work queue threads on the target cpu and
* its sibling cpu. Once all sibling threads wake up, the scan test gets executed and * its sibling cpu. Once all sibling threads wake up, the scan test gets executed and
...@@ -236,6 +315,8 @@ static void ifs_test_core(int cpu, struct device *dev) ...@@ -236,6 +315,8 @@ static void ifs_test_core(int cpu, struct device *dev)
*/ */
int do_core_test(int cpu, struct device *dev) int do_core_test(int cpu, struct device *dev)
{ {
const struct ifs_test_caps *test = ifs_get_test_caps(dev);
struct ifs_data *ifsd = ifs_get_data(dev);
int ret = 0; int ret = 0;
/* Prevent CPUs from being taken offline during the scan test */ /* Prevent CPUs from being taken offline during the scan test */
...@@ -247,7 +328,18 @@ int do_core_test(int cpu, struct device *dev) ...@@ -247,7 +328,18 @@ int do_core_test(int cpu, struct device *dev)
goto out; goto out;
} }
ifs_test_core(cpu, dev); switch (test->test_num) {
case IFS_TYPE_SAF:
if (!ifsd->loaded)
return -EPERM;
ifs_test_core(cpu, dev);
break;
case IFS_TYPE_ARRAY_BIST:
ifs_array_test_core(cpu, dev);
break;
default:
return -EINVAL;
}
out: out:
cpus_read_unlock(); cpus_read_unlock();
return ret; return ret;
......
...@@ -64,7 +64,6 @@ static ssize_t run_test_store(struct device *dev, ...@@ -64,7 +64,6 @@ static ssize_t run_test_store(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct ifs_data *ifsd = ifs_get_data(dev);
unsigned int cpu; unsigned int cpu;
int rc; int rc;
...@@ -75,10 +74,7 @@ static ssize_t run_test_store(struct device *dev, ...@@ -75,10 +74,7 @@ static ssize_t run_test_store(struct device *dev,
if (down_interruptible(&ifs_sem)) if (down_interruptible(&ifs_sem))
return -EINTR; return -EINTR;
if (!ifsd->loaded) rc = do_core_test(cpu, dev);
rc = -EPERM;
else
rc = do_core_test(cpu, dev);
up(&ifs_sem); up(&ifs_sem);
...@@ -141,7 +137,7 @@ static ssize_t image_version_show(struct device *dev, ...@@ -141,7 +137,7 @@ static ssize_t image_version_show(struct device *dev,
static DEVICE_ATTR_RO(image_version); static DEVICE_ATTR_RO(image_version);
/* global scan sysfs attributes */ /* global scan sysfs attributes */
static struct attribute *plat_ifs_attrs[] = { struct attribute *plat_ifs_attrs[] = {
&dev_attr_details.attr, &dev_attr_details.attr,
&dev_attr_status.attr, &dev_attr_status.attr,
&dev_attr_run_test.attr, &dev_attr_run_test.attr,
...@@ -150,9 +146,10 @@ static struct attribute *plat_ifs_attrs[] = { ...@@ -150,9 +146,10 @@ static struct attribute *plat_ifs_attrs[] = {
NULL NULL
}; };
ATTRIBUTE_GROUPS(plat_ifs); /* global array sysfs attributes */
struct attribute *plat_ifs_array_attrs[] = {
const struct attribute_group **ifs_get_groups(void) &dev_attr_details.attr,
{ &dev_attr_status.attr,
return plat_ifs_groups; &dev_attr_run_test.attr,
} NULL
};
...@@ -223,11 +223,10 @@ static int int0002_probe(struct platform_device *pdev) ...@@ -223,11 +223,10 @@ static int int0002_probe(struct platform_device *pdev)
return 0; return 0;
} }
static int int0002_remove(struct platform_device *pdev) static void int0002_remove(struct platform_device *pdev)
{ {
device_init_wakeup(&pdev->dev, false); device_init_wakeup(&pdev->dev, false);
acpi_unregister_wakeup_handler(int0002_check_wake, NULL); acpi_unregister_wakeup_handler(int0002_check_wake, NULL);
return 0;
} }
static int int0002_suspend(struct device *dev) static int int0002_suspend(struct device *dev)
...@@ -273,7 +272,7 @@ static struct platform_driver int0002_driver = { ...@@ -273,7 +272,7 @@ static struct platform_driver int0002_driver = {
.pm = &int0002_pm_ops, .pm = &int0002_pm_ops,
}, },
.probe = int0002_probe, .probe = int0002_probe,
.remove = int0002_remove, .remove_new = int0002_remove,
}; };
module_platform_driver(int0002_driver); module_platform_driver(int0002_driver);
......
...@@ -292,7 +292,7 @@ static int sar_probe(struct platform_device *device) ...@@ -292,7 +292,7 @@ static int sar_probe(struct platform_device *device)
return result; return result;
} }
static int sar_remove(struct platform_device *device) static void sar_remove(struct platform_device *device)
{ {
struct wwan_sar_context *context = dev_get_drvdata(&device->dev); struct wwan_sar_context *context = dev_get_drvdata(&device->dev);
int reg; int reg;
...@@ -304,12 +304,11 @@ static int sar_remove(struct platform_device *device) ...@@ -304,12 +304,11 @@ static int sar_remove(struct platform_device *device)
kfree(context->config_data[reg].device_mode_info); kfree(context->config_data[reg].device_mode_info);
kfree(context); kfree(context);
return 0;
} }
static struct platform_driver sar_driver = { static struct platform_driver sar_driver = {
.probe = sar_probe, .probe = sar_probe,
.remove = sar_remove, .remove_new = sar_remove,
.driver = { .driver = {
.name = DRVNAME, .name = DRVNAME,
.acpi_match_table = ACPI_PTR(sar_device_ids) .acpi_match_table = ACPI_PTR(sar_device_ids)
......
...@@ -317,7 +317,7 @@ static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472) ...@@ -317,7 +317,7 @@ static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472)
return 0; return 0;
} }
static int skl_int3472_discrete_remove(struct platform_device *pdev) static void skl_int3472_discrete_remove(struct platform_device *pdev)
{ {
struct int3472_discrete_device *int3472 = platform_get_drvdata(pdev); struct int3472_discrete_device *int3472 = platform_get_drvdata(pdev);
...@@ -326,8 +326,6 @@ static int skl_int3472_discrete_remove(struct platform_device *pdev) ...@@ -326,8 +326,6 @@ static int skl_int3472_discrete_remove(struct platform_device *pdev)
skl_int3472_unregister_clock(int3472); skl_int3472_unregister_clock(int3472);
skl_int3472_unregister_pled(int3472); skl_int3472_unregister_pled(int3472);
skl_int3472_unregister_regulator(int3472); skl_int3472_unregister_regulator(int3472);
return 0;
} }
static int skl_int3472_discrete_probe(struct platform_device *pdev) static int skl_int3472_discrete_probe(struct platform_device *pdev)
...@@ -392,7 +390,7 @@ static struct platform_driver int3472_discrete = { ...@@ -392,7 +390,7 @@ static struct platform_driver int3472_discrete = {
.acpi_match_table = int3472_device_id, .acpi_match_table = int3472_device_id,
}, },
.probe = skl_int3472_discrete_probe, .probe = skl_int3472_discrete_probe,
.remove = skl_int3472_discrete_remove, .remove_new = skl_int3472_discrete_remove,
}; };
module_platform_driver(int3472_discrete); module_platform_driver(int3472_discrete);
......
...@@ -78,13 +78,12 @@ static int mrfld_pwrbtn_probe(struct platform_device *pdev) ...@@ -78,13 +78,12 @@ static int mrfld_pwrbtn_probe(struct platform_device *pdev)
return 0; return 0;
} }
static int mrfld_pwrbtn_remove(struct platform_device *pdev) static void mrfld_pwrbtn_remove(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
dev_pm_clear_wake_irq(dev); dev_pm_clear_wake_irq(dev);
device_init_wakeup(dev, false); device_init_wakeup(dev, false);
return 0;
} }
static const struct platform_device_id mrfld_pwrbtn_id_table[] = { static const struct platform_device_id mrfld_pwrbtn_id_table[] = {
...@@ -98,7 +97,7 @@ static struct platform_driver mrfld_pwrbtn_driver = { ...@@ -98,7 +97,7 @@ static struct platform_driver mrfld_pwrbtn_driver = {
.name = "mrfld_bcove_pwrbtn", .name = "mrfld_bcove_pwrbtn",
}, },
.probe = mrfld_pwrbtn_probe, .probe = mrfld_pwrbtn_probe,
.remove = mrfld_pwrbtn_remove, .remove_new = mrfld_pwrbtn_remove,
.id_table = mrfld_pwrbtn_id_table, .id_table = mrfld_pwrbtn_id_table,
}; };
module_platform_driver(mrfld_pwrbtn_driver); module_platform_driver(mrfld_pwrbtn_driver);
......
...@@ -1160,7 +1160,7 @@ static int pmc_core_probe(struct platform_device *pdev) ...@@ -1160,7 +1160,7 @@ static int pmc_core_probe(struct platform_device *pdev)
return 0; return 0;
} }
static int pmc_core_remove(struct platform_device *pdev) static void pmc_core_remove(struct platform_device *pdev)
{ {
struct pmc_dev *pmcdev = platform_get_drvdata(pdev); struct pmc_dev *pmcdev = platform_get_drvdata(pdev);
...@@ -1168,7 +1168,6 @@ static int pmc_core_remove(struct platform_device *pdev) ...@@ -1168,7 +1168,6 @@ static int pmc_core_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
mutex_destroy(&pmcdev->lock); mutex_destroy(&pmcdev->lock);
iounmap(pmcdev->regbase); iounmap(pmcdev->regbase);
return 0;
} }
static bool warn_on_s0ix_failures; static bool warn_on_s0ix_failures;
...@@ -1275,7 +1274,7 @@ static struct platform_driver pmc_core_driver = { ...@@ -1275,7 +1274,7 @@ static struct platform_driver pmc_core_driver = {
.dev_groups = pmc_dev_groups, .dev_groups = pmc_dev_groups,
}, },
.probe = pmc_core_probe, .probe = pmc_core_probe,
.remove = pmc_core_remove, .remove_new = pmc_core_remove,
}; };
module_platform_driver(pmc_core_driver); module_platform_driver(pmc_core_driver);
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
* *
*/ */
#include <linux/pci.h>
#include "core.h" #include "core.h"
const struct pmc_reg_map mtl_reg_map = { const struct pmc_reg_map mtl_reg_map = {
...@@ -45,8 +46,38 @@ void mtl_core_configure(struct pmc_dev *pmcdev) ...@@ -45,8 +46,38 @@ void mtl_core_configure(struct pmc_dev *pmcdev)
pmc_core_send_ltr_ignore(pmcdev, 3); pmc_core_send_ltr_ignore(pmcdev, 3);
} }
#define MTL_GNA_PCI_DEV 0x7e4c
#define MTL_IPU_PCI_DEV 0x7d19
#define MTL_VPU_PCI_DEV 0x7d1d
static void mtl_set_device_d3(unsigned int device)
{
struct pci_dev *pcidev;
pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, device, NULL);
if (pcidev) {
if (!device_trylock(&pcidev->dev)) {
pci_dev_put(pcidev);
return;
}
if (!pcidev->dev.driver) {
dev_info(&pcidev->dev, "Setting to D3hot\n");
pci_set_power_state(pcidev, PCI_D3hot);
}
device_unlock(&pcidev->dev);
pci_dev_put(pcidev);
}
}
void mtl_core_init(struct pmc_dev *pmcdev) void mtl_core_init(struct pmc_dev *pmcdev)
{ {
pmcdev->map = &mtl_reg_map; pmcdev->map = &mtl_reg_map;
pmcdev->core_configure = mtl_core_configure; pmcdev->core_configure = mtl_core_configure;
/*
* Set power state of select devices that do not have drivers to D3
* so that they do not block Package C entry.
*/
mtl_set_device_d3(MTL_GNA_PCI_DEV);
mtl_set_device_d3(MTL_IPU_PCI_DEV);
mtl_set_device_d3(MTL_VPU_PCI_DEV);
} }
...@@ -33,7 +33,7 @@ bool intel_pmt_is_early_client_hw(struct device *dev) ...@@ -33,7 +33,7 @@ bool intel_pmt_is_early_client_hw(struct device *dev)
*/ */
return !!(ivdev->info->quirks & VSEC_QUIRK_EARLY_HW); return !!(ivdev->info->quirks & VSEC_QUIRK_EARLY_HW);
} }
EXPORT_SYMBOL_GPL(intel_pmt_is_early_client_hw); EXPORT_SYMBOL_NS_GPL(intel_pmt_is_early_client_hw, INTEL_PMT);
static inline int static inline int
pmt_memcpy64_fromio(void *to, const u64 __iomem *from, size_t count) pmt_memcpy64_fromio(void *to, const u64 __iomem *from, size_t count)
...@@ -327,7 +327,7 @@ int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespa ...@@ -327,7 +327,7 @@ int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespa
return intel_pmt_dev_register(entry, ns, dev); return intel_pmt_dev_register(entry, ns, dev);
} }
EXPORT_SYMBOL_GPL(intel_pmt_dev_create); EXPORT_SYMBOL_NS_GPL(intel_pmt_dev_create, INTEL_PMT);
void intel_pmt_dev_destroy(struct intel_pmt_entry *entry, void intel_pmt_dev_destroy(struct intel_pmt_entry *entry,
struct intel_pmt_namespace *ns) struct intel_pmt_namespace *ns)
...@@ -343,7 +343,7 @@ void intel_pmt_dev_destroy(struct intel_pmt_entry *entry, ...@@ -343,7 +343,7 @@ void intel_pmt_dev_destroy(struct intel_pmt_entry *entry,
device_unregister(dev); device_unregister(dev);
xa_erase(ns->xa, entry->devid); xa_erase(ns->xa, entry->devid);
} }
EXPORT_SYMBOL_GPL(intel_pmt_dev_destroy); EXPORT_SYMBOL_NS_GPL(intel_pmt_dev_destroy, INTEL_PMT);
static int __init pmt_class_init(void) static int __init pmt_class_init(void)
{ {
......
...@@ -328,3 +328,4 @@ module_exit(pmt_crashlog_exit); ...@@ -328,3 +328,4 @@ module_exit(pmt_crashlog_exit);
MODULE_AUTHOR("Alexander Duyck <alexander.h.duyck@linux.intel.com>"); MODULE_AUTHOR("Alexander Duyck <alexander.h.duyck@linux.intel.com>");
MODULE_DESCRIPTION("Intel PMT Crashlog driver"); MODULE_DESCRIPTION("Intel PMT Crashlog driver");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS(INTEL_PMT);
...@@ -78,7 +78,7 @@ static int pmt_telem_header_decode(struct intel_pmt_entry *entry, ...@@ -78,7 +78,7 @@ static int pmt_telem_header_decode(struct intel_pmt_entry *entry,
* reserved for future use. They have zero size. Do not fail * reserved for future use. They have zero size. Do not fail
* probe for these. Just ignore them. * probe for these. Just ignore them.
*/ */
if (header->size == 0) if (header->size == 0 || header->access_type == 0xF)
return 1; return 1;
return 0; return 0;
...@@ -160,3 +160,4 @@ module_exit(pmt_telem_exit); ...@@ -160,3 +160,4 @@ module_exit(pmt_telem_exit);
MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
MODULE_DESCRIPTION("Intel PMT Telemetry driver"); MODULE_DESCRIPTION("Intel PMT Telemetry driver");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS(INTEL_PMT);
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
#define SDSI_MBOX_CMD_SUCCESS 0x40 #define SDSI_MBOX_CMD_SUCCESS 0x40
#define SDSI_MBOX_CMD_TIMEOUT 0x80 #define SDSI_MBOX_CMD_TIMEOUT 0x80
#define MBOX_TIMEOUT_US 2000 #define MBOX_TIMEOUT_US 500000
#define MBOX_TIMEOUT_ACQUIRE_US 1000 #define MBOX_TIMEOUT_ACQUIRE_US 1000
#define MBOX_POLLING_PERIOD_US 100 #define MBOX_POLLING_PERIOD_US 100
#define MBOX_ACQUIRE_NUM_RETRIES 5 #define MBOX_ACQUIRE_NUM_RETRIES 5
......
...@@ -2,8 +2,12 @@ menu "Intel Speed Select Technology interface support" ...@@ -2,8 +2,12 @@ menu "Intel Speed Select Technology interface support"
depends on PCI depends on PCI
depends on X86_64 || COMPILE_TEST depends on X86_64 || COMPILE_TEST
config INTEL_SPEED_SELECT_TPMI
tristate
config INTEL_SPEED_SELECT_INTERFACE config INTEL_SPEED_SELECT_INTERFACE
tristate "Intel(R) Speed Select Technology interface drivers" tristate "Intel(R) Speed Select Technology interface drivers"
select INTEL_SPEED_SELECT_TPMI if INTEL_TPMI
help help
This config enables the Intel(R) Speed Select Technology interface This config enables the Intel(R) Speed Select Technology interface
drivers. The Intel(R) speed select technology features are non drivers. The Intel(R) speed select technology features are non
......
...@@ -8,3 +8,5 @@ obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_common.o ...@@ -8,3 +8,5 @@ obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_common.o
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mmio.o obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mmio.o
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mbox_pci.o obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mbox_pci.o
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mbox_msr.o obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mbox_msr.o
obj-$(CONFIG_INTEL_SPEED_SELECT_TPMI) += isst_tpmi_core.o
obj-$(CONFIG_INTEL_SPEED_SELECT_TPMI) += isst_tpmi.o
...@@ -19,9 +19,13 @@ ...@@ -19,9 +19,13 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <uapi/linux/isst_if.h> #include <uapi/linux/isst_if.h>
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
#include "isst_if_common.h" #include "isst_if_common.h"
#define MSR_THREAD_ID_INFO 0x53 #define MSR_THREAD_ID_INFO 0x53
#define MSR_PM_LOGICAL_ID 0x54
#define MSR_CPU_BUS_NUMBER 0x128 #define MSR_CPU_BUS_NUMBER 0x128
static struct isst_if_cmd_cb punit_callbacks[ISST_IF_DEV_MAX]; static struct isst_if_cmd_cb punit_callbacks[ISST_IF_DEV_MAX];
...@@ -31,6 +35,7 @@ static int punit_msr_white_list[] = { ...@@ -31,6 +35,7 @@ static int punit_msr_white_list[] = {
MSR_CONFIG_TDP_CONTROL, MSR_CONFIG_TDP_CONTROL,
MSR_TURBO_RATIO_LIMIT1, MSR_TURBO_RATIO_LIMIT1,
MSR_TURBO_RATIO_LIMIT2, MSR_TURBO_RATIO_LIMIT2,
MSR_PM_LOGICAL_ID,
}; };
struct isst_valid_cmd_ranges { struct isst_valid_cmd_ranges {
...@@ -73,6 +78,8 @@ struct isst_cmd { ...@@ -73,6 +78,8 @@ struct isst_cmd {
u32 param; u32 param;
}; };
static bool isst_hpm_support;
static DECLARE_HASHTABLE(isst_hash, 8); static DECLARE_HASHTABLE(isst_hash, 8);
static DEFINE_MUTEX(isst_hash_lock); static DEFINE_MUTEX(isst_hash_lock);
...@@ -262,11 +269,13 @@ bool isst_if_mbox_cmd_set_req(struct isst_if_mbox_cmd *cmd) ...@@ -262,11 +269,13 @@ bool isst_if_mbox_cmd_set_req(struct isst_if_mbox_cmd *cmd)
} }
EXPORT_SYMBOL_GPL(isst_if_mbox_cmd_set_req); EXPORT_SYMBOL_GPL(isst_if_mbox_cmd_set_req);
static int isst_if_api_version;
static int isst_if_get_platform_info(void __user *argp) static int isst_if_get_platform_info(void __user *argp)
{ {
struct isst_if_platform_info info; struct isst_if_platform_info info;
info.api_version = ISST_IF_API_VERSION; info.api_version = isst_if_api_version;
info.driver_version = ISST_IF_DRIVER_VERSION; info.driver_version = ISST_IF_DRIVER_VERSION;
info.max_cmds_per_ioctl = ISST_IF_CMD_LIMIT; info.max_cmds_per_ioctl = ISST_IF_CMD_LIMIT;
info.mbox_supported = punit_callbacks[ISST_IF_DEV_MBOX].registered; info.mbox_supported = punit_callbacks[ISST_IF_DEV_MBOX].registered;
...@@ -409,11 +418,20 @@ static int isst_if_cpu_online(unsigned int cpu) ...@@ -409,11 +418,20 @@ static int isst_if_cpu_online(unsigned int cpu)
isst_cpu_info[cpu].pci_dev[1] = _isst_if_get_pci_dev(cpu, 1, 30, 1); isst_cpu_info[cpu].pci_dev[1] = _isst_if_get_pci_dev(cpu, 1, 30, 1);
} }
if (isst_hpm_support) {
ret = rdmsrl_safe(MSR_PM_LOGICAL_ID, &data);
if (!ret)
goto set_punit_id;
}
ret = rdmsrl_safe(MSR_THREAD_ID_INFO, &data); ret = rdmsrl_safe(MSR_THREAD_ID_INFO, &data);
if (ret) { if (ret) {
isst_cpu_info[cpu].punit_cpu_id = -1; isst_cpu_info[cpu].punit_cpu_id = -1;
return ret; return ret;
} }
set_punit_id:
isst_cpu_info[cpu].punit_cpu_id = data; isst_cpu_info[cpu].punit_cpu_id = data;
isst_restore_msr_local(cpu); isst_restore_msr_local(cpu);
...@@ -588,6 +606,7 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd, ...@@ -588,6 +606,7 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd,
struct isst_if_cmd_cb cmd_cb; struct isst_if_cmd_cb cmd_cb;
struct isst_if_cmd_cb *cb; struct isst_if_cmd_cb *cb;
long ret = -ENOTTY; long ret = -ENOTTY;
int i;
switch (cmd) { switch (cmd) {
case ISST_IF_GET_PLATFORM_INFO: case ISST_IF_GET_PLATFORM_INFO:
...@@ -616,6 +635,16 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd, ...@@ -616,6 +635,16 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd,
ret = isst_if_exec_multi_cmd(argp, &cmd_cb); ret = isst_if_exec_multi_cmd(argp, &cmd_cb);
break; break;
default: default:
for (i = 0; i < ISST_IF_DEV_MAX; ++i) {
struct isst_if_cmd_cb *cb = &punit_callbacks[i];
int ret;
if (cb->def_ioctl) {
ret = cb->def_ioctl(file, cmd, arg);
if (!ret)
return ret;
}
}
break; break;
} }
...@@ -691,6 +720,12 @@ static struct miscdevice isst_if_char_driver = { ...@@ -691,6 +720,12 @@ static struct miscdevice isst_if_char_driver = {
.fops = &isst_if_char_driver_ops, .fops = &isst_if_char_driver_ops,
}; };
static const struct x86_cpu_id hpm_cpu_ids[] = {
X86_MATCH_INTEL_FAM6_MODEL(GRANITERAPIDS_X, NULL),
X86_MATCH_INTEL_FAM6_MODEL(SIERRAFOREST_X, NULL),
{}
};
static int isst_misc_reg(void) static int isst_misc_reg(void)
{ {
mutex_lock(&punit_misc_dev_reg_lock); mutex_lock(&punit_misc_dev_reg_lock);
...@@ -698,6 +733,12 @@ static int isst_misc_reg(void) ...@@ -698,6 +733,12 @@ static int isst_misc_reg(void)
goto unlock_exit; goto unlock_exit;
if (!misc_usage_count) { if (!misc_usage_count) {
const struct x86_cpu_id *id;
id = x86_match_cpu(hpm_cpu_ids);
if (id)
isst_hpm_support = true;
misc_device_ret = isst_if_cpu_info_init(); misc_device_ret = isst_if_cpu_info_init();
if (misc_device_ret) if (misc_device_ret)
goto unlock_exit; goto unlock_exit;
...@@ -756,6 +797,10 @@ int isst_if_cdev_register(int device_type, struct isst_if_cmd_cb *cb) ...@@ -756,6 +797,10 @@ int isst_if_cdev_register(int device_type, struct isst_if_cmd_cb *cb)
mutex_unlock(&punit_misc_dev_open_lock); mutex_unlock(&punit_misc_dev_open_lock);
return -EAGAIN; return -EAGAIN;
} }
if (!cb->api_version)
cb->api_version = ISST_IF_API_VERSION;
if (cb->api_version > isst_if_api_version)
isst_if_api_version = cb->api_version;
memcpy(&punit_callbacks[device_type], cb, sizeof(*cb)); memcpy(&punit_callbacks[device_type], cb, sizeof(*cb));
punit_callbacks[device_type].registered = 1; punit_callbacks[device_type].registered = 1;
mutex_unlock(&punit_misc_dev_open_lock); mutex_unlock(&punit_misc_dev_open_lock);
......
...@@ -30,7 +30,8 @@ ...@@ -30,7 +30,8 @@
#define ISST_IF_DEV_MBOX 0 #define ISST_IF_DEV_MBOX 0
#define ISST_IF_DEV_MMIO 1 #define ISST_IF_DEV_MMIO 1
#define ISST_IF_DEV_MAX 2 #define ISST_IF_DEV_TPMI 2
#define ISST_IF_DEV_MAX 3
/** /**
* struct isst_if_cmd_cb - Used to register a IOCTL handler * struct isst_if_cmd_cb - Used to register a IOCTL handler
...@@ -40,6 +41,7 @@ ...@@ -40,6 +41,7 @@
* @offset: Offset to the first valid member in command structure. * @offset: Offset to the first valid member in command structure.
* This will be the offset of the start of the command * This will be the offset of the start of the command
* after command count field * after command count field
* @api_version: API version supported for this target. 0, if none.
* @owner: Registered module owner * @owner: Registered module owner
* @cmd_callback: Callback function to handle IOCTL. The callback has the * @cmd_callback: Callback function to handle IOCTL. The callback has the
* command pointer with data for command. There is a pointer * command pointer with data for command. There is a pointer
...@@ -47,6 +49,8 @@ ...@@ -47,6 +49,8 @@
* response to user ioctl buffer. The "resume" argument * response to user ioctl buffer. The "resume" argument
* can be used to avoid storing the command for replay * can be used to avoid storing the command for replay
* during system resume * during system resume
* @def_ioctl: Default IOCTL handler callback, if there is no match in
* the existing list of IOCTL handled by the common handler.
* *
* This structure is used to register an handler for IOCTL. To avoid * This structure is used to register an handler for IOCTL. To avoid
* code duplication common code handles all the IOCTL command read/write * code duplication common code handles all the IOCTL command read/write
...@@ -57,8 +61,10 @@ struct isst_if_cmd_cb { ...@@ -57,8 +61,10 @@ struct isst_if_cmd_cb {
int registered; int registered;
int cmd_size; int cmd_size;
int offset; int offset;
int api_version;
struct module *owner; struct module *owner;
long (*cmd_callback)(u8 *ptr, int *write_only, int resume); long (*cmd_callback)(u8 *ptr, int *write_only, int resume);
long (*def_ioctl)(struct file *file, unsigned int cmd, unsigned long arg);
}; };
/* Internal interface functions */ /* Internal interface functions */
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* isst_tpmi.c: SST TPMI interface
*
* Copyright (c) 2023, Intel Corporation.
* All Rights Reserved.
*
*/
#include <linux/auxiliary_bus.h>
#include <linux/module.h>
#include <linux/intel_tpmi.h>
#include "isst_tpmi_core.h"
static int intel_sst_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id)
{
int ret;
ret = tpmi_sst_init();
if (ret)
return ret;
ret = tpmi_sst_dev_add(auxdev);
if (ret)
tpmi_sst_exit();
return ret;
}
static void intel_sst_remove(struct auxiliary_device *auxdev)
{
tpmi_sst_dev_remove(auxdev);
tpmi_sst_exit();
}
static int intel_sst_suspend(struct device *dev)
{
tpmi_sst_dev_suspend(to_auxiliary_dev(dev));
return 0;
}
static int intel_sst_resume(struct device *dev)
{
tpmi_sst_dev_resume(to_auxiliary_dev(dev));
return 0;
}
static DEFINE_SIMPLE_DEV_PM_OPS(intel_sst_pm, intel_sst_suspend, intel_sst_resume);
static const struct auxiliary_device_id intel_sst_id_table[] = {
{ .name = "intel_vsec.tpmi-sst" },
{}
};
MODULE_DEVICE_TABLE(auxiliary, intel_sst_id_table);
static struct auxiliary_driver intel_sst_aux_driver = {
.id_table = intel_sst_id_table,
.remove = intel_sst_remove,
.probe = intel_sst_probe,
.driver = {
.pm = pm_sleep_ptr(&intel_sst_pm),
},
};
module_auxiliary_driver(intel_sst_aux_driver);
MODULE_IMPORT_NS(INTEL_TPMI_SST);
MODULE_DESCRIPTION("Intel TPMI SST Driver");
MODULE_LICENSE("GPL");
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Intel Speed Select Interface: Drivers Internal defines
* Copyright (c) 2023, Intel Corporation.
* All rights reserved.
*
*/
#ifndef _ISST_TPMI_CORE_H
#define _ISST_TPMI_CORE_H
int tpmi_sst_init(void);
void tpmi_sst_exit(void);
int tpmi_sst_dev_add(struct auxiliary_device *auxdev);
void tpmi_sst_dev_remove(struct auxiliary_device *auxdev);
void tpmi_sst_dev_suspend(struct auxiliary_device *auxdev);
void tpmi_sst_dev_resume(struct auxiliary_device *auxdev);
#endif
...@@ -1156,15 +1156,14 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev) ...@@ -1156,15 +1156,14 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev)
return ret; return ret;
} }
static int telemetry_pltdrv_remove(struct platform_device *pdev) static void telemetry_pltdrv_remove(struct platform_device *pdev)
{ {
telemetry_clear_pltdata(); telemetry_clear_pltdata();
return 0;
} }
static struct platform_driver telemetry_soc_driver = { static struct platform_driver telemetry_soc_driver = {
.probe = telemetry_pltdrv_probe, .probe = telemetry_pltdrv_probe,
.remove = telemetry_pltdrv_remove, .remove_new = telemetry_pltdrv_remove,
.driver = { .driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
}, },
......
...@@ -204,6 +204,13 @@ static const struct x86_cpu_id intel_uncore_cpu_ids[] = { ...@@ -204,6 +204,13 @@ static const struct x86_cpu_id intel_uncore_cpu_ids[] = {
X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_D, NULL), X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_D, NULL),
X86_MATCH_INTEL_FAM6_MODEL(SAPPHIRERAPIDS_X, NULL), X86_MATCH_INTEL_FAM6_MODEL(SAPPHIRERAPIDS_X, NULL),
X86_MATCH_INTEL_FAM6_MODEL(EMERALDRAPIDS_X, NULL), X86_MATCH_INTEL_FAM6_MODEL(EMERALDRAPIDS_X, NULL),
X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, NULL),
X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, NULL),
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, NULL),
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, NULL),
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, NULL),
X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE, NULL),
X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE_L, NULL),
{} {}
}; };
MODULE_DEVICE_TABLE(x86cpu, intel_uncore_cpu_ids); MODULE_DEVICE_TABLE(x86cpu, intel_uncore_cpu_ids);
......
...@@ -325,18 +325,12 @@ static int intel_vbtn_probe(struct platform_device *device) ...@@ -325,18 +325,12 @@ static int intel_vbtn_probe(struct platform_device *device)
return 0; return 0;
} }
static int intel_vbtn_remove(struct platform_device *device) static void intel_vbtn_remove(struct platform_device *device)
{ {
acpi_handle handle = ACPI_HANDLE(&device->dev); acpi_handle handle = ACPI_HANDLE(&device->dev);
device_init_wakeup(&device->dev, false); device_init_wakeup(&device->dev, false);
acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler); acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
/*
* Even if we failed to shut off the event stream, we can still
* safely detach from the device.
*/
return 0;
} }
static int intel_vbtn_pm_prepare(struct device *dev) static int intel_vbtn_pm_prepare(struct device *dev)
...@@ -377,7 +371,7 @@ static struct platform_driver intel_vbtn_pl_driver = { ...@@ -377,7 +371,7 @@ static struct platform_driver intel_vbtn_pl_driver = {
.pm = &intel_vbtn_pm_ops, .pm = &intel_vbtn_pm_ops,
}, },
.probe = intel_vbtn_probe, .probe = intel_vbtn_probe,
.remove = intel_vbtn_remove, .remove_new = intel_vbtn_remove,
}; };
static acpi_status __init static acpi_status __init
......
...@@ -67,14 +67,6 @@ enum intel_vsec_id { ...@@ -67,14 +67,6 @@ enum intel_vsec_id {
VSEC_ID_TPMI = 66, VSEC_ID_TPMI = 66,
}; };
static enum intel_vsec_id intel_vsec_allow_list[] = {
VSEC_ID_TELEMETRY,
VSEC_ID_WATCHER,
VSEC_ID_CRASHLOG,
VSEC_ID_SDSI,
VSEC_ID_TPMI,
};
static const char *intel_vsec_name(enum intel_vsec_id id) static const char *intel_vsec_name(enum intel_vsec_id id)
{ {
switch (id) { switch (id) {
...@@ -98,26 +90,19 @@ static const char *intel_vsec_name(enum intel_vsec_id id) ...@@ -98,26 +90,19 @@ static const char *intel_vsec_name(enum intel_vsec_id id)
} }
} }
static bool intel_vsec_allowed(u16 id) static bool intel_vsec_supported(u16 id, unsigned long caps)
{
int i;
for (i = 0; i < ARRAY_SIZE(intel_vsec_allow_list); i++)
if (intel_vsec_allow_list[i] == id)
return true;
return false;
}
static bool intel_vsec_disabled(u16 id, unsigned long quirks)
{ {
switch (id) { switch (id) {
case VSEC_ID_TELEMETRY:
return !!(caps & VSEC_CAP_TELEMETRY);
case VSEC_ID_WATCHER: case VSEC_ID_WATCHER:
return !!(quirks & VSEC_QUIRK_NO_WATCHER); return !!(caps & VSEC_CAP_WATCHER);
case VSEC_ID_CRASHLOG: case VSEC_ID_CRASHLOG:
return !!(quirks & VSEC_QUIRK_NO_CRASHLOG); return !!(caps & VSEC_CAP_CRASHLOG);
case VSEC_ID_SDSI:
return !!(caps & VSEC_CAP_SDSI);
case VSEC_ID_TPMI:
return !!(caps & VSEC_CAP_TPMI);
default: default:
return false; return false;
} }
...@@ -169,11 +154,7 @@ int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, ...@@ -169,11 +154,7 @@ int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent,
ret = auxiliary_device_init(auxdev); ret = auxiliary_device_init(auxdev);
if (ret < 0) { if (ret < 0) {
mutex_lock(&vsec_ida_lock); intel_vsec_dev_release(&auxdev->dev);
ida_free(intel_vsec_dev->ida, auxdev->id);
mutex_unlock(&vsec_ida_lock);
kfree(intel_vsec_dev->resource);
kfree(intel_vsec_dev);
return ret; return ret;
} }
...@@ -206,7 +187,7 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he ...@@ -206,7 +187,7 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he
unsigned long quirks = info->quirks; unsigned long quirks = info->quirks;
int i; int i;
if (!intel_vsec_allowed(header->id) || intel_vsec_disabled(header->id, quirks)) if (!intel_vsec_supported(header->id, info->caps))
return -EINVAL; return -EINVAL;
if (!header->num_entries) { if (!header->num_entries) {
...@@ -261,14 +242,14 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he ...@@ -261,14 +242,14 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he
static bool intel_vsec_walk_header(struct pci_dev *pdev, static bool intel_vsec_walk_header(struct pci_dev *pdev,
struct intel_vsec_platform_info *info) struct intel_vsec_platform_info *info)
{ {
struct intel_vsec_header **header = info->capabilities; struct intel_vsec_header **header = info->headers;
bool have_devices = false; bool have_devices = false;
int ret; int ret;
for ( ; *header; header++) { for ( ; *header; header++) {
ret = intel_vsec_add_dev(pdev, *header, info); ret = intel_vsec_add_dev(pdev, *header, info);
if (ret) if (ret)
dev_info(&pdev->dev, "Could not add device for DVSEC id %d\n", dev_info(&pdev->dev, "Could not add device for VSEC id %d\n",
(*header)->id); (*header)->id);
else else
have_devices = true; have_devices = true;
...@@ -403,14 +384,8 @@ static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id ...@@ -403,14 +384,8 @@ static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id
return 0; return 0;
} }
/* TGL info */
static const struct intel_vsec_platform_info tgl_info = {
.quirks = VSEC_QUIRK_NO_WATCHER | VSEC_QUIRK_NO_CRASHLOG |
VSEC_QUIRK_TABLE_SHIFT | VSEC_QUIRK_EARLY_HW,
};
/* DG1 info */ /* DG1 info */
static struct intel_vsec_header dg1_telemetry = { static struct intel_vsec_header dg1_header = {
.length = 0x10, .length = 0x10,
.id = 2, .id = 2,
.num_entries = 1, .num_entries = 1,
...@@ -419,19 +394,31 @@ static struct intel_vsec_header dg1_telemetry = { ...@@ -419,19 +394,31 @@ static struct intel_vsec_header dg1_telemetry = {
.offset = 0x466000, .offset = 0x466000,
}; };
static struct intel_vsec_header *dg1_capabilities[] = { static struct intel_vsec_header *dg1_headers[] = {
&dg1_telemetry, &dg1_header,
NULL NULL
}; };
static const struct intel_vsec_platform_info dg1_info = { static const struct intel_vsec_platform_info dg1_info = {
.capabilities = dg1_capabilities, .caps = VSEC_CAP_TELEMETRY,
.headers = dg1_headers,
.quirks = VSEC_QUIRK_NO_DVSEC | VSEC_QUIRK_EARLY_HW, .quirks = VSEC_QUIRK_NO_DVSEC | VSEC_QUIRK_EARLY_HW,
}; };
/* MTL info */ /* MTL info */
static const struct intel_vsec_platform_info mtl_info = { static const struct intel_vsec_platform_info mtl_info = {
.quirks = VSEC_QUIRK_NO_WATCHER | VSEC_QUIRK_NO_CRASHLOG, .caps = VSEC_CAP_TELEMETRY,
};
/* OOBMSM info */
static const struct intel_vsec_platform_info oobmsm_info = {
.caps = VSEC_CAP_TELEMETRY | VSEC_CAP_SDSI | VSEC_CAP_TPMI,
};
/* TGL info */
static const struct intel_vsec_platform_info tgl_info = {
.caps = VSEC_CAP_TELEMETRY,
.quirks = VSEC_QUIRK_TABLE_SHIFT | VSEC_QUIRK_EARLY_HW,
}; };
#define PCI_DEVICE_ID_INTEL_VSEC_ADL 0x467d #define PCI_DEVICE_ID_INTEL_VSEC_ADL 0x467d
...@@ -446,7 +433,7 @@ static const struct pci_device_id intel_vsec_pci_ids[] = { ...@@ -446,7 +433,7 @@ static const struct pci_device_id intel_vsec_pci_ids[] = {
{ PCI_DEVICE_DATA(INTEL, VSEC_DG1, &dg1_info) }, { PCI_DEVICE_DATA(INTEL, VSEC_DG1, &dg1_info) },
{ PCI_DEVICE_DATA(INTEL, VSEC_MTL_M, &mtl_info) }, { PCI_DEVICE_DATA(INTEL, VSEC_MTL_M, &mtl_info) },
{ PCI_DEVICE_DATA(INTEL, VSEC_MTL_S, &mtl_info) }, { PCI_DEVICE_DATA(INTEL, VSEC_MTL_S, &mtl_info) },
{ PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM, &(struct intel_vsec_platform_info) {}) }, { PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM, &oobmsm_info) },
{ PCI_DEVICE_DATA(INTEL, VSEC_RPL, &tgl_info) }, { PCI_DEVICE_DATA(INTEL, VSEC_RPL, &tgl_info) },
{ PCI_DEVICE_DATA(INTEL, VSEC_TGL, &tgl_info) }, { PCI_DEVICE_DATA(INTEL, VSEC_TGL, &tgl_info) },
{ } { }
......
...@@ -5,6 +5,12 @@ ...@@ -5,6 +5,12 @@
#include <linux/auxiliary_bus.h> #include <linux/auxiliary_bus.h>
#include <linux/bits.h> #include <linux/bits.h>
#define VSEC_CAP_TELEMETRY BIT(0)
#define VSEC_CAP_WATCHER BIT(1)
#define VSEC_CAP_CRASHLOG BIT(2)
#define VSEC_CAP_SDSI BIT(3)
#define VSEC_CAP_TPMI BIT(4)
struct pci_dev; struct pci_dev;
struct resource; struct resource;
...@@ -27,7 +33,8 @@ enum intel_vsec_quirks { ...@@ -27,7 +33,8 @@ enum intel_vsec_quirks {
/* Platform specific data */ /* Platform specific data */
struct intel_vsec_platform_info { struct intel_vsec_platform_info {
struct intel_vsec_header **capabilities; struct intel_vsec_header **headers;
unsigned long caps;
unsigned long quirks; unsigned long quirks;
}; };
......
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* lenovo-ymc.c - Lenovo Yoga Mode Control driver
*
* Copyright © 2022 Gergo Koteles <soyer@irl.hu>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/acpi.h>
#include <linux/dmi.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <linux/wmi.h>
#include "ideapad-laptop.h"
#define LENOVO_YMC_EVENT_GUID "06129D99-6083-4164-81AD-F092F9D773A6"
#define LENOVO_YMC_QUERY_GUID "09B0EE6E-C3FD-4243-8DA1-7911FF80BB8C"
#define LENOVO_YMC_QUERY_INSTANCE 0
#define LENOVO_YMC_QUERY_METHOD 0x01
static bool ec_trigger __read_mostly;
module_param(ec_trigger, bool, 0444);
MODULE_PARM_DESC(ec_trigger, "Enable EC triggering work-around to force emitting tablet mode events");
static const struct dmi_system_id ec_trigger_quirk_dmi_table[] = {
{
/* Lenovo Yoga 7 14ARB7 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "82QF"),
},
},
{ }
};
struct lenovo_ymc_private {
struct input_dev *input_dev;
struct acpi_device *ec_acpi_dev;
};
static void lenovo_ymc_trigger_ec(struct wmi_device *wdev, struct lenovo_ymc_private *priv)
{
int err;
if (!priv->ec_acpi_dev)
return;
err = write_ec_cmd(priv->ec_acpi_dev->handle, VPCCMD_W_YMC, 1);
if (err)
dev_warn(&wdev->dev, "Could not write YMC: %d\n", err);
}
static const struct key_entry lenovo_ymc_keymap[] = {
/* Laptop */
{ KE_SW, 0x01, { .sw = { SW_TABLET_MODE, 0 } } },
/* Tablet */
{ KE_SW, 0x02, { .sw = { SW_TABLET_MODE, 1 } } },
/* Drawing Board */
{ KE_SW, 0x03, { .sw = { SW_TABLET_MODE, 1 } } },
/* Tent */
{ KE_SW, 0x04, { .sw = { SW_TABLET_MODE, 1 } } },
{ KE_END },
};
static void lenovo_ymc_notify(struct wmi_device *wdev, union acpi_object *data)
{
struct lenovo_ymc_private *priv = dev_get_drvdata(&wdev->dev);
u32 input_val = 0;
struct acpi_buffer input = { sizeof(input_val), &input_val };
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
acpi_status status;
int code;
status = wmi_evaluate_method(LENOVO_YMC_QUERY_GUID,
LENOVO_YMC_QUERY_INSTANCE,
LENOVO_YMC_QUERY_METHOD,
&input, &output);
if (ACPI_FAILURE(status)) {
dev_warn(&wdev->dev,
"Failed to evaluate query method: %s\n",
acpi_format_exception(status));
return;
}
obj = output.pointer;
if (obj->type != ACPI_TYPE_INTEGER) {
dev_warn(&wdev->dev,
"WMI event data is not an integer\n");
goto free_obj;
}
code = obj->integer.value;
if (!sparse_keymap_report_event(priv->input_dev, code, 1, true))
dev_warn(&wdev->dev, "Unknown key %d pressed\n", code);
free_obj:
kfree(obj);
lenovo_ymc_trigger_ec(wdev, priv);
}
static void acpi_dev_put_helper(void *p) { acpi_dev_put(p); }
static int lenovo_ymc_probe(struct wmi_device *wdev, const void *ctx)
{
struct lenovo_ymc_private *priv;
struct input_dev *input_dev;
int err;
ec_trigger |= dmi_check_system(ec_trigger_quirk_dmi_table);
priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
if (ec_trigger) {
pr_debug("Lenovo YMC enable EC triggering.\n");
priv->ec_acpi_dev = acpi_dev_get_first_match_dev("VPC2004", NULL, -1);
if (!priv->ec_acpi_dev) {
dev_err(&wdev->dev, "Could not find EC ACPI device.\n");
return -ENODEV;
}
err = devm_add_action_or_reset(&wdev->dev,
acpi_dev_put_helper, priv->ec_acpi_dev);
if (err) {
dev_err(&wdev->dev,
"Could not clean up EC ACPI device: %d\n", err);
return err;
}
}
input_dev = devm_input_allocate_device(&wdev->dev);
if (!input_dev)
return -ENOMEM;
input_dev->name = "Lenovo Yoga Tablet Mode Control switch";
input_dev->phys = LENOVO_YMC_EVENT_GUID "/input0";
input_dev->id.bustype = BUS_HOST;
input_dev->dev.parent = &wdev->dev;
err = sparse_keymap_setup(input_dev, lenovo_ymc_keymap, NULL);
if (err) {
dev_err(&wdev->dev,
"Could not set up input device keymap: %d\n", err);
return err;
}
err = input_register_device(input_dev);
if (err) {
dev_err(&wdev->dev,
"Could not register input device: %d\n", err);
return err;
}
priv->input_dev = input_dev;
dev_set_drvdata(&wdev->dev, priv);
/* Report the state for the first time on probe */
lenovo_ymc_trigger_ec(wdev, priv);
lenovo_ymc_notify(wdev, NULL);
return 0;
}
static const struct wmi_device_id lenovo_ymc_wmi_id_table[] = {
{ .guid_string = LENOVO_YMC_EVENT_GUID },
{ }
};
MODULE_DEVICE_TABLE(wmi, lenovo_ymc_wmi_id_table);
static struct wmi_driver lenovo_ymc_driver = {
.driver = {
.name = "lenovo-ymc",
},
.id_table = lenovo_ymc_wmi_id_table,
.probe = lenovo_ymc_probe,
.notify = lenovo_ymc_notify,
};
module_wmi_driver(lenovo_ymc_driver);
MODULE_AUTHOR("Gergo Koteles <soyer@irl.hu>");
MODULE_DESCRIPTION("Lenovo Yoga Mode Control driver");
MODULE_LICENSE("GPL");
This diff is collapsed.
This diff is collapsed.
...@@ -291,5 +291,4 @@ MODULE_AUTHOR("Enrico Weigelt, metux IT consult <info@metux.net>"); ...@@ -291,5 +291,4 @@ MODULE_AUTHOR("Enrico Weigelt, metux IT consult <info@metux.net>");
MODULE_DESCRIPTION("PC Engines APUv2/APUv3 board GPIO/LEDs/keys driver"); MODULE_DESCRIPTION("PC Engines APUv2/APUv3 board GPIO/LEDs/keys driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(dmi, apu_gpio_dmi_table); MODULE_DEVICE_TABLE(dmi, apu_gpio_dmi_table);
MODULE_ALIAS("platform:pcengines-apuv2");
MODULE_SOFTDEP("pre: platform:" AMD_FCH_GPIO_DRIVER_NAME " platform:leds-gpio platform:gpio_keys_polled"); MODULE_SOFTDEP("pre: platform:" AMD_FCH_GPIO_DRIVER_NAME " platform:leds-gpio platform:gpio_keys_polled");
This diff is collapsed.
...@@ -65,14 +65,12 @@ static int samsungq10_probe(struct platform_device *pdev) ...@@ -65,14 +65,12 @@ static int samsungq10_probe(struct platform_device *pdev)
return 0; return 0;
} }
static int samsungq10_remove(struct platform_device *pdev) static void samsungq10_remove(struct platform_device *pdev)
{ {
struct backlight_device *bd = platform_get_drvdata(pdev); struct backlight_device *bd = platform_get_drvdata(pdev);
backlight_device_unregister(bd); backlight_device_unregister(bd);
return 0;
} }
static struct platform_driver samsungq10_driver = { static struct platform_driver samsungq10_driver = {
...@@ -80,7 +78,7 @@ static struct platform_driver samsungq10_driver = { ...@@ -80,7 +78,7 @@ static struct platform_driver samsungq10_driver = {
.name = KBUILD_MODNAME, .name = KBUILD_MODNAME,
}, },
.probe = samsungq10_probe, .probe = samsungq10_probe,
.remove = samsungq10_remove, .remove_new = samsungq10_remove,
}; };
static struct platform_device *samsungq10_device; static struct platform_device *samsungq10_device;
......
...@@ -265,13 +265,11 @@ static int smi_probe(struct platform_device *pdev) ...@@ -265,13 +265,11 @@ static int smi_probe(struct platform_device *pdev)
} }
} }
static int smi_remove(struct platform_device *pdev) static void smi_remove(struct platform_device *pdev)
{ {
struct smi *smi = platform_get_drvdata(pdev); struct smi *smi = platform_get_drvdata(pdev);
smi_devs_unregister(smi); smi_devs_unregister(smi);
return 0;
} }
static const struct smi_node bsg1160_data = { static const struct smi_node bsg1160_data = {
...@@ -339,7 +337,7 @@ static struct platform_driver smi_driver = { ...@@ -339,7 +337,7 @@ static struct platform_driver smi_driver = {
.acpi_match_table = smi_acpi_ids, .acpi_match_table = smi_acpi_ids,
}, },
.probe = smi_probe, .probe = smi_probe,
.remove = smi_remove, .remove_new = smi_remove,
}; };
module_platform_driver(smi_driver); module_platform_driver(smi_driver);
......
...@@ -3287,7 +3287,7 @@ static void sony_nc_remove(struct acpi_device *device) ...@@ -3287,7 +3287,7 @@ static void sony_nc_remove(struct acpi_device *device)
dprintk(SONY_NC_DRIVER_NAME " removed.\n"); dprintk(SONY_NC_DRIVER_NAME " removed.\n");
} }
static const struct acpi_device_id sony_device_ids[] = { static const struct acpi_device_id sony_device_ids[] __maybe_unused = {
{SONY_NC_HID, 0}, {SONY_NC_HID, 0},
{SONY_PIC_HID, 0}, {SONY_PIC_HID, 0},
{"", 0}, {"", 0},
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
# SPDX-License-Identifier: GPL-2.0-or-later
#
# X86 Android tablet support Makefile
#
obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o
x86-android-tablets-y := core.o dmi.o shared-psy-info.o \
asus.o lenovo.o other.o
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -285,6 +285,7 @@ config BACKLIGHT_MT6370 ...@@ -285,6 +285,7 @@ config BACKLIGHT_MT6370
config BACKLIGHT_APPLE config BACKLIGHT_APPLE
tristate "Apple Backlight Driver" tristate "Apple Backlight Driver"
depends on X86 && ACPI depends on X86 && ACPI
depends on ACPI_VIDEO=n || ACPI_VIDEO
help help
If you have an Intel-based Apple say Y to enable a driver for its If you have an Intel-based Apple say Y to enable a driver for its
backlight. backlight.
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment