summaryrefslogtreecommitdiff
path: root/drivers/platform
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/arm64/Kconfig21
-rw-r--r--drivers/platform/arm64/Makefile1
-rw-r--r--drivers/platform/arm64/huawei-gaokun-ec.c825
-rw-r--r--drivers/platform/chrome/Kconfig7
-rw-r--r--drivers/platform/chrome/Makefile4
-rw-r--r--drivers/platform/chrome/cros_ec.c5
-rw-r--r--drivers/platform/chrome/cros_ec_i2c.c3
-rw-r--r--drivers/platform/chrome/cros_ec_ishtp.c2
-rw-r--r--drivers/platform/chrome/cros_ec_lpc.c227
-rw-r--r--drivers/platform/chrome/cros_ec_proto.c69
-rw-r--r--drivers/platform/chrome/cros_ec_rpmsg.c2
-rw-r--r--drivers/platform/chrome/cros_ec_spi.c4
-rw-r--r--drivers/platform/chrome/cros_ec_sysfs.c71
-rw-r--r--drivers/platform/chrome/cros_ec_trace.c10
-rw-r--r--drivers/platform/chrome/cros_ec_typec.c80
-rw-r--r--drivers/platform/chrome/cros_ec_typec.h2
-rw-r--r--drivers/platform/chrome/cros_ec_uart.c2
-rw-r--r--drivers/platform/chrome/cros_ec_vbc.c10
-rw-r--r--drivers/platform/chrome/cros_kbd_led_backlight.c79
-rw-r--r--drivers/platform/chrome/cros_typec_altmode.c373
-rw-r--r--drivers/platform/chrome/cros_typec_altmode.h51
-rw-r--r--drivers/platform/chrome/cros_usbpd_logger.c5
-rw-r--r--drivers/platform/cznic/turris-omnia-mcu-base.c3
-rw-r--r--drivers/platform/cznic/turris-omnia-mcu.h130
-rw-r--r--drivers/platform/loongarch/loongson-laptop.c87
-rw-r--r--drivers/platform/mellanox/Kconfig13
-rw-r--r--drivers/platform/mellanox/Makefile1
-rw-r--r--drivers/platform/mellanox/mlx-platform.c (renamed from drivers/platform/x86/mlx-platform.c)17
-rw-r--r--drivers/platform/mellanox/mlxbf-bootctl.c40
-rw-r--r--drivers/platform/mellanox/mlxbf-bootctl.h5
-rw-r--r--drivers/platform/mellanox/mlxbf-pmc.c107
-rw-r--r--drivers/platform/mellanox/mlxbf-tmfifo.c2
-rw-r--r--drivers/platform/mellanox/mlxreg-hotplug.c2
-rw-r--r--drivers/platform/mellanox/mlxreg-io.c2
-rw-r--r--drivers/platform/surface/surface_aggregator_registry.c5
-rw-r--r--drivers/platform/surface/surface_platform_profile.c44
-rw-r--r--drivers/platform/x86/Kconfig41
-rw-r--r--drivers/platform/x86/Makefile7
-rw-r--r--drivers/platform/x86/acer-wmi.c505
-rw-r--r--drivers/platform/x86/amd/Makefile2
-rw-r--r--drivers/platform/x86/amd/hsmp/Kconfig2
-rw-r--r--drivers/platform/x86/amd/hsmp/Makefile6
-rw-r--r--drivers/platform/x86/amd/hsmp/acpi.c22
-rw-r--r--drivers/platform/x86/amd/hsmp/hsmp.c48
-rw-r--r--drivers/platform/x86/amd/hsmp/hsmp.h4
-rw-r--r--drivers/platform/x86/amd/hsmp/plat.c54
-rw-r--r--drivers/platform/x86/amd/pmc/Kconfig2
-rw-r--r--drivers/platform/x86/amd/pmc/Makefile6
-rw-r--r--drivers/platform/x86/amd/pmc/mp1_stb.c332
-rw-r--r--drivers/platform/x86/amd/pmc/pmc-quirks.c7
-rw-r--r--drivers/platform/x86/amd/pmc/pmc.c512
-rw-r--r--drivers/platform/x86/amd/pmc/pmc.h106
-rw-r--r--drivers/platform/x86/amd/pmf/Kconfig2
-rw-r--r--drivers/platform/x86/amd/pmf/Makefile8
-rw-r--r--drivers/platform/x86/amd/pmf/acpi.c32
-rw-r--r--drivers/platform/x86/amd/pmf/auto-mode.c4
-rw-r--r--drivers/platform/x86/amd/pmf/cnqf.c8
-rw-r--r--drivers/platform/x86/amd/pmf/core.c41
-rw-r--r--drivers/platform/x86/amd/pmf/pmf-quirks.c66
-rw-r--r--drivers/platform/x86/amd/pmf/pmf.h45
-rw-r--r--drivers/platform/x86/amd/pmf/spc.c77
-rw-r--r--drivers/platform/x86/amd/pmf/sps.c72
-rw-r--r--drivers/platform/x86/amd/pmf/tee-if.c130
-rw-r--r--drivers/platform/x86/asus-laptop.c9
-rw-r--r--drivers/platform/x86/asus-nb-wmi.c3
-rw-r--r--drivers/platform/x86/asus-tf103c-dock.c2
-rw-r--r--drivers/platform/x86/asus-wmi.c69
-rw-r--r--drivers/platform/x86/asus-wmi.h3
-rw-r--r--drivers/platform/x86/compal-laptop.c1
-rw-r--r--drivers/platform/x86/dell/Kconfig31
-rw-r--r--drivers/platform/x86/dell/Makefile44
-rw-r--r--drivers/platform/x86/dell/alienware-wmi-base.c491
-rw-r--r--drivers/platform/x86/dell/alienware-wmi-legacy.c95
-rw-r--r--drivers/platform/x86/dell/alienware-wmi-wmax.c822
-rw-r--r--drivers/platform/x86/dell/alienware-wmi.c1281
-rw-r--r--drivers/platform/x86/dell/alienware-wmi.h117
-rw-r--r--drivers/platform/x86/dell/dcdbas.c10
-rw-r--r--drivers/platform/x86/dell/dcdbas.h8
-rw-r--r--drivers/platform/x86/dell/dell-laptop.c60
-rw-r--r--drivers/platform/x86/dell/dell-lis3lv02d.c256
-rw-r--r--drivers/platform/x86/dell/dell-pc.c69
-rw-r--r--drivers/platform/x86/dell/dell-smo8800-ids.h27
-rw-r--r--drivers/platform/x86/dell/dell-smo8800.c16
-rw-r--r--drivers/platform/x86/dell/dell-uart-backlight.c4
-rw-r--r--drivers/platform/x86/dell/dell-wmi-ddv.c84
-rw-r--r--drivers/platform/x86/dell/dell-wmi-sysman/Makefile2
-rw-r--r--drivers/platform/x86/dell/dell-wmi-sysman/passobj-attributes.c2
-rw-r--r--drivers/platform/x86/dell/dell-wmi-sysman/sysman.c17
-rw-r--r--drivers/platform/x86/dell/dell_rbu.c26
-rw-r--r--drivers/platform/x86/firmware_attributes_class.c42
-rw-r--r--drivers/platform/x86/firmware_attributes_class.h5
-rw-r--r--drivers/platform/x86/fujitsu-laptop.c41
-rw-r--r--drivers/platform/x86/gigabyte-wmi.c4
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/Makefile2
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/bioscfg.c29
-rw-r--r--drivers/platform/x86/hp/hp-wmi.c449
-rw-r--r--drivers/platform/x86/hp/hp_accel.c4
-rw-r--r--drivers/platform/x86/ideapad-laptop.c101
-rw-r--r--drivers/platform/x86/inspur_platform_profile.c43
-rw-r--r--drivers/platform/x86/intel/Kconfig1
-rw-r--r--drivers/platform/x86/intel/bytcrc_pwrsrc.c79
-rw-r--r--drivers/platform/x86/intel/hid.c28
-rw-r--r--drivers/platform/x86/intel/ifs/Makefile2
-rw-r--r--drivers/platform/x86/intel/ifs/ifs.h9
-rw-r--r--drivers/platform/x86/intel/int0002_vgpio.c14
-rw-r--r--drivers/platform/x86/intel/int3472/common.c2
-rw-r--r--drivers/platform/x86/intel/int3472/discrete.c124
-rw-r--r--drivers/platform/x86/intel/plr_tpmi.c2
-rw-r--r--drivers/platform/x86/intel/pmc/Makefile2
-rw-r--r--drivers/platform/x86/intel/pmc/adl.c56
-rw-r--r--drivers/platform/x86/intel/pmc/arl.c138
-rw-r--r--drivers/platform/x86/intel/pmc/cnp.c29
-rw-r--r--drivers/platform/x86/intel/pmc/core.c126
-rw-r--r--drivers/platform/x86/intel/pmc/core.h199
-rw-r--r--drivers/platform/x86/intel/pmc/icl.c24
-rw-r--r--drivers/platform/x86/intel/pmc/lnl.c67
-rw-r--r--drivers/platform/x86/intel/pmc/mtl.c109
-rw-r--r--drivers/platform/x86/intel/pmc/ptl.c550
-rw-r--r--drivers/platform/x86/intel/pmc/spt.c45
-rw-r--r--drivers/platform/x86/intel/pmc/tgl.c59
-rw-r--r--drivers/platform/x86/intel/pmt/class.c4
-rw-r--r--drivers/platform/x86/intel/punit_ipc.c33
-rw-r--r--drivers/platform/x86/intel/sdsi.c34
-rw-r--r--drivers/platform/x86/intel/speed_select_if/isst_if_common.c2
-rw-r--r--drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c9
-rw-r--r--drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c13
-rw-r--r--drivers/platform/x86/intel/vsec.c7
-rw-r--r--drivers/platform/x86/intel_ips.c2
-rw-r--r--drivers/platform/x86/lenovo-wmi-camera.c69
-rw-r--r--drivers/platform/x86/lenovo-wmi-hotkey-utilities.c212
-rw-r--r--drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c2
-rw-r--r--drivers/platform/x86/msi-laptop.c6
-rw-r--r--drivers/platform/x86/msi-wmi-platform.c99
-rw-r--r--drivers/platform/x86/panasonic-laptop.c4
-rw-r--r--drivers/platform/x86/quickstart.c1
-rw-r--r--drivers/platform/x86/samsung-galaxybook.c1425
-rw-r--r--drivers/platform/x86/serdev_helpers.h60
-rw-r--r--drivers/platform/x86/serial-multi-instantiate.c12
-rw-r--r--drivers/platform/x86/sony-laptop.c2
-rw-r--r--drivers/platform/x86/think-lmi.c90
-rw-r--r--drivers/platform/x86/think-lmi.h3
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c286
-rw-r--r--drivers/platform/x86/toshiba_acpi.c4
-rw-r--r--drivers/platform/x86/wmi-bmof.c75
-rw-r--r--drivers/platform/x86/wmi.c143
-rw-r--r--drivers/platform/x86/x86-android-tablets/Kconfig1
-rw-r--r--drivers/platform/x86/x86-android-tablets/Makefile2
-rw-r--r--drivers/platform/x86/x86-android-tablets/asus.c4
-rw-r--r--drivers/platform/x86/x86-android-tablets/core.c27
-rw-r--r--drivers/platform/x86/x86-android-tablets/dmi.c14
-rw-r--r--drivers/platform/x86/x86-android-tablets/lenovo.c4
-rw-r--r--drivers/platform/x86/x86-android-tablets/other.c130
-rw-r--r--drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c261
-rw-r--r--drivers/platform/x86/x86-android-tablets/x86-android-tablets.h16
154 files changed, 9522 insertions, 3920 deletions
diff --git a/drivers/platform/arm64/Kconfig b/drivers/platform/arm64/Kconfig
index f88395ea3376..0abe5377891b 100644
--- a/drivers/platform/arm64/Kconfig
+++ b/drivers/platform/arm64/Kconfig
@@ -33,6 +33,27 @@ config EC_ACER_ASPIRE1
laptop where this information is not properly exposed via the
standard ACPI devices.
+config EC_HUAWEI_GAOKUN
+ tristate "Huawei Matebook E Go Embedded Controller driver"
+ depends on ARCH_QCOM || COMPILE_TEST
+ depends on I2C
+ depends on INPUT
+ depends on HWMON
+ select AUXILIARY_BUS
+
+ help
+ Say Y here to enable the EC driver for the Huawei Matebook E Go
+ which is a sc8280xp-based 2-in-1 tablet. The driver handles battery
+ (information, charge control) and USB Type-C DP HPD events as well
+ as some misc functions like the lid sensor and temperature sensors,
+ etc.
+
+ This driver provides battery and AC status support for the mentioned
+ laptop where this information is not properly exposed via the
+ standard ACPI devices.
+
+ Say M or Y here to include this support.
+
config EC_LENOVO_YOGA_C630
tristate "Lenovo Yoga C630 Embedded Controller driver"
depends on ARCH_QCOM || COMPILE_TEST
diff --git a/drivers/platform/arm64/Makefile b/drivers/platform/arm64/Makefile
index b2ae9114fdd8..46a99eba3264 100644
--- a/drivers/platform/arm64/Makefile
+++ b/drivers/platform/arm64/Makefile
@@ -6,4 +6,5 @@
#
obj-$(CONFIG_EC_ACER_ASPIRE1) += acer-aspire1-ec.o
+obj-$(CONFIG_EC_HUAWEI_GAOKUN) += huawei-gaokun-ec.o
obj-$(CONFIG_EC_LENOVO_YOGA_C630) += lenovo-yoga-c630.o
diff --git a/drivers/platform/arm64/huawei-gaokun-ec.c b/drivers/platform/arm64/huawei-gaokun-ec.c
new file mode 100644
index 000000000000..97c2607f8d9f
--- /dev/null
+++ b/drivers/platform/arm64/huawei-gaokun-ec.c
@@ -0,0 +1,825 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * huawei-gaokun-ec - An EC driver for HUAWEI Matebook E Go
+ *
+ * Copyright (C) 2024-2025 Pengyu Luo <mitltlatltl@gmail.com>
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/notifier.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_data/huawei-gaokun-ec.h>
+
+#define EC_EVENT 0x06
+
+/* Also can be found in ACPI specification 12.3 */
+#define EC_READ 0x80
+#define EC_WRITE 0x81
+#define EC_BURST 0x82
+#define EC_QUERY 0x84
+
+#define EC_FN_LOCK_ON 0x5A
+#define EC_FN_LOCK_OFF 0x55
+#define EC_FN_LOCK_READ 0x6B
+#define EC_FN_LOCK_WRITE 0x6C
+
+#define EC_EVENT_LID 0x81
+
+#define EC_LID_STATE 0x80
+#define EC_LID_OPEN BIT(1)
+
+#define EC_TEMP_REG 0x61
+
+#define EC_STANDBY_REG 0xB2
+#define EC_STANDBY_ENTER 0xDB
+#define EC_STANDBY_EXIT 0xEB
+
+enum gaokun_ec_smart_charge_cmd {
+ SMART_CHARGE_DATA_WRITE = 0xE3,
+ SMART_CHARGE_DATA_READ,
+ SMART_CHARGE_ENABLE_WRITE,
+ SMART_CHARGE_ENABLE_READ,
+};
+
+enum gaokun_ec_ucsi_cmd {
+ UCSI_REG_WRITE = 0xD2,
+ UCSI_REG_READ,
+ UCSI_DATA_WRITE,
+ UCSI_DATA_READ,
+};
+
+#define UCSI_REG_SIZE 7
+
+/*
+ * For tx, command sequences are arranged as
+ * {master_cmd, slave_cmd, data_len, data_seq}
+ */
+#define REQ_HDR_SIZE 3
+#define INPUT_SIZE_OFFSET 2
+#define REQ_LEN(req) (REQ_HDR_SIZE + (req)[INPUT_SIZE_OFFSET])
+
+/*
+ * For rx, data sequences are arranged as
+ * {status, data_len(unreliable), data_seq}
+ */
+#define RESP_HDR_SIZE 2
+
+#define MKREQ(REG0, REG1, SIZE, ...) \
+{ \
+ REG0, REG1, SIZE, \
+ /* ## will remove comma when SIZE is 0 */ \
+ ## __VA_ARGS__, \
+ /* make sure len(pkt[3:]) >= SIZE */ \
+ [3 + (SIZE)] = 0, \
+}
+
+#define MKRESP(SIZE) \
+{ \
+ [RESP_HDR_SIZE + (SIZE) - 1] = 0, \
+}
+
+/* Possible size 1, 4, 20, 24. Most of the time, the size is 1. */
+static inline void refill_req(u8 *dest, const u8 *src, size_t size)
+{
+ memcpy(dest + REQ_HDR_SIZE, src, size);
+}
+
+static inline void refill_req_byte(u8 *dest, const u8 *src)
+{
+ dest[REQ_HDR_SIZE] = *src;
+}
+
+/* Possible size 1, 2, 4, 7, 20. Most of the time, the size is 1. */
+static inline void extr_resp(u8 *dest, const u8 *src, size_t size)
+{
+ memcpy(dest, src + RESP_HDR_SIZE, size);
+}
+
+static inline void extr_resp_byte(u8 *dest, const u8 *src)
+{
+ *dest = src[RESP_HDR_SIZE];
+}
+
+static inline void *extr_resp_shallow(const u8 *src)
+{
+ return (void *)(src + RESP_HDR_SIZE);
+}
+
+struct gaokun_ec {
+ struct i2c_client *client;
+ struct mutex lock; /* EC transaction lock */
+ struct blocking_notifier_head notifier_list;
+ struct device *hwmon_dev;
+ struct input_dev *idev;
+ bool suspended;
+};
+
+static int gaokun_ec_request(struct gaokun_ec *ec, const u8 *req,
+ size_t resp_len, u8 *resp)
+{
+ struct i2c_client *client = ec->client;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = client->flags,
+ .len = REQ_LEN(req),
+ .buf = (void *)req,
+ }, {
+ .addr = client->addr,
+ .flags = client->flags | I2C_M_RD,
+ .len = resp_len,
+ .buf = resp,
+ },
+ };
+ int ret;
+
+ guard(mutex)(&ec->lock);
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs)) {
+ dev_err(&client->dev, "I2C transfer error %d\n", ret);
+ goto out_after_break;
+ }
+
+ ret = *resp;
+ if (ret)
+ dev_err(&client->dev, "EC transaction error %d\n", ret);
+
+out_after_break:
+ usleep_range(2000, 2500); /* have a break, ACPI did this */
+
+ return ret;
+}
+
+/* -------------------------------------------------------------------------- */
+/* Common API */
+
+/**
+ * gaokun_ec_read - Read from EC
+ * @ec: The gaokun_ec structure
+ * @req: The sequence to request
+ * @resp_len: The size to read
+ * @resp: The buffer to store response sequence
+ *
+ * This function is used to read data after writing a magic sequence to EC.
+ * All EC operations depend on this function.
+ *
+ * Huawei uses magic sequences everywhere to complete various functions, all
+ * these sequences are passed to ECCD(a ACPI method which is quiet similar
+ * to gaokun_ec_request), there is no good abstraction to generalize these
+ * sequences, so just wrap it for now. Almost all magic sequences are kept
+ * in this file.
+ *
+ * Return: 0 on success or negative error code.
+ */
+int gaokun_ec_read(struct gaokun_ec *ec, const u8 *req,
+ size_t resp_len, u8 *resp)
+{
+ return gaokun_ec_request(ec, req, resp_len, resp);
+}
+EXPORT_SYMBOL_GPL(gaokun_ec_read);
+
+/**
+ * gaokun_ec_write - Write to EC
+ * @ec: The gaokun_ec structure
+ * @req: The sequence to request
+ *
+ * This function has no big difference from gaokun_ec_read. When caller care
+ * only write status and no actual data are returned, then use it.
+ *
+ * Return: 0 on success or negative error code.
+ */
+int gaokun_ec_write(struct gaokun_ec *ec, const u8 *req)
+{
+ u8 ec_resp[] = MKRESP(0);
+
+ return gaokun_ec_request(ec, req, sizeof(ec_resp), ec_resp);
+}
+EXPORT_SYMBOL_GPL(gaokun_ec_write);
+
+int gaokun_ec_read_byte(struct gaokun_ec *ec, const u8 *req, u8 *byte)
+{
+ int ret;
+ u8 ec_resp[] = MKRESP(sizeof(*byte));
+
+ ret = gaokun_ec_read(ec, req, sizeof(ec_resp), ec_resp);
+ extr_resp_byte(byte, ec_resp);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(gaokun_ec_read_byte);
+
+/**
+ * gaokun_ec_register_notify - Register a notifier callback for EC events.
+ * @ec: The gaokun_ec structure
+ * @nb: Notifier block pointer to register
+ *
+ * Return: 0 on success or negative error code.
+ */
+int gaokun_ec_register_notify(struct gaokun_ec *ec, struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&ec->notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(gaokun_ec_register_notify);
+
+/**
+ * gaokun_ec_unregister_notify - Unregister notifier callback for EC events.
+ * @ec: The gaokun_ec structure
+ * @nb: Notifier block pointer to unregister
+ *
+ * Unregister a notifier callback that was previously registered with
+ * gaokun_ec_register_notify().
+ */
+void gaokun_ec_unregister_notify(struct gaokun_ec *ec, struct notifier_block *nb)
+{
+ blocking_notifier_chain_unregister(&ec->notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(gaokun_ec_unregister_notify);
+
+/* -------------------------------------------------------------------------- */
+/* API for PSY */
+
+/**
+ * gaokun_ec_psy_multi_read - Read contiguous registers
+ * @ec: The gaokun_ec structure
+ * @reg: The start register
+ * @resp_len: The number of registers to be read
+ * @resp: The buffer to store response sequence
+ *
+ * Return: 0 on success or negative error code.
+ */
+int gaokun_ec_psy_multi_read(struct gaokun_ec *ec, u8 reg,
+ size_t resp_len, u8 *resp)
+{
+ u8 ec_req[] = MKREQ(0x02, EC_READ, 1, 0);
+ u8 ec_resp[] = MKRESP(1);
+ int i, ret;
+
+ for (i = 0; i < resp_len; ++i, reg++) {
+ refill_req_byte(ec_req, &reg);
+ ret = gaokun_ec_read(ec, ec_req, sizeof(ec_resp), ec_resp);
+ if (ret)
+ return ret;
+ extr_resp_byte(&resp[i], ec_resp);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gaokun_ec_psy_multi_read);
+
+/* Smart charge */
+
+/**
+ * gaokun_ec_psy_get_smart_charge - Get smart charge data from EC
+ * @ec: The gaokun_ec structure
+ * @resp: The buffer to store response sequence (mode, delay, start, end)
+ *
+ * Return: 0 on success or negative error code.
+ */
+int gaokun_ec_psy_get_smart_charge(struct gaokun_ec *ec,
+ u8 resp[GAOKUN_SMART_CHARGE_DATA_SIZE])
+{
+ /* GBCM */
+ u8 ec_req[] = MKREQ(0x02, SMART_CHARGE_DATA_READ, 0);
+ u8 ec_resp[] = MKRESP(GAOKUN_SMART_CHARGE_DATA_SIZE);
+ int ret;
+
+ ret = gaokun_ec_read(ec, ec_req, sizeof(ec_resp), ec_resp);
+ if (ret)
+ return ret;
+
+ extr_resp(resp, ec_resp, GAOKUN_SMART_CHARGE_DATA_SIZE);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gaokun_ec_psy_get_smart_charge);
+
+static inline bool validate_battery_threshold_range(u8 start, u8 end)
+{
+ return end != 0 && start <= end && end <= 100;
+}
+
+/**
+ * gaokun_ec_psy_set_smart_charge - Set smart charge data
+ * @ec: The gaokun_ec structure
+ * @req: The sequence to request (mode, delay, start, end)
+ *
+ * Return: 0 on success or negative error code.
+ */
+int gaokun_ec_psy_set_smart_charge(struct gaokun_ec *ec,
+ const u8 req[GAOKUN_SMART_CHARGE_DATA_SIZE])
+{
+ /* SBCM */
+ u8 ec_req[] = MKREQ(0x02, SMART_CHARGE_DATA_WRITE,
+ GAOKUN_SMART_CHARGE_DATA_SIZE);
+
+ if (!validate_battery_threshold_range(req[2], req[3]))
+ return -EINVAL;
+
+ refill_req(ec_req, req, GAOKUN_SMART_CHARGE_DATA_SIZE);
+
+ return gaokun_ec_write(ec, ec_req);
+}
+EXPORT_SYMBOL_GPL(gaokun_ec_psy_set_smart_charge);
+
+/* Smart charge enable */
+
+/**
+ * gaokun_ec_psy_get_smart_charge_enable - Get smart charge state
+ * @ec: The gaokun_ec structure
+ * @on: The state
+ *
+ * Return: 0 on success or negative error code.
+ */
+int gaokun_ec_psy_get_smart_charge_enable(struct gaokun_ec *ec, bool *on)
+{
+ /* GBAC */
+ u8 ec_req[] = MKREQ(0x02, SMART_CHARGE_ENABLE_READ, 0);
+ u8 state;
+ int ret;
+
+ ret = gaokun_ec_read_byte(ec, ec_req, &state);
+ if (ret)
+ return ret;
+
+ *on = !!state;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gaokun_ec_psy_get_smart_charge_enable);
+
+/**
+ * gaokun_ec_psy_set_smart_charge_enable - Set smart charge state
+ * @ec: The gaokun_ec structure
+ * @on: The state
+ *
+ * Return: 0 on success or negative error code.
+ */
+int gaokun_ec_psy_set_smart_charge_enable(struct gaokun_ec *ec, bool on)
+{
+ /* SBAC */
+ u8 ec_req[] = MKREQ(0x02, SMART_CHARGE_ENABLE_WRITE, 1, on);
+
+ return gaokun_ec_write(ec, ec_req);
+}
+EXPORT_SYMBOL_GPL(gaokun_ec_psy_set_smart_charge_enable);
+
+/* -------------------------------------------------------------------------- */
+/* API for UCSI */
+
+/**
+ * gaokun_ec_ucsi_read - Read UCSI data from EC
+ * @ec: The gaokun_ec structure
+ * @resp: The buffer to store response sequence
+ *
+ * Read CCI and MSGI (used by UCSI subdriver).
+ *
+ * Return: 0 on success or negative error code.
+ */
+int gaokun_ec_ucsi_read(struct gaokun_ec *ec,
+ u8 resp[GAOKUN_UCSI_READ_SIZE])
+{
+ u8 ec_req[] = MKREQ(0x03, UCSI_DATA_READ, 0);
+ u8 ec_resp[] = MKRESP(GAOKUN_UCSI_READ_SIZE);
+ int ret;
+
+ ret = gaokun_ec_read(ec, ec_req, sizeof(ec_resp), ec_resp);
+ if (ret)
+ return ret;
+
+ extr_resp(resp, ec_resp, GAOKUN_UCSI_READ_SIZE);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gaokun_ec_ucsi_read);
+
+/**
+ * gaokun_ec_ucsi_write - Write UCSI data to EC
+ * @ec: The gaokun_ec structure
+ * @req: The sequence to request
+ *
+ * Write CTRL and MSGO (used by UCSI subdriver).
+ *
+ * Return: 0 on success or negative error code.
+ */
+int gaokun_ec_ucsi_write(struct gaokun_ec *ec,
+ const u8 req[GAOKUN_UCSI_WRITE_SIZE])
+{
+ u8 ec_req[] = MKREQ(0x03, UCSI_DATA_WRITE, GAOKUN_UCSI_WRITE_SIZE);
+
+ refill_req(ec_req, req, GAOKUN_UCSI_WRITE_SIZE);
+
+ return gaokun_ec_write(ec, ec_req);
+}
+EXPORT_SYMBOL_GPL(gaokun_ec_ucsi_write);
+
+/**
+ * gaokun_ec_ucsi_get_reg - Get UCSI register from EC
+ * @ec: The gaokun_ec structure
+ * @ureg: The gaokun ucsi register
+ *
+ * Get UCSI register data (used by UCSI subdriver).
+ *
+ * Return: 0 on success or negative error code.
+ */
+int gaokun_ec_ucsi_get_reg(struct gaokun_ec *ec, struct gaokun_ucsi_reg *ureg)
+{
+ u8 ec_req[] = MKREQ(0x03, UCSI_REG_READ, 0);
+ u8 ec_resp[] = MKRESP(UCSI_REG_SIZE);
+ int ret;
+
+ ret = gaokun_ec_read(ec, ec_req, sizeof(ec_resp), ec_resp);
+ if (ret)
+ return ret;
+
+ extr_resp((u8 *)ureg, ec_resp, UCSI_REG_SIZE);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gaokun_ec_ucsi_get_reg);
+
+/**
+ * gaokun_ec_ucsi_pan_ack - Ack pin assignment notifications from EC
+ * @ec: The gaokun_ec structure
+ * @port_id: The port id receiving and handling the notifications
+ *
+ * Ack pin assignment notifications (used by UCSI subdriver).
+ *
+ * Return: 0 on success or negative error code.
+ */
+int gaokun_ec_ucsi_pan_ack(struct gaokun_ec *ec, int port_id)
+{
+ u8 ec_req[] = MKREQ(0x03, UCSI_REG_WRITE, 1);
+ u8 data = 1 << port_id;
+
+ if (port_id == GAOKUN_UCSI_NO_PORT_UPDATE)
+ data = 0;
+
+ refill_req_byte(ec_req, &data);
+
+ return gaokun_ec_write(ec, ec_req);
+}
+EXPORT_SYMBOL_GPL(gaokun_ec_ucsi_pan_ack);
+
+/* -------------------------------------------------------------------------- */
+/* EC Sysfs */
+
+/* Fn lock */
+static int gaokun_ec_get_fn_lock(struct gaokun_ec *ec, bool *on)
+{
+ /* GFRS */
+ u8 ec_req[] = MKREQ(0x02, EC_FN_LOCK_READ, 0);
+ int ret;
+ u8 state;
+
+ ret = gaokun_ec_read_byte(ec, ec_req, &state);
+ if (ret)
+ return ret;
+
+ if (state == EC_FN_LOCK_ON)
+ *on = true;
+ else if (state == EC_FN_LOCK_OFF)
+ *on = false;
+ else
+ return -EIO;
+
+ return 0;
+}
+
+static int gaokun_ec_set_fn_lock(struct gaokun_ec *ec, bool on)
+{
+ /* SFRS */
+ u8 ec_req[] = MKREQ(0x02, EC_FN_LOCK_WRITE, 1,
+ on ? EC_FN_LOCK_ON : EC_FN_LOCK_OFF);
+
+ return gaokun_ec_write(ec, ec_req);
+}
+
+static ssize_t fn_lock_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct gaokun_ec *ec = dev_get_drvdata(dev);
+ bool on;
+ int ret;
+
+ ret = gaokun_ec_get_fn_lock(ec, &on);
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%d\n", on);
+}
+
+static ssize_t fn_lock_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct gaokun_ec *ec = dev_get_drvdata(dev);
+ bool on;
+ int ret;
+
+ if (kstrtobool(buf, &on))
+ return -EINVAL;
+
+ ret = gaokun_ec_set_fn_lock(ec, on);
+ if (ret)
+ return ret;
+
+ return size;
+}
+
+static DEVICE_ATTR_RW(fn_lock);
+
+static struct attribute *gaokun_ec_attrs[] = {
+ &dev_attr_fn_lock.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(gaokun_ec);
+
+/* -------------------------------------------------------------------------- */
+/* Thermal Zone HwMon */
+
+/* Range from 0 to 0x2C, partially valid */
+static const u8 temp_reg[] = {
+ 0x05, 0x07, 0x08, 0x0E, 0x0F, 0x12, 0x15, 0x1E,
+ 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26,
+ 0x27, 0x28, 0x29, 0x2A
+};
+
+static int gaokun_ec_get_temp(struct gaokun_ec *ec, u8 idx, long *temp)
+{
+ /* GTMP */
+ u8 ec_req[] = MKREQ(0x02, EC_TEMP_REG, 1, temp_reg[idx]);
+ u8 ec_resp[] = MKRESP(sizeof(__le16));
+ __le16 *tmp;
+ int ret;
+
+ ret = gaokun_ec_read(ec, ec_req, sizeof(ec_resp), ec_resp);
+ if (ret)
+ return ret;
+
+ tmp = (__le16 *)extr_resp_shallow(ec_resp);
+ *temp = le16_to_cpu(*tmp) * 100; /* convert to HwMon's unit */
+
+ return 0;
+}
+
+static umode_t
+gaokun_ec_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ return type == hwmon_temp ? 0444 : 0;
+}
+
+static int
+gaokun_ec_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct gaokun_ec *ec = dev_get_drvdata(dev);
+
+ if (type == hwmon_temp)
+ return gaokun_ec_get_temp(ec, channel, val);
+
+ return -EINVAL;
+}
+
+static const struct hwmon_ops gaokun_ec_hwmon_ops = {
+ .is_visible = gaokun_ec_hwmon_is_visible,
+ .read = gaokun_ec_hwmon_read,
+};
+
+static u32 gaokun_ec_temp_config[] = {
+ [0 ... ARRAY_SIZE(temp_reg) - 1] = HWMON_T_INPUT,
+ 0
+};
+
+static const struct hwmon_channel_info gaokun_ec_temp = {
+ .type = hwmon_temp,
+ .config = gaokun_ec_temp_config,
+};
+
+static const struct hwmon_channel_info * const gaokun_ec_hwmon_info[] = {
+ &gaokun_ec_temp,
+ NULL
+};
+
+static const struct hwmon_chip_info gaokun_ec_hwmon_chip_info = {
+ .ops = &gaokun_ec_hwmon_ops,
+ .info = gaokun_ec_hwmon_info,
+};
+
+/* -------------------------------------------------------------------------- */
+/* Modern Standby */
+
+static int gaokun_ec_suspend(struct device *dev)
+{
+ struct gaokun_ec *ec = dev_get_drvdata(dev);
+ u8 ec_req[] = MKREQ(0x02, EC_STANDBY_REG, 1, EC_STANDBY_ENTER);
+ int ret;
+
+ if (ec->suspended)
+ return 0;
+
+ ret = gaokun_ec_write(ec, ec_req);
+ if (ret)
+ return ret;
+
+ ec->suspended = true;
+
+ return 0;
+}
+
+static int gaokun_ec_resume(struct device *dev)
+{
+ struct gaokun_ec *ec = dev_get_drvdata(dev);
+ u8 ec_req[] = MKREQ(0x02, EC_STANDBY_REG, 1, EC_STANDBY_EXIT);
+ int ret;
+ int i;
+
+ if (!ec->suspended)
+ return 0;
+
+ for (i = 0; i < 3; ++i) {
+ ret = gaokun_ec_write(ec, ec_req);
+ if (ret == 0)
+ break;
+
+ msleep(100); /* EC need time to resume */
+ };
+
+ ec->suspended = false;
+
+ return 0;
+}
+
+static void gaokun_aux_release(struct device *dev)
+{
+ struct auxiliary_device *adev = to_auxiliary_dev(dev);
+
+ kfree(adev);
+}
+
+static void gaokun_aux_remove(void *data)
+{
+ struct auxiliary_device *adev = data;
+
+ auxiliary_device_delete(adev);
+ auxiliary_device_uninit(adev);
+}
+
+static int gaokun_aux_init(struct device *parent, const char *name,
+ struct gaokun_ec *ec)
+{
+ struct auxiliary_device *adev;
+ int ret;
+
+ adev = kzalloc(sizeof(*adev), GFP_KERNEL);
+ if (!adev)
+ return -ENOMEM;
+
+ adev->name = name;
+ adev->id = 0;
+ adev->dev.parent = parent;
+ adev->dev.release = gaokun_aux_release;
+ adev->dev.platform_data = ec;
+ /* Allow aux devices to access parent's DT nodes directly */
+ device_set_of_node_from_dev(&adev->dev, parent);
+
+ ret = auxiliary_device_init(adev);
+ if (ret) {
+ kfree(adev);
+ return ret;
+ }
+
+ ret = auxiliary_device_add(adev);
+ if (ret) {
+ auxiliary_device_uninit(adev);
+ return ret;
+ }
+
+ return devm_add_action_or_reset(parent, gaokun_aux_remove, adev);
+}
+
+/* -------------------------------------------------------------------------- */
+/* EC */
+
+static irqreturn_t gaokun_ec_irq_handler(int irq, void *data)
+{
+ struct gaokun_ec *ec = data;
+ u8 ec_req[] = MKREQ(EC_EVENT, EC_QUERY, 0);
+ u8 status, id;
+ int ret;
+
+ ret = gaokun_ec_read_byte(ec, ec_req, &id);
+ if (ret)
+ return IRQ_HANDLED;
+
+ switch (id) {
+ case 0x0: /* No event */
+ break;
+
+ case EC_EVENT_LID:
+ gaokun_ec_psy_read_byte(ec, EC_LID_STATE, &status);
+ status &= EC_LID_OPEN;
+ input_report_switch(ec->idev, SW_LID, !status);
+ input_sync(ec->idev);
+ break;
+
+ default:
+ blocking_notifier_call_chain(&ec->notifier_list, id, ec);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int gaokun_ec_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct gaokun_ec *ec;
+ int ret;
+
+ ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL);
+ if (!ec)
+ return -ENOMEM;
+
+ ret = devm_mutex_init(dev, &ec->lock);
+ if (ret)
+ return ret;
+
+ ec->client = client;
+ i2c_set_clientdata(client, ec);
+ BLOCKING_INIT_NOTIFIER_HEAD(&ec->notifier_list);
+
+ /* Lid switch */
+ ec->idev = devm_input_allocate_device(dev);
+ if (!ec->idev)
+ return -ENOMEM;
+
+ ec->idev->name = "LID";
+ ec->idev->phys = "gaokun-ec/input0";
+ input_set_capability(ec->idev, EV_SW, SW_LID);
+
+ ret = input_register_device(ec->idev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register input device\n");
+
+ ret = gaokun_aux_init(dev, GAOKUN_DEV_PSY, ec);
+ if (ret)
+ return ret;
+
+ ret = gaokun_aux_init(dev, GAOKUN_DEV_UCSI, ec);
+ if (ret)
+ return ret;
+
+ ret = devm_request_threaded_irq(dev, client->irq, NULL,
+ gaokun_ec_irq_handler, IRQF_ONESHOT,
+ dev_name(dev), ec);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to request IRQ\n");
+
+ ec->hwmon_dev = devm_hwmon_device_register_with_info(dev, "gaokun_ec_hwmon",
+ ec, &gaokun_ec_hwmon_chip_info, NULL);
+ if (IS_ERR(ec->hwmon_dev))
+ return dev_err_probe(dev, PTR_ERR(ec->hwmon_dev),
+ "Failed to register hwmon device\n");
+
+ return 0;
+}
+
+static const struct i2c_device_id gaokun_ec_id[] = {
+ { "gaokun-ec", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, gaokun_ec_id);
+
+static const struct of_device_id gaokun_ec_of_match[] = {
+ { .compatible = "huawei,gaokun3-ec", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, gaokun_ec_of_match);
+
+static const struct dev_pm_ops gaokun_ec_pm_ops = {
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(gaokun_ec_suspend, gaokun_ec_resume)
+};
+
+static struct i2c_driver gaokun_ec_driver = {
+ .driver = {
+ .name = "gaokun-ec",
+ .of_match_table = gaokun_ec_of_match,
+ .pm = &gaokun_ec_pm_ops,
+ .dev_groups = gaokun_ec_groups,
+ },
+ .probe = gaokun_ec_probe,
+ .id_table = gaokun_ec_id,
+};
+module_i2c_driver(gaokun_ec_driver);
+
+MODULE_DESCRIPTION("HUAWEI Matebook E Go EC driver");
+MODULE_AUTHOR("Pengyu Luo <mitltlatltl@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig
index b7dbaf77b6db..1b2f2bd09662 100644
--- a/drivers/platform/chrome/Kconfig
+++ b/drivers/platform/chrome/Kconfig
@@ -237,12 +237,19 @@ config CROS_EC_SYSFS
To compile this driver as a module, choose M here: the
module will be called cros_ec_sysfs.
+config CROS_EC_TYPEC_ALTMODES
+ bool
+ help
+ Selectable symbol to enable altmodes.
+
config CROS_EC_TYPEC
tristate "ChromeOS EC Type-C Connector Control"
depends on MFD_CROS_EC_DEV && TYPEC
depends on CROS_USBPD_NOTIFY
depends on USB_ROLE_SWITCH
default MFD_CROS_EC_DEV
+ select CROS_EC_TYPEC_ALTMODES if TYPEC_DP_ALTMODE
+ select CROS_EC_TYPEC_ALTMODES if TYPEC_TBT_ALTMODE
help
If you say Y here, you get support for accessing Type C connector
information from the Chrome OS EC.
diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
index fb8335458a22..1a5a484563cc 100644
--- a/drivers/platform/chrome/Makefile
+++ b/drivers/platform/chrome/Makefile
@@ -19,7 +19,11 @@ obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o
obj-$(CONFIG_CROS_EC_UART) += cros_ec_uart.o
cros_ec_lpcs-objs := cros_ec_lpc.o cros_ec_lpc_mec.o
cros-ec-typec-objs := cros_ec_typec.o cros_typec_vdm.o
+ifneq ($(CONFIG_CROS_EC_TYPEC_ALTMODES),)
+ cros-ec-typec-objs += cros_typec_altmode.o
+endif
obj-$(CONFIG_CROS_EC_TYPEC) += cros-ec-typec.o
+
obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpcs.o
obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o cros_ec_trace.o
obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT) += cros_kbd_led_backlight.o
diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c
index e821b3d39590..110771a8645e 100644
--- a/drivers/platform/chrome/cros_ec.c
+++ b/drivers/platform/chrome/cros_ec.c
@@ -204,6 +204,11 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
mutex_init(&ec_dev->lock);
lockdep_set_class(&ec_dev->lock, &ec_dev->lockdep_key);
+ /* Send RWSIG continue to jump to RW for devices using RWSIG. */
+ err = cros_ec_rwsig_continue(ec_dev);
+ if (err)
+ dev_info(dev, "Failed to continue RWSIG: %d\n", err);
+
err = cros_ec_query_all(ec_dev);
if (err) {
dev_err(dev, "Cannot identify the EC: error %d\n", err);
diff --git a/drivers/platform/chrome/cros_ec_i2c.c b/drivers/platform/chrome/cros_ec_i2c.c
index 62662ba5bf6e..38af97cdaab2 100644
--- a/drivers/platform/chrome/cros_ec_i2c.c
+++ b/drivers/platform/chrome/cros_ec_i2c.c
@@ -305,7 +305,8 @@ static int cros_ec_i2c_probe(struct i2c_client *client)
ec_dev->phys_name = client->adapter->name;
ec_dev->din_size = sizeof(struct ec_host_response_i2c) +
sizeof(struct ec_response_get_protocol_info);
- ec_dev->dout_size = sizeof(struct ec_host_request_i2c);
+ ec_dev->dout_size = sizeof(struct ec_host_request_i2c) +
+ sizeof(struct ec_params_rwsig_action);
err = cros_ec_register(ec_dev);
if (err) {
diff --git a/drivers/platform/chrome/cros_ec_ishtp.c b/drivers/platform/chrome/cros_ec_ishtp.c
index 5ac37bd024c8..7e7190b30cbb 100644
--- a/drivers/platform/chrome/cros_ec_ishtp.c
+++ b/drivers/platform/chrome/cros_ec_ishtp.c
@@ -557,7 +557,7 @@ static int cros_ec_dev_init(struct ishtp_cl_data *client_data)
ec_dev->phys_name = dev_name(dev);
ec_dev->din_size = sizeof(struct cros_ish_in_msg) +
sizeof(struct ec_response_get_protocol_info);
- ec_dev->dout_size = sizeof(struct cros_ish_out_msg);
+ ec_dev->dout_size = sizeof(struct cros_ish_out_msg) + sizeof(struct ec_params_rwsig_action);
return cros_ec_register(ec_dev);
}
diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c
index 8470b7f2b135..7d9a78289c96 100644
--- a/drivers/platform/chrome/cros_ec_lpc.c
+++ b/drivers/platform/chrome/cros_ec_lpc.c
@@ -30,6 +30,7 @@
#define DRV_NAME "cros_ec_lpcs"
#define ACPI_DRV_NAME "GOOG0004"
+#define FRMW_ACPI_DRV_NAME "FRMWC004"
/* True if ACPI device is present */
static bool cros_ec_lpc_acpi_device_found;
@@ -70,13 +71,8 @@ struct lpc_driver_data {
/**
* struct cros_ec_lpc - LPC device-specific data
* @mmio_memory_base: The first I/O port addressing EC mapped memory.
- */
-struct cros_ec_lpc {
- u16 mmio_memory_base;
-};
-
-/**
- * struct lpc_driver_ops - LPC driver operations
+ * @base: For EC supporting memory mapping, base address of the mapped region.
+ * @mem32: Information about the memory mapped register region, if present.
* @read: Copy length bytes from EC address offset into buffer dest.
* Returns a negative error code on error, or the 8-bit checksum
* of all bytes read.
@@ -84,18 +80,21 @@ struct cros_ec_lpc {
* Returns a negative error code on error, or the 8-bit checksum
* of all bytes written.
*/
-struct lpc_driver_ops {
- int (*read)(unsigned int offset, unsigned int length, u8 *dest);
- int (*write)(unsigned int offset, unsigned int length, const u8 *msg);
+struct cros_ec_lpc {
+ u16 mmio_memory_base;
+ void __iomem *base;
+ struct acpi_resource_fixed_memory32 mem32;
+ int (*read)(struct cros_ec_lpc *ec_lpc, unsigned int offset,
+ unsigned int length, u8 *dest);
+ int (*write)(struct cros_ec_lpc *ec_lpc, unsigned int offset,
+ unsigned int length, const u8 *msg);
};
-static struct lpc_driver_ops cros_ec_lpc_ops = { };
-
/*
* A generic instance of the read function of struct lpc_driver_ops, used for
* the LPC EC.
*/
-static int cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length,
+static int cros_ec_lpc_read_bytes(struct cros_ec_lpc *_, unsigned int offset, unsigned int length,
u8 *dest)
{
u8 sum = 0;
@@ -114,7 +113,7 @@ static int cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length,
* A generic instance of the write function of struct lpc_driver_ops, used for
* the LPC EC.
*/
-static int cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length,
+static int cros_ec_lpc_write_bytes(struct cros_ec_lpc *_, unsigned int offset, unsigned int length,
const u8 *msg)
{
u8 sum = 0;
@@ -133,8 +132,8 @@ static int cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length,
* An instance of the read function of struct lpc_driver_ops, used for the
* MEC variant of LPC EC.
*/
-static int cros_ec_lpc_mec_read_bytes(unsigned int offset, unsigned int length,
- u8 *dest)
+static int cros_ec_lpc_mec_read_bytes(struct cros_ec_lpc *ec_lpc, unsigned int offset,
+ unsigned int length, u8 *dest)
{
int in_range = cros_ec_lpc_mec_in_range(offset, length);
@@ -145,15 +144,15 @@ static int cros_ec_lpc_mec_read_bytes(unsigned int offset, unsigned int length,
cros_ec_lpc_io_bytes_mec(MEC_IO_READ,
offset - EC_HOST_CMD_REGION0,
length, dest) :
- cros_ec_lpc_read_bytes(offset, length, dest);
+ cros_ec_lpc_read_bytes(ec_lpc, offset, length, dest);
}
/*
* An instance of the write function of struct lpc_driver_ops, used for the
* MEC variant of LPC EC.
*/
-static int cros_ec_lpc_mec_write_bytes(unsigned int offset, unsigned int length,
- const u8 *msg)
+static int cros_ec_lpc_mec_write_bytes(struct cros_ec_lpc *ec_lpc, unsigned int offset,
+ unsigned int length, const u8 *msg)
{
int in_range = cros_ec_lpc_mec_in_range(offset, length);
@@ -164,10 +163,50 @@ static int cros_ec_lpc_mec_write_bytes(unsigned int offset, unsigned int length,
cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE,
offset - EC_HOST_CMD_REGION0,
length, (u8 *)msg) :
- cros_ec_lpc_write_bytes(offset, length, msg);
+ cros_ec_lpc_write_bytes(ec_lpc, offset, length, msg);
+}
+
+static int cros_ec_lpc_direct_read(struct cros_ec_lpc *ec_lpc, unsigned int offset,
+ unsigned int length, u8 *dest)
+{
+ int sum = 0;
+ int i;
+
+ if (offset < EC_HOST_CMD_REGION0 || offset > EC_LPC_ADDR_MEMMAP +
+ EC_MEMMAP_SIZE) {
+ return cros_ec_lpc_read_bytes(ec_lpc, offset, length, dest);
+ }
+
+ for (i = 0; i < length; ++i) {
+ dest[i] = readb(ec_lpc->base + offset - EC_HOST_CMD_REGION0 + i);
+ sum += dest[i];
+ }
+
+ /* Return checksum of all bytes read */
+ return sum;
+}
+
+static int cros_ec_lpc_direct_write(struct cros_ec_lpc *ec_lpc, unsigned int offset,
+ unsigned int length, const u8 *msg)
+{
+ int sum = 0;
+ int i;
+
+ if (offset < EC_HOST_CMD_REGION0 || offset > EC_LPC_ADDR_MEMMAP +
+ EC_MEMMAP_SIZE) {
+ return cros_ec_lpc_write_bytes(ec_lpc, offset, length, msg);
+ }
+
+ for (i = 0; i < length; ++i) {
+ writeb(msg[i], ec_lpc->base + offset - EC_HOST_CMD_REGION0 + i);
+ sum += msg[i];
+ }
+
+ /* Return checksum of all bytes written */
+ return sum;
}
-static int ec_response_timed_out(void)
+static int ec_response_timed_out(struct cros_ec_lpc *ec_lpc)
{
unsigned long one_second = jiffies + HZ;
u8 data;
@@ -175,7 +214,7 @@ static int ec_response_timed_out(void)
usleep_range(200, 300);
do {
- ret = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_CMD, 1, &data);
+ ret = ec_lpc->read(ec_lpc, EC_LPC_ADDR_HOST_CMD, 1, &data);
if (ret < 0)
return ret;
if (!(data & EC_LPC_STATUS_BUSY_MASK))
@@ -189,6 +228,7 @@ static int ec_response_timed_out(void)
static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec,
struct cros_ec_command *msg)
{
+ struct cros_ec_lpc *ec_lpc = ec->priv;
struct ec_host_response response;
u8 sum;
int ret = 0;
@@ -199,17 +239,17 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec,
goto done;
/* Write buffer */
- ret = cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_PACKET, ret, ec->dout);
+ ret = ec_lpc->write(ec_lpc, EC_LPC_ADDR_HOST_PACKET, ret, ec->dout);
if (ret < 0)
goto done;
/* Here we go */
sum = EC_COMMAND_PROTOCOL_3;
- ret = cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_CMD, 1, &sum);
+ ret = ec_lpc->write(ec_lpc, EC_LPC_ADDR_HOST_CMD, 1, &sum);
if (ret < 0)
goto done;
- ret = ec_response_timed_out();
+ ret = ec_response_timed_out(ec_lpc);
if (ret < 0)
goto done;
if (ret) {
@@ -219,7 +259,7 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec,
}
/* Check result */
- ret = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_DATA, 1, &sum);
+ ret = ec_lpc->read(ec_lpc, EC_LPC_ADDR_HOST_DATA, 1, &sum);
if (ret < 0)
goto done;
msg->result = ret;
@@ -229,7 +269,7 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec,
/* Read back response */
dout = (u8 *)&response;
- ret = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_PACKET, sizeof(response),
+ ret = ec_lpc->read(ec_lpc, EC_LPC_ADDR_HOST_PACKET, sizeof(response),
dout);
if (ret < 0)
goto done;
@@ -246,7 +286,7 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec,
}
/* Read response and process checksum */
- ret = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_PACKET +
+ ret = ec_lpc->read(ec_lpc, EC_LPC_ADDR_HOST_PACKET +
sizeof(response), response.data_len,
msg->data);
if (ret < 0)
@@ -270,6 +310,7 @@ done:
static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
struct cros_ec_command *msg)
{
+ struct cros_ec_lpc *ec_lpc = ec->priv;
struct ec_lpc_host_args args;
u8 sum;
int ret = 0;
@@ -291,7 +332,7 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
sum = msg->command + args.flags + args.command_version + args.data_size;
/* Copy data and update checksum */
- ret = cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_PARAM, msg->outsize,
+ ret = ec_lpc->write(ec_lpc, EC_LPC_ADDR_HOST_PARAM, msg->outsize,
msg->data);
if (ret < 0)
goto done;
@@ -299,18 +340,18 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
/* Finalize checksum and write args */
args.checksum = sum;
- ret = cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_ARGS, sizeof(args),
+ ret = ec_lpc->write(ec_lpc, EC_LPC_ADDR_HOST_ARGS, sizeof(args),
(u8 *)&args);
if (ret < 0)
goto done;
/* Here we go */
sum = msg->command;
- ret = cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_CMD, 1, &sum);
+ ret = ec_lpc->write(ec_lpc, EC_LPC_ADDR_HOST_CMD, 1, &sum);
if (ret < 0)
goto done;
- ret = ec_response_timed_out();
+ ret = ec_response_timed_out(ec_lpc);
if (ret < 0)
goto done;
if (ret) {
@@ -320,7 +361,7 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
}
/* Check result */
- ret = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_DATA, 1, &sum);
+ ret = ec_lpc->read(ec_lpc, EC_LPC_ADDR_HOST_DATA, 1, &sum);
if (ret < 0)
goto done;
msg->result = ret;
@@ -329,7 +370,7 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
goto done;
/* Read back args */
- ret = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_ARGS, sizeof(args), (u8 *)&args);
+ ret = ec_lpc->read(ec_lpc, EC_LPC_ADDR_HOST_ARGS, sizeof(args), (u8 *)&args);
if (ret < 0)
goto done;
@@ -345,7 +386,7 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
sum = msg->command + args.flags + args.command_version + args.data_size;
/* Read response and update checksum */
- ret = cros_ec_lpc_ops.read(EC_LPC_ADDR_HOST_PARAM, args.data_size,
+ ret = ec_lpc->read(ec_lpc, EC_LPC_ADDR_HOST_PARAM, args.data_size,
msg->data);
if (ret < 0)
goto done;
@@ -381,7 +422,7 @@ static int cros_ec_lpc_readmem(struct cros_ec_device *ec, unsigned int offset,
/* fixed length */
if (bytes) {
- ret = cros_ec_lpc_ops.read(ec_lpc->mmio_memory_base + offset, bytes, s);
+ ret = ec_lpc->read(ec_lpc, ec_lpc->mmio_memory_base + offset, bytes, s);
if (ret < 0)
return ret;
return bytes;
@@ -389,7 +430,7 @@ static int cros_ec_lpc_readmem(struct cros_ec_device *ec, unsigned int offset,
/* string */
for (; i < EC_MEMMAP_SIZE; i++, s++) {
- ret = cros_ec_lpc_ops.read(ec_lpc->mmio_memory_base + i, 1, s);
+ ret = ec_lpc->read(ec_lpc, ec_lpc->mmio_memory_base + i, 1, s);
if (ret < 0)
return ret;
cnt++;
@@ -414,12 +455,12 @@ static void cros_ec_lpc_acpi_notify(acpi_handle device, u32 value, void *data)
blocking_notifier_call_chain(&ec_dev->panic_notifier, 0, ec_dev);
kobject_uevent_env(&ec_dev->dev->kobj, KOBJ_CHANGE, (char **)env);
/* Begin orderly shutdown. EC will force reset after a short period. */
- hw_protection_shutdown("CrOS EC Panic", -1);
+ __hw_protection_trigger("CrOS EC Panic", -1, HWPROT_ACT_SHUTDOWN);
/* Do not query for other events after a panic is reported */
return;
}
- if (ec_dev->mkbp_event_supported)
+ if (value == ACPI_NOTIFY_CROS_EC_MKBP && ec_dev->mkbp_event_supported)
do {
ret = cros_ec_get_next_event(ec_dev, NULL,
&ec_has_more_events);
@@ -453,6 +494,20 @@ static struct acpi_device *cros_ec_lpc_get_device(const char *id)
return adev;
}
+static acpi_status cros_ec_lpc_resources(struct acpi_resource *res, void *data)
+{
+ struct cros_ec_lpc *ec_lpc = data;
+
+ switch (res->type) {
+ case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
+ ec_lpc->mem32 = res->data.fixed_memory32;
+ break;
+ default:
+ break;
+ }
+ return AE_OK;
+}
+
static int cros_ec_lpc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -460,7 +515,7 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
acpi_status status;
struct cros_ec_device *ec_dev;
struct cros_ec_lpc *ec_lpc;
- struct lpc_driver_data *driver_data;
+ const struct lpc_driver_data *driver_data;
u8 buf[2] = {};
int irq, ret;
u32 quirks;
@@ -472,6 +527,9 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
ec_lpc->mmio_memory_base = EC_LPC_ADDR_MEMMAP;
driver_data = platform_get_drvdata(pdev);
+ if (!driver_data)
+ driver_data = acpi_device_get_match_data(dev);
+
if (driver_data) {
quirks = driver_data->quirks;
@@ -492,8 +550,7 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
}
if (quirks & CROS_EC_LPC_QUIRK_AML_MUTEX) {
- const char *name
- = driver_data->quirk_aml_mutex_name;
+ const char *name = driver_data->quirk_aml_mutex_name;
ret = cros_ec_lpc_mec_acpi_mutex(ACPI_COMPANION(dev), name);
if (ret) {
dev_err(dev, "failed to get AML mutex '%s'", name);
@@ -502,30 +559,49 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
dev_info(dev, "got AML mutex '%s'", name);
}
}
-
- /*
- * The Framework Laptop (and possibly other non-ChromeOS devices)
- * only exposes the eight I/O ports that are required for the Microchip EC.
- * Requesting a larger reservation will fail.
- */
- if (!devm_request_region(dev, EC_HOST_CMD_REGION0,
- EC_HOST_CMD_MEC_REGION_SIZE, dev_name(dev))) {
- dev_err(dev, "couldn't reserve MEC region\n");
- return -EBUSY;
+ adev = ACPI_COMPANION(dev);
+ if (adev) {
+ /*
+ * Retrieve the resource information in the CRS register, if available.
+ */
+ status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS,
+ cros_ec_lpc_resources, ec_lpc);
+ if (ACPI_SUCCESS(status) && ec_lpc->mem32.address_length) {
+ ec_lpc->base = devm_ioremap(dev,
+ ec_lpc->mem32.address,
+ ec_lpc->mem32.address_length);
+ if (!ec_lpc->base)
+ return -EINVAL;
+
+ ec_lpc->read = cros_ec_lpc_direct_read;
+ ec_lpc->write = cros_ec_lpc_direct_write;
+ }
}
+ if (!ec_lpc->read) {
+ /*
+ * The Framework Laptop (and possibly other non-ChromeOS devices)
+ * only exposes the eight I/O ports that are required for the Microchip EC.
+ * Requesting a larger reservation will fail.
+ */
+ if (!devm_request_region(dev, EC_HOST_CMD_REGION0,
+ EC_HOST_CMD_MEC_REGION_SIZE, dev_name(dev))) {
+ dev_err(dev, "couldn't reserve MEC region\n");
+ return -EBUSY;
+ }
- cros_ec_lpc_mec_init(EC_HOST_CMD_REGION0,
- EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE);
+ cros_ec_lpc_mec_init(EC_HOST_CMD_REGION0,
+ EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE);
- /*
- * Read the mapped ID twice, the first one is assuming the
- * EC is a Microchip Embedded Controller (MEC) variant, if the
- * protocol fails, fallback to the non MEC variant and try to
- * read again the ID.
- */
- cros_ec_lpc_ops.read = cros_ec_lpc_mec_read_bytes;
- cros_ec_lpc_ops.write = cros_ec_lpc_mec_write_bytes;
- ret = cros_ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID, 2, buf);
+ /*
+ * Read the mapped ID twice, the first one is assuming the
+ * EC is a Microchip Embedded Controller (MEC) variant, if the
+ * protocol fails, fallback to the non MEC variant and try to
+ * read again the ID.
+ */
+ ec_lpc->read = cros_ec_lpc_mec_read_bytes;
+ ec_lpc->write = cros_ec_lpc_mec_write_bytes;
+ }
+ ret = ec_lpc->read(ec_lpc, EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID, 2, buf);
if (ret < 0)
return ret;
if (buf[0] != 'E' || buf[1] != 'C') {
@@ -536,9 +612,9 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
}
/* Re-assign read/write operations for the non MEC variant */
- cros_ec_lpc_ops.read = cros_ec_lpc_read_bytes;
- cros_ec_lpc_ops.write = cros_ec_lpc_write_bytes;
- ret = cros_ec_lpc_ops.read(ec_lpc->mmio_memory_base + EC_MEMMAP_ID, 2,
+ ec_lpc->read = cros_ec_lpc_read_bytes;
+ ec_lpc->write = cros_ec_lpc_write_bytes;
+ ret = ec_lpc->read(ec_lpc, ec_lpc->mmio_memory_base + EC_MEMMAP_ID, 2,
buf);
if (ret < 0)
return ret;
@@ -573,7 +649,7 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
ec_dev->cmd_readmem = cros_ec_lpc_readmem;
ec_dev->din_size = sizeof(struct ec_host_response) +
sizeof(struct ec_response_get_protocol_info);
- ec_dev->dout_size = sizeof(struct ec_host_request);
+ ec_dev->dout_size = sizeof(struct ec_host_request) + sizeof(struct ec_params_rwsig_action);
ec_dev->priv = ec_lpc;
/*
@@ -598,7 +674,6 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
* Connect a notify handler to process MKBP messages if we have a
* companion ACPI device.
*/
- adev = ACPI_COMPANION(dev);
if (adev) {
status = acpi_install_notify_handler(adev->handle,
ACPI_ALL_NOTIFY,
@@ -625,12 +700,6 @@ static void cros_ec_lpc_remove(struct platform_device *pdev)
cros_ec_unregister(ec_dev);
}
-static const struct acpi_device_id cros_ec_lpc_acpi_device_ids[] = {
- { ACPI_DRV_NAME, 0 },
- { }
-};
-MODULE_DEVICE_TABLE(acpi, cros_ec_lpc_acpi_device_ids);
-
static const struct lpc_driver_data framework_laptop_npcx_lpc_driver_data __initconst = {
.quirks = CROS_EC_LPC_QUIRK_REMAP_MEMORY,
.quirk_mmio_memory_base = 0xE00,
@@ -642,6 +711,13 @@ static const struct lpc_driver_data framework_laptop_mec_lpc_driver_data __initc
.quirk_aml_mutex_name = "ECMT",
};
+static const struct acpi_device_id cros_ec_lpc_acpi_device_ids[] = {
+ { ACPI_DRV_NAME, 0 },
+ { FRMW_ACPI_DRV_NAME, (kernel_ulong_t)&framework_laptop_npcx_lpc_driver_data },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, cros_ec_lpc_acpi_device_ids);
+
static const struct dmi_system_id cros_ec_lpc_dmi_table[] __initconst = {
{
/*
@@ -795,7 +871,8 @@ static int __init cros_ec_lpc_init(void)
int ret;
const struct dmi_system_id *dmi_match;
- cros_ec_lpc_acpi_device_found = !!cros_ec_lpc_get_device(ACPI_DRV_NAME);
+ cros_ec_lpc_acpi_device_found = !!cros_ec_lpc_get_device(ACPI_DRV_NAME) ||
+ !!cros_ec_lpc_get_device(FRMW_ACPI_DRV_NAME);
dmi_match = dmi_first_match(cros_ec_lpc_dmi_table);
diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c
index 5c9a53dffcf9..877b107fee4b 100644
--- a/drivers/platform/chrome/cros_ec_proto.c
+++ b/drivers/platform/chrome/cros_ec_proto.c
@@ -15,6 +15,8 @@
#include "cros_ec_trace.h"
#define EC_COMMAND_RETRIES 50
+#define RWSIG_CONTINUE_RETRIES 8
+#define RWSIG_CONTINUE_MAX_ERRORS_IN_ROW 3
static const int cros_ec_error_map[] = {
[EC_RES_INVALID_COMMAND] = -EOPNOTSUPP,
@@ -288,6 +290,64 @@ exit:
return ret;
}
+int cros_ec_rwsig_continue(struct cros_ec_device *ec_dev)
+{
+ struct cros_ec_command *msg;
+ struct ec_params_rwsig_action *rwsig_action;
+ int ret = 0;
+ int error_count = 0;
+
+ ec_dev->proto_version = 3;
+
+ msg = kmalloc(sizeof(*msg) + sizeof(*rwsig_action), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->version = 0;
+ msg->command = EC_CMD_RWSIG_ACTION;
+ msg->insize = 0;
+ msg->outsize = sizeof(*rwsig_action);
+
+ rwsig_action = (struct ec_params_rwsig_action *)msg->data;
+ rwsig_action->action = RWSIG_ACTION_CONTINUE;
+
+ for (int i = 0; i < RWSIG_CONTINUE_RETRIES; i++) {
+ ret = cros_ec_send_command(ec_dev, msg);
+
+ if (ret < 0) {
+ if (++error_count >= RWSIG_CONTINUE_MAX_ERRORS_IN_ROW)
+ break;
+ } else if (msg->result == EC_RES_INVALID_COMMAND) {
+ /*
+ * If EC_RES_INVALID_COMMAND is retured, it means RWSIG
+ * is not supported or EC is already in RW, so there is
+ * nothing left to do.
+ */
+ break;
+ } else if (msg->result != EC_RES_SUCCESS) {
+ /* Unexpected command error. */
+ ret = cros_ec_map_error(msg->result);
+ break;
+ } else {
+ /*
+ * The EC_CMD_RWSIG_ACTION succeed. Send the command
+ * more times, to make sure EC is in RW. A following
+ * command can timeout, because EC may need some time to
+ * initialize after jump to RW.
+ */
+ error_count = 0;
+ }
+
+ if (ret != -ETIMEDOUT)
+ usleep_range(90000, 100000);
+ }
+
+ kfree(msg);
+
+ return ret;
+}
+EXPORT_SYMBOL(cros_ec_rwsig_continue);
+
static int cros_ec_get_proto_info(struct cros_ec_device *ec_dev, int devidx)
{
struct cros_ec_command *msg;
@@ -306,15 +366,6 @@ static int cros_ec_get_proto_info(struct cros_ec_device *ec_dev, int devidx)
msg->insize = sizeof(*info);
ret = cros_ec_send_command(ec_dev, msg);
- /*
- * Send command once again when timeout occurred.
- * Fingerprint MCU (FPMCU) is restarted during system boot which
- * introduces small window in which FPMCU won't respond for any
- * messages sent by kernel. There is no need to wait before next
- * attempt because we waited at least EC_MSG_DEADLINE_MS.
- */
- if (ret == -ETIMEDOUT)
- ret = cros_ec_send_command(ec_dev, msg);
if (ret < 0) {
dev_dbg(ec_dev->dev,
diff --git a/drivers/platform/chrome/cros_ec_rpmsg.c b/drivers/platform/chrome/cros_ec_rpmsg.c
index 39d3b50a7c09..bc2666491db1 100644
--- a/drivers/platform/chrome/cros_ec_rpmsg.c
+++ b/drivers/platform/chrome/cros_ec_rpmsg.c
@@ -231,7 +231,7 @@ static int cros_ec_rpmsg_probe(struct rpmsg_device *rpdev)
ec_dev->phys_name = dev_name(&rpdev->dev);
ec_dev->din_size = sizeof(struct ec_host_response) +
sizeof(struct ec_response_get_protocol_info);
- ec_dev->dout_size = sizeof(struct ec_host_request);
+ ec_dev->dout_size = sizeof(struct ec_host_request) + sizeof(struct ec_params_rwsig_action);
dev_set_drvdata(dev, ec_dev);
ec_rpmsg->rpdev = rpdev;
diff --git a/drivers/platform/chrome/cros_ec_spi.c b/drivers/platform/chrome/cros_ec_spi.c
index 86a3d32a7763..8ca0f854e7ac 100644
--- a/drivers/platform/chrome/cros_ec_spi.c
+++ b/drivers/platform/chrome/cros_ec_spi.c
@@ -715,7 +715,7 @@ static int cros_ec_spi_devm_high_pri_alloc(struct device *dev,
int err;
ec_spi->high_pri_worker =
- kthread_create_worker(0, "cros_ec_spi_high_pri");
+ kthread_run_worker(0, "cros_ec_spi_high_pri");
if (IS_ERR(ec_spi->high_pri_worker)) {
err = PTR_ERR(ec_spi->high_pri_worker);
@@ -766,7 +766,7 @@ static int cros_ec_spi_probe(struct spi_device *spi)
ec_dev->din_size = EC_MSG_PREAMBLE_COUNT +
sizeof(struct ec_host_response) +
sizeof(struct ec_response_get_protocol_info);
- ec_dev->dout_size = sizeof(struct ec_host_request);
+ ec_dev->dout_size = sizeof(struct ec_host_request) + sizeof(struct ec_params_rwsig_action);
ec_spi->last_transfer_ns = ktime_get_ns();
diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c
index bc1a5ba09528..f22e9523da3e 100644
--- a/drivers/platform/chrome/cros_ec_sysfs.c
+++ b/drivers/platform/chrome/cros_ec_sysfs.c
@@ -296,18 +296,81 @@ static ssize_t kb_wake_angle_store(struct device *dev,
return count;
}
+static ssize_t usbpdmuxinfo_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct cros_ec_dev *ec = to_cros_ec_dev(dev);
+ ssize_t count = 0;
+ struct ec_response_usb_pd_ports resp_pd_ports;
+ int ret;
+ int i;
+
+ ret = cros_ec_cmd(ec->ec_dev, 0, EC_CMD_USB_PD_PORTS, NULL, 0,
+ &resp_pd_ports, sizeof(resp_pd_ports));
+ if (ret < 0)
+ return -EIO;
+
+ for (i = 0; i < resp_pd_ports.num_ports; i++) {
+ struct ec_response_usb_pd_mux_info resp_mux;
+ struct ec_params_usb_pd_mux_info req = {
+ .port = i,
+ };
+
+ ret = cros_ec_cmd(ec->ec_dev, 0, EC_CMD_USB_PD_MUX_INFO,
+ &req, sizeof(req), &resp_mux, sizeof(resp_mux));
+
+ if (ret >= 0) {
+ count += sysfs_emit_at(buf, count, "Port %d:", i);
+ count += sysfs_emit_at(buf, count, " USB=%d",
+ !!(resp_mux.flags & USB_PD_MUX_USB_ENABLED));
+ count += sysfs_emit_at(buf, count, " DP=%d",
+ !!(resp_mux.flags & USB_PD_MUX_DP_ENABLED));
+ count += sysfs_emit_at(buf, count, " POLARITY=%s",
+ (resp_mux.flags & USB_PD_MUX_POLARITY_INVERTED) ?
+ "INVERTED" : "NORMAL");
+ count += sysfs_emit_at(buf, count, " HPD_IRQ=%d",
+ !!(resp_mux.flags & USB_PD_MUX_HPD_IRQ));
+ count += sysfs_emit_at(buf, count, " HPD_LVL=%d",
+ !!(resp_mux.flags & USB_PD_MUX_HPD_LVL));
+ count += sysfs_emit_at(buf, count, " SAFE=%d",
+ !!(resp_mux.flags & USB_PD_MUX_SAFE_MODE));
+ count += sysfs_emit_at(buf, count, " TBT=%d",
+ !!(resp_mux.flags & USB_PD_MUX_TBT_COMPAT_ENABLED));
+ count += sysfs_emit_at(buf, count, " USB4=%d\n",
+ !!(resp_mux.flags & USB_PD_MUX_USB4_ENABLED));
+ }
+ }
+
+ return count ? : -EIO;
+}
+
+static ssize_t ap_mode_entry_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cros_ec_dev *ec = to_cros_ec_dev(dev);
+ const bool ap_driven_altmode = cros_ec_check_features(
+ ec, EC_FEATURE_TYPEC_REQUIRE_AP_MODE_ENTRY);
+
+ return sysfs_emit(buf, "%s\n", ap_driven_altmode ? "yes" : "no");
+}
+
/* Module initialization */
static DEVICE_ATTR_RW(reboot);
static DEVICE_ATTR_RO(version);
static DEVICE_ATTR_RO(flashinfo);
static DEVICE_ATTR_RW(kb_wake_angle);
+static DEVICE_ATTR_RO(usbpdmuxinfo);
+static DEVICE_ATTR_RO(ap_mode_entry);
static struct attribute *__ec_attrs[] = {
&dev_attr_kb_wake_angle.attr,
&dev_attr_reboot.attr,
&dev_attr_version.attr,
&dev_attr_flashinfo.attr,
+ &dev_attr_usbpdmuxinfo.attr,
+ &dev_attr_ap_mode_entry.attr,
NULL,
};
@@ -320,6 +383,14 @@ static umode_t cros_ec_ctrl_visible(struct kobject *kobj,
if (a == &dev_attr_kb_wake_angle.attr && !ec->has_kb_wake_angle)
return 0;
+ if (a == &dev_attr_usbpdmuxinfo.attr ||
+ a == &dev_attr_ap_mode_entry.attr) {
+ struct cros_ec_platform *ec_platform = dev_get_platdata(ec->dev);
+
+ if (strcmp(ec_platform->ec_name, CROS_EC_DEV_NAME))
+ return 0;
+ }
+
return a->mode;
}
diff --git a/drivers/platform/chrome/cros_ec_trace.c b/drivers/platform/chrome/cros_ec_trace.c
index 425e9441b7ca..9827b3117597 100644
--- a/drivers/platform/chrome/cros_ec_trace.c
+++ b/drivers/platform/chrome/cros_ec_trace.c
@@ -122,8 +122,10 @@
TRACE_SYMBOL(EC_CMD_ENTERING_MODE), \
TRACE_SYMBOL(EC_CMD_I2C_PASSTHRU_PROTECT), \
TRACE_SYMBOL(EC_CMD_CEC_WRITE_MSG), \
+ TRACE_SYMBOL(EC_CMD_CEC_READ_MSG), \
TRACE_SYMBOL(EC_CMD_CEC_SET), \
TRACE_SYMBOL(EC_CMD_CEC_GET), \
+ TRACE_SYMBOL(EC_CMD_CEC_PORT_COUNT), \
TRACE_SYMBOL(EC_CMD_EC_CODEC), \
TRACE_SYMBOL(EC_CMD_EC_CODEC_DMIC), \
TRACE_SYMBOL(EC_CMD_EC_CODEC_I2S_RX), \
@@ -161,11 +163,18 @@
TRACE_SYMBOL(EC_CMD_ADC_READ), \
TRACE_SYMBOL(EC_CMD_ROLLBACK_INFO), \
TRACE_SYMBOL(EC_CMD_AP_RESET), \
+ TRACE_SYMBOL(EC_CMD_PCHG_COUNT), \
+ TRACE_SYMBOL(EC_CMD_PCHG), \
+ TRACE_SYMBOL(EC_CMD_PCHG_UPDATE), \
TRACE_SYMBOL(EC_CMD_REGULATOR_GET_INFO), \
TRACE_SYMBOL(EC_CMD_REGULATOR_ENABLE), \
TRACE_SYMBOL(EC_CMD_REGULATOR_IS_ENABLED), \
TRACE_SYMBOL(EC_CMD_REGULATOR_SET_VOLTAGE), \
TRACE_SYMBOL(EC_CMD_REGULATOR_GET_VOLTAGE), \
+ TRACE_SYMBOL(EC_CMD_TYPEC_DISCOVERY), \
+ TRACE_SYMBOL(EC_CMD_TYPEC_CONTROL), \
+ TRACE_SYMBOL(EC_CMD_TYPEC_STATUS), \
+ TRACE_SYMBOL(EC_CMD_TYPEC_VDM_RESPONSE), \
TRACE_SYMBOL(EC_CMD_CR51_BASE), \
TRACE_SYMBOL(EC_CMD_CR51_LAST), \
TRACE_SYMBOL(EC_CMD_FP_PASSTHRU), \
@@ -184,6 +193,7 @@
TRACE_SYMBOL(EC_CMD_BATTERY_GET_STATIC), \
TRACE_SYMBOL(EC_CMD_BATTERY_GET_DYNAMIC), \
TRACE_SYMBOL(EC_CMD_CHARGER_CONTROL), \
+ TRACE_SYMBOL(EC_CMD_USB_PD_MUX_ACK), \
TRACE_SYMBOL(EC_CMD_BOARD_SPECIFIC_BASE), \
TRACE_SYMBOL(EC_CMD_BOARD_SPECIFIC_LAST)
diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c
index ae2f86296954..7678e3d05fd3 100644
--- a/drivers/platform/chrome/cros_ec_typec.c
+++ b/drivers/platform/chrome/cros_ec_typec.c
@@ -18,11 +18,14 @@
#include "cros_ec_typec.h"
#include "cros_typec_vdm.h"
+#include "cros_typec_altmode.h"
#define DRV_NAME "cros-ec-typec"
-#define DP_PORT_VDO (DP_CONF_SET_PIN_ASSIGN(BIT(DP_PIN_ASSIGN_C) | BIT(DP_PIN_ASSIGN_D)) | \
- DP_CAP_DFP_D | DP_CAP_RECEPTACLE)
+#define DP_PORT_VDO (DP_CAP_DFP_D | DP_CAP_RECEPTACLE | \
+ DP_CONF_SET_PIN_ASSIGN(BIT(DP_PIN_ASSIGN_C) | \
+ BIT(DP_PIN_ASSIGN_D) | \
+ BIT(DP_PIN_ASSIGN_E)))
static void cros_typec_role_switch_quirk(struct fwnode_handle *fwnode)
{
@@ -41,6 +44,24 @@ static void cros_typec_role_switch_quirk(struct fwnode_handle *fwnode)
#endif
}
+static int cros_typec_enter_usb_mode(struct typec_port *tc_port, enum usb_mode mode)
+{
+ struct cros_typec_port *port = typec_get_drvdata(tc_port);
+ struct ec_params_typec_control req = {
+ .port = port->port_num,
+ .command = (mode == USB_MODE_USB4) ?
+ TYPEC_CONTROL_COMMAND_ENTER_MODE : TYPEC_CONTROL_COMMAND_EXIT_MODES,
+ .mode_to_enter = CROS_EC_ALTMODE_USB4
+ };
+
+ return cros_ec_cmd(port->typec_data->ec, 0, EC_CMD_TYPEC_CONTROL,
+ &req, sizeof(req), NULL, 0);
+}
+
+static const struct typec_operations cros_typec_usb_mode_ops = {
+ .enter_usb_mode = cros_typec_enter_usb_mode
+};
+
static int cros_typec_parse_port_props(struct typec_capability *cap,
struct fwnode_handle *fwnode,
struct device *dev)
@@ -83,6 +104,13 @@ static int cros_typec_parse_port_props(struct typec_capability *cap,
cap->prefer_role = ret;
}
+ if (fwnode_property_present(fwnode, "usb2-port"))
+ cap->usb_capability |= USB_CAPABILITY_USB2;
+ if (fwnode_property_present(fwnode, "usb3-port"))
+ cap->usb_capability |= USB_CAPABILITY_USB3;
+ if (fwnode_property_present(fwnode, "usb4-port"))
+ cap->usb_capability |= USB_CAPABILITY_USB4;
+
cros_typec_role_switch_quirk(fwnode);
cap->fwnode = fwnode;
@@ -290,30 +318,32 @@ static int cros_typec_register_port_altmodes(struct cros_typec_data *typec,
struct typec_altmode *amode;
/* All PD capable CrOS devices are assumed to support DP altmode. */
+ memset(&desc, 0, sizeof(desc));
desc.svid = USB_TYPEC_DP_SID;
desc.mode = USB_TYPEC_DP_MODE;
desc.vdo = DP_PORT_VDO;
- amode = typec_port_register_altmode(port->port, &desc);
+ amode = cros_typec_register_displayport(port, &desc,
+ typec->ap_driven_altmode);
if (IS_ERR(amode))
return PTR_ERR(amode);
port->port_altmode[CROS_EC_ALTMODE_DP] = amode;
- typec_altmode_set_drvdata(amode, port);
- amode->ops = &port_amode_ops;
/*
* Register TBT compatibility alt mode. The EC will not enter the mode
- * if it doesn't support it, so it's safe to register it unconditionally
- * here for now.
+ * if it doesn't support it and it will not enter automatically by
+ * design so we can use the |ap_driven_altmode| feature to check if we
+ * should register it.
*/
- memset(&desc, 0, sizeof(desc));
- desc.svid = USB_TYPEC_TBT_SID;
- desc.mode = TYPEC_ANY_MODE;
- amode = typec_port_register_altmode(port->port, &desc);
- if (IS_ERR(amode))
- return PTR_ERR(amode);
- port->port_altmode[CROS_EC_ALTMODE_TBT] = amode;
- typec_altmode_set_drvdata(amode, port);
- amode->ops = &port_amode_ops;
+ if (typec->ap_driven_altmode) {
+ memset(&desc, 0, sizeof(desc));
+ desc.svid = USB_TYPEC_TBT_SID;
+ desc.mode = TBT_MODE;
+ desc.inactive = true;
+ amode = cros_typec_register_thunderbolt(port, &desc);
+ if (IS_ERR(amode))
+ return PTR_ERR(amode);
+ port->port_altmode[CROS_EC_ALTMODE_TBT] = amode;
+ }
port->state.alt = NULL;
port->state.mode = TYPEC_STATE_USB;
@@ -376,6 +406,9 @@ static int cros_typec_init_ports(struct cros_typec_data *typec)
if (ret < 0)
goto unregister_ports;
+ cap->driver_data = cros_port;
+ cap->ops = &cros_typec_usb_mode_ops;
+
cros_port->port = typec_register_port(dev, cap);
if (IS_ERR(cros_port->port)) {
ret = PTR_ERR(cros_port->port);
@@ -576,6 +609,10 @@ static int cros_typec_enable_dp(struct cros_typec_data *typec,
if (!ret)
ret = typec_mux_set(port->mux, &port->state);
+ if (!ret)
+ ret = cros_typec_displayport_status_update(port->state.alt,
+ port->state.data);
+
return ret;
}
@@ -619,6 +656,7 @@ static int cros_typec_configure_mux(struct cros_typec_data *typec, int port_num,
};
struct ec_params_usb_pd_mux_ack mux_ack;
enum typec_orientation orientation;
+ struct cros_typec_altmode_node *node;
int ret;
ret = cros_ec_cmd(typec->ec, 0, EC_CMD_USB_PD_MUX_INFO,
@@ -677,6 +715,14 @@ static int cros_typec_configure_mux(struct cros_typec_data *typec, int port_num,
port->mux_flags);
}
+ /* Iterate all partner alt-modes and set the active alternate mode. */
+ list_for_each_entry(node, &port->partner_mode_list, list) {
+ typec_altmode_update_active(
+ node->amode,
+ port->state.alt &&
+ node->amode->svid == port->state.alt->svid);
+ }
+
mux_ack:
if (!typec->needs_mux_ack)
return ret;
@@ -1244,6 +1290,8 @@ static int cros_typec_probe(struct platform_device *pdev)
typec->typec_cmd_supported = cros_ec_check_features(ec_dev, EC_FEATURE_TYPEC_CMD);
typec->needs_mux_ack = cros_ec_check_features(ec_dev, EC_FEATURE_TYPEC_MUX_REQUIRE_AP_ACK);
+ typec->ap_driven_altmode = cros_ec_check_features(
+ ec_dev, EC_FEATURE_TYPEC_REQUIRE_AP_MODE_ENTRY);
ret = cros_ec_cmd(typec->ec, 0, EC_CMD_USB_PD_PORTS, NULL, 0,
&resp, sizeof(resp));
diff --git a/drivers/platform/chrome/cros_ec_typec.h b/drivers/platform/chrome/cros_ec_typec.h
index deda180a646f..f9c31f04c102 100644
--- a/drivers/platform/chrome/cros_ec_typec.h
+++ b/drivers/platform/chrome/cros_ec_typec.h
@@ -18,6 +18,7 @@
enum {
CROS_EC_ALTMODE_DP = 0,
CROS_EC_ALTMODE_TBT,
+ CROS_EC_ALTMODE_USB4,
CROS_EC_ALTMODE_MAX,
};
@@ -39,6 +40,7 @@ struct cros_typec_data {
struct work_struct port_work;
bool typec_cmd_supported;
bool needs_mux_ack;
+ bool ap_driven_altmode;
};
/* Per port data. */
diff --git a/drivers/platform/chrome/cros_ec_uart.c b/drivers/platform/chrome/cros_ec_uart.c
index 62bc24f6dcc7..19c179d49c90 100644
--- a/drivers/platform/chrome/cros_ec_uart.c
+++ b/drivers/platform/chrome/cros_ec_uart.c
@@ -283,7 +283,7 @@ static int cros_ec_uart_probe(struct serdev_device *serdev)
ec_dev->pkt_xfer = cros_ec_uart_pkt_xfer;
ec_dev->din_size = sizeof(struct ec_host_response) +
sizeof(struct ec_response_get_protocol_info);
- ec_dev->dout_size = sizeof(struct ec_host_request);
+ ec_dev->dout_size = sizeof(struct ec_host_request) + sizeof(struct ec_params_rwsig_action);
serdev_device_set_client_ops(serdev, &cros_ec_uart_client_ops);
diff --git a/drivers/platform/chrome/cros_ec_vbc.c b/drivers/platform/chrome/cros_ec_vbc.c
index 7bdb489354c5..963c4db23055 100644
--- a/drivers/platform/chrome/cros_ec_vbc.c
+++ b/drivers/platform/chrome/cros_ec_vbc.c
@@ -15,7 +15,7 @@
#define DRV_NAME "cros-ec-vbc"
static ssize_t vboot_context_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *att, char *buf,
+ const struct bin_attribute *att, char *buf,
loff_t pos, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@@ -59,7 +59,7 @@ static ssize_t vboot_context_read(struct file *filp, struct kobject *kobj,
}
static ssize_t vboot_context_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *attr, char *buf,
+ const struct bin_attribute *attr, char *buf,
loff_t pos, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@@ -99,16 +99,16 @@ static ssize_t vboot_context_write(struct file *filp, struct kobject *kobj,
return data_sz;
}
-static BIN_ATTR_RW(vboot_context, 16);
+static const BIN_ATTR_RW(vboot_context, 16);
-static struct bin_attribute *cros_ec_vbc_bin_attrs[] = {
+static const struct bin_attribute *const cros_ec_vbc_bin_attrs[] = {
&bin_attr_vboot_context,
NULL
};
static const struct attribute_group cros_ec_vbc_attr_group = {
.name = "vbc",
- .bin_attrs = cros_ec_vbc_bin_attrs,
+ .bin_attrs_new = cros_ec_vbc_bin_attrs,
};
static int cros_ec_vbc_probe(struct platform_device *pd)
diff --git a/drivers/platform/chrome/cros_kbd_led_backlight.c b/drivers/platform/chrome/cros_kbd_led_backlight.c
index 78097c8a4966..fc27bd7fc4b9 100644
--- a/drivers/platform/chrome/cros_kbd_led_backlight.c
+++ b/drivers/platform/chrome/cros_kbd_led_backlight.c
@@ -121,7 +121,17 @@ static const struct keyboard_led_drvdata keyboard_led_drvdata_acpi = {
#endif /* CONFIG_ACPI */
-#if IS_ENABLED(CONFIG_CROS_EC)
+#if IS_ENABLED(CONFIG_MFD_CROS_EC_DEV)
+static int keyboard_led_init_ec_pwm_mfd(struct platform_device *pdev)
+{
+ struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent);
+ struct cros_ec_device *cros_ec = ec_dev->ec_dev;
+ struct keyboard_led *keyboard_led = platform_get_drvdata(pdev);
+
+ keyboard_led->ec = cros_ec;
+
+ return 0;
+}
static int
keyboard_led_set_brightness_ec_pwm(struct led_classdev *cdev,
@@ -169,44 +179,6 @@ keyboard_led_get_brightness_ec_pwm(struct led_classdev *cdev)
return resp->percent;
}
-static int keyboard_led_init_ec_pwm(struct platform_device *pdev)
-{
- struct keyboard_led *keyboard_led = platform_get_drvdata(pdev);
-
- keyboard_led->ec = dev_get_drvdata(pdev->dev.parent);
- if (!keyboard_led->ec) {
- dev_err(&pdev->dev, "no parent EC device\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static const __maybe_unused struct keyboard_led_drvdata keyboard_led_drvdata_ec_pwm = {
- .init = keyboard_led_init_ec_pwm,
- .brightness_set_blocking = keyboard_led_set_brightness_ec_pwm,
- .brightness_get = keyboard_led_get_brightness_ec_pwm,
- .max_brightness = KEYBOARD_BACKLIGHT_MAX,
-};
-
-#else /* IS_ENABLED(CONFIG_CROS_EC) */
-
-static const __maybe_unused struct keyboard_led_drvdata keyboard_led_drvdata_ec_pwm = {};
-
-#endif /* IS_ENABLED(CONFIG_CROS_EC) */
-
-#if IS_ENABLED(CONFIG_MFD_CROS_EC_DEV)
-static int keyboard_led_init_ec_pwm_mfd(struct platform_device *pdev)
-{
- struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent);
- struct cros_ec_device *cros_ec = ec_dev->ec_dev;
- struct keyboard_led *keyboard_led = platform_get_drvdata(pdev);
-
- keyboard_led->ec = cros_ec;
-
- return 0;
-}
-
static const struct keyboard_led_drvdata keyboard_led_drvdata_ec_pwm_mfd = {
.init = keyboard_led_init_ec_pwm_mfd,
.brightness_set_blocking = keyboard_led_set_brightness_ec_pwm,
@@ -229,7 +201,7 @@ static int keyboard_led_probe(struct platform_device *pdev)
{
const struct keyboard_led_drvdata *drvdata;
struct keyboard_led *keyboard_led;
- int error;
+ int err;
if (keyboard_led_is_mfd_device(pdev))
drvdata = &keyboard_led_drvdata_ec_pwm_mfd;
@@ -244,9 +216,9 @@ static int keyboard_led_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, keyboard_led);
if (drvdata->init) {
- error = drvdata->init(pdev);
- if (error)
- return error;
+ err = drvdata->init(pdev);
+ if (err)
+ return err;
}
keyboard_led->cdev.name = "chromeos::kbd_backlight";
@@ -256,13 +228,10 @@ static int keyboard_led_probe(struct platform_device *pdev)
keyboard_led->cdev.brightness_set_blocking = drvdata->brightness_set_blocking;
keyboard_led->cdev.brightness_get = drvdata->brightness_get;
- error = devm_led_classdev_register(&pdev->dev, &keyboard_led->cdev);
- if (error == -EEXIST) /* Already bound via other mechanism */
+ err = devm_led_classdev_register(&pdev->dev, &keyboard_led->cdev);
+ if (err == -EEXIST) /* Already bound via other mechanism */
return -ENODEV;
- if (error)
- return error;
-
- return 0;
+ return err;
}
#ifdef CONFIG_ACPI
@@ -273,17 +242,6 @@ static const struct acpi_device_id keyboard_led_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, keyboard_led_acpi_match);
#endif
-#ifdef CONFIG_OF
-static const struct of_device_id keyboard_led_of_match[] = {
- {
- .compatible = "google,cros-kbd-led-backlight",
- .data = &keyboard_led_drvdata_ec_pwm,
- },
- {}
-};
-MODULE_DEVICE_TABLE(of, keyboard_led_of_match);
-#endif
-
static const struct platform_device_id keyboard_led_id[] = {
{ "cros-keyboard-leds", 0 },
{}
@@ -294,7 +252,6 @@ static struct platform_driver keyboard_led_driver = {
.driver = {
.name = "cros-keyboard-leds",
.acpi_match_table = ACPI_PTR(keyboard_led_acpi_match),
- .of_match_table = of_match_ptr(keyboard_led_of_match),
},
.probe = keyboard_led_probe,
.id_table = keyboard_led_id,
diff --git a/drivers/platform/chrome/cros_typec_altmode.c b/drivers/platform/chrome/cros_typec_altmode.c
new file mode 100644
index 000000000000..557340b53af0
--- /dev/null
+++ b/drivers/platform/chrome/cros_typec_altmode.c
@@ -0,0 +1,373 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Alt-mode implementation on ChromeOS EC.
+ *
+ * Copyright 2024 Google LLC
+ * Author: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
+ */
+#include "cros_ec_typec.h"
+
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/usb/typec_dp.h>
+#include <linux/usb/typec_tbt.h>
+#include <linux/usb/pd_vdo.h>
+
+#include "cros_typec_altmode.h"
+
+struct cros_typec_altmode_data {
+ struct work_struct work;
+ struct cros_typec_port *port;
+ struct typec_altmode *alt;
+ bool ap_mode_entry;
+
+ struct mutex lock;
+ u32 header;
+ u32 *vdo_data;
+ u8 vdo_size;
+
+ u16 sid;
+ u8 mode;
+};
+
+struct cros_typec_dp_data {
+ struct cros_typec_altmode_data adata;
+ struct typec_displayport_data data;
+ bool configured;
+ bool pending_status_update;
+};
+
+static void cros_typec_altmode_work(struct work_struct *work)
+{
+ struct cros_typec_altmode_data *data =
+ container_of(work, struct cros_typec_altmode_data, work);
+
+ mutex_lock(&data->lock);
+
+ if (typec_altmode_vdm(data->alt, data->header, data->vdo_data,
+ data->vdo_size))
+ dev_err(&data->alt->dev, "VDM 0x%x failed\n", data->header);
+
+ data->header = 0;
+ data->vdo_data = NULL;
+ data->vdo_size = 0;
+
+ mutex_unlock(&data->lock);
+}
+
+static int cros_typec_altmode_enter(struct typec_altmode *alt, u32 *vdo)
+{
+ struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt);
+ struct ec_params_typec_control req = {
+ .port = adata->port->port_num,
+ .command = TYPEC_CONTROL_COMMAND_ENTER_MODE,
+ };
+ int svdm_version;
+ int ret;
+
+ if (!adata->ap_mode_entry) {
+ dev_warn(&alt->dev,
+ "EC does not support AP driven mode entry\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (adata->sid == USB_TYPEC_DP_SID)
+ req.mode_to_enter = CROS_EC_ALTMODE_DP;
+ else if (adata->sid == USB_TYPEC_TBT_SID)
+ req.mode_to_enter = CROS_EC_ALTMODE_TBT;
+ else
+ return -EOPNOTSUPP;
+
+ ret = cros_ec_cmd(adata->port->typec_data->ec, 0, EC_CMD_TYPEC_CONTROL,
+ &req, sizeof(req), NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ svdm_version = typec_altmode_get_svdm_version(alt);
+ if (svdm_version < 0)
+ return svdm_version;
+
+ mutex_lock(&adata->lock);
+
+ adata->header = VDO(adata->sid, 1, svdm_version, CMD_ENTER_MODE);
+ adata->header |= VDO_OPOS(adata->mode);
+ adata->header |= VDO_CMDT(CMDT_RSP_ACK);
+ adata->vdo_data = NULL;
+ adata->vdo_size = 1;
+ schedule_work(&adata->work);
+
+ mutex_unlock(&adata->lock);
+ return ret;
+}
+
+static int cros_typec_altmode_exit(struct typec_altmode *alt)
+{
+ struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt);
+ struct ec_params_typec_control req = {
+ .port = adata->port->port_num,
+ .command = TYPEC_CONTROL_COMMAND_EXIT_MODES,
+ };
+ int svdm_version;
+ int ret;
+
+ if (!adata->ap_mode_entry) {
+ dev_warn(&alt->dev,
+ "EC does not support AP driven mode exit\n");
+ return -EOPNOTSUPP;
+ }
+
+ ret = cros_ec_cmd(adata->port->typec_data->ec, 0, EC_CMD_TYPEC_CONTROL,
+ &req, sizeof(req), NULL, 0);
+
+ if (ret < 0)
+ return ret;
+
+ svdm_version = typec_altmode_get_svdm_version(alt);
+ if (svdm_version < 0)
+ return svdm_version;
+
+ mutex_lock(&adata->lock);
+
+ adata->header = VDO(adata->sid, 1, svdm_version, CMD_EXIT_MODE);
+ adata->header |= VDO_OPOS(adata->mode);
+ adata->header |= VDO_CMDT(CMDT_RSP_ACK);
+ adata->vdo_data = NULL;
+ adata->vdo_size = 1;
+ schedule_work(&adata->work);
+
+ mutex_unlock(&adata->lock);
+ return ret;
+}
+
+static int cros_typec_displayport_vdm(struct typec_altmode *alt, u32 header,
+ const u32 *data, int count)
+{
+ struct cros_typec_dp_data *dp_data = typec_altmode_get_drvdata(alt);
+ struct cros_typec_altmode_data *adata = &dp_data->adata;
+
+
+ int cmd_type = PD_VDO_CMDT(header);
+ int cmd = PD_VDO_CMD(header);
+ int svdm_version;
+
+ svdm_version = typec_altmode_get_svdm_version(alt);
+ if (svdm_version < 0)
+ return svdm_version;
+
+ mutex_lock(&adata->lock);
+
+ switch (cmd_type) {
+ case CMDT_INIT:
+ if (PD_VDO_SVDM_VER(header) < svdm_version) {
+ typec_partner_set_svdm_version(adata->port->partner,
+ PD_VDO_SVDM_VER(header));
+ svdm_version = PD_VDO_SVDM_VER(header);
+ }
+
+ adata->header = VDO(adata->sid, 1, svdm_version, cmd);
+ adata->header |= VDO_OPOS(adata->mode);
+
+ /*
+ * DP_CMD_CONFIGURE: We can't actually do anything with the
+ * provided VDO yet so just send back an ACK.
+ *
+ * DP_CMD_STATUS_UPDATE: We wait for Mux changes to send
+ * DPStatus Acks.
+ */
+ switch (cmd) {
+ case DP_CMD_CONFIGURE:
+ dp_data->data.conf = *data;
+ adata->header |= VDO_CMDT(CMDT_RSP_ACK);
+ dp_data->configured = true;
+ schedule_work(&adata->work);
+ break;
+ case DP_CMD_STATUS_UPDATE:
+ dp_data->pending_status_update = true;
+ break;
+ default:
+ adata->header |= VDO_CMDT(CMDT_RSP_ACK);
+ schedule_work(&adata->work);
+ break;
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ mutex_unlock(&adata->lock);
+ return 0;
+}
+
+static int cros_typec_thunderbolt_vdm(struct typec_altmode *alt, u32 header,
+ const u32 *data, int count)
+{
+ struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt);
+
+ int cmd_type = PD_VDO_CMDT(header);
+ int cmd = PD_VDO_CMD(header);
+ int svdm_version;
+
+ svdm_version = typec_altmode_get_svdm_version(alt);
+ if (svdm_version < 0)
+ return svdm_version;
+
+ mutex_lock(&adata->lock);
+
+ switch (cmd_type) {
+ case CMDT_INIT:
+ if (PD_VDO_SVDM_VER(header) < svdm_version) {
+ typec_partner_set_svdm_version(adata->port->partner,
+ PD_VDO_SVDM_VER(header));
+ svdm_version = PD_VDO_SVDM_VER(header);
+ }
+
+ adata->header = VDO(adata->sid, 1, svdm_version, cmd);
+ adata->header |= VDO_OPOS(adata->mode);
+
+ switch (cmd) {
+ case CMD_ENTER_MODE:
+ /* Don't respond to the enter mode vdm because it
+ * triggers mux configuration. This is handled directly
+ * by the cros_ec_typec driver so the Thunderbolt driver
+ * doesn't need to be involved.
+ */
+ break;
+ default:
+ adata->header |= VDO_CMDT(CMDT_RSP_ACK);
+ schedule_work(&adata->work);
+ break;
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ mutex_unlock(&adata->lock);
+ return 0;
+}
+
+
+static int cros_typec_altmode_vdm(struct typec_altmode *alt, u32 header,
+ const u32 *data, int count)
+{
+ struct cros_typec_altmode_data *adata = typec_altmode_get_drvdata(alt);
+
+ if (!adata->ap_mode_entry)
+ return -EOPNOTSUPP;
+
+ if (adata->sid == USB_TYPEC_DP_SID)
+ return cros_typec_displayport_vdm(alt, header, data, count);
+
+ if (adata->sid == USB_TYPEC_TBT_SID)
+ return cros_typec_thunderbolt_vdm(alt, header, data, count);
+
+ return -EINVAL;
+}
+
+static const struct typec_altmode_ops cros_typec_altmode_ops = {
+ .enter = cros_typec_altmode_enter,
+ .exit = cros_typec_altmode_exit,
+ .vdm = cros_typec_altmode_vdm,
+};
+
+#if IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE)
+int cros_typec_displayport_status_update(struct typec_altmode *altmode,
+ struct typec_displayport_data *data)
+{
+ struct cros_typec_dp_data *dp_data =
+ typec_altmode_get_drvdata(altmode);
+ struct cros_typec_altmode_data *adata = &dp_data->adata;
+
+ if (!dp_data->pending_status_update) {
+ dev_dbg(&altmode->dev,
+ "Got DPStatus without a pending request\n");
+ return 0;
+ }
+
+ if (dp_data->configured && dp_data->data.conf != data->conf)
+ dev_dbg(&altmode->dev,
+ "DP Conf doesn't match. Requested 0x%04x, Actual 0x%04x\n",
+ dp_data->data.conf, data->conf);
+
+ mutex_lock(&adata->lock);
+
+ dp_data->data = *data;
+ dp_data->pending_status_update = false;
+ adata->header |= VDO_CMDT(CMDT_RSP_ACK);
+ adata->vdo_data = &dp_data->data.status;
+ adata->vdo_size = 2;
+ schedule_work(&adata->work);
+
+ mutex_unlock(&adata->lock);
+
+ return 0;
+}
+
+struct typec_altmode *
+cros_typec_register_displayport(struct cros_typec_port *port,
+ struct typec_altmode_desc *desc,
+ bool ap_mode_entry)
+{
+ struct typec_altmode *alt;
+ struct cros_typec_dp_data *dp_data;
+ struct cros_typec_altmode_data *adata;
+
+ alt = typec_port_register_altmode(port->port, desc);
+ if (IS_ERR(alt))
+ return alt;
+
+ dp_data = devm_kzalloc(&alt->dev, sizeof(*dp_data), GFP_KERNEL);
+ if (!dp_data) {
+ typec_unregister_altmode(alt);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ adata = &dp_data->adata;
+ INIT_WORK(&adata->work, cros_typec_altmode_work);
+ mutex_init(&adata->lock);
+ adata->alt = alt;
+ adata->port = port;
+ adata->ap_mode_entry = ap_mode_entry;
+ adata->sid = desc->svid;
+ adata->mode = desc->mode;
+
+ typec_altmode_set_ops(alt, &cros_typec_altmode_ops);
+ typec_altmode_set_drvdata(alt, adata);
+
+ return alt;
+}
+#endif
+
+#if IS_ENABLED(CONFIG_TYPEC_TBT_ALTMODE)
+struct typec_altmode *
+cros_typec_register_thunderbolt(struct cros_typec_port *port,
+ struct typec_altmode_desc *desc)
+{
+ struct typec_altmode *alt;
+ struct cros_typec_altmode_data *adata;
+
+ alt = typec_port_register_altmode(port->port, desc);
+ if (IS_ERR(alt))
+ return alt;
+
+ adata = devm_kzalloc(&alt->dev, sizeof(*adata), GFP_KERNEL);
+ if (!adata) {
+ typec_unregister_altmode(alt);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ INIT_WORK(&adata->work, cros_typec_altmode_work);
+ adata->alt = alt;
+ adata->port = port;
+ adata->ap_mode_entry = true;
+ adata->sid = desc->svid;
+ adata->mode = desc->mode;
+
+ typec_altmode_set_ops(alt, &cros_typec_altmode_ops);
+ typec_altmode_set_drvdata(alt, adata);
+
+ return alt;
+}
+#endif
diff --git a/drivers/platform/chrome/cros_typec_altmode.h b/drivers/platform/chrome/cros_typec_altmode.h
new file mode 100644
index 000000000000..3f2aa95d065a
--- /dev/null
+++ b/drivers/platform/chrome/cros_typec_altmode.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __CROS_TYPEC_ALTMODE_H__
+#define __CROS_TYPEC_ALTMODE_H__
+
+#include <linux/kconfig.h>
+#include <linux/usb/typec.h>
+
+struct cros_typec_port;
+struct typec_altmode;
+struct typec_altmode_desc;
+struct typec_displayport_data;
+
+#if IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE)
+struct typec_altmode *
+cros_typec_register_displayport(struct cros_typec_port *port,
+ struct typec_altmode_desc *desc,
+ bool ap_mode_entry);
+
+int cros_typec_displayport_status_update(struct typec_altmode *altmode,
+ struct typec_displayport_data *data);
+#else
+static inline struct typec_altmode *
+cros_typec_register_displayport(struct cros_typec_port *port,
+ struct typec_altmode_desc *desc,
+ bool ap_mode_entry)
+{
+ return typec_port_register_altmode(port->port, desc);
+}
+
+static inline int cros_typec_displayport_status_update(struct typec_altmode *altmode,
+ struct typec_displayport_data *data)
+{
+ return 0;
+}
+#endif
+
+#if IS_ENABLED(CONFIG_TYPEC_TBT_ALTMODE)
+struct typec_altmode *
+cros_typec_register_thunderbolt(struct cros_typec_port *port,
+ struct typec_altmode_desc *desc);
+#else
+static inline struct typec_altmode *
+cros_typec_register_thunderbolt(struct cros_typec_port *port,
+ struct typec_altmode_desc *desc)
+{
+ return typec_port_register_altmode(port->port, desc);
+}
+#endif
+
+#endif /* __CROS_TYPEC_ALTMODE_H__ */
diff --git a/drivers/platform/chrome/cros_usbpd_logger.c b/drivers/platform/chrome/cros_usbpd_logger.c
index cd71f1caea81..7ce75e2e039e 100644
--- a/drivers/platform/chrome/cros_usbpd_logger.c
+++ b/drivers/platform/chrome/cros_usbpd_logger.c
@@ -13,6 +13,7 @@
#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
+#include <linux/string_choices.h>
#define DRV_NAME "cros-usbpd-logger"
@@ -135,8 +136,8 @@ static void cros_usbpd_print_log_entry(struct ec_response_pd_log *r,
len += append_str(buf, len, "Power supply fault: %s", fault);
break;
case PD_EVENT_VIDEO_DP_MODE:
- len += append_str(buf, len, "DP mode %sabled", r->data == 1 ?
- "en" : "dis");
+ len += append_str(buf, len, "DP mode %s",
+ str_enabled_disabled(r->data == 1));
break;
case PD_EVENT_VIDEO_CODEC:
minfo = (struct mcdp_info *)r->payload;
diff --git a/drivers/platform/cznic/turris-omnia-mcu-base.c b/drivers/platform/cznic/turris-omnia-mcu-base.c
index 58f9afae2867..770e680b96f9 100644
--- a/drivers/platform/cznic/turris-omnia-mcu-base.c
+++ b/drivers/platform/cznic/turris-omnia-mcu-base.c
@@ -52,6 +52,7 @@ int omnia_cmd_write_read(const struct i2c_client *client,
return 0;
}
+EXPORT_SYMBOL_GPL(omnia_cmd_write_read);
static int omnia_get_version_hash(struct omnia_mcu *mcu, bool bootloader,
char version[static OMNIA_FW_VERSION_HEX_LEN])
@@ -257,6 +258,8 @@ static int omnia_mcu_read_features(struct omnia_mcu *mcu)
_DEF_FEAT(NEW_INT_API, "new interrupt API"),
_DEF_FEAT(POWEROFF_WAKEUP, "poweroff and wakeup"),
_DEF_FEAT(TRNG, "true random number generator"),
+ _DEF_FEAT(BRIGHTNESS_INT, "LED panel brightness change interrupt"),
+ _DEF_FEAT(LED_GAMMA_CORRECTION, "LED gamma correction"),
#undef _DEF_FEAT
};
struct i2c_client *client = mcu->client;
diff --git a/drivers/platform/cznic/turris-omnia-mcu.h b/drivers/platform/cznic/turris-omnia-mcu.h
index 2b13e28ee323..088541be3f4c 100644
--- a/drivers/platform/cznic/turris-omnia-mcu.h
+++ b/drivers/platform/cznic/turris-omnia-mcu.h
@@ -8,7 +8,6 @@
#ifndef __TURRIS_OMNIA_MCU_H
#define __TURRIS_OMNIA_MCU_H
-#include <linux/bitops.h>
#include <linux/completion.h>
#include <linux/gpio/driver.h>
#include <linux/hw_random.h>
@@ -17,8 +16,6 @@
#include <linux/types.h>
#include <linux/watchdog.h>
#include <linux/workqueue.h>
-#include <asm/byteorder.h>
-#include <linux/unaligned.h>
struct i2c_client;
struct rtc_device;
@@ -93,133 +90,6 @@ struct omnia_mcu {
#endif
};
-int omnia_cmd_write_read(const struct i2c_client *client,
- void *cmd, unsigned int cmd_len,
- void *reply, unsigned int reply_len);
-
-static inline int omnia_cmd_write(const struct i2c_client *client, void *cmd,
- unsigned int len)
-{
- return omnia_cmd_write_read(client, cmd, len, NULL, 0);
-}
-
-static inline int omnia_cmd_write_u8(const struct i2c_client *client, u8 cmd,
- u8 val)
-{
- u8 buf[2] = { cmd, val };
-
- return omnia_cmd_write(client, buf, sizeof(buf));
-}
-
-static inline int omnia_cmd_write_u16(const struct i2c_client *client, u8 cmd,
- u16 val)
-{
- u8 buf[3];
-
- buf[0] = cmd;
- put_unaligned_le16(val, &buf[1]);
-
- return omnia_cmd_write(client, buf, sizeof(buf));
-}
-
-static inline int omnia_cmd_write_u32(const struct i2c_client *client, u8 cmd,
- u32 val)
-{
- u8 buf[5];
-
- buf[0] = cmd;
- put_unaligned_le32(val, &buf[1]);
-
- return omnia_cmd_write(client, buf, sizeof(buf));
-}
-
-static inline int omnia_cmd_read(const struct i2c_client *client, u8 cmd,
- void *reply, unsigned int len)
-{
- return omnia_cmd_write_read(client, &cmd, 1, reply, len);
-}
-
-static inline unsigned int
-omnia_compute_reply_length(unsigned long mask, bool interleaved,
- unsigned int offset)
-{
- if (!mask)
- return 0;
-
- return ((__fls(mask) >> 3) << interleaved) + 1 + offset;
-}
-
-/* Returns 0 on success */
-static inline int omnia_cmd_read_bits(const struct i2c_client *client, u8 cmd,
- unsigned long bits, unsigned long *dst)
-{
- __le32 reply;
- int err;
-
- if (!bits) {
- *dst = 0;
- return 0;
- }
-
- err = omnia_cmd_read(client, cmd, &reply,
- omnia_compute_reply_length(bits, false, 0));
- if (err)
- return err;
-
- *dst = le32_to_cpu(reply) & bits;
-
- return 0;
-}
-
-static inline int omnia_cmd_read_bit(const struct i2c_client *client, u8 cmd,
- unsigned long bit)
-{
- unsigned long reply;
- int err;
-
- err = omnia_cmd_read_bits(client, cmd, bit, &reply);
- if (err)
- return err;
-
- return !!reply;
-}
-
-static inline int omnia_cmd_read_u32(const struct i2c_client *client, u8 cmd,
- u32 *dst)
-{
- __le32 reply;
- int err;
-
- err = omnia_cmd_read(client, cmd, &reply, sizeof(reply));
- if (err)
- return err;
-
- *dst = le32_to_cpu(reply);
-
- return 0;
-}
-
-static inline int omnia_cmd_read_u16(const struct i2c_client *client, u8 cmd,
- u16 *dst)
-{
- __le16 reply;
- int err;
-
- err = omnia_cmd_read(client, cmd, &reply, sizeof(reply));
- if (err)
- return err;
-
- *dst = le16_to_cpu(reply);
-
- return 0;
-}
-
-static inline int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd,
- u8 *reply)
-{
- return omnia_cmd_read(client, cmd, reply, sizeof(*reply));
-}
-
#ifdef CONFIG_TURRIS_OMNIA_MCU_GPIO
extern const u8 omnia_int_to_gpio_idx[32];
extern const struct attribute_group omnia_mcu_gpio_group;
diff --git a/drivers/platform/loongarch/loongson-laptop.c b/drivers/platform/loongarch/loongson-laptop.c
index 99203584949d..61b18ac206c9 100644
--- a/drivers/platform/loongarch/loongson-laptop.c
+++ b/drivers/platform/loongarch/loongson-laptop.c
@@ -56,8 +56,7 @@ static struct input_dev *generic_inputdev;
static acpi_handle hotkey_handle;
static struct key_entry hotkey_keycode_map[GENERIC_HOTKEY_MAP_MAX];
-int loongson_laptop_turn_on_backlight(void);
-int loongson_laptop_turn_off_backlight(void);
+static bool bl_powered;
static int loongson_laptop_backlight_update(struct backlight_device *bd);
/* 2. ACPI Helpers and device model */
@@ -354,16 +353,42 @@ static int ec_backlight_level(u8 level)
return level;
}
+static int ec_backlight_set_power(bool state)
+{
+ int status;
+ union acpi_object arg0 = { ACPI_TYPE_INTEGER };
+ struct acpi_object_list args = { 1, &arg0 };
+
+ arg0.integer.value = state;
+ status = acpi_evaluate_object(NULL, "\\BLSW", &args, NULL);
+ if (ACPI_FAILURE(status)) {
+ pr_info("Loongson lvds error: 0x%x\n", status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
static int loongson_laptop_backlight_update(struct backlight_device *bd)
{
- int lvl = ec_backlight_level(bd->props.brightness);
+ bool target_powered = !backlight_is_blank(bd);
+ int ret = 0, lvl = ec_backlight_level(bd->props.brightness);
if (lvl < 0)
return -EIO;
+
if (ec_set_brightness(lvl))
return -EIO;
- return 0;
+ if (target_powered != bl_powered) {
+ ret = ec_backlight_set_power(target_powered);
+ if (ret < 0)
+ return ret;
+
+ bl_powered = target_powered;
+ }
+
+ return ret;
}
static int loongson_laptop_get_brightness(struct backlight_device *bd)
@@ -384,7 +409,7 @@ static const struct backlight_ops backlight_laptop_ops = {
static int laptop_backlight_register(void)
{
- int status = 0;
+ int status = 0, ret;
struct backlight_properties props;
memset(&props, 0, sizeof(props));
@@ -392,44 +417,20 @@ static int laptop_backlight_register(void)
if (!acpi_evalf(hotkey_handle, &status, "ECLL", "d"))
return -EIO;
- props.brightness = 1;
+ ret = ec_backlight_set_power(true);
+ if (ret)
+ return ret;
+
+ bl_powered = true;
+
props.max_brightness = status;
+ props.brightness = ec_get_brightness();
+ props.power = BACKLIGHT_POWER_ON;
props.type = BACKLIGHT_PLATFORM;
backlight_device_register("loongson_laptop",
NULL, NULL, &backlight_laptop_ops, &props);
- return 0;
-}
-
-int loongson_laptop_turn_on_backlight(void)
-{
- int status;
- union acpi_object arg0 = { ACPI_TYPE_INTEGER };
- struct acpi_object_list args = { 1, &arg0 };
-
- arg0.integer.value = 1;
- status = acpi_evaluate_object(NULL, "\\BLSW", &args, NULL);
- if (ACPI_FAILURE(status)) {
- pr_info("Loongson lvds error: 0x%x\n", status);
- return -ENODEV;
- }
-
- return 0;
-}
-
-int loongson_laptop_turn_off_backlight(void)
-{
- int status;
- union acpi_object arg0 = { ACPI_TYPE_INTEGER };
- struct acpi_object_list args = { 1, &arg0 };
-
- arg0.integer.value = 0;
- status = acpi_evaluate_object(NULL, "\\BLSW", &args, NULL);
- if (ACPI_FAILURE(status)) {
- pr_info("Loongson lvds error: 0x%x\n", status);
- return -ENODEV;
- }
return 0;
}
@@ -611,11 +612,17 @@ static int __init generic_acpi_laptop_init(void)
static void __exit generic_acpi_laptop_exit(void)
{
+ int i;
+
if (generic_inputdev) {
- if (input_device_registered)
- input_unregister_device(generic_inputdev);
- else
+ if (!input_device_registered) {
input_free_device(generic_inputdev);
+ } else {
+ input_unregister_device(generic_inputdev);
+
+ for (i = 0; i < ARRAY_SIZE(generic_sub_drivers); i++)
+ generic_subdriver_exit(&generic_sub_drivers[i]);
+ }
}
}
diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig
index f7dfa0e785fd..aa760f064a17 100644
--- a/drivers/platform/mellanox/Kconfig
+++ b/drivers/platform/mellanox/Kconfig
@@ -14,6 +14,19 @@ menuconfig MELLANOX_PLATFORM
if MELLANOX_PLATFORM
+config MLX_PLATFORM
+ tristate "Mellanox Technologies platform support"
+ depends on ACPI && I2C && PCI
+ select REGMAP
+ help
+ This option enables system support for the Mellanox Technologies
+ platform. The Mellanox systems provide data center networking
+ solutions based on Virtual Protocol Interconnect (VPI) technology
+ enable seamless connectivity to 56/100Gb/s InfiniBand or 10/40/56GbE
+ connection.
+
+ If you have a Mellanox system, say Y or M here.
+
config MLXREG_HOTPLUG
tristate "Mellanox platform hotplug driver support"
depends on HWMON
diff --git a/drivers/platform/mellanox/Makefile b/drivers/platform/mellanox/Makefile
index 04703c0416b1..ba56485cbe8c 100644
--- a/drivers/platform/mellanox/Makefile
+++ b/drivers/platform/mellanox/Makefile
@@ -3,6 +3,7 @@
# Makefile for linux/drivers/platform/mellanox
# Mellanox Platform-Specific Drivers
#
+obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o
obj-$(CONFIG_MLXBF_BOOTCTL) += mlxbf-bootctl.o
obj-$(CONFIG_MLXBF_PMC) += mlxbf-pmc.o
obj-$(CONFIG_MLXBF_TMFIFO) += mlxbf-tmfifo.o
diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/mellanox/mlx-platform.c
index 9c7f30a47f1f..08b0430a2899 100644
--- a/drivers/platform/x86/mlx-platform.c
+++ b/drivers/platform/mellanox/mlx-platform.c
@@ -145,7 +145,7 @@
#define MLXPLAT_CPLD_LPC_REG_WD3_TMR_OFFSET 0xd1
#define MLXPLAT_CPLD_LPC_REG_WD3_TLEFT_OFFSET 0xd2
#define MLXPLAT_CPLD_LPC_REG_WD3_ACT_OFFSET 0xd3
-#define MLXPLAT_CPLD_LPC_REG_DBG_CTRL_OFFSET 0xd9
+#define MLXPLAT_CPLD_LPC_REG_CPLD6_MVER_OFFSET 0xd9
#define MLXPLAT_CPLD_LPC_REG_I2C_CH1_OFFSET 0xdb
#define MLXPLAT_CPLD_LPC_REG_I2C_CH2_OFFSET 0xda
#define MLXPLAT_CPLD_LPC_REG_I2C_CH3_OFFSET 0xdc
@@ -2247,7 +2247,7 @@ struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_modular_data = {
.mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW,
};
-/* Platform hotplug for NVLink blade systems family data */
+/* Platform hotplug for NVLink blade systems family data */
static struct mlxreg_core_data mlxplat_mlxcpld_global_wp_items_data[] = {
{
.label = "global_wp_grant",
@@ -2279,7 +2279,7 @@ struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_chassis_blade_data = {
.mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW,
};
-/* Platform hotplug for switch systems family data */
+/* Platform hotplug for switch systems family data */
static struct mlxreg_core_data mlxplat_mlxcpld_erot_ap_items_data[] = {
{
.label = "erot1_ap",
@@ -2387,7 +2387,7 @@ static struct mlxreg_core_hotplug_notifier mlxplat_mlxcpld_l1_switch_pwr_events_
.user_handler = mlxplat_mlxcpld_l1_switch_pwr_events_handler,
};
-/* Platform hotplug for l1 switch systems family data */
+/* Platform hotplug for l1 switch systems family data */
static struct mlxreg_core_data mlxplat_mlxcpld_l1_switch_pwr_events_items_data[] = {
{
.label = "power_button",
@@ -4401,7 +4401,7 @@ static struct mlxreg_core_platform_data mlxplat_modular_regs_io_data = {
.counter = ARRAY_SIZE(mlxplat_mlxcpld_modular_regs_io_data),
};
-/* Platform register access for chassis blade systems family data */
+/* Platform register access for chassis blade systems family data */
static struct mlxreg_core_data mlxplat_mlxcpld_chassis_blade_regs_io_data[] = {
{
.label = "cpld1_version",
@@ -5050,7 +5050,6 @@ static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg)
case MLXPLAT_CPLD_LPC_REG_WD3_TMR_OFFSET:
case MLXPLAT_CPLD_LPC_REG_WD3_TLEFT_OFFSET:
case MLXPLAT_CPLD_LPC_REG_WD3_ACT_OFFSET:
- case MLXPLAT_CPLD_LPC_REG_DBG_CTRL_OFFSET:
case MLXPLAT_CPLD_LPC_REG_I2C_CH1_OFFSET:
case MLXPLAT_CPLD_LPC_REG_I2C_CH2_OFFSET:
case MLXPLAT_CPLD_LPC_REG_I2C_CH3_OFFSET:
@@ -5186,7 +5185,7 @@ static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg)
case MLXPLAT_CPLD_LPC_REG_WD3_TMR_OFFSET:
case MLXPLAT_CPLD_LPC_REG_WD3_TLEFT_OFFSET:
case MLXPLAT_CPLD_LPC_REG_WD3_ACT_OFFSET:
- case MLXPLAT_CPLD_LPC_REG_DBG_CTRL_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_CPLD6_MVER_OFFSET:
case MLXPLAT_CPLD_LPC_REG_I2C_CH1_OFFSET:
case MLXPLAT_CPLD_LPC_REG_I2C_CH2_OFFSET:
case MLXPLAT_CPLD_LPC_REG_I2C_CH3_OFFSET:
@@ -5343,7 +5342,7 @@ static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg)
case MLXPLAT_CPLD_LPC_REG_WD2_TLEFT_OFFSET:
case MLXPLAT_CPLD_LPC_REG_WD3_TMR_OFFSET:
case MLXPLAT_CPLD_LPC_REG_WD3_TLEFT_OFFSET:
- case MLXPLAT_CPLD_LPC_REG_DBG_CTRL_OFFSET:
+ case MLXPLAT_CPLD_LPC_REG_CPLD6_MVER_OFFSET:
case MLXPLAT_CPLD_LPC_REG_I2C_CH1_OFFSET:
case MLXPLAT_CPLD_LPC_REG_I2C_CH2_OFFSET:
case MLXPLAT_CPLD_LPC_REG_I2C_CH3_OFFSET:
@@ -6573,7 +6572,7 @@ static int mlxplat_probe(struct platform_device *pdev)
}
/* Set default registers. */
- for (i = 0; i < mlxplat_regmap_config->num_reg_defaults; i++) {
+ for (i = 0; i < mlxplat_regmap_config->num_reg_defaults; i++) {
err = regmap_write(priv->regmap,
mlxplat_regmap_config->reg_defaults[i].reg,
mlxplat_regmap_config->reg_defaults[i].def);
diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c
index c5b36837e694..c18a5b96de5c 100644
--- a/drivers/platform/mellanox/mlxbf-bootctl.c
+++ b/drivers/platform/mellanox/mlxbf-bootctl.c
@@ -91,6 +91,7 @@ static const char * const mlxbf_rsh_log_level[] = {
static DEFINE_MUTEX(icm_ops_lock);
static DEFINE_MUTEX(os_up_lock);
static DEFINE_MUTEX(mfg_ops_lock);
+static DEFINE_MUTEX(rtc_ops_lock);
/*
* Objects are stored within the MFG partition per type.
@@ -177,7 +178,7 @@ static ssize_t post_reset_wdog_show(struct device *dev,
if (ret < 0)
return ret;
- return sprintf(buf, "%d\n", ret);
+ return sysfs_emit(buf, "%d\n", ret);
}
static ssize_t post_reset_wdog_store(struct device *dev,
@@ -206,7 +207,7 @@ static ssize_t mlxbf_bootctl_show(int smc_op, char *buf)
if (action < 0)
return action;
- return sprintf(buf, "%s\n", mlxbf_bootctl_action_to_string(action));
+ return sysfs_emit(buf, "%s\n", mlxbf_bootctl_action_to_string(action));
}
static int mlxbf_bootctl_store(int smc_op, const char *buf, size_t count)
@@ -274,14 +275,14 @@ static ssize_t lifecycle_state_show(struct device *dev,
* due to using the test bits.
*/
if (test_state) {
- return sprintf(buf, "%s(test)\n",
+ return sysfs_emit(buf, "%s(test)\n",
mlxbf_bootctl_lifecycle_states[lc_state]);
} else if (use_dev_key &&
(lc_state == MLXBF_BOOTCTL_SB_LIFECYCLE_GA_SECURE)) {
- return sprintf(buf, "Secured (development)\n");
+ return sysfs_emit(buf, "Secured (development)\n");
}
- return sprintf(buf, "%s\n", mlxbf_bootctl_lifecycle_states[lc_state]);
+ return sysfs_emit(buf, "%s\n", mlxbf_bootctl_lifecycle_states[lc_state]);
}
static ssize_t secure_boot_fuse_state_show(struct device *dev,
@@ -332,9 +333,9 @@ static ssize_t secure_boot_fuse_state_show(struct device *dev,
else
status = valid ? "Invalid" : "Free";
}
- buf_len += sprintf(buf + buf_len, "%d:%s ", key, status);
+ buf_len += sysfs_emit_at(buf, buf_len, "%d:%s ", key, status);
}
- buf_len += sprintf(buf + buf_len, "\n");
+ buf_len += sysfs_emit_at(buf, buf_len, "\n");
return buf_len;
}
@@ -489,6 +490,23 @@ static ssize_t large_icm_store(struct device *dev,
return res.a0 ? -EPERM : count;
}
+static ssize_t rtc_battery_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct arm_smccc_res res;
+
+ mutex_lock(&rtc_ops_lock);
+ arm_smccc_smc(MLNX_HANDLE_GET_RTC_LOW_BATT, 0, 0, 0, 0,
+ 0, 0, 0, &res);
+ mutex_unlock(&rtc_ops_lock);
+
+ if (res.a0)
+ return -EPERM;
+
+ return sysfs_emit(buf, "0x%lx\n", res.a1);
+}
+
static ssize_t os_up_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
@@ -906,6 +924,7 @@ static DEVICE_ATTR_RW(sn);
static DEVICE_ATTR_RW(uuid);
static DEVICE_ATTR_RW(rev);
static DEVICE_ATTR_WO(mfg_lock);
+static DEVICE_ATTR_RO(rtc_battery);
static struct attribute *mlxbf_bootctl_attrs[] = {
&dev_attr_post_reset_wdog.attr,
@@ -925,6 +944,7 @@ static struct attribute *mlxbf_bootctl_attrs[] = {
&dev_attr_uuid.attr,
&dev_attr_rev.attr,
&dev_attr_mfg_lock.attr,
+ &dev_attr_rtc_battery.attr,
NULL
};
@@ -939,7 +959,7 @@ 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,
+ const struct bin_attribute *bin_attr,
char *buf, loff_t pos,
size_t count)
{
@@ -971,9 +991,9 @@ static ssize_t mlxbf_bootctl_bootfifo_read(struct file *filp,
return p - buf;
}
-static struct bin_attribute mlxbf_bootctl_bootfifo_sysfs_attr = {
+static const struct bin_attribute mlxbf_bootctl_bootfifo_sysfs_attr = {
.attr = { .name = "bootfifo", .mode = 0400 },
- .read = mlxbf_bootctl_bootfifo_read,
+ .read_new = mlxbf_bootctl_bootfifo_read,
};
static bool mlxbf_bootctl_guid_match(const guid_t *guid,
diff --git a/drivers/platform/mellanox/mlxbf-bootctl.h b/drivers/platform/mellanox/mlxbf-bootctl.h
index 1299750a8661..90bbbdc65879 100644
--- a/drivers/platform/mellanox/mlxbf-bootctl.h
+++ b/drivers/platform/mellanox/mlxbf-bootctl.h
@@ -103,6 +103,11 @@
*/
#define MLNX_HANDLE_OS_UP 0x82000014
+/*
+ * SMC function ID to get and clear the RTC low voltage bit
+ */
+#define MLNX_HANDLE_GET_RTC_LOW_BATT 0x82000023
+
/* SMC function IDs for SiP Service queries */
#define MLXBF_BOOTCTL_SIP_SVC_CALL_COUNT 0x8200ff00
#define MLXBF_BOOTCTL_SIP_SVC_UID 0x8200ff01
diff --git a/drivers/platform/mellanox/mlxbf-pmc.c b/drivers/platform/mellanox/mlxbf-pmc.c
index 9ff7b487dc48..36a00692347d 100644
--- a/drivers/platform/mellanox/mlxbf-pmc.c
+++ b/drivers/platform/mellanox/mlxbf-pmc.c
@@ -88,6 +88,7 @@
#define MLXBF_PMC_CRSPACE_PERFMON_CTL(n) (n * MLXBF_PMC_CRSPACE_PERFMON_REG0_SZ)
#define MLXBF_PMC_CRSPACE_PERFMON_EN BIT(30)
#define MLXBF_PMC_CRSPACE_PERFMON_CLR BIT(28)
+#define MLXBF_PMC_CRSPACE_PERFMON_COUNT_CLOCK(n) (MLXBF_PMC_CRSPACE_PERFMON_CTL(n) + 0x4)
#define MLXBF_PMC_CRSPACE_PERFMON_VAL0(n) (MLXBF_PMC_CRSPACE_PERFMON_CTL(n) + 0xc)
/**
@@ -114,6 +115,7 @@ struct mlxbf_pmc_attribute {
* @attr_event: Attributes for "event" sysfs files
* @attr_event_list: Attributes for "event_list" sysfs files
* @attr_enable: Attributes for "enable" sysfs files
+ * @attr_count_clock: Attributes for "count_clock" sysfs files
* @block_attr: All attributes needed for the block
* @block_attr_grp: Attribute group for the block
*/
@@ -126,6 +128,7 @@ struct mlxbf_pmc_block_info {
struct mlxbf_pmc_attribute *attr_event;
struct mlxbf_pmc_attribute attr_event_list;
struct mlxbf_pmc_attribute attr_enable;
+ struct mlxbf_pmc_attribute attr_count_clock;
struct attribute *block_attr[MLXBF_PMC_MAX_ATTRS];
struct attribute_group block_attr_grp;
};
@@ -859,6 +862,37 @@ static const struct mlxbf_pmc_events mlxbf_pmc_llt_miss_events[] = {
{75, "HISTOGRAM_HISTOGRAM_BIN9"},
};
+static const struct mlxbf_pmc_events mlxbf_pmc_clock_events[] = {
+ { 0x0, "FMON_CLK_LAST_COUNT_PLL_D1_INST0" },
+ { 0x4, "REFERENCE_WINDOW_WIDTH_PLL_D1_INST0" },
+ { 0x8, "FMON_CLK_LAST_COUNT_PLL_D1_INST1" },
+ { 0xc, "REFERENCE_WINDOW_WIDTH_PLL_D1_INST1" },
+ { 0x10, "FMON_CLK_LAST_COUNT_PLL_G1" },
+ { 0x14, "REFERENCE_WINDOW_WIDTH_PLL_G1" },
+ { 0x18, "FMON_CLK_LAST_COUNT_PLL_W1" },
+ { 0x1c, "REFERENCE_WINDOW_WIDTH_PLL_W1" },
+ { 0x20, "FMON_CLK_LAST_COUNT_PLL_T1" },
+ { 0x24, "REFERENCE_WINDOW_WIDTH_PLL_T1" },
+ { 0x28, "FMON_CLK_LAST_COUNT_PLL_A0" },
+ { 0x2c, "REFERENCE_WINDOW_WIDTH_PLL_A0" },
+ { 0x30, "FMON_CLK_LAST_COUNT_PLL_C0" },
+ { 0x34, "REFERENCE_WINDOW_WIDTH_PLL_C0" },
+ { 0x38, "FMON_CLK_LAST_COUNT_PLL_N1" },
+ { 0x3c, "REFERENCE_WINDOW_WIDTH_PLL_N1" },
+ { 0x40, "FMON_CLK_LAST_COUNT_PLL_I1" },
+ { 0x44, "REFERENCE_WINDOW_WIDTH_PLL_I1" },
+ { 0x48, "FMON_CLK_LAST_COUNT_PLL_R1" },
+ { 0x4c, "REFERENCE_WINDOW_WIDTH_PLL_R1" },
+ { 0x50, "FMON_CLK_LAST_COUNT_PLL_P1" },
+ { 0x54, "REFERENCE_WINDOW_WIDTH_PLL_P1" },
+ { 0x58, "FMON_CLK_LAST_COUNT_REF_100_INST0" },
+ { 0x5c, "REFERENCE_WINDOW_WIDTH_REF_100_INST0" },
+ { 0x60, "FMON_CLK_LAST_COUNT_REF_100_INST1" },
+ { 0x64, "REFERENCE_WINDOW_WIDTH_REF_100_INST1" },
+ { 0x68, "FMON_CLK_LAST_COUNT_REF_156" },
+ { 0x6c, "REFERENCE_WINDOW_WIDTH_REF_156" },
+};
+
static struct mlxbf_pmc_context *pmc;
/* UUID used to probe ATF service. */
@@ -1032,6 +1066,9 @@ static const struct mlxbf_pmc_events *mlxbf_pmc_event_list(const char *blk, size
} else if (strstr(blk, "llt")) {
events = mlxbf_pmc_llt_events;
size = ARRAY_SIZE(mlxbf_pmc_llt_events);
+ } else if (strstr(blk, "clock_measure")) {
+ events = mlxbf_pmc_clock_events;
+ size = ARRAY_SIZE(mlxbf_pmc_clock_events);
} else {
events = NULL;
size = 0;
@@ -1466,14 +1503,15 @@ static int mlxbf_pmc_read_event(unsigned int blk_num, u32 cnt_num, bool is_l3, u
/* Method to read a register */
static int mlxbf_pmc_read_reg(unsigned int blk_num, u32 offset, u64 *result)
{
- u32 ecc_out;
+ u32 reg;
- if (strstr(pmc->block_name[blk_num], "ecc")) {
+ if ((strstr(pmc->block_name[blk_num], "ecc")) ||
+ (strstr(pmc->block_name[blk_num], "clock_measure"))) {
if (mlxbf_pmc_readl(pmc->block[blk_num].mmio_base + offset,
- &ecc_out))
+ &reg))
return -EFAULT;
- *result = ecc_out;
+ *result = reg;
return 0;
}
@@ -1487,6 +1525,9 @@ static int mlxbf_pmc_read_reg(unsigned int blk_num, u32 offset, u64 *result)
/* Method to write to a register */
static int mlxbf_pmc_write_reg(unsigned int blk_num, u32 offset, u64 data)
{
+ if (strstr(pmc->block_name[blk_num], "clock_measure"))
+ return -EINVAL;
+
if (strstr(pmc->block_name[blk_num], "ecc")) {
return mlxbf_pmc_write(pmc->block[blk_num].mmio_base + offset,
MLXBF_PMC_WRITE_REG_32, data);
@@ -1763,6 +1804,49 @@ static ssize_t mlxbf_pmc_enable_store(struct device *dev,
return count;
}
+/* Show function for "count_clock" sysfs files - only for crspace */
+static ssize_t mlxbf_pmc_count_clock_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mlxbf_pmc_attribute *attr_count_clock = container_of(
+ attr, struct mlxbf_pmc_attribute, dev_attr);
+ unsigned int blk_num;
+ u32 reg;
+
+ blk_num = attr_count_clock->nr;
+
+ if (mlxbf_pmc_readl(pmc->block[blk_num].mmio_base +
+ MLXBF_PMC_CRSPACE_PERFMON_COUNT_CLOCK(pmc->block[blk_num].counters),
+ &reg))
+ return -EINVAL;
+
+ return sysfs_emit(buf, "%u\n", reg);
+}
+
+/* Store function for "count_clock" sysfs files - only for crspace */
+static ssize_t mlxbf_pmc_count_clock_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mlxbf_pmc_attribute *attr_count_clock = container_of(
+ attr, struct mlxbf_pmc_attribute, dev_attr);
+ unsigned int blk_num;
+ u32 reg;
+ int err;
+
+ blk_num = attr_count_clock->nr;
+
+ err = kstrtouint(buf, 0, &reg);
+ if (err < 0)
+ return err;
+
+ mlxbf_pmc_write(pmc->block[blk_num].mmio_base +
+ MLXBF_PMC_CRSPACE_PERFMON_COUNT_CLOCK(pmc->block[blk_num].counters),
+ MLXBF_PMC_WRITE_REG_32, reg);
+
+ return count;
+}
+
/* Populate attributes for blocks with counters to monitor performance */
static int mlxbf_pmc_init_perftype_counter(struct device *dev, unsigned int blk_num)
{
@@ -1801,6 +1885,21 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, unsigned int blk_
attr = NULL;
}
+ if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_CRSPACE) {
+ /* Program crspace counters to count clock cycles using "count_clock" sysfs */
+ attr = &pmc->block[blk_num].attr_count_clock;
+ attr->dev_attr.attr.mode = 0644;
+ attr->dev_attr.show = mlxbf_pmc_count_clock_show;
+ attr->dev_attr.store = mlxbf_pmc_count_clock_store;
+ attr->nr = blk_num;
+ attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL,
+ "count_clock");
+ if (!attr->dev_attr.attr.name)
+ return -ENOMEM;
+ pmc->block[blk_num].block_attr[++i] = &attr->dev_attr.attr;
+ attr = NULL;
+ }
+
pmc->block[blk_num].attr_counter = devm_kcalloc(
dev, pmc->block[blk_num].counters,
sizeof(struct mlxbf_pmc_attribute), GFP_KERNEL);
diff --git a/drivers/platform/mellanox/mlxbf-tmfifo.c b/drivers/platform/mellanox/mlxbf-tmfifo.c
index 300cdaa75a17..aae99adb29eb 100644
--- a/drivers/platform/mellanox/mlxbf-tmfifo.c
+++ b/drivers/platform/mellanox/mlxbf-tmfifo.c
@@ -1320,7 +1320,7 @@ static void mlxbf_tmfifo_cleanup(struct mlxbf_tmfifo *fifo)
int i;
fifo->is_ready = false;
- del_timer_sync(&fifo->timer);
+ timer_delete_sync(&fifo->timer);
mlxbf_tmfifo_disable_irqs(fifo);
cancel_work_sync(&fifo->work);
for (i = 0; i < MLXBF_TMFIFO_VDEV_MAX; i++)
diff --git a/drivers/platform/mellanox/mlxreg-hotplug.c b/drivers/platform/mellanox/mlxreg-hotplug.c
index 6aa2a4650367..b347000e4329 100644
--- a/drivers/platform/mellanox/mlxreg-hotplug.c
+++ b/drivers/platform/mellanox/mlxreg-hotplug.c
@@ -232,7 +232,7 @@ static ssize_t mlxreg_hotplug_attr_show(struct device *dev,
regval = !!(regval & data->mask);
}
- return sprintf(buf, "%u\n", regval);
+ return sysfs_emit(buf, "%u\n", regval);
}
#define PRIV_ATTR(i) priv->mlxreg_hotplug_attr[i]
diff --git a/drivers/platform/mellanox/mlxreg-io.c b/drivers/platform/mellanox/mlxreg-io.c
index 595276206baf..97fefe6c38d1 100644
--- a/drivers/platform/mellanox/mlxreg-io.c
+++ b/drivers/platform/mellanox/mlxreg-io.c
@@ -126,7 +126,7 @@ mlxreg_io_attr_show(struct device *dev, struct device_attribute *attr,
mutex_unlock(&priv->io_lock);
- return sprintf(buf, "%u\n", regval);
+ return sysfs_emit(buf, "%u\n", regval);
access_error:
mutex_unlock(&priv->io_lock);
diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c
index d4f32ad66530..a594d5fcfcfd 100644
--- a/drivers/platform/surface/surface_aggregator_registry.c
+++ b/drivers/platform/surface/surface_aggregator_registry.c
@@ -371,7 +371,7 @@ static const struct software_node *ssam_node_group_sp8[] = {
NULL,
};
-/* Devices for Surface Pro 9 (Intel/x86) and 10 */
+/* Devices for Surface Pro 9, 10 and 11 (Intel/x86) */
static const struct software_node *ssam_node_group_sp9[] = {
&ssam_node_root,
&ssam_node_hub_kip,
@@ -430,6 +430,9 @@ static const struct acpi_device_id ssam_platform_hub_acpi_match[] = {
/* Surface Pro 10 */
{ "MSHW0510", (unsigned long)ssam_node_group_sp9 },
+ /* Surface Pro 11 */
+ { "MSHW0583", (unsigned long)ssam_node_group_sp9 },
+
/* Surface Book 2 */
{ "MSHW0107", (unsigned long)ssam_node_group_gen5 },
diff --git a/drivers/platform/surface/surface_platform_profile.c b/drivers/platform/surface/surface_platform_profile.c
index 08db878f1d7d..0e479e35e66e 100644
--- a/drivers/platform/surface/surface_platform_profile.c
+++ b/drivers/platform/surface/surface_platform_profile.c
@@ -40,7 +40,7 @@ struct ssam_tmp_profile_info {
struct ssam_platform_profile_device {
struct ssam_device *sdev;
- struct platform_profile_handler handler;
+ struct device *ppdev;
bool has_fan;
};
@@ -154,14 +154,14 @@ static int convert_profile_to_ssam_fan(struct ssam_device *sdev, enum platform_p
}
}
-static int ssam_platform_profile_get(struct platform_profile_handler *pprof,
+static int ssam_platform_profile_get(struct device *dev,
enum platform_profile_option *profile)
{
struct ssam_platform_profile_device *tpd;
enum ssam_tmp_profile tp;
int status;
- tpd = container_of(pprof, struct ssam_platform_profile_device, handler);
+ tpd = dev_get_drvdata(dev);
status = ssam_tmp_profile_get(tpd->sdev, &tp);
if (status)
@@ -175,13 +175,13 @@ static int ssam_platform_profile_get(struct platform_profile_handler *pprof,
return 0;
}
-static int ssam_platform_profile_set(struct platform_profile_handler *pprof,
+static int ssam_platform_profile_set(struct device *dev,
enum platform_profile_option profile)
{
struct ssam_platform_profile_device *tpd;
int tp;
- tpd = container_of(pprof, struct ssam_platform_profile_device, handler);
+ tpd = dev_get_drvdata(dev);
tp = convert_profile_to_ssam_tmp(tpd->sdev, profile);
if (tp < 0)
@@ -201,6 +201,22 @@ static int ssam_platform_profile_set(struct platform_profile_handler *pprof,
return tp;
}
+static int ssam_platform_profile_probe(void *drvdata, unsigned long *choices)
+{
+ set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
+ set_bit(PLATFORM_PROFILE_BALANCED, choices);
+ set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices);
+ set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
+
+ return 0;
+}
+
+static const struct platform_profile_ops ssam_platform_profile_ops = {
+ .probe = ssam_platform_profile_probe,
+ .profile_get = ssam_platform_profile_get,
+ .profile_set = ssam_platform_profile_set,
+};
+
static int surface_platform_profile_probe(struct ssam_device *sdev)
{
struct ssam_platform_profile_device *tpd;
@@ -210,23 +226,14 @@ static int surface_platform_profile_probe(struct ssam_device *sdev)
return -ENOMEM;
tpd->sdev = sdev;
-
- tpd->handler.profile_get = ssam_platform_profile_get;
- tpd->handler.profile_set = ssam_platform_profile_set;
+ ssam_device_set_drvdata(sdev, tpd);
tpd->has_fan = device_property_read_bool(&sdev->dev, "has_fan");
- set_bit(PLATFORM_PROFILE_LOW_POWER, tpd->handler.choices);
- set_bit(PLATFORM_PROFILE_BALANCED, tpd->handler.choices);
- set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, tpd->handler.choices);
- set_bit(PLATFORM_PROFILE_PERFORMANCE, tpd->handler.choices);
+ tpd->ppdev = devm_platform_profile_register(&sdev->dev, "Surface Platform Profile",
+ tpd, &ssam_platform_profile_ops);
- return platform_profile_register(&tpd->handler);
-}
-
-static void surface_platform_profile_remove(struct ssam_device *sdev)
-{
- platform_profile_remove();
+ return PTR_ERR_OR_ZERO(tpd->ppdev);
}
static const struct ssam_device_id ssam_platform_profile_match[] = {
@@ -237,7 +244,6 @@ MODULE_DEVICE_TABLE(ssam, ssam_platform_profile_match);
static struct ssam_device_driver surface_platform_profile = {
.probe = surface_platform_profile_probe,
- .remove = surface_platform_profile_remove,
.match_table = ssam_platform_profile_match,
.driver = {
.name = "surface_platform_profile",
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 0258dd879d64..43407e76476b 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -475,6 +475,17 @@ config IDEAPAD_LAPTOP
This is a driver for Lenovo IdeaPad netbooks contains drivers for
rfkill switch, hotkey, fan control and backlight control.
+config LENOVO_WMI_HOTKEY_UTILITIES
+ tristate "Lenovo Hotkey Utility WMI extras driver"
+ depends on ACPI_WMI
+ select NEW_LEDS
+ select LEDS_CLASS
+ imply IDEAPAD_LAPTOP
+ help
+ This driver provides WMI support for Lenovo customized hotkeys function,
+ such as LED control for audio/mic mute event for Ideapad, YOGA, XiaoXin,
+ Gaming, ThinkBook and so on.
+
config LENOVO_YMC
tristate "Lenovo Yoga Tablet Mode Control"
depends on ACPI_WMI
@@ -778,6 +789,23 @@ config BARCO_P50_GPIO
To compile this driver as a module, choose M here: the module
will be called barco-p50-gpio.
+config SAMSUNG_GALAXYBOOK
+ tristate "Samsung Galaxy Book driver"
+ depends on ACPI
+ depends on ACPI_BATTERY
+ depends on INPUT
+ depends on LEDS_CLASS
+ depends on SERIO_I8042
+ select ACPI_PLATFORM_PROFILE
+ select FW_ATTR_CLASS
+ help
+ This is a driver for Samsung Galaxy Book series notebooks. It adds
+ support for the keyboard backlight control, performance mode control,
+ function keys, and various firmware attributes.
+
+ For more information about this driver, see
+ <file:Documentation/admin-guide/laptops/samsung-galaxybook.rst>.
+
config SAMSUNG_LAPTOP
tristate "Samsung Laptop driver"
depends on RFKILL || RFKILL = n
@@ -1012,19 +1040,6 @@ config SERIAL_MULTI_INSTANTIATE
To compile this driver as a module, choose M here: the module
will be called serial-multi-instantiate.
-config MLX_PLATFORM
- tristate "Mellanox Technologies platform support"
- depends on ACPI && I2C && PCI
- select REGMAP
- help
- This option enables system support for the Mellanox Technologies
- platform. The Mellanox systems provide data center networking
- solutions based on Virtual Protocol Interconnect (VPI) technology
- enable seamless connectivity to 56/100Gb/s InfiniBand or 10/40/56GbE
- connection.
-
- If you have a Mellanox system, say Y or M here.
-
config TOUCHSCREEN_DMI
bool "DMI based touchscreen configuration info"
depends on ACPI && DMI && I2C=y && TOUCHSCREEN_SILEAD
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index e1b142947067..650dfbebb6c8 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_UV_SYSFS) += uv_sysfs.o
# IBM Thinkpad and Lenovo
obj-$(CONFIG_IBM_RTL) += ibm_rtl.o
obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o
+obj-$(CONFIG_LENOVO_WMI_HOTKEY_UTILITIES) += lenovo-wmi-hotkey-utilities.o
obj-$(CONFIG_LENOVO_YMC) += lenovo-ymc.o
obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
@@ -95,8 +96,9 @@ obj-$(CONFIG_PCENGINES_APU2) += pcengines-apuv2.o
obj-$(CONFIG_BARCO_P50_GPIO) += barco-p50-gpio.o
# Samsung
-obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop.o
-obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o
+obj-$(CONFIG_SAMSUNG_GALAXYBOOK) += samsung-galaxybook.o
+obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop.o
+obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o
# Toshiba
obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
@@ -122,7 +124,6 @@ obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
# Platform drivers
obj-$(CONFIG_FW_ATTR_CLASS) += firmware_attributes_class.o
obj-$(CONFIG_SERIAL_MULTI_INSTANTIATE) += serial-multi-instantiate.o
-obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o
obj-$(CONFIG_TOUCHSCREEN_DMI) += touchscreen_dmi.o
obj-$(CONFIG_WIRELESS_HOTKEY) += wireless-hotkey.o
obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets/
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index ac4f8ab45bdc..69336bd778ee 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -30,7 +30,10 @@
#include <linux/input/sparse-keymap.h>
#include <acpi/video.h>
#include <linux/hwmon.h>
+#include <linux/units.h>
+#include <linux/unaligned.h>
#include <linux/bitfield.h>
+#include <linux/bitmap.h>
MODULE_AUTHOR("Carlos Corbacho");
MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver");
@@ -67,10 +70,16 @@ MODULE_LICENSE("GPL");
#define ACER_WMID_GET_GAMING_SYS_INFO_METHODID 5
#define ACER_WMID_SET_GAMING_FAN_BEHAVIOR 14
#define ACER_WMID_SET_GAMING_MISC_SETTING_METHODID 22
+#define ACER_WMID_GET_GAMING_MISC_SETTING_METHODID 23
-#define ACER_PREDATOR_V4_THERMAL_PROFILE_EC_OFFSET 0x54
+#define ACER_GAMING_MISC_SETTING_STATUS_MASK GENMASK_ULL(7, 0)
+#define ACER_GAMING_MISC_SETTING_INDEX_MASK GENMASK_ULL(7, 0)
+#define ACER_GAMING_MISC_SETTING_VALUE_MASK GENMASK_ULL(15, 8)
-#define ACER_PREDATOR_V4_FAN_SPEED_READ_BIT_MASK GENMASK(20, 8)
+#define ACER_PREDATOR_V4_RETURN_STATUS_BIT_MASK GENMASK_ULL(7, 0)
+#define ACER_PREDATOR_V4_SENSOR_INDEX_BIT_MASK GENMASK_ULL(15, 8)
+#define ACER_PREDATOR_V4_SENSOR_READING_BIT_MASK GENMASK_ULL(23, 8)
+#define ACER_PREDATOR_V4_SUPPORTED_SENSORS_BIT_MASK GENMASK_ULL(39, 24)
/*
* Acer ACPI method GUIDs
@@ -99,9 +108,29 @@ enum acer_wmi_event_ids {
};
enum acer_wmi_predator_v4_sys_info_command {
- ACER_WMID_CMD_GET_PREDATOR_V4_BAT_STATUS = 0x02,
- ACER_WMID_CMD_GET_PREDATOR_V4_CPU_FAN_SPEED = 0x0201,
- ACER_WMID_CMD_GET_PREDATOR_V4_GPU_FAN_SPEED = 0x0601,
+ ACER_WMID_CMD_GET_PREDATOR_V4_SUPPORTED_SENSORS = 0x0000,
+ ACER_WMID_CMD_GET_PREDATOR_V4_SENSOR_READING = 0x0001,
+ ACER_WMID_CMD_GET_PREDATOR_V4_BAT_STATUS = 0x0002,
+};
+
+enum acer_wmi_predator_v4_sensor_id {
+ ACER_WMID_SENSOR_CPU_TEMPERATURE = 0x01,
+ ACER_WMID_SENSOR_CPU_FAN_SPEED = 0x02,
+ ACER_WMID_SENSOR_EXTERNAL_TEMPERATURE_2 = 0x03,
+ ACER_WMID_SENSOR_GPU_FAN_SPEED = 0x06,
+ ACER_WMID_SENSOR_GPU_TEMPERATURE = 0x0A,
+};
+
+enum acer_wmi_predator_v4_oc {
+ ACER_WMID_OC_NORMAL = 0x0000,
+ ACER_WMID_OC_TURBO = 0x0002,
+};
+
+enum acer_wmi_gaming_misc_setting {
+ ACER_WMID_MISC_SETTING_OC_1 = 0x0005,
+ ACER_WMID_MISC_SETTING_OC_2 = 0x0007,
+ ACER_WMID_MISC_SETTING_SUPPORTED_PROFILES = 0x000A,
+ ACER_WMID_MISC_SETTING_PLATFORM_PROFILE = 0x000B,
};
static const struct key_entry acer_wmi_keymap[] __initconst = {
@@ -247,7 +276,7 @@ struct hotkey_function_type_aa {
#define ACER_CAP_TURBO_LED BIT(8)
#define ACER_CAP_TURBO_FAN BIT(9)
#define ACER_CAP_PLATFORM_PROFILE BIT(10)
-#define ACER_CAP_FAN_SPEED_READ BIT(11)
+#define ACER_CAP_HWMON BIT(11)
/*
* Interface type flags
@@ -272,6 +301,7 @@ static u16 commun_func_bitmap;
static u8 commun_fn_key_number;
static bool cycle_gaming_thermal_profile = true;
static bool predator_v4;
+static u64 supported_sensors;
module_param(mailled, int, 0444);
module_param(brightness, int, 0444);
@@ -359,7 +389,7 @@ static void __init set_quirks(void)
if (quirks->predator_v4)
interface->capability |= ACER_CAP_PLATFORM_PROFILE |
- ACER_CAP_FAN_SPEED_READ;
+ ACER_CAP_HWMON;
}
static int __init dmi_matched(const struct dmi_system_id *dmi)
@@ -755,29 +785,24 @@ static const struct dmi_system_id non_acer_quirks[] __initconst = {
{}
};
-static struct platform_profile_handler platform_profile_handler;
+static struct device *platform_profile_device;
static bool platform_profile_support;
/*
* The profile used before turbo mode. This variable is needed for
* returning from turbo mode when the mode key is in toggle mode.
*/
-static int last_non_turbo_profile;
-
-enum acer_predator_v4_thermal_profile_ec {
- ACER_PREDATOR_V4_THERMAL_PROFILE_ECO = 0x04,
- ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO = 0x03,
- ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE = 0x02,
- ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET = 0x01,
- ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED = 0x00,
-};
+static int last_non_turbo_profile = INT_MIN;
+
+/* The most performant supported profile */
+static int acer_predator_v4_max_perf;
-enum acer_predator_v4_thermal_profile_wmi {
- ACER_PREDATOR_V4_THERMAL_PROFILE_ECO_WMI = 0x060B,
- ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI = 0x050B,
- ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE_WMI = 0x040B,
- ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET_WMI = 0x0B,
- ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI = 0x010B,
+enum acer_predator_v4_thermal_profile {
+ ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET = 0x00,
+ ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED = 0x01,
+ ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE = 0x04,
+ ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO = 0x05,
+ ACER_PREDATOR_V4_THERMAL_PROFILE_ECO = 0x06,
};
/* Find which quirks are needed for a particular vendor/ model pair */
@@ -1490,6 +1515,45 @@ WMI_gaming_execute_u64(u32 method_id, u64 in, u64 *out)
return status;
}
+static int WMI_gaming_execute_u32_u64(u32 method_id, u32 in, u64 *out)
+{
+ struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_buffer input = {
+ .length = sizeof(in),
+ .pointer = &in,
+ };
+ union acpi_object *obj;
+ acpi_status status;
+ int ret = 0;
+
+ status = wmi_evaluate_method(WMID_GUID4, 0, method_id, &input, &result);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ obj = result.pointer;
+ if (obj && out) {
+ switch (obj->type) {
+ case ACPI_TYPE_INTEGER:
+ *out = obj->integer.value;
+ break;
+ case ACPI_TYPE_BUFFER:
+ if (obj->buffer.length < sizeof(*out))
+ ret = -ENOMSG;
+ else
+ *out = get_unaligned_le64(obj->buffer.pointer);
+
+ break;
+ default:
+ ret = -ENOMSG;
+ break;
+ }
+ }
+
+ kfree(obj);
+
+ return ret;
+}
+
static acpi_status WMID_gaming_set_u64(u64 value, u32 cap)
{
u32 method_id = 0;
@@ -1504,9 +1568,6 @@ static acpi_status WMID_gaming_set_u64(u64 value, u32 cap)
case ACER_CAP_TURBO_FAN:
method_id = ACER_WMID_SET_GAMING_FAN_BEHAVIOR;
break;
- case ACER_CAP_TURBO_OC:
- method_id = ACER_WMID_SET_GAMING_MISC_SETTING_METHODID;
- break;
default:
return AE_BAD_PARAMETER;
}
@@ -1539,6 +1600,24 @@ static acpi_status WMID_gaming_get_u64(u64 *value, u32 cap)
return status;
}
+static int WMID_gaming_get_sys_info(u32 command, u64 *out)
+{
+ acpi_status status;
+ u64 result;
+
+ status = WMI_gaming_execute_u64(ACER_WMID_GET_GAMING_SYS_INFO_METHODID, command, &result);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ /* The return status must be zero for the operation to have succeeded */
+ if (FIELD_GET(ACER_PREDATOR_V4_RETURN_STATUS_BIT_MASK, result))
+ return -EIO;
+
+ *out = result;
+
+ return 0;
+}
+
static void WMID_gaming_set_fan_mode(u8 fan_mode)
{
/* fan_mode = 1 is used for auto, fan_mode = 2 used for turbo*/
@@ -1560,6 +1639,48 @@ static void WMID_gaming_set_fan_mode(u8 fan_mode)
WMID_gaming_set_u64(gpu_fan_config2 | gpu_fan_config1 << 16, ACER_CAP_TURBO_FAN);
}
+static int WMID_gaming_set_misc_setting(enum acer_wmi_gaming_misc_setting setting, u8 value)
+{
+ acpi_status status;
+ u64 input = 0;
+ u64 result;
+
+ input |= FIELD_PREP(ACER_GAMING_MISC_SETTING_INDEX_MASK, setting);
+ input |= FIELD_PREP(ACER_GAMING_MISC_SETTING_VALUE_MASK, value);
+
+ status = WMI_gaming_execute_u64(ACER_WMID_SET_GAMING_MISC_SETTING_METHODID, input, &result);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ /* The return status must be zero for the operation to have succeeded */
+ if (FIELD_GET(ACER_GAMING_MISC_SETTING_STATUS_MASK, result))
+ return -EIO;
+
+ return 0;
+}
+
+static int WMID_gaming_get_misc_setting(enum acer_wmi_gaming_misc_setting setting, u8 *value)
+{
+ u64 input = 0;
+ u64 result;
+ int ret;
+
+ input |= FIELD_PREP(ACER_GAMING_MISC_SETTING_INDEX_MASK, setting);
+
+ ret = WMI_gaming_execute_u32_u64(ACER_WMID_GET_GAMING_MISC_SETTING_METHODID, input,
+ &result);
+ if (ret < 0)
+ return ret;
+
+ /* The return status must be zero for the operation to have succeeded */
+ if (FIELD_GET(ACER_GAMING_MISC_SETTING_STATUS_MASK, result))
+ return -EIO;
+
+ *value = FIELD_GET(ACER_GAMING_MISC_SETTING_VALUE_MASK, result);
+
+ return 0;
+}
+
/*
* Generic Device (interface-independent)
*/
@@ -1786,26 +1907,6 @@ static int acer_gsensor_event(void)
return 0;
}
-static int acer_get_fan_speed(int fan)
-{
- if (quirks->predator_v4) {
- acpi_status status;
- u64 fanspeed;
-
- status = WMI_gaming_execute_u64(
- ACER_WMID_GET_GAMING_SYS_INFO_METHODID,
- fan == 0 ? ACER_WMID_CMD_GET_PREDATOR_V4_CPU_FAN_SPEED :
- ACER_WMID_CMD_GET_PREDATOR_V4_GPU_FAN_SPEED,
- &fanspeed);
-
- if (ACPI_FAILURE(status))
- return -EIO;
-
- return FIELD_GET(ACER_PREDATOR_V4_FAN_SPEED_READ_BIT_MASK, fanspeed);
- }
- return -EOPNOTSUPP;
-}
-
/*
* Predator series turbo button
*/
@@ -1825,8 +1926,12 @@ static int acer_toggle_turbo(void)
WMID_gaming_set_fan_mode(0x1);
/* Set OC to normal */
- WMID_gaming_set_u64(0x5, ACER_CAP_TURBO_OC);
- WMID_gaming_set_u64(0x7, ACER_CAP_TURBO_OC);
+ if (has_cap(ACER_CAP_TURBO_OC)) {
+ WMID_gaming_set_misc_setting(ACER_WMID_MISC_SETTING_OC_1,
+ ACER_WMID_OC_NORMAL);
+ WMID_gaming_set_misc_setting(ACER_WMID_MISC_SETTING_OC_2,
+ ACER_WMID_OC_NORMAL);
+ }
} else {
/* Turn on turbo led */
WMID_gaming_set_u64(0x10001, ACER_CAP_TURBO_LED);
@@ -1835,22 +1940,25 @@ static int acer_toggle_turbo(void)
WMID_gaming_set_fan_mode(0x2);
/* Set OC to turbo mode */
- WMID_gaming_set_u64(0x205, ACER_CAP_TURBO_OC);
- WMID_gaming_set_u64(0x207, ACER_CAP_TURBO_OC);
+ if (has_cap(ACER_CAP_TURBO_OC)) {
+ WMID_gaming_set_misc_setting(ACER_WMID_MISC_SETTING_OC_1,
+ ACER_WMID_OC_TURBO);
+ WMID_gaming_set_misc_setting(ACER_WMID_MISC_SETTING_OC_2,
+ ACER_WMID_OC_TURBO);
+ }
}
return turbo_led_state;
}
static int
-acer_predator_v4_platform_profile_get(struct platform_profile_handler *pprof,
+acer_predator_v4_platform_profile_get(struct device *dev,
enum platform_profile_option *profile)
{
u8 tp;
int err;
- err = ec_read(ACER_PREDATOR_V4_THERMAL_PROFILE_EC_OFFSET, &tp);
-
- if (err < 0)
+ err = WMID_gaming_get_misc_setting(ACER_WMID_MISC_SETTING_PLATFORM_PROFILE, &tp);
+ if (err)
return err;
switch (tp) {
@@ -1877,74 +1985,112 @@ acer_predator_v4_platform_profile_get(struct platform_profile_handler *pprof,
}
static int
-acer_predator_v4_platform_profile_set(struct platform_profile_handler *pprof,
+acer_predator_v4_platform_profile_set(struct device *dev,
enum platform_profile_option profile)
{
- int tp;
- acpi_status status;
+ int err, tp;
switch (profile) {
case PLATFORM_PROFILE_PERFORMANCE:
- tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI;
+ tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO;
break;
case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
- tp = ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE_WMI;
+ tp = ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE;
break;
case PLATFORM_PROFILE_BALANCED:
- tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI;
+ tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED;
break;
case PLATFORM_PROFILE_QUIET:
- tp = ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET_WMI;
+ tp = ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET;
break;
case PLATFORM_PROFILE_LOW_POWER:
- tp = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO_WMI;
+ tp = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO;
break;
default:
return -EOPNOTSUPP;
}
- status = WMI_gaming_execute_u64(
- ACER_WMID_SET_GAMING_MISC_SETTING_METHODID, tp, NULL);
-
- if (ACPI_FAILURE(status))
- return -EIO;
+ err = WMID_gaming_set_misc_setting(ACER_WMID_MISC_SETTING_PLATFORM_PROFILE, tp);
+ if (err)
+ return err;
- if (tp != ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI)
+ if (tp != acer_predator_v4_max_perf)
last_non_turbo_profile = tp;
return 0;
}
-static int acer_platform_profile_setup(void)
+static int
+acer_predator_v4_platform_profile_probe(void *drvdata, unsigned long *choices)
+{
+ unsigned long supported_profiles;
+ int err;
+
+ err = WMID_gaming_get_misc_setting(ACER_WMID_MISC_SETTING_SUPPORTED_PROFILES,
+ (u8 *)&supported_profiles);
+ if (err)
+ return err;
+
+ /* Iterate through supported profiles in order of increasing performance */
+ if (test_bit(ACER_PREDATOR_V4_THERMAL_PROFILE_ECO, &supported_profiles)) {
+ set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
+ acer_predator_v4_max_perf = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO;
+ last_non_turbo_profile = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO;
+ }
+
+ if (test_bit(ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET, &supported_profiles)) {
+ set_bit(PLATFORM_PROFILE_QUIET, choices);
+ acer_predator_v4_max_perf = ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET;
+ last_non_turbo_profile = ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET;
+ }
+
+ if (test_bit(ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED, &supported_profiles)) {
+ set_bit(PLATFORM_PROFILE_BALANCED, choices);
+ acer_predator_v4_max_perf = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED;
+ last_non_turbo_profile = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED;
+ }
+
+ if (test_bit(ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE, &supported_profiles)) {
+ set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices);
+ acer_predator_v4_max_perf = ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE;
+
+ /* We only use this profile as a fallback option in case no prior
+ * profile is supported.
+ */
+ if (last_non_turbo_profile < 0)
+ last_non_turbo_profile = ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE;
+ }
+
+ if (test_bit(ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO, &supported_profiles)) {
+ set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
+ acer_predator_v4_max_perf = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO;
+
+ /* We need to handle the hypothetical case where only the turbo profile
+ * is supported. In this case the turbo toggle will essentially be a
+ * no-op.
+ */
+ if (last_non_turbo_profile < 0)
+ last_non_turbo_profile = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO;
+ }
+
+ return 0;
+}
+
+static const struct platform_profile_ops acer_predator_v4_platform_profile_ops = {
+ .probe = acer_predator_v4_platform_profile_probe,
+ .profile_get = acer_predator_v4_platform_profile_get,
+ .profile_set = acer_predator_v4_platform_profile_set,
+};
+
+static int acer_platform_profile_setup(struct platform_device *device)
{
if (quirks->predator_v4) {
- int err;
-
- platform_profile_handler.profile_get =
- acer_predator_v4_platform_profile_get;
- platform_profile_handler.profile_set =
- acer_predator_v4_platform_profile_set;
-
- set_bit(PLATFORM_PROFILE_PERFORMANCE,
- platform_profile_handler.choices);
- set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE,
- platform_profile_handler.choices);
- set_bit(PLATFORM_PROFILE_BALANCED,
- platform_profile_handler.choices);
- set_bit(PLATFORM_PROFILE_QUIET,
- platform_profile_handler.choices);
- set_bit(PLATFORM_PROFILE_LOW_POWER,
- platform_profile_handler.choices);
-
- err = platform_profile_register(&platform_profile_handler);
- if (err)
- return err;
+ platform_profile_device = devm_platform_profile_register(
+ &device->dev, "acer-wmi", NULL, &acer_predator_v4_platform_profile_ops);
+ if (IS_ERR(platform_profile_device))
+ return PTR_ERR(platform_profile_device);
platform_profile_support = true;
-
- /* Set default non-turbo profile */
- last_non_turbo_profile =
- ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI;
}
return 0;
}
@@ -1952,83 +2098,41 @@ static int acer_platform_profile_setup(void)
static int acer_thermal_profile_change(void)
{
/*
- * This mode key can rotate each mode or toggle turbo mode.
- * On battery, only ECO and BALANCED mode are available.
+ * This mode key will either cycle through each mode or toggle the
+ * most performant profile.
*/
if (quirks->predator_v4) {
u8 current_tp;
- int tp, err;
- u64 on_AC;
- acpi_status status;
+ int err, tp;
- err = ec_read(ACER_PREDATOR_V4_THERMAL_PROFILE_EC_OFFSET,
- &current_tp);
-
- if (err < 0)
- return err;
+ if (cycle_gaming_thermal_profile) {
+ platform_profile_cycle();
+ } else {
+ /* Do nothing if no suitable platform profiles where found */
+ if (last_non_turbo_profile < 0)
+ return 0;
- /* Check power source */
- status = WMI_gaming_execute_u64(
- ACER_WMID_GET_GAMING_SYS_INFO_METHODID,
- ACER_WMID_CMD_GET_PREDATOR_V4_BAT_STATUS, &on_AC);
+ err = WMID_gaming_get_misc_setting(
+ ACER_WMID_MISC_SETTING_PLATFORM_PROFILE, &current_tp);
+ if (err)
+ return err;
- if (ACPI_FAILURE(status))
- return -EIO;
-
- switch (current_tp) {
- case ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO:
- if (!on_AC)
- tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI;
- else if (cycle_gaming_thermal_profile)
- tp = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO_WMI;
- else
+ if (current_tp == acer_predator_v4_max_perf)
tp = last_non_turbo_profile;
- break;
- case ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE:
- if (!on_AC)
- tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI;
- else
- tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI;
- break;
- case ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED:
- if (!on_AC)
- tp = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO_WMI;
- else if (cycle_gaming_thermal_profile)
- tp = ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE_WMI;
- else
- tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI;
- break;
- case ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET:
- if (!on_AC)
- tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI;
- else if (cycle_gaming_thermal_profile)
- tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI;
- else
- tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI;
- break;
- case ACER_PREDATOR_V4_THERMAL_PROFILE_ECO:
- if (!on_AC)
- tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI;
- else if (cycle_gaming_thermal_profile)
- tp = ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET_WMI;
else
- tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI;
- break;
- default:
- return -EOPNOTSUPP;
- }
-
- status = WMI_gaming_execute_u64(
- ACER_WMID_SET_GAMING_MISC_SETTING_METHODID, tp, NULL);
+ tp = acer_predator_v4_max_perf;
- if (ACPI_FAILURE(status))
- return -EIO;
+ err = WMID_gaming_set_misc_setting(
+ ACER_WMID_MISC_SETTING_PLATFORM_PROFILE, tp);
+ if (err)
+ return err;
- /* Store non-turbo profile for turbo mode toggle*/
- if (tp != ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI)
- last_non_turbo_profile = tp;
+ /* Store last profile for toggle */
+ if (current_tp != acer_predator_v4_max_perf)
+ last_non_turbo_profile = current_tp;
- platform_profile_notify();
+ platform_profile_notify(platform_profile_device);
+ }
}
return 0;
@@ -2575,12 +2679,12 @@ static int acer_platform_probe(struct platform_device *device)
goto error_rfkill;
if (has_cap(ACER_CAP_PLATFORM_PROFILE)) {
- err = acer_platform_profile_setup();
+ err = acer_platform_profile_setup(device);
if (err)
goto error_platform_profile;
}
- if (has_cap(ACER_CAP_FAN_SPEED_READ)) {
+ if (has_cap(ACER_CAP_HWMON)) {
err = acer_wmi_hwmon_init();
if (err)
goto error_hwmon;
@@ -2589,8 +2693,6 @@ static int acer_platform_probe(struct platform_device *device)
return 0;
error_hwmon:
- if (platform_profile_support)
- platform_profile_remove();
error_platform_profile:
acer_rfkill_exit();
error_rfkill:
@@ -2611,9 +2713,6 @@ static void acer_platform_remove(struct platform_device *device)
acer_backlight_exit();
acer_rfkill_exit();
-
- if (platform_profile_support)
- platform_profile_remove();
}
#ifdef CONFIG_PM_SLEEP
@@ -2700,43 +2799,86 @@ static void __init create_debugfs(void)
&interface->debug.wmid_devices);
}
+static const enum acer_wmi_predator_v4_sensor_id acer_wmi_temp_channel_to_sensor_id[] = {
+ [0] = ACER_WMID_SENSOR_CPU_TEMPERATURE,
+ [1] = ACER_WMID_SENSOR_GPU_TEMPERATURE,
+ [2] = ACER_WMID_SENSOR_EXTERNAL_TEMPERATURE_2,
+};
+
+static const enum acer_wmi_predator_v4_sensor_id acer_wmi_fan_channel_to_sensor_id[] = {
+ [0] = ACER_WMID_SENSOR_CPU_FAN_SPEED,
+ [1] = ACER_WMID_SENSOR_GPU_FAN_SPEED,
+};
+
static umode_t acer_wmi_hwmon_is_visible(const void *data,
enum hwmon_sensor_types type, u32 attr,
int channel)
{
+ enum acer_wmi_predator_v4_sensor_id sensor_id;
+ const u64 *supported_sensors = data;
+
switch (type) {
+ case hwmon_temp:
+ sensor_id = acer_wmi_temp_channel_to_sensor_id[channel];
+ break;
case hwmon_fan:
- if (acer_get_fan_speed(channel) >= 0)
- return 0444;
+ sensor_id = acer_wmi_fan_channel_to_sensor_id[channel];
break;
default:
return 0;
}
+ if (*supported_sensors & BIT(sensor_id - 1))
+ return 0444;
+
return 0;
}
static int acer_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
+ u64 command = ACER_WMID_CMD_GET_PREDATOR_V4_SENSOR_READING;
+ u64 result;
int ret;
switch (type) {
+ case hwmon_temp:
+ command |= FIELD_PREP(ACER_PREDATOR_V4_SENSOR_INDEX_BIT_MASK,
+ acer_wmi_temp_channel_to_sensor_id[channel]);
+
+ ret = WMID_gaming_get_sys_info(command, &result);
+ if (ret < 0)
+ return ret;
+
+ result = FIELD_GET(ACER_PREDATOR_V4_SENSOR_READING_BIT_MASK, result);
+ *val = result * MILLIDEGREE_PER_DEGREE;
+ return 0;
case hwmon_fan:
- ret = acer_get_fan_speed(channel);
+ command |= FIELD_PREP(ACER_PREDATOR_V4_SENSOR_INDEX_BIT_MASK,
+ acer_wmi_fan_channel_to_sensor_id[channel]);
+
+ ret = WMID_gaming_get_sys_info(command, &result);
if (ret < 0)
return ret;
- *val = ret;
- break;
+
+ *val = FIELD_GET(ACER_PREDATOR_V4_SENSOR_READING_BIT_MASK, result);
+ return 0;
default:
return -EOPNOTSUPP;
}
-
- return 0;
}
static const struct hwmon_channel_info *const acer_wmi_hwmon_info[] = {
- HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT), NULL
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT,
+ HWMON_T_INPUT,
+ HWMON_T_INPUT
+ ),
+ HWMON_CHANNEL_INFO(fan,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT
+ ),
+ NULL
};
static const struct hwmon_ops acer_wmi_hwmon_ops = {
@@ -2753,9 +2895,20 @@ static int acer_wmi_hwmon_init(void)
{
struct device *dev = &acer_platform_device->dev;
struct device *hwmon;
+ u64 result;
+ int ret;
+
+ ret = WMID_gaming_get_sys_info(ACER_WMID_CMD_GET_PREDATOR_V4_SUPPORTED_SENSORS, &result);
+ if (ret < 0)
+ return ret;
+
+ /* Return early if no sensors are available */
+ supported_sensors = FIELD_GET(ACER_PREDATOR_V4_SUPPORTED_SENSORS_BIT_MASK, result);
+ if (!supported_sensors)
+ return 0;
hwmon = devm_hwmon_device_register_with_info(dev, "acer",
- &acer_platform_driver,
+ &supported_sensors,
&acer_wmi_hwmon_chip_info,
NULL);
diff --git a/drivers/platform/x86/amd/Makefile b/drivers/platform/x86/amd/Makefile
index 56f62fc9c97b..c6c40bdcbded 100644
--- a/drivers/platform/x86/amd/Makefile
+++ b/drivers/platform/x86/amd/Makefile
@@ -5,7 +5,7 @@
#
obj-$(CONFIG_AMD_3D_VCACHE) += amd_3d_vcache.o
-amd_3d_vcache-objs := x3d_vcache.o
+amd_3d_vcache-y := x3d_vcache.o
obj-$(CONFIG_AMD_PMC) += pmc/
obj-$(CONFIG_AMD_HSMP) += hsmp/
obj-$(CONFIG_AMD_PMF) += pmf/
diff --git a/drivers/platform/x86/amd/hsmp/Kconfig b/drivers/platform/x86/amd/hsmp/Kconfig
index 7d10d4462a45..d6f7a62d55b5 100644
--- a/drivers/platform/x86/amd/hsmp/Kconfig
+++ b/drivers/platform/x86/amd/hsmp/Kconfig
@@ -7,7 +7,7 @@ config AMD_HSMP
tristate
menu "AMD HSMP Driver"
- depends on AMD_NB || COMPILE_TEST
+ depends on AMD_NODE || COMPILE_TEST
config AMD_HSMP_ACPI
tristate "AMD HSMP ACPI device driver"
diff --git a/drivers/platform/x86/amd/hsmp/Makefile b/drivers/platform/x86/amd/hsmp/Makefile
index 3175d8885e87..0759bbcd13f6 100644
--- a/drivers/platform/x86/amd/hsmp/Makefile
+++ b/drivers/platform/x86/amd/hsmp/Makefile
@@ -5,8 +5,8 @@
#
obj-$(CONFIG_AMD_HSMP) += hsmp_common.o
-hsmp_common-objs := hsmp.o
+hsmp_common-y := hsmp.o
obj-$(CONFIG_AMD_HSMP_PLAT) += amd_hsmp.o
-amd_hsmp-objs := plat.o
+amd_hsmp-y := plat.o
obj-$(CONFIG_AMD_HSMP_ACPI) += hsmp_acpi.o
-hsmp_acpi-objs := acpi.o
+hsmp_acpi-y := acpi.o
diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/x86/amd/hsmp/acpi.c
index e981d45e1c12..eaae044e4f82 100644
--- a/drivers/platform/x86/amd/hsmp/acpi.c
+++ b/drivers/platform/x86/amd/hsmp/acpi.c
@@ -10,7 +10,6 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <asm/amd_hsmp.h>
-#include <asm/amd_nb.h>
#include <linux/acpi.h>
#include <linux/device.h>
@@ -24,11 +23,12 @@
#include <uapi/asm-generic/errno-base.h>
+#include <asm/amd_node.h>
+
#include "hsmp.h"
-#define DRIVER_NAME "amd_hsmp"
+#define DRIVER_NAME "hsmp_acpi"
#define DRIVER_VERSION "2.3"
-#define ACPI_HSMP_DEVICE_HID "AMDI0097"
/* These are the strings specified in ACPI table */
#define MSG_IDOFF_STR "MsgIdOffset"
@@ -226,7 +226,7 @@ static int hsmp_parse_acpi_table(struct device *dev, u16 sock_ind)
}
static ssize_t hsmp_metric_tbl_acpi_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr, char *buf,
+ const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
@@ -285,19 +285,19 @@ static int init_acpi(struct device *dev)
return ret;
}
-static struct bin_attribute hsmp_metric_tbl_attr = {
+static const struct bin_attribute hsmp_metric_tbl_attr = {
.attr = { .name = HSMP_METRICS_TABLE_NAME, .mode = 0444},
- .read = hsmp_metric_tbl_acpi_read,
+ .read_new = hsmp_metric_tbl_acpi_read,
.size = sizeof(struct hsmp_metric_table),
};
-static struct bin_attribute *hsmp_attr_list[] = {
+static const struct bin_attribute *hsmp_attr_list[] = {
&hsmp_metric_tbl_attr,
NULL
};
-static struct attribute_group hsmp_attr_grp = {
- .bin_attrs = hsmp_attr_list,
+static const struct attribute_group hsmp_attr_grp = {
+ .bin_attrs_new = hsmp_attr_list,
.is_bin_visible = hsmp_is_sock_attr_visible,
};
@@ -321,8 +321,8 @@ static int hsmp_acpi_probe(struct platform_device *pdev)
return -ENOMEM;
if (!hsmp_pdev->is_probed) {
- hsmp_pdev->num_sockets = amd_nb_num();
- if (hsmp_pdev->num_sockets == 0 || hsmp_pdev->num_sockets > MAX_AMD_SOCKETS)
+ hsmp_pdev->num_sockets = amd_num_nodes();
+ if (hsmp_pdev->num_sockets == 0 || hsmp_pdev->num_sockets > MAX_AMD_NUM_NODES)
return -ENODEV;
hsmp_pdev->sock = devm_kcalloc(&pdev->dev, hsmp_pdev->num_sockets,
diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/x86/amd/hsmp/hsmp.c
index 227b4ad4a51a..a3ac09a90de4 100644
--- a/drivers/platform/x86/amd/hsmp/hsmp.c
+++ b/drivers/platform/x86/amd/hsmp/hsmp.c
@@ -8,7 +8,6 @@
*/
#include <asm/amd_hsmp.h>
-#include <asm/amd_nb.h>
#include <linux/acpi.h>
#include <linux/delay.h>
@@ -33,7 +32,13 @@
#define HSMP_WR true
#define HSMP_RD false
-#define DRIVER_VERSION "2.3"
+#define DRIVER_VERSION "2.4"
+
+/*
+ * When same message numbers are used for both GET and SET operation,
+ * bit:31 indicates whether its SET or GET operation.
+ */
+#define CHECK_GET_BIT BIT(31)
static struct hsmp_plat_device hsmp_pdev;
@@ -167,11 +172,28 @@ static int validate_message(struct hsmp_message *msg)
if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_RSVD)
return -ENOMSG;
- /* num_args and response_sz against the HSMP spec */
- if (msg->num_args != hsmp_msg_desc_table[msg->msg_id].num_args ||
- msg->response_sz != hsmp_msg_desc_table[msg->msg_id].response_sz)
+ /*
+ * num_args passed by user should match the num_args specified in
+ * message description table.
+ */
+ if (msg->num_args != hsmp_msg_desc_table[msg->msg_id].num_args)
return -EINVAL;
+ /*
+ * Some older HSMP SET messages are updated to add GET in the same message.
+ * In these messages, GET returns the current value and SET also returns
+ * the successfully set value. To support this GET and SET in same message
+ * while maintaining backward compatibility for the HSMP users,
+ * hsmp_msg_desc_table[] indicates only maximum allowed response_sz.
+ */
+ if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_SET_GET) {
+ if (msg->response_sz > hsmp_msg_desc_table[msg->msg_id].response_sz)
+ return -EINVAL;
+ } else {
+ /* only HSMP_SET or HSMP_GET messages go through this strict check */
+ if (msg->response_sz != hsmp_msg_desc_table[msg->msg_id].response_sz)
+ return -EINVAL;
+ }
return 0;
}
@@ -239,6 +261,18 @@ int hsmp_test(u16 sock_ind, u32 value)
}
EXPORT_SYMBOL_NS_GPL(hsmp_test, "AMD_HSMP");
+static bool is_get_msg(struct hsmp_message *msg)
+{
+ if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_GET)
+ return true;
+
+ if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_SET_GET &&
+ (msg->args[0] & CHECK_GET_BIT))
+ return true;
+
+ return false;
+}
+
long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
int __user *arguser = (int __user *)arg;
@@ -261,7 +295,7 @@ long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
* Device is opened in O_WRONLY mode
* Execute only set/configure commands
*/
- if (hsmp_msg_desc_table[msg.msg_id].type != HSMP_SET)
+ if (is_get_msg(&msg))
return -EPERM;
break;
case FMODE_READ:
@@ -269,7 +303,7 @@ long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
* Device is opened in O_RDONLY mode
* Execute only get/monitor commands
*/
- if (hsmp_msg_desc_table[msg.msg_id].type != HSMP_GET)
+ if (!is_get_msg(&msg))
return -EPERM;
break;
case FMODE_READ | FMODE_WRITE:
diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/x86/amd/hsmp/hsmp.h
index e852f0a947e4..d58d4f0c20d5 100644
--- a/drivers/platform/x86/amd/hsmp/hsmp.h
+++ b/drivers/platform/x86/amd/hsmp/hsmp.h
@@ -21,10 +21,9 @@
#define HSMP_ATTR_GRP_NAME_SIZE 10
-#define MAX_AMD_SOCKETS 8
-
#define HSMP_CDEV_NAME "hsmp_cdev"
#define HSMP_DEVNODE_NAME "hsmp"
+#define ACPI_HSMP_DEVICE_HID "AMDI0097"
struct hsmp_mbaddr_info {
u32 base_addr;
@@ -41,7 +40,6 @@ struct hsmp_socket {
void __iomem *virt_base_addr;
struct semaphore hsmp_sem;
char name[HSMP_ATTR_GRP_NAME_SIZE];
- struct pci_dev *root;
struct device *dev;
u16 sock_ind;
int (*amd_hsmp_rdwr)(struct hsmp_socket *sock, u32 off, u32 *val, bool rw);
diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c
index a61f815c9f80..81931e808bbc 100644
--- a/drivers/platform/x86/amd/hsmp/plat.c
+++ b/drivers/platform/x86/amd/hsmp/plat.c
@@ -10,14 +10,17 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <asm/amd_hsmp.h>
-#include <asm/amd_nb.h>
+#include <linux/acpi.h>
+#include <linux/build_bug.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/sysfs.h>
+#include <asm/amd_node.h>
+
#include "hsmp.h"
#define DRIVER_NAME "amd_hsmp"
@@ -34,32 +37,16 @@
#define SMN_HSMP_MSG_RESP 0x0010980
#define SMN_HSMP_MSG_DATA 0x00109E0
-#define HSMP_INDEX_REG 0xc4
-#define HSMP_DATA_REG 0xc8
-
static struct hsmp_plat_device *hsmp_pdev;
static int amd_hsmp_pci_rdwr(struct hsmp_socket *sock, u32 offset,
u32 *value, bool write)
{
- int ret;
-
- if (!sock->root)
- return -ENODEV;
-
- ret = pci_write_config_dword(sock->root, HSMP_INDEX_REG,
- sock->mbinfo.base_addr + offset);
- if (ret)
- return ret;
-
- ret = (write ? pci_write_config_dword(sock->root, HSMP_DATA_REG, *value)
- : pci_read_config_dword(sock->root, HSMP_DATA_REG, value));
-
- return ret;
+ return amd_smn_hsmp_rdwr(sock->sock_ind, sock->mbinfo.base_addr + offset, value, write);
}
static ssize_t hsmp_metric_tbl_plat_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr, char *buf,
+ const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct hsmp_socket *sock;
@@ -95,15 +82,20 @@ static umode_t hsmp_is_sock_attr_visible(struct kobject *kobj,
* Static array of 8 + 1(for NULL) elements is created below
* to create sysfs groups for sockets.
* is_bin_visible function is used to show / hide the necessary groups.
+ *
+ * Validate the maximum number against MAX_AMD_NUM_NODES. If this changes,
+ * then the attributes and groups below must be adjusted.
*/
+static_assert(MAX_AMD_NUM_NODES == 8);
+
#define HSMP_BIN_ATTR(index, _list) \
-static struct bin_attribute attr##index = { \
+static const struct bin_attribute attr##index = { \
.attr = { .name = HSMP_METRICS_TABLE_NAME, .mode = 0444}, \
.private = (void *)index, \
- .read = hsmp_metric_tbl_plat_read, \
+ .read_new = hsmp_metric_tbl_plat_read, \
.size = sizeof(struct hsmp_metric_table), \
}; \
-static struct bin_attribute _list[] = { \
+static const struct bin_attribute _list[] = { \
&attr##index, \
NULL \
}
@@ -118,8 +110,8 @@ HSMP_BIN_ATTR(6, *sock6_attr_list);
HSMP_BIN_ATTR(7, *sock7_attr_list);
#define HSMP_BIN_ATTR_GRP(index, _list, _name) \
-static struct attribute_group sock##index##_attr_grp = { \
- .bin_attrs = _list, \
+static const struct attribute_group sock##index##_attr_grp = { \
+ .bin_attrs_new = _list, \
.is_bin_visible = hsmp_is_sock_attr_visible, \
.name = #_name, \
}
@@ -159,10 +151,7 @@ static int init_platform_device(struct device *dev)
int ret, i;
for (i = 0; i < hsmp_pdev->num_sockets; i++) {
- if (!node_to_amd_nb(i))
- return -ENODEV;
sock = &hsmp_pdev->sock[i];
- sock->root = node_to_amd_nb(i)->root;
sock->sock_ind = i;
sock->dev = dev;
sock->mbinfo.base_addr = SMN_HSMP_BASE;
@@ -278,7 +267,7 @@ static bool legacy_hsmp_support(void)
}
case 0x1A:
switch (boot_cpu_data.x86_model) {
- case 0x00 ... 0x1F:
+ case 0x00 ... 0x0F:
return true;
default:
return false;
@@ -300,16 +289,19 @@ static int __init hsmp_plt_init(void)
return ret;
}
+ if (acpi_dev_present(ACPI_HSMP_DEVICE_HID, NULL, -1))
+ return -ENODEV;
+
hsmp_pdev = get_hsmp_pdev();
if (!hsmp_pdev)
return -ENOMEM;
/*
- * amd_nb_num() returns number of SMN/DF interfaces present in the system
+ * amd_num_nodes() returns number of SMN/DF interfaces present in the system
* if we have N SMN/DF interfaces that ideally means N sockets
*/
- hsmp_pdev->num_sockets = amd_nb_num();
- if (hsmp_pdev->num_sockets == 0 || hsmp_pdev->num_sockets > MAX_AMD_SOCKETS)
+ hsmp_pdev->num_sockets = amd_num_nodes();
+ if (hsmp_pdev->num_sockets == 0 || hsmp_pdev->num_sockets > MAX_AMD_NUM_NODES)
return ret;
ret = platform_driver_register(&amd_hsmp_driver);
diff --git a/drivers/platform/x86/amd/pmc/Kconfig b/drivers/platform/x86/amd/pmc/Kconfig
index 94f9563d8be7..eeffdafd686e 100644
--- a/drivers/platform/x86/amd/pmc/Kconfig
+++ b/drivers/platform/x86/amd/pmc/Kconfig
@@ -5,7 +5,7 @@
config AMD_PMC
tristate "AMD SoC PMC driver"
- depends on ACPI && PCI && RTC_CLASS && AMD_NB
+ depends on ACPI && PCI && RTC_CLASS && AMD_NODE
depends on SUSPEND
select SERIO
help
diff --git a/drivers/platform/x86/amd/pmc/Makefile b/drivers/platform/x86/amd/pmc/Makefile
index f1d9ab19d24c..bb6905c4cae9 100644
--- a/drivers/platform/x86/amd/pmc/Makefile
+++ b/drivers/platform/x86/amd/pmc/Makefile
@@ -4,6 +4,6 @@
# AMD Power Management Controller Driver
#
-amd-pmc-objs := pmc.o pmc-quirks.o
-obj-$(CONFIG_AMD_PMC) += amd-pmc.o
-amd-pmc-$(CONFIG_AMD_MP2_STB) += mp2_stb.o
+obj-$(CONFIG_AMD_PMC) += amd-pmc.o
+amd-pmc-y := pmc.o pmc-quirks.o mp1_stb.o
+amd-pmc-$(CONFIG_AMD_MP2_STB) += mp2_stb.o
diff --git a/drivers/platform/x86/amd/pmc/mp1_stb.c b/drivers/platform/x86/amd/pmc/mp1_stb.c
new file mode 100644
index 000000000000..c005f00988f7
--- /dev/null
+++ b/drivers/platform/x86/amd/pmc/mp1_stb.c
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AMD MP1 Smart Trace Buffer (STB) Layer
+ *
+ * Copyright (c) 2024, Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
+ * Sanket Goswami <Sanket.Goswami@amd.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <asm/amd_nb.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+
+#include "pmc.h"
+
+/* STB Spill to DRAM Parameters */
+#define S2D_TELEMETRY_DRAMBYTES_MAX 0x1000000
+#define S2D_TELEMETRY_BYTES_MAX 0x100000U
+#define S2D_RSVD_RAM_SPACE 0x100000
+
+/* STB Registers */
+#define AMD_STB_PMI_0 0x03E30600
+#define AMD_PMC_STB_DUMMY_PC 0xC6000007
+
+/* STB Spill to DRAM Message Definition */
+#define STB_FORCE_FLUSH_DATA 0xCF
+#define FIFO_SIZE 4096
+
+/* STB S2D(Spill to DRAM) has different message port offset */
+#define AMD_S2D_REGISTER_MESSAGE 0xA20
+#define AMD_S2D_REGISTER_RESPONSE 0xA80
+#define AMD_S2D_REGISTER_ARGUMENT 0xA88
+
+/* STB S2D (Spill to DRAM) message port offset for 44h model */
+#define AMD_GNR_REGISTER_MESSAGE 0x524
+#define AMD_GNR_REGISTER_RESPONSE 0x570
+#define AMD_GNR_REGISTER_ARGUMENT 0xA40
+
+static bool enable_stb;
+module_param(enable_stb, bool, 0644);
+MODULE_PARM_DESC(enable_stb, "Enable the STB debug mechanism");
+
+static bool dump_custom_stb;
+module_param(dump_custom_stb, bool, 0644);
+MODULE_PARM_DESC(dump_custom_stb, "Enable to dump full STB buffer");
+
+enum s2d_arg {
+ S2D_TELEMETRY_SIZE = 0x01,
+ S2D_PHYS_ADDR_LOW,
+ S2D_PHYS_ADDR_HIGH,
+ S2D_NUM_SAMPLES,
+ S2D_DRAM_SIZE,
+};
+
+struct amd_stb_v2_data {
+ size_t size;
+ u8 data[] __counted_by(size);
+};
+
+int amd_stb_write(struct amd_pmc_dev *dev, u32 data)
+{
+ int err;
+
+ err = amd_smn_write(0, AMD_STB_PMI_0, data);
+ if (err) {
+ dev_err(dev->dev, "failed to write data in stb: 0x%X\n", AMD_STB_PMI_0);
+ return pcibios_err_to_errno(err);
+ }
+
+ return 0;
+}
+
+int amd_stb_read(struct amd_pmc_dev *dev, u32 *buf)
+{
+ int i, err;
+
+ for (i = 0; i < FIFO_SIZE; i++) {
+ err = amd_smn_read(0, AMD_STB_PMI_0, buf++);
+ if (err) {
+ dev_err(dev->dev, "error reading data from stb: 0x%X\n", AMD_STB_PMI_0);
+ return pcibios_err_to_errno(err);
+ }
+ }
+
+ return 0;
+}
+
+static int amd_stb_debugfs_open(struct inode *inode, struct file *filp)
+{
+ struct amd_pmc_dev *dev = filp->f_inode->i_private;
+ u32 size = FIFO_SIZE * sizeof(u32);
+ u32 *buf;
+ int rc;
+
+ buf = kzalloc(size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ rc = amd_stb_read(dev, buf);
+ if (rc) {
+ kfree(buf);
+ return rc;
+ }
+
+ filp->private_data = buf;
+ return rc;
+}
+
+static ssize_t amd_stb_debugfs_read(struct file *filp, char __user *buf, size_t size, loff_t *pos)
+{
+ if (!filp->private_data)
+ return -EINVAL;
+
+ return simple_read_from_buffer(buf, size, pos, filp->private_data,
+ FIFO_SIZE * sizeof(u32));
+}
+
+static int amd_stb_debugfs_release(struct inode *inode, struct file *filp)
+{
+ kfree(filp->private_data);
+ return 0;
+}
+
+static const struct file_operations amd_stb_debugfs_fops = {
+ .owner = THIS_MODULE,
+ .open = amd_stb_debugfs_open,
+ .read = amd_stb_debugfs_read,
+ .release = amd_stb_debugfs_release,
+};
+
+/* Enhanced STB Firmware Reporting Mechanism */
+static int amd_stb_handle_efr(struct file *filp)
+{
+ struct amd_pmc_dev *dev = filp->f_inode->i_private;
+ struct amd_stb_v2_data *stb_data_arr;
+ u32 fsize;
+
+ fsize = dev->dram_size - S2D_RSVD_RAM_SPACE;
+ stb_data_arr = kmalloc(struct_size(stb_data_arr, data, fsize), GFP_KERNEL);
+ if (!stb_data_arr)
+ return -ENOMEM;
+
+ stb_data_arr->size = fsize;
+ memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr, fsize);
+ filp->private_data = stb_data_arr;
+
+ return 0;
+}
+
+static int amd_stb_debugfs_open_v2(struct inode *inode, struct file *filp)
+{
+ struct amd_pmc_dev *dev = filp->f_inode->i_private;
+ u32 fsize, num_samples, val, stb_rdptr_offset = 0;
+ struct amd_stb_v2_data *stb_data_arr;
+ int ret;
+
+ /* Write dummy postcode while reading the STB buffer */
+ ret = amd_stb_write(dev, AMD_PMC_STB_DUMMY_PC);
+ if (ret)
+ dev_err(dev->dev, "error writing to STB: %d\n", ret);
+
+ /* Spill to DRAM num_samples uses separate SMU message port */
+ dev->msg_port = MSG_PORT_S2D;
+
+ ret = amd_pmc_send_cmd(dev, 0, &val, STB_FORCE_FLUSH_DATA, 1);
+ if (ret)
+ dev_dbg_once(dev->dev, "S2D force flush not supported: %d\n", ret);
+
+ /*
+ * We have a custom stb size and the PMFW is supposed to give
+ * the enhanced dram size. Note that we land here only for the
+ * platforms that support enhanced dram size reporting.
+ */
+ if (dump_custom_stb)
+ return amd_stb_handle_efr(filp);
+
+ /* Get the num_samples to calculate the last push location */
+ ret = amd_pmc_send_cmd(dev, S2D_NUM_SAMPLES, &num_samples, dev->stb_arg.s2d_msg_id, true);
+ /* Clear msg_port for other SMU operation */
+ dev->msg_port = MSG_PORT_PMC;
+ if (ret) {
+ dev_err(dev->dev, "error: S2D_NUM_SAMPLES not supported : %d\n", ret);
+ return ret;
+ }
+
+ fsize = min(num_samples, S2D_TELEMETRY_BYTES_MAX);
+ stb_data_arr = kmalloc(struct_size(stb_data_arr, data, fsize), GFP_KERNEL);
+ if (!stb_data_arr)
+ return -ENOMEM;
+
+ stb_data_arr->size = fsize;
+
+ /*
+ * Start capturing data from the last push location.
+ * This is for general cases, where the stb limits
+ * are meant for standard usage.
+ */
+ if (num_samples > S2D_TELEMETRY_BYTES_MAX) {
+ /* First read oldest data starting 1 behind last write till end of ringbuffer */
+ stb_rdptr_offset = num_samples % S2D_TELEMETRY_BYTES_MAX;
+ fsize = S2D_TELEMETRY_BYTES_MAX - stb_rdptr_offset;
+
+ memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr + stb_rdptr_offset, fsize);
+ /* Second copy the newer samples from offset 0 - last write */
+ memcpy_fromio(stb_data_arr->data + fsize, dev->stb_virt_addr, stb_rdptr_offset);
+ } else {
+ memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr, fsize);
+ }
+
+ filp->private_data = stb_data_arr;
+
+ return 0;
+}
+
+static ssize_t amd_stb_debugfs_read_v2(struct file *filp, char __user *buf, size_t size,
+ loff_t *pos)
+{
+ struct amd_stb_v2_data *data = filp->private_data;
+
+ return simple_read_from_buffer(buf, size, pos, data->data, data->size);
+}
+
+static int amd_stb_debugfs_release_v2(struct inode *inode, struct file *filp)
+{
+ kfree(filp->private_data);
+ return 0;
+}
+
+static const struct file_operations amd_stb_debugfs_fops_v2 = {
+ .owner = THIS_MODULE,
+ .open = amd_stb_debugfs_open_v2,
+ .read = amd_stb_debugfs_read_v2,
+ .release = amd_stb_debugfs_release_v2,
+};
+
+static void amd_stb_update_args(struct amd_pmc_dev *dev)
+{
+ if (cpu_feature_enabled(X86_FEATURE_ZEN5))
+ switch (boot_cpu_data.x86_model) {
+ case 0x44:
+ dev->stb_arg.msg = AMD_GNR_REGISTER_MESSAGE;
+ dev->stb_arg.arg = AMD_GNR_REGISTER_ARGUMENT;
+ dev->stb_arg.resp = AMD_GNR_REGISTER_RESPONSE;
+ return;
+ default:
+ break;
+ }
+
+ dev->stb_arg.msg = AMD_S2D_REGISTER_MESSAGE;
+ dev->stb_arg.arg = AMD_S2D_REGISTER_ARGUMENT;
+ dev->stb_arg.resp = AMD_S2D_REGISTER_RESPONSE;
+}
+
+static bool amd_is_stb_supported(struct amd_pmc_dev *dev)
+{
+ switch (dev->cpu_id) {
+ case AMD_CPU_ID_YC:
+ case AMD_CPU_ID_CB:
+ if (boot_cpu_data.x86_model == 0x44)
+ dev->stb_arg.s2d_msg_id = 0x9B;
+ else
+ dev->stb_arg.s2d_msg_id = 0xBE;
+ break;
+ case AMD_CPU_ID_PS:
+ dev->stb_arg.s2d_msg_id = 0x85;
+ break;
+ case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
+ case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT:
+ if (boot_cpu_data.x86_model == 0x70)
+ dev->stb_arg.s2d_msg_id = 0xF1;
+ else
+ dev->stb_arg.s2d_msg_id = 0xDE;
+ break;
+ default:
+ return false;
+ }
+
+ amd_stb_update_args(dev);
+ return true;
+}
+
+int amd_stb_s2d_init(struct amd_pmc_dev *dev)
+{
+ u32 phys_addr_low, phys_addr_hi;
+ u64 stb_phys_addr;
+ u32 size = 0;
+ int ret;
+
+ if (!enable_stb)
+ return 0;
+
+ if (amd_is_stb_supported(dev)) {
+ debugfs_create_file("stb_read", 0644, dev->dbgfs_dir, dev,
+ &amd_stb_debugfs_fops_v2);
+ } else {
+ debugfs_create_file("stb_read", 0644, dev->dbgfs_dir, dev,
+ &amd_stb_debugfs_fops);
+ return 0;
+ }
+
+ /* Spill to DRAM feature uses separate SMU message port */
+ dev->msg_port = MSG_PORT_S2D;
+
+ amd_pmc_send_cmd(dev, S2D_TELEMETRY_SIZE, &size, dev->stb_arg.s2d_msg_id, true);
+ if (size != S2D_TELEMETRY_BYTES_MAX)
+ return -EIO;
+
+ /* Get DRAM size */
+ ret = amd_pmc_send_cmd(dev, S2D_DRAM_SIZE, &dev->dram_size, dev->stb_arg.s2d_msg_id, true);
+ if (ret || !dev->dram_size)
+ dev->dram_size = S2D_TELEMETRY_DRAMBYTES_MAX;
+
+ /* Get STB DRAM address */
+ amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_LOW, &phys_addr_low, dev->stb_arg.s2d_msg_id, true);
+ amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_HIGH, &phys_addr_hi, dev->stb_arg.s2d_msg_id, true);
+
+ stb_phys_addr = ((u64)phys_addr_hi << 32 | phys_addr_low);
+
+ /* Clear msg_port for other SMU operation */
+ dev->msg_port = MSG_PORT_PMC;
+
+ dev->stb_virt_addr = devm_ioremap(dev->dev, stb_phys_addr, dev->dram_size);
+ if (!dev->stb_virt_addr)
+ return -ENOMEM;
+
+ return 0;
+}
diff --git a/drivers/platform/x86/amd/pmc/pmc-quirks.c b/drivers/platform/x86/amd/pmc/pmc-quirks.c
index b4f49720c87f..2e3f6fc67c56 100644
--- a/drivers/platform/x86/amd/pmc/pmc-quirks.c
+++ b/drivers/platform/x86/amd/pmc/pmc-quirks.c
@@ -217,6 +217,13 @@ static const struct dmi_system_id fwbug_list[] = {
DMI_MATCH(DMI_BIOS_VERSION, "03.05"),
}
},
+ {
+ .ident = "MECHREVO Wujie 14X (GX4HRXL)",
+ .driver_data = &quirk_spurious_8042,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "WUJIE14-GX4HRXL"),
+ }
+ },
{}
};
diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c
index a254debb9256..f45525bbd154 100644
--- a/drivers/platform/x86/amd/pmc/pmc.c
+++ b/drivers/platform/x86/amd/pmc/pmc.c
@@ -10,8 +10,8 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <asm/amd_nb.h>
#include <linux/acpi.h>
+#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/debugfs.h>
@@ -28,99 +28,36 @@
#include <linux/seq_file.h>
#include <linux/uaccess.h>
-#include "pmc.h"
-
-/* SMU communication registers */
-#define AMD_PMC_REGISTER_RESPONSE 0x980
-#define AMD_PMC_REGISTER_ARGUMENT 0x9BC
-
-/* PMC Scratch Registers */
-#define AMD_PMC_SCRATCH_REG_CZN 0x94
-#define AMD_PMC_SCRATCH_REG_YC 0xD14
-#define AMD_PMC_SCRATCH_REG_1AH 0xF14
-
-/* STB Registers */
-#define AMD_PMC_STB_PMI_0 0x03E30600
-#define AMD_PMC_STB_S2IDLE_PREPARE 0xC6000001
-#define AMD_PMC_STB_S2IDLE_RESTORE 0xC6000002
-#define AMD_PMC_STB_S2IDLE_CHECK 0xC6000003
-#define AMD_PMC_STB_DUMMY_PC 0xC6000007
-
-/* STB S2D(Spill to DRAM) has different message port offset */
-#define AMD_S2D_REGISTER_MESSAGE 0xA20
-#define AMD_S2D_REGISTER_RESPONSE 0xA80
-#define AMD_S2D_REGISTER_ARGUMENT 0xA88
-
-/* STB Spill to DRAM Parameters */
-#define S2D_TELEMETRY_BYTES_MAX 0x100000U
-#define S2D_RSVD_RAM_SPACE 0x100000
-#define S2D_TELEMETRY_DRAMBYTES_MAX 0x1000000
-
-/* STB Spill to DRAM Message Definition */
-#define STB_FORCE_FLUSH_DATA 0xCF
-
-/* Base address of SMU for mapping physical address to virtual address */
-#define AMD_PMC_MAPPING_SIZE 0x01000
-#define AMD_PMC_BASE_ADDR_OFFSET 0x10000
-#define AMD_PMC_BASE_ADDR_LO 0x13B102E8
-#define AMD_PMC_BASE_ADDR_HI 0x13B102EC
-#define AMD_PMC_BASE_ADDR_LO_MASK GENMASK(15, 0)
-#define AMD_PMC_BASE_ADDR_HI_MASK GENMASK(31, 20)
-
-/* SMU Response Codes */
-#define AMD_PMC_RESULT_OK 0x01
-#define AMD_PMC_RESULT_CMD_REJECT_BUSY 0xFC
-#define AMD_PMC_RESULT_CMD_REJECT_PREREQ 0xFD
-#define AMD_PMC_RESULT_CMD_UNKNOWN 0xFE
-#define AMD_PMC_RESULT_FAILED 0xFF
-
-/* FCH SSC Registers */
-#define FCH_S0I3_ENTRY_TIME_L_OFFSET 0x30
-#define FCH_S0I3_ENTRY_TIME_H_OFFSET 0x34
-#define FCH_S0I3_EXIT_TIME_L_OFFSET 0x38
-#define FCH_S0I3_EXIT_TIME_H_OFFSET 0x3C
-#define FCH_SSC_MAPPING_SIZE 0x800
-#define FCH_BASE_PHY_ADDR_LOW 0xFED81100
-#define FCH_BASE_PHY_ADDR_HIGH 0x00000000
-
-/* SMU Message Definations */
-#define SMU_MSG_GETSMUVERSION 0x02
-#define SMU_MSG_LOG_GETDRAM_ADDR_HI 0x04
-#define SMU_MSG_LOG_GETDRAM_ADDR_LO 0x05
-#define SMU_MSG_LOG_START 0x06
-#define SMU_MSG_LOG_RESET 0x07
-#define SMU_MSG_LOG_DUMP_DATA 0x08
-#define SMU_MSG_GET_SUP_CONSTRAINTS 0x09
-
-#define PMC_MSG_DELAY_MIN_US 50
-#define RESPONSE_REGISTER_LOOP_MAX 20000
-
-#define DELAY_MIN_US 2000
-#define DELAY_MAX_US 3000
-#define FIFO_SIZE 4096
-
-enum amd_pmc_def {
- MSG_TEST = 0x01,
- MSG_OS_HINT_PCO,
- MSG_OS_HINT_RN,
-};
-
-enum s2d_arg {
- S2D_TELEMETRY_SIZE = 0x01,
- S2D_PHYS_ADDR_LOW,
- S2D_PHYS_ADDR_HIGH,
- S2D_NUM_SAMPLES,
- S2D_DRAM_SIZE,
-};
+#include <asm/amd_node.h>
-struct amd_pmc_stb_v2_data {
- size_t size;
- u8 data[] __counted_by(size);
-};
+#include "pmc.h"
-struct amd_pmc_bit_map {
- const char *name;
- u32 bit_mask;
+static const struct amd_pmc_bit_map soc15_ip_blk_v2[] = {
+ {"DISPLAY", BIT(0)},
+ {"CPU", BIT(1)},
+ {"GFX", BIT(2)},
+ {"VDD", BIT(3)},
+ {"VDD_CCX", BIT(4)},
+ {"ACP", BIT(5)},
+ {"VCN_0", BIT(6)},
+ {"VCN_1", BIT(7)},
+ {"ISP", BIT(8)},
+ {"NBIO", BIT(9)},
+ {"DF", BIT(10)},
+ {"USB3_0", BIT(11)},
+ {"USB3_1", BIT(12)},
+ {"LAPIC", BIT(13)},
+ {"USB3_2", BIT(14)},
+ {"USB4_RT0", BIT(15)},
+ {"USB4_RT1", BIT(16)},
+ {"USB4_0", BIT(17)},
+ {"USB4_1", BIT(18)},
+ {"MPM", BIT(19)},
+ {"JPEG_0", BIT(20)},
+ {"JPEG_1", BIT(21)},
+ {"IPU", BIT(22)},
+ {"UMSCH", BIT(23)},
+ {"VPE", BIT(24)},
};
static const struct amd_pmc_bit_map soc15_ip_blk[] = {
@@ -146,25 +83,13 @@ static const struct amd_pmc_bit_map soc15_ip_blk[] = {
{"IPU", BIT(19)},
{"UMSCH", BIT(20)},
{"VPE", BIT(21)},
- {}
};
-static bool enable_stb;
-module_param(enable_stb, bool, 0644);
-MODULE_PARM_DESC(enable_stb, "Enable the STB debug mechanism");
-
static bool disable_workarounds;
module_param(disable_workarounds, bool, 0644);
MODULE_PARM_DESC(disable_workarounds, "Disable workarounds for platform bugs");
-static bool dump_custom_stb;
-module_param(dump_custom_stb, bool, 0644);
-MODULE_PARM_DESC(dump_custom_stb, "Enable to dump full STB buffer");
-
static struct amd_pmc_dev pmc;
-static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret);
-static int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf);
-static int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data);
static inline u32 amd_pmc_reg_read(struct amd_pmc_dev *dev, int reg_offset)
{
@@ -176,172 +101,6 @@ static inline void amd_pmc_reg_write(struct amd_pmc_dev *dev, int reg_offset, u3
iowrite32(val, dev->regbase + reg_offset);
}
-struct smu_metrics {
- u32 table_version;
- u32 hint_count;
- u32 s0i3_last_entry_status;
- u32 timein_s0i2;
- u64 timeentering_s0i3_lastcapture;
- u64 timeentering_s0i3_totaltime;
- u64 timeto_resume_to_os_lastcapture;
- u64 timeto_resume_to_os_totaltime;
- u64 timein_s0i3_lastcapture;
- u64 timein_s0i3_totaltime;
- u64 timein_swdrips_lastcapture;
- u64 timein_swdrips_totaltime;
- u64 timecondition_notmet_lastcapture[32];
- u64 timecondition_notmet_totaltime[32];
-} __packed;
-
-static int amd_pmc_stb_debugfs_open(struct inode *inode, struct file *filp)
-{
- struct amd_pmc_dev *dev = filp->f_inode->i_private;
- u32 size = FIFO_SIZE * sizeof(u32);
- u32 *buf;
- int rc;
-
- buf = kzalloc(size, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- rc = amd_pmc_read_stb(dev, buf);
- if (rc) {
- kfree(buf);
- return rc;
- }
-
- filp->private_data = buf;
- return rc;
-}
-
-static ssize_t amd_pmc_stb_debugfs_read(struct file *filp, char __user *buf, size_t size,
- loff_t *pos)
-{
- if (!filp->private_data)
- return -EINVAL;
-
- return simple_read_from_buffer(buf, size, pos, filp->private_data,
- FIFO_SIZE * sizeof(u32));
-}
-
-static int amd_pmc_stb_debugfs_release(struct inode *inode, struct file *filp)
-{
- kfree(filp->private_data);
- return 0;
-}
-
-static const struct file_operations amd_pmc_stb_debugfs_fops = {
- .owner = THIS_MODULE,
- .open = amd_pmc_stb_debugfs_open,
- .read = amd_pmc_stb_debugfs_read,
- .release = amd_pmc_stb_debugfs_release,
-};
-
-/* Enhanced STB Firmware Reporting Mechanism */
-static int amd_pmc_stb_handle_efr(struct file *filp)
-{
- struct amd_pmc_dev *dev = filp->f_inode->i_private;
- struct amd_pmc_stb_v2_data *stb_data_arr;
- u32 fsize;
-
- fsize = dev->dram_size - S2D_RSVD_RAM_SPACE;
- stb_data_arr = kmalloc(struct_size(stb_data_arr, data, fsize), GFP_KERNEL);
- if (!stb_data_arr)
- return -ENOMEM;
-
- stb_data_arr->size = fsize;
- memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr, fsize);
- filp->private_data = stb_data_arr;
-
- return 0;
-}
-
-static int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp)
-{
- struct amd_pmc_dev *dev = filp->f_inode->i_private;
- u32 fsize, num_samples, val, stb_rdptr_offset = 0;
- struct amd_pmc_stb_v2_data *stb_data_arr;
- int ret;
-
- /* Write dummy postcode while reading the STB buffer */
- ret = amd_pmc_write_stb(dev, AMD_PMC_STB_DUMMY_PC);
- if (ret)
- dev_err(dev->dev, "error writing to STB: %d\n", ret);
-
- /* Spill to DRAM num_samples uses separate SMU message port */
- dev->msg_port = 1;
-
- ret = amd_pmc_send_cmd(dev, 0, &val, STB_FORCE_FLUSH_DATA, 1);
- if (ret)
- dev_dbg_once(dev->dev, "S2D force flush not supported: %d\n", ret);
-
- /*
- * We have a custom stb size and the PMFW is supposed to give
- * the enhanced dram size. Note that we land here only for the
- * platforms that support enhanced dram size reporting.
- */
- if (dump_custom_stb)
- return amd_pmc_stb_handle_efr(filp);
-
- /* Get the num_samples to calculate the last push location */
- ret = amd_pmc_send_cmd(dev, S2D_NUM_SAMPLES, &num_samples, dev->s2d_msg_id, true);
- /* Clear msg_port for other SMU operation */
- dev->msg_port = 0;
- if (ret) {
- dev_err(dev->dev, "error: S2D_NUM_SAMPLES not supported : %d\n", ret);
- return ret;
- }
-
- fsize = min(num_samples, S2D_TELEMETRY_BYTES_MAX);
- stb_data_arr = kmalloc(struct_size(stb_data_arr, data, fsize), GFP_KERNEL);
- if (!stb_data_arr)
- return -ENOMEM;
-
- stb_data_arr->size = fsize;
-
- /*
- * Start capturing data from the last push location.
- * This is for general cases, where the stb limits
- * are meant for standard usage.
- */
- if (num_samples > S2D_TELEMETRY_BYTES_MAX) {
- /* First read oldest data starting 1 behind last write till end of ringbuffer */
- stb_rdptr_offset = num_samples % S2D_TELEMETRY_BYTES_MAX;
- fsize = S2D_TELEMETRY_BYTES_MAX - stb_rdptr_offset;
-
- memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr + stb_rdptr_offset, fsize);
- /* Second copy the newer samples from offset 0 - last write */
- memcpy_fromio(stb_data_arr->data + fsize, dev->stb_virt_addr, stb_rdptr_offset);
- } else {
- memcpy_fromio(stb_data_arr->data, dev->stb_virt_addr, fsize);
- }
-
- filp->private_data = stb_data_arr;
-
- return 0;
-}
-
-static ssize_t amd_pmc_stb_debugfs_read_v2(struct file *filp, char __user *buf, size_t size,
- loff_t *pos)
-{
- struct amd_pmc_stb_v2_data *data = filp->private_data;
-
- return simple_read_from_buffer(buf, size, pos, data->data, data->size);
-}
-
-static int amd_pmc_stb_debugfs_release_v2(struct inode *inode, struct file *filp)
-{
- kfree(filp->private_data);
- return 0;
-}
-
-static const struct file_operations amd_pmc_stb_debugfs_fops_v2 = {
- .owner = THIS_MODULE,
- .open = amd_pmc_stb_debugfs_open_v2,
- .read = amd_pmc_stb_debugfs_read_v2,
- .release = amd_pmc_stb_debugfs_release_v2,
-};
-
static void amd_pmc_get_ip_info(struct amd_pmc_dev *dev)
{
switch (dev->cpu_id) {
@@ -350,18 +109,23 @@ static void amd_pmc_get_ip_info(struct amd_pmc_dev *dev)
case AMD_CPU_ID_YC:
case AMD_CPU_ID_CB:
dev->num_ips = 12;
- dev->s2d_msg_id = 0xBE;
+ dev->ips_ptr = soc15_ip_blk;
dev->smu_msg = 0x538;
break;
case AMD_CPU_ID_PS:
dev->num_ips = 21;
- dev->s2d_msg_id = 0x85;
+ dev->ips_ptr = soc15_ip_blk;
dev->smu_msg = 0x538;
break;
case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT:
- dev->num_ips = 22;
- dev->s2d_msg_id = 0xDE;
+ if (boot_cpu_data.x86_model == 0x70) {
+ dev->num_ips = ARRAY_SIZE(soc15_ip_blk_v2);
+ dev->ips_ptr = soc15_ip_blk_v2;
+ } else {
+ dev->num_ips = ARRAY_SIZE(soc15_ip_blk);
+ dev->ips_ptr = soc15_ip_blk;
+ }
dev->smu_msg = 0x938;
break;
}
@@ -393,6 +157,8 @@ static int amd_pmc_setup_smu_logging(struct amd_pmc_dev *dev)
return -ENOMEM;
}
+ memset_io(dev->smu_virt_addr, 0, sizeof(struct smu_metrics));
+
/* Start the logging */
amd_pmc_send_cmd(dev, 0, NULL, SMU_MSG_LOG_RESET, false);
amd_pmc_send_cmd(dev, 0, NULL, SMU_MSG_LOG_START, false);
@@ -402,11 +168,12 @@ static int amd_pmc_setup_smu_logging(struct amd_pmc_dev *dev)
static int get_metrics_table(struct amd_pmc_dev *pdev, struct smu_metrics *table)
{
- if (!pdev->smu_virt_addr) {
- int ret = amd_pmc_setup_smu_logging(pdev);
+ int rc;
- if (ret)
- return ret;
+ if (!pdev->smu_virt_addr) {
+ rc = amd_pmc_setup_smu_logging(pdev);
+ if (rc)
+ return rc;
}
if (pdev->cpu_id == AMD_CPU_ID_PCO)
@@ -455,10 +222,10 @@ static ssize_t smu_fw_version_show(struct device *d, struct device_attribute *at
char *buf)
{
struct amd_pmc_dev *dev = dev_get_drvdata(d);
+ int rc;
if (!dev->major) {
- int rc = amd_pmc_get_smu_version(dev);
-
+ rc = amd_pmc_get_smu_version(dev);
if (rc)
return rc;
}
@@ -469,10 +236,10 @@ static ssize_t smu_program_show(struct device *d, struct device_attribute *attr,
char *buf)
{
struct amd_pmc_dev *dev = dev_get_drvdata(d);
+ int rc;
if (!dev->major) {
- int rc = amd_pmc_get_smu_version(dev);
-
+ rc = amd_pmc_get_smu_version(dev);
if (rc)
return rc;
}
@@ -529,8 +296,8 @@ static int smu_fw_info_show(struct seq_file *s, void *unused)
seq_puts(s, "\n=== Active time (in us) ===\n");
for (idx = 0 ; idx < dev->num_ips ; idx++) {
- if (soc15_ip_blk[idx].bit_mask & dev->active_ips)
- seq_printf(s, "%-8s : %lld\n", soc15_ip_blk[idx].name,
+ if (dev->ips_ptr[idx].bit_mask & dev->active_ips)
+ seq_printf(s, "%-8s : %lld\n", dev->ips_ptr[idx].name,
table.timecondition_notmet_lastcapture[idx]);
}
@@ -625,20 +392,6 @@ static void amd_pmc_dbgfs_unregister(struct amd_pmc_dev *dev)
debugfs_remove_recursive(dev->dbgfs_dir);
}
-static bool amd_pmc_is_stb_supported(struct amd_pmc_dev *dev)
-{
- switch (dev->cpu_id) {
- case AMD_CPU_ID_YC:
- case AMD_CPU_ID_CB:
- case AMD_CPU_ID_PS:
- case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT:
- case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT:
- return true;
- default:
- return false;
- }
-}
-
static void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev)
{
dev->dbgfs_dir = debugfs_create_dir("amd_pmc", NULL);
@@ -648,14 +401,17 @@ static void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev)
&s0ix_stats_fops);
debugfs_create_file("amd_pmc_idlemask", 0644, dev->dbgfs_dir, dev,
&amd_pmc_idlemask_fops);
- /* Enable STB only when the module_param is set */
- if (enable_stb) {
- if (amd_pmc_is_stb_supported(dev))
- debugfs_create_file("stb_read", 0644, dev->dbgfs_dir, dev,
- &amd_pmc_stb_debugfs_fops_v2);
- else
- debugfs_create_file("stb_read", 0644, dev->dbgfs_dir, dev,
- &amd_pmc_stb_debugfs_fops);
+}
+
+static char *amd_pmc_get_msg_port(struct amd_pmc_dev *dev)
+{
+ switch (dev->msg_port) {
+ case MSG_PORT_PMC:
+ return "PMC";
+ case MSG_PORT_S2D:
+ return "S2D";
+ default:
+ return "Invalid message port";
}
}
@@ -663,10 +419,10 @@ static void amd_pmc_dump_registers(struct amd_pmc_dev *dev)
{
u32 value, message, argument, response;
- if (dev->msg_port) {
- message = AMD_S2D_REGISTER_MESSAGE;
- argument = AMD_S2D_REGISTER_ARGUMENT;
- response = AMD_S2D_REGISTER_RESPONSE;
+ if (dev->msg_port == MSG_PORT_S2D) {
+ message = dev->stb_arg.msg;
+ argument = dev->stb_arg.arg;
+ response = dev->stb_arg.resp;
} else {
message = dev->smu_msg;
argument = AMD_PMC_REGISTER_ARGUMENT;
@@ -674,26 +430,26 @@ static void amd_pmc_dump_registers(struct amd_pmc_dev *dev)
}
value = amd_pmc_reg_read(dev, response);
- dev_dbg(dev->dev, "AMD_%s_REGISTER_RESPONSE:%x\n", dev->msg_port ? "S2D" : "PMC", value);
+ dev_dbg(dev->dev, "AMD_%s_REGISTER_RESPONSE:%x\n", amd_pmc_get_msg_port(dev), value);
value = amd_pmc_reg_read(dev, argument);
- dev_dbg(dev->dev, "AMD_%s_REGISTER_ARGUMENT:%x\n", dev->msg_port ? "S2D" : "PMC", value);
+ dev_dbg(dev->dev, "AMD_%s_REGISTER_ARGUMENT:%x\n", amd_pmc_get_msg_port(dev), value);
value = amd_pmc_reg_read(dev, message);
- dev_dbg(dev->dev, "AMD_%s_REGISTER_MESSAGE:%x\n", dev->msg_port ? "S2D" : "PMC", value);
+ dev_dbg(dev->dev, "AMD_%s_REGISTER_MESSAGE:%x\n", amd_pmc_get_msg_port(dev), value);
}
-static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret)
+int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret)
{
int rc;
u32 val, message, argument, response;
- mutex_lock(&dev->lock);
+ guard(mutex)(&dev->lock);
- if (dev->msg_port) {
- message = AMD_S2D_REGISTER_MESSAGE;
- argument = AMD_S2D_REGISTER_ARGUMENT;
- response = AMD_S2D_REGISTER_RESPONSE;
+ if (dev->msg_port == MSG_PORT_S2D) {
+ message = dev->stb_arg.msg;
+ argument = dev->stb_arg.arg;
+ response = dev->stb_arg.resp;
} else {
message = dev->smu_msg;
argument = AMD_PMC_REGISTER_ARGUMENT;
@@ -706,7 +462,7 @@ static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg,
PMC_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX);
if (rc) {
dev_err(dev->dev, "failed to talk to SMU\n");
- goto out_unlock;
+ return rc;
}
/* Write zero to response register */
@@ -724,7 +480,7 @@ static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg,
PMC_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX);
if (rc) {
dev_err(dev->dev, "SMU response timed out\n");
- goto out_unlock;
+ return rc;
}
switch (val) {
@@ -738,21 +494,19 @@ static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg,
case AMD_PMC_RESULT_CMD_REJECT_BUSY:
dev_err(dev->dev, "SMU not ready. err: 0x%x\n", val);
rc = -EBUSY;
- goto out_unlock;
+ break;
case AMD_PMC_RESULT_CMD_UNKNOWN:
dev_err(dev->dev, "SMU cmd unknown. err: 0x%x\n", val);
rc = -EINVAL;
- goto out_unlock;
+ break;
case AMD_PMC_RESULT_CMD_REJECT_PREREQ:
case AMD_PMC_RESULT_FAILED:
default:
dev_err(dev->dev, "SMU cmd failed. err: 0x%x\n", val);
rc = -EIO;
- goto out_unlock;
+ break;
}
-out_unlock:
- mutex_unlock(&dev->lock);
amd_pmc_dump_registers(dev);
return rc;
}
@@ -881,7 +635,7 @@ static void amd_pmc_s2idle_prepare(void)
return;
}
- rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_S2IDLE_PREPARE);
+ rc = amd_stb_write(pdev, AMD_PMC_STB_S2IDLE_PREPARE);
if (rc)
dev_err(pdev->dev, "error writing to STB: %d\n", rc);
}
@@ -892,15 +646,14 @@ static void amd_pmc_s2idle_check(void)
struct smu_metrics table;
int rc;
- /* CZN: Ensure that future s0i3 entry attempts at least 10ms passed */
- if (pdev->cpu_id == AMD_CPU_ID_CZN && !get_metrics_table(pdev, &table) &&
- table.s0i3_last_entry_status)
- usleep_range(10000, 20000);
+ /* Avoid triggering OVP */
+ if (!get_metrics_table(pdev, &table) && table.s0i3_last_entry_status)
+ msleep(2500);
/* Dump the IdleMask before we add to the STB */
amd_pmc_idlemask_read(pdev, pdev->dev, NULL);
- rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_S2IDLE_CHECK);
+ rc = amd_stb_write(pdev, AMD_PMC_STB_S2IDLE_CHECK);
if (rc)
dev_err(pdev->dev, "error writing to STB: %d\n", rc);
}
@@ -927,7 +680,7 @@ static void amd_pmc_s2idle_restore(void)
/* Let SMU know that we are looking for stats */
amd_pmc_dump_data(pdev);
- rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_S2IDLE_RESTORE);
+ rc = amd_stb_write(pdev, AMD_PMC_STB_S2IDLE_RESTORE);
if (rc)
dev_err(pdev->dev, "error writing to STB: %d\n", rc);
@@ -946,14 +699,14 @@ static struct acpi_s2idle_dev_ops amd_pmc_s2idle_dev_ops = {
static int amd_pmc_suspend_handler(struct device *dev)
{
struct amd_pmc_dev *pdev = dev_get_drvdata(dev);
+ int rc;
/*
* Must be called only from the same set of dev_pm_ops handlers
* as i8042_pm_suspend() is called: currently just from .suspend.
*/
if (pdev->disable_8042_wakeup && !disable_workarounds) {
- int rc = amd_pmc_wa_irq1(pdev);
-
+ rc = amd_pmc_wa_irq1(pdev);
if (rc) {
dev_err(pdev->dev, "failed to adjust keyboard wakeup: %d\n", rc);
return rc;
@@ -976,79 +729,12 @@ static const struct pci_device_id pmc_pci_ids[] = {
{ 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_SP) },
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_SHP) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M20H_ROOT) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M60H_ROOT) },
{ }
};
-static int amd_pmc_s2d_init(struct amd_pmc_dev *dev)
-{
- u32 phys_addr_low, phys_addr_hi;
- u64 stb_phys_addr;
- u32 size = 0;
- int ret;
-
- /* Spill to DRAM feature uses separate SMU message port */
- dev->msg_port = 1;
-
- amd_pmc_send_cmd(dev, S2D_TELEMETRY_SIZE, &size, dev->s2d_msg_id, true);
- if (size != S2D_TELEMETRY_BYTES_MAX)
- return -EIO;
-
- /* Get DRAM size */
- ret = amd_pmc_send_cmd(dev, S2D_DRAM_SIZE, &dev->dram_size, dev->s2d_msg_id, true);
- if (ret || !dev->dram_size)
- dev->dram_size = S2D_TELEMETRY_DRAMBYTES_MAX;
-
- /* Get STB DRAM address */
- amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_LOW, &phys_addr_low, dev->s2d_msg_id, true);
- amd_pmc_send_cmd(dev, S2D_PHYS_ADDR_HIGH, &phys_addr_hi, dev->s2d_msg_id, true);
-
- if (!phys_addr_hi && !phys_addr_low) {
- dev_err(dev->dev, "STB is not enabled on the system; disable enable_stb or contact system vendor\n");
- return -EINVAL;
- }
-
- stb_phys_addr = ((u64)phys_addr_hi << 32 | phys_addr_low);
-
- /* Clear msg_port for other SMU operation */
- dev->msg_port = 0;
-
- dev->stb_virt_addr = devm_ioremap(dev->dev, stb_phys_addr, dev->dram_size);
- if (!dev->stb_virt_addr)
- return -ENOMEM;
-
- return 0;
-}
-
-static int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data)
-{
- int err;
-
- err = amd_smn_write(0, AMD_PMC_STB_PMI_0, data);
- if (err) {
- dev_err(dev->dev, "failed to write data in stb: 0x%X\n", AMD_PMC_STB_PMI_0);
- return pcibios_err_to_errno(err);
- }
-
- return 0;
-}
-
-static int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf)
-{
- int i, err;
-
- for (i = 0; i < FIFO_SIZE; i++) {
- err = amd_smn_read(0, AMD_PMC_STB_PMI_0, buf++);
- if (err) {
- dev_err(dev->dev, "error reading data from stb: 0x%X\n", AMD_PMC_STB_PMI_0);
- return pcibios_err_to_errno(err);
- }
- }
-
- return 0;
-}
-
static int amd_pmc_probe(struct platform_device *pdev)
{
struct amd_pmc_dev *dev = &pmc;
@@ -1059,7 +745,6 @@ static int amd_pmc_probe(struct platform_device *pdev)
u32 val;
dev->dev = &pdev->dev;
-
rdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0));
if (!rdev || !pci_match_id(pmc_pci_ids, rdev)) {
err = -ENODEV;
@@ -1067,8 +752,7 @@ static int amd_pmc_probe(struct platform_device *pdev)
}
dev->cpu_id = rdev->device;
-
- if (dev->cpu_id == AMD_CPU_ID_SP) {
+ if (dev->cpu_id == AMD_CPU_ID_SP || dev->cpu_id == AMD_CPU_ID_SHP) {
dev_warn_once(dev->dev, "S0i3 is not supported on this hardware\n");
err = -ENODEV;
goto err_pci_dev_put;
@@ -1083,7 +767,6 @@ static int amd_pmc_probe(struct platform_device *pdev)
}
base_addr_lo = val & AMD_PMC_BASE_ADDR_HI_MASK;
-
err = amd_smn_read(0, AMD_PMC_BASE_ADDR_HI, &val);
if (err) {
dev_err(dev->dev, "error reading 0x%x\n", AMD_PMC_BASE_ADDR_HI);
@@ -1101,17 +784,13 @@ static int amd_pmc_probe(struct platform_device *pdev)
goto err_pci_dev_put;
}
- mutex_init(&dev->lock);
+ err = devm_mutex_init(dev->dev, &dev->lock);
+ if (err)
+ goto err_pci_dev_put;
/* Get num of IP blocks within the SoC */
amd_pmc_get_ip_info(dev);
- if (enable_stb && amd_pmc_is_stb_supported(dev)) {
- err = amd_pmc_s2d_init(dev);
- if (err)
- goto err_pci_dev_put;
- }
-
platform_set_drvdata(pdev, dev);
if (IS_ENABLED(CONFIG_SUSPEND)) {
err = acpi_register_lps0_dev(&amd_pmc_s2idle_dev_ops);
@@ -1122,6 +801,10 @@ static int amd_pmc_probe(struct platform_device *pdev)
}
amd_pmc_dbgfs_register(dev);
+ err = amd_stb_s2d_init(dev);
+ if (err)
+ goto err_pci_dev_put;
+
if (IS_ENABLED(CONFIG_AMD_MP2_STB))
amd_mp2_stb_init(dev);
pm_report_max_hw_sleep(U64_MAX);
@@ -1142,7 +825,6 @@ static void amd_pmc_remove(struct platform_device *pdev)
pci_dev_put(dev->rdev);
if (IS_ENABLED(CONFIG_AMD_MP2_STB))
amd_mp2_stb_deinit(dev);
- mutex_destroy(&dev->lock);
}
static const struct acpi_device_id amd_pmc_acpi_ids[] = {
diff --git a/drivers/platform/x86/amd/pmc/pmc.h b/drivers/platform/x86/amd/pmc/pmc.h
index f1166d15c856..62f3e51020fd 100644
--- a/drivers/platform/x86/amd/pmc/pmc.h
+++ b/drivers/platform/x86/amd/pmc/pmc.h
@@ -14,6 +14,64 @@
#include <linux/types.h>
#include <linux/mutex.h>
+/* SMU communication registers */
+#define AMD_PMC_REGISTER_RESPONSE 0x980
+#define AMD_PMC_REGISTER_ARGUMENT 0x9BC
+
+/* PMC Scratch Registers */
+#define AMD_PMC_SCRATCH_REG_CZN 0x94
+#define AMD_PMC_SCRATCH_REG_YC 0xD14
+#define AMD_PMC_SCRATCH_REG_1AH 0xF14
+
+/* STB Registers */
+#define AMD_PMC_STB_S2IDLE_PREPARE 0xC6000001
+#define AMD_PMC_STB_S2IDLE_RESTORE 0xC6000002
+#define AMD_PMC_STB_S2IDLE_CHECK 0xC6000003
+
+/* Base address of SMU for mapping physical address to virtual address */
+#define AMD_PMC_MAPPING_SIZE 0x01000
+#define AMD_PMC_BASE_ADDR_OFFSET 0x10000
+#define AMD_PMC_BASE_ADDR_LO 0x13B102E8
+#define AMD_PMC_BASE_ADDR_HI 0x13B102EC
+#define AMD_PMC_BASE_ADDR_LO_MASK GENMASK(15, 0)
+#define AMD_PMC_BASE_ADDR_HI_MASK GENMASK(31, 20)
+
+/* SMU Response Codes */
+#define AMD_PMC_RESULT_OK 0x01
+#define AMD_PMC_RESULT_CMD_REJECT_BUSY 0xFC
+#define AMD_PMC_RESULT_CMD_REJECT_PREREQ 0xFD
+#define AMD_PMC_RESULT_CMD_UNKNOWN 0xFE
+#define AMD_PMC_RESULT_FAILED 0xFF
+
+/* FCH SSC Registers */
+#define FCH_S0I3_ENTRY_TIME_L_OFFSET 0x30
+#define FCH_S0I3_ENTRY_TIME_H_OFFSET 0x34
+#define FCH_S0I3_EXIT_TIME_L_OFFSET 0x38
+#define FCH_S0I3_EXIT_TIME_H_OFFSET 0x3C
+#define FCH_SSC_MAPPING_SIZE 0x800
+#define FCH_BASE_PHY_ADDR_LOW 0xFED81100
+#define FCH_BASE_PHY_ADDR_HIGH 0x00000000
+
+/* SMU Message Definations */
+#define SMU_MSG_GETSMUVERSION 0x02
+#define SMU_MSG_LOG_GETDRAM_ADDR_HI 0x04
+#define SMU_MSG_LOG_GETDRAM_ADDR_LO 0x05
+#define SMU_MSG_LOG_START 0x06
+#define SMU_MSG_LOG_RESET 0x07
+#define SMU_MSG_LOG_DUMP_DATA 0x08
+#define SMU_MSG_GET_SUP_CONSTRAINTS 0x09
+
+#define PMC_MSG_DELAY_MIN_US 50
+#define RESPONSE_REGISTER_LOOP_MAX 20000
+
+#define DELAY_MIN_US 2000
+#define DELAY_MAX_US 3000
+
+enum s2d_msg_port {
+ MSG_PORT_PMC,
+ MSG_PORT_S2D,
+};
+
struct amd_mp2_dev {
void __iomem *mmio;
void __iomem *vslbase;
@@ -25,24 +83,31 @@ struct amd_mp2_dev {
bool is_stb_data;
};
+struct stb_arg {
+ u32 s2d_msg_id;
+ u32 msg;
+ u32 arg;
+ u32 resp;
+};
+
struct amd_pmc_dev {
void __iomem *regbase;
void __iomem *smu_virt_addr;
void __iomem *stb_virt_addr;
void __iomem *fch_virt_addr;
- bool msg_port;
u32 base_addr;
u32 cpu_id;
- u32 active_ips;
u32 dram_size;
+ u32 active_ips;
+ const struct amd_pmc_bit_map *ips_ptr;
u32 num_ips;
- u32 s2d_msg_id;
u32 smu_msg;
/* SMU version information */
u8 smu_program;
u8 major;
u8 minor;
u8 rev;
+ u8 msg_port;
struct device *dev;
struct pci_dev *rdev;
struct mutex lock; /* generic mutex lock */
@@ -50,6 +115,35 @@ struct amd_pmc_dev {
struct quirk_entry *quirks;
bool disable_8042_wakeup;
struct amd_mp2_dev *mp2;
+ struct stb_arg stb_arg;
+};
+
+struct amd_pmc_bit_map {
+ const char *name;
+ u32 bit_mask;
+};
+
+struct smu_metrics {
+ u32 table_version;
+ u32 hint_count;
+ u32 s0i3_last_entry_status;
+ u32 timein_s0i2;
+ u64 timeentering_s0i3_lastcapture;
+ u64 timeentering_s0i3_totaltime;
+ u64 timeto_resume_to_os_lastcapture;
+ u64 timeto_resume_to_os_totaltime;
+ u64 timein_s0i3_lastcapture;
+ u64 timein_s0i3_totaltime;
+ u64 timein_swdrips_lastcapture;
+ u64 timein_swdrips_totaltime;
+ u64 timecondition_notmet_lastcapture[32];
+ u64 timecondition_notmet_totaltime[32];
+} __packed;
+
+enum amd_pmc_def {
+ MSG_TEST = 0x01,
+ MSG_OS_HINT_PCO,
+ MSG_OS_HINT_RN,
};
void amd_pmc_process_restore_quirks(struct amd_pmc_dev *dev);
@@ -66,8 +160,14 @@ void amd_mp2_stb_deinit(struct amd_pmc_dev *dev);
#define AMD_CPU_ID_CB 0x14D8
#define AMD_CPU_ID_PS 0x14E8
#define AMD_CPU_ID_SP 0x14A4
+#define AMD_CPU_ID_SHP 0x153A
#define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507
#define PCI_DEVICE_ID_AMD_1AH_M60H_ROOT 0x1122
#define PCI_DEVICE_ID_AMD_MP2_STB 0x172c
+int amd_stb_s2d_init(struct amd_pmc_dev *dev);
+int amd_stb_read(struct amd_pmc_dev *dev, u32 *buf);
+int amd_stb_write(struct amd_pmc_dev *dev, u32 data);
+int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret);
+
#endif /* PMC_H */
diff --git a/drivers/platform/x86/amd/pmf/Kconfig b/drivers/platform/x86/amd/pmf/Kconfig
index 99d67cdbd91e..25b8f7ae3abd 100644
--- a/drivers/platform/x86/amd/pmf/Kconfig
+++ b/drivers/platform/x86/amd/pmf/Kconfig
@@ -7,7 +7,7 @@ config AMD_PMF
tristate "AMD Platform Management Framework"
depends on ACPI && PCI
depends on POWER_SUPPLY
- depends on AMD_NB
+ depends on AMD_NODE
select ACPI_PLATFORM_PROFILE
depends on TEE && AMDTEE
depends on AMD_SFH_HID
diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile
index 7d6079b02589..5978464e0eb7 100644
--- a/drivers/platform/x86/amd/pmf/Makefile
+++ b/drivers/platform/x86/amd/pmf/Makefile
@@ -4,7 +4,7 @@
# AMD Platform Management Framework
#
-obj-$(CONFIG_AMD_PMF) += amd-pmf.o
-amd-pmf-objs := core.o acpi.o sps.o \
- auto-mode.o cnqf.o \
- tee-if.o spc.o pmf-quirks.o
+obj-$(CONFIG_AMD_PMF) += amd-pmf.o
+amd-pmf-y := core.o acpi.o sps.o \
+ auto-mode.o cnqf.o \
+ tee-if.o spc.o
diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
index 1b9c7acf0ddf..f75f7ecd8cd9 100644
--- a/drivers/platform/x86/amd/pmf/acpi.c
+++ b/drivers/platform/x86/amd/pmf/acpi.c
@@ -220,7 +220,7 @@ static void apmf_sbios_heartbeat_notify(struct work_struct *work)
if (!info)
return;
- schedule_delayed_work(&dev->heart_beat, msecs_to_jiffies(dev->hb_interval * 1000));
+ schedule_delayed_work(&dev->heart_beat, secs_to_jiffies(dev->hb_interval));
kfree(info);
}
@@ -321,17 +321,29 @@ int apmf_get_sbios_requests(struct amd_pmf_dev *pdev, struct apmf_sbios_req *req
req, sizeof(*req));
}
+static void apmf_event_handler_v2(acpi_handle handle, u32 event, void *data)
+{
+ struct amd_pmf_dev *pmf_dev = data;
+ int ret;
+
+ guard(mutex)(&pmf_dev->cb_mutex);
+
+ ret = apmf_get_sbios_requests_v2(pmf_dev, &pmf_dev->req);
+ if (ret)
+ dev_err(pmf_dev->dev, "Failed to get v2 SBIOS requests: %d\n", ret);
+}
+
static void apmf_event_handler(acpi_handle handle, u32 event, void *data)
{
struct amd_pmf_dev *pmf_dev = data;
struct apmf_sbios_req req;
int ret;
- mutex_lock(&pmf_dev->update_mutex);
+ guard(mutex)(&pmf_dev->update_mutex);
ret = apmf_get_sbios_requests(pmf_dev, &req);
if (ret) {
dev_err(pmf_dev->dev, "Failed to get SBIOS requests:%d\n", ret);
- goto out;
+ return;
}
if (req.pending_req & BIT(APMF_AMT_NOTIFICATION)) {
@@ -353,8 +365,6 @@ static void apmf_event_handler(acpi_handle handle, u32 event, void *data)
if (pmf_dev->amt_enabled)
amd_pmf_update_2_cql(pmf_dev, req.cql_event);
}
-out:
- mutex_unlock(&pmf_dev->update_mutex);
}
static int apmf_if_verify_interface(struct amd_pmf_dev *pdev)
@@ -430,6 +440,15 @@ int apmf_install_handler(struct amd_pmf_dev *pmf_dev)
apmf_event_handler(ahandle, 0, pmf_dev);
}
+ if (pmf_dev->smart_pc_enabled && pmf_dev->pmf_if_version == PMF_IF_V2) {
+ status = acpi_install_notify_handler(ahandle, ACPI_ALL_NOTIFY,
+ apmf_event_handler_v2, pmf_dev);
+ if (ACPI_FAILURE(status)) {
+ dev_err(pmf_dev->dev, "failed to install notify handler for custom BIOS inputs\n");
+ return -ENODEV;
+ }
+ }
+
return 0;
}
@@ -480,6 +499,9 @@ void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev)
if (is_apmf_func_supported(pmf_dev, APMF_FUNC_AUTO_MODE) &&
is_apmf_func_supported(pmf_dev, APMF_FUNC_SBIOS_REQUESTS))
acpi_remove_notify_handler(ahandle, ACPI_ALL_NOTIFY, apmf_event_handler);
+
+ if (pmf_dev->smart_pc_enabled && pmf_dev->pmf_if_version == PMF_IF_V2)
+ acpi_remove_notify_handler(ahandle, ACPI_ALL_NOTIFY, apmf_event_handler_v2);
}
int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
diff --git a/drivers/platform/x86/amd/pmf/auto-mode.c b/drivers/platform/x86/amd/pmf/auto-mode.c
index 02ff68be10d0..a184922bba8d 100644
--- a/drivers/platform/x86/amd/pmf/auto-mode.c
+++ b/drivers/platform/x86/amd/pmf/auto-mode.c
@@ -120,9 +120,9 @@ static void amd_pmf_set_automode(struct amd_pmf_dev *dev, int idx,
amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, pwr_ctrl->sppt_apu_only, NULL);
amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, pwr_ctrl->stt_min, NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
- pwr_ctrl->stt_skin_temp[STT_TEMP_APU], NULL);
+ fixp_q88_fromint(pwr_ctrl->stt_skin_temp[STT_TEMP_APU]), NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
- pwr_ctrl->stt_skin_temp[STT_TEMP_HS2], NULL);
+ fixp_q88_fromint(pwr_ctrl->stt_skin_temp[STT_TEMP_HS2]), NULL);
if (is_apmf_func_supported(dev, APMF_FUNC_SET_FAN_IDX))
apmf_update_fan_idx(dev, config_store.mode_set[idx].fan_control.manual,
diff --git a/drivers/platform/x86/amd/pmf/cnqf.c b/drivers/platform/x86/amd/pmf/cnqf.c
index bc8899e15c91..207a0b33d8d3 100644
--- a/drivers/platform/x86/amd/pmf/cnqf.c
+++ b/drivers/platform/x86/amd/pmf/cnqf.c
@@ -81,10 +81,10 @@ static int amd_pmf_set_cnqf(struct amd_pmf_dev *dev, int src, int idx,
amd_pmf_send_cmd(dev, SET_SPPT, false, pc->sppt, NULL);
amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, pc->sppt_apu_only, NULL);
amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, pc->stt_min, NULL);
- amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, pc->stt_skin_temp[STT_TEMP_APU],
- NULL);
- amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, pc->stt_skin_temp[STT_TEMP_HS2],
- NULL);
+ amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
+ fixp_q88_fromint(pc->stt_skin_temp[STT_TEMP_APU]), NULL);
+ amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
+ fixp_q88_fromint(pc->stt_skin_temp[STT_TEMP_HS2]), NULL);
if (is_apmf_func_supported(dev, APMF_FUNC_SET_FAN_IDX))
apmf_update_fan_idx(dev,
diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
index 06a97c533cb8..395c011e837f 100644
--- a/drivers/platform/x86/amd/pmf/core.c
+++ b/drivers/platform/x86/amd/pmf/core.c
@@ -8,13 +8,13 @@
* Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
*/
-#include <asm/amd_nb.h>
#include <linux/debugfs.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
+#include <asm/amd_node.h>
#include "pmf.h"
/* PMF-SMU communication registers */
@@ -127,7 +127,8 @@ static void amd_pmf_get_metrics(struct work_struct *work)
ktime_t time_elapsed_ms;
int socket_power;
- mutex_lock(&dev->update_mutex);
+ guard(mutex)(&dev->update_mutex);
+
/* Transfer table contents */
memset(dev->buf, 0, sizeof(dev->m_table));
amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL);
@@ -149,7 +150,6 @@ static void amd_pmf_get_metrics(struct work_struct *work)
dev->start_time = ktime_to_ms(ktime_get());
schedule_delayed_work(&dev->work_buffer, msecs_to_jiffies(metrics_table_loop_ms));
- mutex_unlock(&dev->update_mutex);
}
static inline u32 amd_pmf_reg_read(struct amd_pmf_dev *dev, int reg_offset)
@@ -176,12 +176,26 @@ static void __maybe_unused amd_pmf_dump_registers(struct amd_pmf_dev *dev)
dev_dbg(dev->dev, "AMD_PMF_REGISTER_MESSAGE:%x\n", value);
}
+/**
+ * fixp_q88_fromint: Convert integer to Q8.8
+ * @val: input value
+ *
+ * Converts an integer into binary fixed point format where 8 bits
+ * are used for integer and 8 bits are used for the decimal.
+ *
+ * Return: unsigned integer converted to Q8.8 format
+ */
+u32 fixp_q88_fromint(u32 val)
+{
+ return val << 8;
+}
+
int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data)
{
int rc;
u32 val;
- mutex_lock(&dev->lock);
+ guard(mutex)(&dev->lock);
/* Wait until we get a valid response */
rc = readx_poll_timeout(ioread32, dev->regbase + AMD_PMF_REGISTER_RESPONSE,
@@ -189,7 +203,7 @@ int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32
PMF_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX);
if (rc) {
dev_err(dev->dev, "failed to talk to SMU\n");
- goto out_unlock;
+ return rc;
}
/* Write zero to response register */
@@ -207,7 +221,7 @@ int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32
PMF_MSG_DELAY_MIN_US * RESPONSE_REGISTER_LOOP_MAX);
if (rc) {
dev_err(dev->dev, "SMU response timed out\n");
- goto out_unlock;
+ return rc;
}
switch (val) {
@@ -221,21 +235,19 @@ int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32
case AMD_PMF_RESULT_CMD_REJECT_BUSY:
dev_err(dev->dev, "SMU not ready. err: 0x%x\n", val);
rc = -EBUSY;
- goto out_unlock;
+ break;
case AMD_PMF_RESULT_CMD_UNKNOWN:
dev_err(dev->dev, "SMU cmd unknown. err: 0x%x\n", val);
rc = -EINVAL;
- goto out_unlock;
+ break;
case AMD_PMF_RESULT_CMD_REJECT_PREREQ:
case AMD_PMF_RESULT_FAILED:
default:
dev_err(dev->dev, "SMU cmd failed. err: 0x%x\n", val);
rc = -EIO;
- goto out_unlock;
+ break;
}
-out_unlock:
- mutex_unlock(&dev->lock);
amd_pmf_dump_registers(dev);
return rc;
}
@@ -268,7 +280,7 @@ int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer)
dev_err(dev->dev, "Invalid CPU id: 0x%x", dev->cpu_id);
}
- dev->buf = kzalloc(dev->mtable_size, GFP_KERNEL);
+ dev->buf = devm_kzalloc(dev->dev, dev->mtable_size, GFP_KERNEL);
if (!dev->buf)
return -ENOMEM;
}
@@ -373,7 +385,6 @@ static void amd_pmf_deinit_features(struct amd_pmf_dev *dev)
if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR) ||
is_apmf_func_supported(dev, APMF_FUNC_OS_POWER_SLIDER_UPDATE)) {
power_supply_unreg_notifier(&dev->pwr_src_notifier);
- amd_pmf_deinit_sps(dev);
}
if (dev->smart_pc_enabled) {
@@ -455,8 +466,8 @@ static int amd_pmf_probe(struct platform_device *pdev)
mutex_init(&dev->lock);
mutex_init(&dev->update_mutex);
+ mutex_init(&dev->cb_mutex);
- amd_pmf_quirks_init(dev);
apmf_acpi_init(dev);
platform_set_drvdata(pdev, dev);
amd_pmf_dbgfs_register(dev);
@@ -481,7 +492,7 @@ static void amd_pmf_remove(struct platform_device *pdev)
amd_pmf_dbgfs_unregister(dev);
mutex_destroy(&dev->lock);
mutex_destroy(&dev->update_mutex);
- kfree(dev->buf);
+ mutex_destroy(&dev->cb_mutex);
}
static const struct attribute_group *amd_pmf_driver_groups[] = {
diff --git a/drivers/platform/x86/amd/pmf/pmf-quirks.c b/drivers/platform/x86/amd/pmf/pmf-quirks.c
deleted file mode 100644
index 7cde5733b9ca..000000000000
--- a/drivers/platform/x86/amd/pmf/pmf-quirks.c
+++ /dev/null
@@ -1,66 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * AMD Platform Management Framework Driver Quirks
- *
- * Copyright (c) 2024, Advanced Micro Devices, Inc.
- * All Rights Reserved.
- *
- * Author: Mario Limonciello <mario.limonciello@amd.com>
- */
-
-#include <linux/dmi.h>
-
-#include "pmf.h"
-
-struct quirk_entry {
- u32 supported_func;
-};
-
-static struct quirk_entry quirk_no_sps_bug = {
- .supported_func = 0x4003,
-};
-
-static const struct dmi_system_id fwbug_list[] = {
- {
- .ident = "ROG Zephyrus G14",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
- DMI_MATCH(DMI_PRODUCT_NAME, "GA403U"),
- },
- .driver_data = &quirk_no_sps_bug,
- },
- {
- .ident = "ROG Ally X",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
- DMI_MATCH(DMI_PRODUCT_NAME, "RC72LA"),
- },
- .driver_data = &quirk_no_sps_bug,
- },
- {
- .ident = "ASUS TUF Gaming A14",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
- DMI_MATCH(DMI_PRODUCT_NAME, "FA401W"),
- },
- .driver_data = &quirk_no_sps_bug,
- },
- {}
-};
-
-void amd_pmf_quirks_init(struct amd_pmf_dev *dev)
-{
- const struct dmi_system_id *dmi_id;
- struct quirk_entry *quirks;
-
- dmi_id = dmi_first_match(fwbug_list);
- if (!dmi_id)
- return;
-
- quirks = dmi_id->driver_data;
- if (quirks->supported_func) {
- dev->supported_func = quirks->supported_func;
- pr_info("Using supported funcs quirk to avoid %s platform firmware bug\n",
- dmi_id->ident);
- }
-}
diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
index a79808fda1d8..45b60238d527 100644
--- a/drivers/platform/x86/amd/pmf/pmf.h
+++ b/drivers/platform/x86/amd/pmf/pmf.h
@@ -106,9 +106,12 @@ struct cookie_header {
#define PMF_TA_IF_VERSION_MAJOR 1
#define TA_PMF_ACTION_MAX 32
#define TA_PMF_UNDO_MAX 8
-#define TA_OUTPUT_RESERVED_MEM 906
+#define TA_OUTPUT_RESERVED_MEM 922
#define MAX_OPERATION_PARAMS 4
+#define TA_ERROR_CRYPTO_INVALID_PARAM 0x20002
+#define TA_ERROR_CRYPTO_BIN_TOO_LARGE 0x2000d
+
#define PMF_IF_V1 1
#define PMF_IF_V2 2
@@ -338,7 +341,7 @@ struct amd_pmf_dev {
struct mutex lock; /* protects the PMF interface */
u32 supported_func;
enum platform_profile_option current_profile;
- struct platform_profile_handler pprof;
+ struct device *ppdev; /* platform profile class device */
struct dentry *dbgfs_dir;
int hb_interval; /* SBIOS heartbeat interval */
struct delayed_work heart_beat;
@@ -370,6 +373,8 @@ struct amd_pmf_dev {
struct input_dev *pmf_idev;
size_t mtable_size;
struct resource *res;
+ struct apmf_sbios_req_v2 req; /* To get custom bios pending request */
+ struct mutex cb_mutex;
};
struct apmf_sps_prop_granular_v2 {
@@ -616,6 +621,30 @@ enum ta_slider {
TA_MAX,
};
+enum apmf_smartpc_custom_bios_inputs {
+ APMF_SMARTPC_CUSTOM_BIOS_INPUT1,
+ APMF_SMARTPC_CUSTOM_BIOS_INPUT2,
+};
+
+enum apmf_preq_smartpc {
+ NOTIFY_CUSTOM_BIOS_INPUT1 = 5,
+ NOTIFY_CUSTOM_BIOS_INPUT2,
+};
+
+enum platform_type {
+ PTYPE_UNKNOWN = 0,
+ LID_CLOSE,
+ CLAMSHELL,
+ FLAT,
+ TENT,
+ STAND,
+ TABLET,
+ BOOK,
+ PRESENTATION,
+ PULL_FWD,
+ PTYPE_INVALID = 0xf,
+};
+
/* Command ids for TA communication */
enum ta_pmf_command {
TA_PMF_COMMAND_POLICY_BUILDER_INITIALIZE,
@@ -657,7 +686,8 @@ struct ta_pmf_condition_info {
u32 power_slider;
u32 lid_state;
bool user_present;
- u32 rsvd1[2];
+ u32 bios_input1;
+ u32 bios_input2;
u32 monitor_count;
u32 rsvd2[2];
u32 bat_design;
@@ -667,7 +697,9 @@ struct ta_pmf_condition_info {
u32 device_state;
u32 socket_power;
u32 skin_temperature;
- u32 rsvd3[5];
+ u32 rsvd3[2];
+ u32 platform_type;
+ u32 rsvd3_1[2];
u32 ambient_light;
u32 length;
u32 avg_c0residency;
@@ -745,13 +777,13 @@ int apmf_install_handler(struct amd_pmf_dev *pmf_dev);
int apmf_os_power_slider_update(struct amd_pmf_dev *dev, u8 flag);
int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer);
int amd_pmf_notify_sbios_heartbeat_event_v2(struct amd_pmf_dev *dev, u8 flag);
+u32 fixp_q88_fromint(u32 val);
/* SPS Layer */
int amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf);
void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
struct amd_pmf_static_slider_granular *table);
int amd_pmf_init_sps(struct amd_pmf_dev *dev);
-void amd_pmf_deinit_sps(struct amd_pmf_dev *dev);
int apmf_get_static_slider_granular(struct amd_pmf_dev *pdev,
struct apmf_static_slider_granular_output *output);
bool is_pprof_balanced(struct amd_pmf_dev *pmf);
@@ -797,7 +829,4 @@ int amd_pmf_smartpc_apply_bios_output(struct amd_pmf_dev *dev, u32 val, u32 preq
void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in);
void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in);
-/* Quirk infrastructure */
-void amd_pmf_quirks_init(struct amd_pmf_dev *dev);
-
#endif /* PMF_H */
diff --git a/drivers/platform/x86/amd/pmf/spc.c b/drivers/platform/x86/amd/pmf/spc.c
index 06226eb0eab3..1d90f9382024 100644
--- a/drivers/platform/x86/amd/pmf/spc.c
+++ b/drivers/platform/x86/amd/pmf/spc.c
@@ -16,6 +16,46 @@
#include "pmf.h"
#ifdef CONFIG_AMD_PMF_DEBUG
+static const char *platform_type_as_str(u16 platform_type)
+{
+ switch (platform_type) {
+ case CLAMSHELL:
+ return "CLAMSHELL";
+ case FLAT:
+ return "FLAT";
+ case TENT:
+ return "TENT";
+ case STAND:
+ return "STAND";
+ case TABLET:
+ return "TABLET";
+ case BOOK:
+ return "BOOK";
+ case PRESENTATION:
+ return "PRESENTATION";
+ case PULL_FWD:
+ return "PULL_FWD";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static const char *laptop_placement_as_str(u16 device_state)
+{
+ switch (device_state) {
+ case ON_TABLE:
+ return "ON_TABLE";
+ case ON_LAP_MOTION:
+ return "ON_LAP_MOTION";
+ case IN_BAG:
+ return "IN_BAG";
+ case OUT_OF_BAG:
+ return "OUT_OF_BAG";
+ default:
+ return "UNKNOWN";
+ }
+}
+
static const char *ta_slider_as_str(unsigned int state)
{
switch (state) {
@@ -47,12 +87,38 @@ void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *
dev_dbg(dev->dev, "LID State: %s\n", in->ev_info.lid_state ? "close" : "open");
dev_dbg(dev->dev, "User Presence: %s\n", in->ev_info.user_present ? "Present" : "Away");
dev_dbg(dev->dev, "Ambient Light: %d\n", in->ev_info.ambient_light);
+ dev_dbg(dev->dev, "Platform type: %s\n", platform_type_as_str(in->ev_info.platform_type));
+ dev_dbg(dev->dev, "Laptop placement: %s\n",
+ laptop_placement_as_str(in->ev_info.device_state));
+ dev_dbg(dev->dev, "Custom BIOS input1: %u\n", in->ev_info.bios_input1);
+ dev_dbg(dev->dev, "Custom BIOS input2: %u\n", in->ev_info.bios_input2);
dev_dbg(dev->dev, "==== TA inputs END ====\n");
}
#else
void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) {}
#endif
+static void amd_pmf_get_custom_bios_inputs(struct amd_pmf_dev *pdev,
+ struct ta_pmf_enact_table *in)
+{
+ if (!pdev->req.pending_req)
+ return;
+
+ switch (pdev->req.pending_req) {
+ case BIT(NOTIFY_CUSTOM_BIOS_INPUT1):
+ in->ev_info.bios_input1 = pdev->req.custom_policy[APMF_SMARTPC_CUSTOM_BIOS_INPUT1];
+ break;
+ case BIT(NOTIFY_CUSTOM_BIOS_INPUT2):
+ in->ev_info.bios_input2 = pdev->req.custom_policy[APMF_SMARTPC_CUSTOM_BIOS_INPUT2];
+ break;
+ default:
+ dev_dbg(pdev->dev, "Invalid preq for BIOS input: 0x%x\n", pdev->req.pending_req);
+ }
+
+ /* Clear pending requests after handling */
+ memset(&pdev->req, 0, sizeof(pdev->req));
+}
+
static void amd_pmf_get_c0_residency(u16 *core_res, size_t size, struct ta_pmf_enact_table *in)
{
u16 max, avg = 0;
@@ -153,12 +219,14 @@ static int amd_pmf_get_slider_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_
switch (dev->current_profile) {
case PLATFORM_PROFILE_PERFORMANCE:
+ case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
val = TA_BEST_PERFORMANCE;
break;
case PLATFORM_PROFILE_BALANCED:
val = TA_BETTER_PERFORMANCE;
break;
case PLATFORM_PROFILE_LOW_POWER:
+ case PLATFORM_PROFILE_QUIET:
val = TA_BEST_BATTERY;
break;
default:
@@ -190,6 +258,14 @@ static void amd_pmf_get_sensor_info(struct amd_pmf_dev *dev, struct ta_pmf_enact
} else {
dev_dbg(dev->dev, "HPD is not enabled/detected\n");
}
+
+ /* Get SRA (Secondary Accelerometer) data */
+ if (!amd_get_sfh_info(&sfh_info, MT_SRA)) {
+ in->ev_info.platform_type = sfh_info.platform_type;
+ in->ev_info.device_state = sfh_info.laptop_placement;
+ } else {
+ dev_dbg(dev->dev, "SRA is not enabled/detected\n");
+ }
}
void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
@@ -201,4 +277,5 @@ void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_tab
amd_pmf_get_battery_info(dev, in);
amd_pmf_get_slider_info(dev, in);
amd_pmf_get_sensor_info(dev, in);
+ amd_pmf_get_custom_bios_inputs(dev, in);
}
diff --git a/drivers/platform/x86/amd/pmf/sps.c b/drivers/platform/x86/amd/pmf/sps.c
index 92f7fb22277d..49e14ca94a9e 100644
--- a/drivers/platform/x86/amd/pmf/sps.c
+++ b/drivers/platform/x86/amd/pmf/sps.c
@@ -198,9 +198,11 @@ static void amd_pmf_update_slider_v2(struct amd_pmf_dev *dev, int idx)
amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false,
apts_config_store.val[idx].stt_min_limit, NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
- apts_config_store.val[idx].stt_skin_temp_limit_apu, NULL);
+ fixp_q88_fromint(apts_config_store.val[idx].stt_skin_temp_limit_apu),
+ NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
- apts_config_store.val[idx].stt_skin_temp_limit_hs2, NULL);
+ fixp_q88_fromint(apts_config_store.val[idx].stt_skin_temp_limit_hs2),
+ NULL);
}
void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
@@ -217,9 +219,11 @@ void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false,
config_store.prop[src][idx].stt_min, NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
- config_store.prop[src][idx].stt_skin_temp[STT_TEMP_APU], NULL);
+ fixp_q88_fromint(config_store.prop[src][idx].stt_skin_temp[STT_TEMP_APU]),
+ NULL);
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
- config_store.prop[src][idx].stt_skin_temp[STT_TEMP_HS2], NULL);
+ fixp_q88_fromint(config_store.prop[src][idx].stt_skin_temp[STT_TEMP_HS2]),
+ NULL);
} else if (op == SLIDER_OP_GET) {
amd_pmf_send_cmd(dev, GET_SPL, true, ARG_NONE, &table->prop[src][idx].spl);
amd_pmf_send_cmd(dev, GET_FPPT, true, ARG_NONE, &table->prop[src][idx].fppt);
@@ -282,10 +286,10 @@ bool is_pprof_balanced(struct amd_pmf_dev *pmf)
return (pmf->current_profile == PLATFORM_PROFILE_BALANCED) ? true : false;
}
-static int amd_pmf_profile_get(struct platform_profile_handler *pprof,
+static int amd_pmf_profile_get(struct device *dev,
enum platform_profile_option *profile)
{
- struct amd_pmf_dev *pmf = container_of(pprof, struct amd_pmf_dev, pprof);
+ struct amd_pmf_dev *pmf = dev_get_drvdata(dev);
*profile = pmf->current_profile;
return 0;
@@ -297,12 +301,14 @@ int amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf)
switch (pmf->current_profile) {
case PLATFORM_PROFILE_PERFORMANCE:
+ case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
mode = POWER_MODE_PERFORMANCE;
break;
case PLATFORM_PROFILE_BALANCED:
mode = POWER_MODE_BALANCED_POWER;
break;
case PLATFORM_PROFILE_LOW_POWER:
+ case PLATFORM_PROFILE_QUIET:
mode = POWER_MODE_POWER_SAVER;
break;
default:
@@ -363,10 +369,10 @@ int amd_pmf_power_slider_update_event(struct amd_pmf_dev *dev)
return 0;
}
-static int amd_pmf_profile_set(struct platform_profile_handler *pprof,
+static int amd_pmf_profile_set(struct device *dev,
enum platform_profile_option profile)
{
- struct amd_pmf_dev *pmf = container_of(pprof, struct amd_pmf_dev, pprof);
+ struct amd_pmf_dev *pmf = dev_get_drvdata(dev);
int ret = 0;
pmf->current_profile = profile;
@@ -387,10 +393,32 @@ static int amd_pmf_profile_set(struct platform_profile_handler *pprof,
return 0;
}
-int amd_pmf_init_sps(struct amd_pmf_dev *dev)
+static int amd_pmf_hidden_choices(void *drvdata, unsigned long *choices)
{
- int err;
+ set_bit(PLATFORM_PROFILE_QUIET, choices);
+ set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices);
+
+ return 0;
+}
+
+static int amd_pmf_profile_probe(void *drvdata, unsigned long *choices)
+{
+ set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
+ set_bit(PLATFORM_PROFILE_BALANCED, choices);
+ set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
+
+ return 0;
+}
+static const struct platform_profile_ops amd_pmf_profile_ops = {
+ .probe = amd_pmf_profile_probe,
+ .hidden_choices = amd_pmf_hidden_choices,
+ .profile_get = amd_pmf_profile_get,
+ .profile_set = amd_pmf_profile_set,
+};
+
+int amd_pmf_init_sps(struct amd_pmf_dev *dev)
+{
dev->current_profile = PLATFORM_PROFILE_BALANCED;
if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) {
@@ -405,24 +433,12 @@ int amd_pmf_init_sps(struct amd_pmf_dev *dev)
amd_pmf_set_sps_power_limits(dev);
}
- dev->pprof.profile_get = amd_pmf_profile_get;
- dev->pprof.profile_set = amd_pmf_profile_set;
-
- /* Setup supported modes */
- set_bit(PLATFORM_PROFILE_LOW_POWER, dev->pprof.choices);
- set_bit(PLATFORM_PROFILE_BALANCED, dev->pprof.choices);
- set_bit(PLATFORM_PROFILE_PERFORMANCE, dev->pprof.choices);
-
/* Create platform_profile structure and register */
- err = platform_profile_register(&dev->pprof);
- if (err)
- dev_err(dev->dev, "Failed to register SPS support, this is most likely an SBIOS bug: %d\n",
- err);
-
- return err;
-}
+ dev->ppdev = devm_platform_profile_register(dev->dev, "amd-pmf", dev,
+ &amd_pmf_profile_ops);
+ if (IS_ERR(dev->ppdev))
+ dev_err(dev->dev, "Failed to register SPS support, this is most likely an SBIOS bug: %ld\n",
+ PTR_ERR(dev->ppdev));
-void amd_pmf_deinit_sps(struct amd_pmf_dev *dev)
-{
- platform_profile_remove();
+ return PTR_ERR_OR_ZERO(dev->ppdev);
}
diff --git a/drivers/platform/x86/amd/pmf/tee-if.c b/drivers/platform/x86/amd/pmf/tee-if.c
index 8c88769ea1d8..76efce48a45c 100644
--- a/drivers/platform/x86/amd/pmf/tee-if.c
+++ b/drivers/platform/x86/amd/pmf/tee-if.c
@@ -27,8 +27,11 @@ module_param(pb_side_load, bool, 0444);
MODULE_PARM_DESC(pb_side_load, "Sideload policy binaries debug policy failures");
#endif
-static const uuid_t amd_pmf_ta_uuid = UUID_INIT(0x6fd93b77, 0x3fb8, 0x524d,
- 0xb1, 0x2d, 0xc5, 0x29, 0xb1, 0x3d, 0x85, 0x43);
+static const uuid_t amd_pmf_ta_uuid[] = { UUID_INIT(0xd9b39bf2, 0x66bd, 0x4154, 0xaf, 0xb8, 0x8a,
+ 0xcc, 0x2b, 0x2b, 0x60, 0xd6),
+ UUID_INIT(0x6fd93b77, 0x3fb8, 0x524d, 0xb1, 0x2d, 0xc5,
+ 0x29, 0xb1, 0x3d, 0x85, 0x43),
+ };
static const char *amd_pmf_uevent_as_str(unsigned int state)
{
@@ -120,7 +123,8 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_
case PMF_POLICY_STT_SKINTEMP_APU:
if (dev->prev_data->stt_skintemp_apu != val) {
- amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, val, NULL);
+ amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
+ fixp_q88_fromint(val), NULL);
dev_dbg(dev->dev, "update STT_SKINTEMP_APU: %u\n", val);
dev->prev_data->stt_skintemp_apu = val;
}
@@ -128,7 +132,8 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_
case PMF_POLICY_STT_SKINTEMP_HS2:
if (dev->prev_data->stt_skintemp_hs2 != val) {
- amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, val, NULL);
+ amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
+ fixp_q88_fromint(val), NULL);
dev_dbg(dev->dev, "update STT_SKINTEMP_HS2: %u\n", val);
dev->prev_data->stt_skintemp_hs2 = val;
}
@@ -321,14 +326,19 @@ static int amd_pmf_start_policy_engine(struct amd_pmf_dev *dev)
*/
schedule_delayed_work(&dev->pb_work, msecs_to_jiffies(pb_actions_ms * 3));
} else {
- dev_err(dev->dev, "ta invoke cmd init failed err: %x\n", res);
+ dev_dbg(dev->dev, "ta invoke cmd init failed err: %x\n", res);
dev->smart_pc_enabled = false;
- return -EIO;
+ return res;
}
return 0;
}
+static inline bool amd_pmf_pb_valid(struct amd_pmf_dev *dev)
+{
+ return memchr_inv(dev->policy_buf, 0xff, dev->policy_sz);
+}
+
#ifdef CONFIG_AMD_PMF_DEBUG
static void amd_pmf_hex_dump_pb(struct amd_pmf_dev *dev)
{
@@ -348,14 +358,22 @@ static ssize_t amd_pmf_get_pb_data(struct file *filp, const char __user *buf,
return -EINVAL;
/* re-alloc to the new buffer length of the policy binary */
- new_policy_buf = memdup_user(buf, length);
- if (IS_ERR(new_policy_buf))
- return PTR_ERR(new_policy_buf);
+ new_policy_buf = devm_kzalloc(dev->dev, length, GFP_KERNEL);
+ if (!new_policy_buf)
+ return -ENOMEM;
- kfree(dev->policy_buf);
+ if (copy_from_user(new_policy_buf, buf, length)) {
+ devm_kfree(dev->dev, new_policy_buf);
+ return -EFAULT;
+ }
+
+ devm_kfree(dev->dev, dev->policy_buf);
dev->policy_buf = new_policy_buf;
dev->policy_sz = length;
+ if (!amd_pmf_pb_valid(dev))
+ return -EINVAL;
+
amd_pmf_hex_dump_pb(dev);
ret = amd_pmf_start_policy_engine(dev);
if (ret < 0)
@@ -390,24 +408,24 @@ static int amd_pmf_amdtee_ta_match(struct tee_ioctl_version_data *ver, const voi
return ver->impl_id == TEE_IMPL_ID_AMDTEE;
}
-static int amd_pmf_ta_open_session(struct tee_context *ctx, u32 *id)
+static int amd_pmf_ta_open_session(struct tee_context *ctx, u32 *id, const uuid_t *uuid)
{
struct tee_ioctl_open_session_arg sess_arg = {};
int rc;
- export_uuid(sess_arg.uuid, &amd_pmf_ta_uuid);
+ export_uuid(sess_arg.uuid, uuid);
sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC;
sess_arg.num_params = 0;
rc = tee_client_open_session(ctx, &sess_arg, NULL);
if (rc < 0 || sess_arg.ret != 0) {
pr_err("Failed to open TEE session err:%#x, rc:%d\n", sess_arg.ret, rc);
- return rc;
+ return rc ?: -EINVAL;
}
*id = sess_arg.session;
- return rc;
+ return 0;
}
static int amd_pmf_register_input_device(struct amd_pmf_dev *dev)
@@ -434,7 +452,7 @@ static int amd_pmf_register_input_device(struct amd_pmf_dev *dev)
return 0;
}
-static int amd_pmf_tee_init(struct amd_pmf_dev *dev)
+static int amd_pmf_tee_init(struct amd_pmf_dev *dev, const uuid_t *uuid)
{
u32 size;
int ret;
@@ -442,10 +460,12 @@ static int amd_pmf_tee_init(struct amd_pmf_dev *dev)
dev->tee_ctx = tee_client_open_context(NULL, amd_pmf_amdtee_ta_match, NULL, NULL);
if (IS_ERR(dev->tee_ctx)) {
dev_err(dev->dev, "Failed to open TEE context\n");
- return PTR_ERR(dev->tee_ctx);
+ ret = PTR_ERR(dev->tee_ctx);
+ dev->tee_ctx = NULL;
+ return ret;
}
- ret = amd_pmf_ta_open_session(dev->tee_ctx, &dev->session_id);
+ ret = amd_pmf_ta_open_session(dev->tee_ctx, &dev->session_id, uuid);
if (ret) {
dev_err(dev->dev, "Failed to open TA session (%d)\n", ret);
ret = -EINVAL;
@@ -482,14 +502,18 @@ out_ctx:
static void amd_pmf_tee_deinit(struct amd_pmf_dev *dev)
{
+ if (!dev->tee_ctx)
+ return;
tee_shm_free(dev->fw_shm_pool);
tee_client_close_session(dev->tee_ctx, dev->session_id);
tee_client_close_context(dev->tee_ctx);
+ dev->tee_ctx = NULL;
}
int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev)
{
- int ret;
+ bool status;
+ int ret, i;
ret = apmf_check_smart_pc(dev);
if (ret) {
@@ -502,53 +526,85 @@ int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev)
return -ENODEV;
}
- ret = amd_pmf_tee_init(dev);
- if (ret)
- return ret;
-
INIT_DELAYED_WORK(&dev->pb_work, amd_pmf_invoke_cmd);
ret = amd_pmf_set_dram_addr(dev, true);
if (ret)
- goto error;
+ goto err_cancel_work;
dev->policy_base = devm_ioremap_resource(dev->dev, dev->res);
if (IS_ERR(dev->policy_base)) {
ret = PTR_ERR(dev->policy_base);
- goto error;
+ goto err_cancel_work;
}
- dev->policy_buf = kzalloc(dev->policy_sz, GFP_KERNEL);
+ dev->policy_buf = devm_kzalloc(dev->dev, dev->policy_sz, GFP_KERNEL);
if (!dev->policy_buf) {
ret = -ENOMEM;
- goto error;
+ goto err_cancel_work;
}
memcpy_fromio(dev->policy_buf, dev->policy_base, dev->policy_sz);
+ if (!amd_pmf_pb_valid(dev)) {
+ dev_info(dev->dev, "No Smart PC policy present\n");
+ ret = -EINVAL;
+ goto err_cancel_work;
+ }
+
amd_pmf_hex_dump_pb(dev);
- dev->prev_data = kzalloc(sizeof(*dev->prev_data), GFP_KERNEL);
+ dev->prev_data = devm_kzalloc(dev->dev, sizeof(*dev->prev_data), GFP_KERNEL);
if (!dev->prev_data) {
ret = -ENOMEM;
- goto error;
+ goto err_cancel_work;
}
- ret = amd_pmf_start_policy_engine(dev);
- if (ret)
- goto error;
+ for (i = 0; i < ARRAY_SIZE(amd_pmf_ta_uuid); i++) {
+ ret = amd_pmf_tee_init(dev, &amd_pmf_ta_uuid[i]);
+ if (ret)
+ goto err_cancel_work;
+
+ ret = amd_pmf_start_policy_engine(dev);
+ switch (ret) {
+ case TA_PMF_TYPE_SUCCESS:
+ status = true;
+ break;
+ case TA_ERROR_CRYPTO_INVALID_PARAM:
+ case TA_ERROR_CRYPTO_BIN_TOO_LARGE:
+ amd_pmf_tee_deinit(dev);
+ status = false;
+ break;
+ default:
+ ret = -EINVAL;
+ amd_pmf_tee_deinit(dev);
+ goto err_cancel_work;
+ }
+
+ if (status)
+ break;
+ }
+
+ if (!status && !pb_side_load) {
+ ret = -EINVAL;
+ goto err_cancel_work;
+ }
if (pb_side_load)
amd_pmf_open_pb(dev, dev->dbgfs_dir);
ret = amd_pmf_register_input_device(dev);
if (ret)
- goto error;
+ goto err_pmf_remove_pb;
return 0;
-error:
- amd_pmf_deinit_smart_pc(dev);
+err_pmf_remove_pb:
+ if (pb_side_load && dev->esbin)
+ amd_pmf_remove_pb(dev);
+ amd_pmf_tee_deinit(dev);
+err_cancel_work:
+ cancel_delayed_work_sync(&dev->pb_work);
return ret;
}
@@ -562,11 +618,5 @@ void amd_pmf_deinit_smart_pc(struct amd_pmf_dev *dev)
amd_pmf_remove_pb(dev);
cancel_delayed_work_sync(&dev->pb_work);
- kfree(dev->prev_data);
- dev->prev_data = NULL;
- kfree(dev->policy_buf);
- dev->policy_buf = NULL;
- kfree(dev->buf);
- dev->buf = NULL;
amd_pmf_tee_deinit(dev);
}
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c
index d460dd194f19..a0a411b4f2d6 100644
--- a/drivers/platform/x86/asus-laptop.c
+++ b/drivers/platform/x86/asus-laptop.c
@@ -426,11 +426,14 @@ static int asus_pega_lucid_set(struct asus_laptop *asus, int unit, bool enable)
static int pega_acc_axis(struct asus_laptop *asus, int curr, char *method)
{
+ unsigned long long val = (unsigned long long)curr;
+ acpi_status status;
int i, delta;
- unsigned long long val;
- for (i = 0; i < PEGA_ACC_RETRIES; i++) {
- acpi_evaluate_integer(asus->handle, method, NULL, &val);
+ for (i = 0; i < PEGA_ACC_RETRIES; i++) {
+ status = acpi_evaluate_integer(asus->handle, method, NULL, &val);
+ if (ACPI_FAILURE(status))
+ continue;
/* The output is noisy. From reading the ASL
* dissassembly, timeout errors are returned with 1's
* in the high word, and the lack of locking around
diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c
index a5933980ade3..3f8b2a324efd 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -50,7 +50,8 @@ MODULE_PARM_DESC(tablet_mode_sw, "Tablet mode detect: -1:auto 0:disable 1:kbd-do
static struct quirk_entry *quirks;
static bool atkbd_reports_vol_keys;
-static bool asus_i8042_filter(unsigned char data, unsigned char str, struct serio *port)
+static bool asus_i8042_filter(unsigned char data, unsigned char str, struct serio *port,
+ void *context)
{
static bool extended_e0;
static bool extended_e1;
diff --git a/drivers/platform/x86/asus-tf103c-dock.c b/drivers/platform/x86/asus-tf103c-dock.c
index ca4670d0dc67..f09a3fc6524a 100644
--- a/drivers/platform/x86/asus-tf103c-dock.c
+++ b/drivers/platform/x86/asus-tf103c-dock.c
@@ -856,7 +856,7 @@ static int tf103c_dock_probe(struct i2c_client *client)
/* 5. Setup irqchip for touchpad IRQ pass-through */
dock->tp_irqchip.name = KBUILD_MODNAME;
- dock->tp_irq_domain = irq_domain_add_linear(NULL, 1, &irq_domain_simple_ops, NULL);
+ dock->tp_irq_domain = irq_domain_create_linear(NULL, 1, &irq_domain_simple_ops, NULL);
if (!dock->tp_irq_domain)
return -ENOMEM;
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 8bd187e8b47f..47cc766624d7 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -304,6 +304,7 @@ struct asus_wmi {
u32 kbd_rgb_dev;
bool kbd_rgb_state_available;
+ bool oobe_state_available;
u8 throttle_thermal_policy_mode;
u32 throttle_thermal_policy_dev;
@@ -313,7 +314,7 @@ struct asus_wmi {
bool mid_fan_curve_available;
struct fan_curve_data custom_fan_curves[3];
- struct platform_profile_handler platform_profile_handler;
+ struct device *ppdev;
bool platform_profile_support;
// The RSOC controls the maximum charging percentage.
@@ -1826,7 +1827,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
goto error;
}
- if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_OOBE)) {
+ if (asus->oobe_state_available) {
/*
* Disable OOBE state, so that e.g. the keyboard backlight
* works.
@@ -3782,7 +3783,7 @@ static ssize_t throttle_thermal_policy_store(struct device *dev,
* Ensure that platform_profile updates userspace with the change to ensure
* that platform_profile and throttle_thermal_policy_mode are in sync.
*/
- platform_profile_notify();
+ platform_profile_notify(asus->ppdev);
return count;
}
@@ -3793,13 +3794,13 @@ static ssize_t throttle_thermal_policy_store(struct device *dev,
static DEVICE_ATTR_RW(throttle_thermal_policy);
/* Platform profile ***********************************************************/
-static int asus_wmi_platform_profile_get(struct platform_profile_handler *pprof,
+static int asus_wmi_platform_profile_get(struct device *dev,
enum platform_profile_option *profile)
{
struct asus_wmi *asus;
int tp;
- asus = container_of(pprof, struct asus_wmi, platform_profile_handler);
+ asus = dev_get_drvdata(dev);
tp = asus->throttle_thermal_policy_mode;
switch (tp) {
@@ -3819,13 +3820,13 @@ static int asus_wmi_platform_profile_get(struct platform_profile_handler *pprof,
return 0;
}
-static int asus_wmi_platform_profile_set(struct platform_profile_handler *pprof,
+static int asus_wmi_platform_profile_set(struct device *dev,
enum platform_profile_option profile)
{
struct asus_wmi *asus;
int tp;
- asus = container_of(pprof, struct asus_wmi, platform_profile_handler);
+ asus = dev_get_drvdata(dev);
switch (profile) {
case PLATFORM_PROFILE_PERFORMANCE:
@@ -3845,6 +3846,21 @@ static int asus_wmi_platform_profile_set(struct platform_profile_handler *pprof,
return throttle_thermal_policy_write(asus);
}
+static int asus_wmi_platform_profile_probe(void *drvdata, unsigned long *choices)
+{
+ set_bit(PLATFORM_PROFILE_QUIET, choices);
+ set_bit(PLATFORM_PROFILE_BALANCED, choices);
+ set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
+
+ return 0;
+}
+
+static const struct platform_profile_ops asus_wmi_platform_profile_ops = {
+ .probe = asus_wmi_platform_profile_probe,
+ .profile_get = asus_wmi_platform_profile_get,
+ .profile_set = asus_wmi_platform_profile_set,
+};
+
static int platform_profile_setup(struct asus_wmi *asus)
{
struct device *dev = &asus->platform_device->dev;
@@ -3869,22 +3885,11 @@ static int platform_profile_setup(struct asus_wmi *asus)
dev_info(dev, "Using throttle_thermal_policy for platform_profile support\n");
- asus->platform_profile_handler.profile_get = asus_wmi_platform_profile_get;
- asus->platform_profile_handler.profile_set = asus_wmi_platform_profile_set;
-
- set_bit(PLATFORM_PROFILE_QUIET, asus->platform_profile_handler.choices);
- set_bit(PLATFORM_PROFILE_BALANCED,
- asus->platform_profile_handler.choices);
- set_bit(PLATFORM_PROFILE_PERFORMANCE,
- asus->platform_profile_handler.choices);
-
- err = platform_profile_register(&asus->platform_profile_handler);
- if (err == -EEXIST) {
- pr_warn("%s, a platform_profile handler is already registered\n", __func__);
- return 0;
- } else if (err) {
- pr_err("%s, failed at platform_profile_register: %d\n", __func__, err);
- return err;
+ asus->ppdev = devm_platform_profile_register(dev, "asus-wmi", asus,
+ &asus_wmi_platform_profile_ops);
+ if (IS_ERR(asus->ppdev)) {
+ dev_err(dev, "Failed to register a platform_profile class device\n");
+ return PTR_ERR(asus->ppdev);
}
asus->platform_profile_support = true;
@@ -4719,6 +4724,7 @@ static int asus_wmi_add(struct platform_device *pdev)
asus->egpu_enable_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_EGPU);
asus->dgpu_disable_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_DGPU);
asus->kbd_rgb_state_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_STATE);
+ asus->oobe_state_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_OOBE);
asus->ally_mcu_usb_switch = acpi_has_method(NULL, ASUS_USB0_PWR_EC0_CSEE)
&& dmi_check_system(asus_ally_mcu_quirk);
@@ -4773,7 +4779,8 @@ static int asus_wmi_add(struct platform_device *pdev)
goto fail_leds;
asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WLAN, &result);
- if (result & (ASUS_WMI_DSTS_PRESENCE_BIT | ASUS_WMI_DSTS_USER_BIT))
+ if ((result & (ASUS_WMI_DSTS_PRESENCE_BIT | ASUS_WMI_DSTS_USER_BIT)) ==
+ (ASUS_WMI_DSTS_PRESENCE_BIT | ASUS_WMI_DSTS_USER_BIT))
asus->driver->wlan_ctrl_by_user = 1;
if (!(asus->driver->wlan_ctrl_by_user && ashs_present())) {
@@ -4815,7 +4822,7 @@ static int asus_wmi_add(struct platform_device *pdev)
}
if (asus->driver->i8042_filter) {
- err = i8042_install_filter(asus->driver->i8042_filter);
+ err = i8042_install_filter(asus->driver->i8042_filter, NULL);
if (err)
pr_warn("Unable to install key filter - %d\n", err);
}
@@ -4842,8 +4849,6 @@ fail_input:
fail_sysfs:
fail_custom_fan_curve:
fail_platform_profile_setup:
- if (asus->platform_profile_support)
- platform_profile_remove();
fail_fan_boost_mode:
fail_platform:
kfree(asus);
@@ -4869,9 +4874,6 @@ static void asus_wmi_remove(struct platform_device *device)
throttle_thermal_policy_set_default(asus);
asus_wmi_battery_exit(asus);
- if (asus->platform_profile_support)
- platform_profile_remove();
-
kfree(asus);
}
@@ -4971,6 +4973,13 @@ static int asus_hotk_restore(struct device *device)
}
if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
kbd_led_update(asus);
+ if (asus->oobe_state_available) {
+ /*
+ * Disable OOBE state, so that e.g. the keyboard backlight
+ * works.
+ */
+ asus_wmi_set_devstate(ASUS_WMI_DEVID_OOBE, 1, NULL);
+ }
if (asus_wmi_has_fnlock_key(asus))
asus_wmi_fnlock_update(asus);
diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h
index d02f15fd3482..018dfde4025e 100644
--- a/drivers/platform/x86/asus-wmi.h
+++ b/drivers/platform/x86/asus-wmi.h
@@ -73,8 +73,7 @@ struct asus_wmi_driver {
void (*key_filter) (struct asus_wmi_driver *driver, int *code,
unsigned int *value, bool *autorelease);
/* Optional standard i8042 filter */
- bool (*i8042_filter)(unsigned char data, unsigned char str,
- struct serio *serio);
+ i8042_filter_t i8042_filter;
int (*probe) (struct platform_device *device);
void (*detect_quirks) (struct asus_wmi_driver *driver);
diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c
index 58754bc5b5b1..abbebd4bfb15 100644
--- a/drivers/platform/x86/compal-laptop.c
+++ b/drivers/platform/x86/compal-laptop.c
@@ -69,7 +69,6 @@
#include <linux/hwmon-sysfs.h>
#include <linux/power_supply.h>
#include <linux/sysfs.h>
-#include <linux/fb.h>
#include <acpi/video.h>
/* ======= */
diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
index 2dddafb3f7fa..f8a0dffcaab7 100644
--- a/drivers/platform/x86/dell/Kconfig
+++ b/drivers/platform/x86/dell/Kconfig
@@ -18,15 +18,35 @@ config ALIENWARE_WMI
tristate "Alienware Special feature control"
default m
depends on ACPI
+ depends on ACPI_WMI
+ depends on DMI
depends on LEDS_CLASS
depends on NEW_LEDS
- depends on ACPI_WMI
+ help
+ This is a driver for controlling Alienware WMI driven features.
+
+ On legacy devices, it exposes an interface for controlling the AlienFX
+ zones on Alienware machines that don't contain a dedicated
+ AlienFX USB MCU such as the X51 and X51-R2.
+
+ On newer devices, it exposes the AWCC thermal control interface through
+ known Kernel APIs.
+
+config ALIENWARE_WMI_LEGACY
+ bool "Alienware Legacy WMI device driver"
+ default y
+ depends on ALIENWARE_WMI
+ help
+ Legacy Alienware WMI driver with AlienFX LED control capabilities.
+
+config ALIENWARE_WMI_WMAX
+ bool "Alienware WMAX WMI device driver"
+ default y
+ depends on ALIENWARE_WMI
select ACPI_PLATFORM_PROFILE
help
- This is a driver for controlling Alienware BIOS driven
- features. It exposes an interface for controlling the AlienFX
- zones on Alienware machines that don't contain a dedicated AlienFX
- USB MCU such as the X51 and X51-R2.
+ Alienware WMI driver with AlienFX LED, HDMI, amplifier, deep sleep and
+ AWCC thermal control capabilities.
config DCDBAS
tristate "Dell Systems Management Base Driver"
@@ -152,6 +172,7 @@ config DELL_SMBIOS_SMM
config DELL_SMO8800
tristate "Dell Latitude freefall driver (ACPI SMO88XX)"
default m
+ depends on I2C
depends on ACPI || COMPILE_TEST
help
Say Y here if you want to support SMO88XX freefall devices
diff --git a/drivers/platform/x86/dell/Makefile b/drivers/platform/x86/dell/Makefile
index 79d60f1bf4c1..c7501c25e627 100644
--- a/drivers/platform/x86/dell/Makefile
+++ b/drivers/platform/x86/dell/Makefile
@@ -4,23 +4,27 @@
# Dell x86 Platform-Specific Drivers
#
-obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o
-obj-$(CONFIG_DCDBAS) += dcdbas.o
-obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
-obj-$(CONFIG_DELL_RBTN) += dell-rbtn.o
-obj-$(CONFIG_DELL_RBU) += dell_rbu.o
-obj-$(CONFIG_DELL_PC) += dell-pc.o
-obj-$(CONFIG_DELL_SMBIOS) += dell-smbios.o
-dell-smbios-objs := dell-smbios-base.o
-dell-smbios-$(CONFIG_DELL_SMBIOS_WMI) += dell-smbios-wmi.o
-dell-smbios-$(CONFIG_DELL_SMBIOS_SMM) += dell-smbios-smm.o
-obj-$(CONFIG_DELL_SMO8800) += dell-smo8800.o
-obj-$(CONFIG_DELL_UART_BACKLIGHT) += dell-uart-backlight.o
-obj-$(CONFIG_DELL_WMI) += dell-wmi.o
-dell-wmi-objs := dell-wmi-base.o
-dell-wmi-$(CONFIG_DELL_WMI_PRIVACY) += dell-wmi-privacy.o
-obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o
-obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o
-obj-$(CONFIG_DELL_WMI_DDV) += dell-wmi-ddv.o
-obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o
-obj-$(CONFIG_DELL_WMI_SYSMAN) += dell-wmi-sysman/
+obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o
+alienware-wmi-y := alienware-wmi-base.o
+alienware-wmi-$(CONFIG_ALIENWARE_WMI_LEGACY) += alienware-wmi-legacy.o
+alienware-wmi-$(CONFIG_ALIENWARE_WMI_WMAX) += alienware-wmi-wmax.o
+obj-$(CONFIG_DCDBAS) += dcdbas.o
+obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
+obj-$(CONFIG_DELL_RBTN) += dell-rbtn.o
+obj-$(CONFIG_DELL_RBU) += dell_rbu.o
+obj-$(CONFIG_DELL_PC) += dell-pc.o
+obj-$(CONFIG_DELL_SMBIOS) += dell-smbios.o
+dell-smbios-y := dell-smbios-base.o
+dell-smbios-$(CONFIG_DELL_SMBIOS_WMI) += dell-smbios-wmi.o
+dell-smbios-$(CONFIG_DELL_SMBIOS_SMM) += dell-smbios-smm.o
+obj-$(CONFIG_DELL_SMO8800) += dell-smo8800.o
+obj-$(CONFIG_DELL_SMO8800) += dell-lis3lv02d.o
+obj-$(CONFIG_DELL_UART_BACKLIGHT) += dell-uart-backlight.o
+obj-$(CONFIG_DELL_WMI) += dell-wmi.o
+dell-wmi-y := dell-wmi-base.o
+dell-wmi-$(CONFIG_DELL_WMI_PRIVACY) += dell-wmi-privacy.o
+obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o
+obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o
+obj-$(CONFIG_DELL_WMI_DDV) += dell-wmi-ddv.o
+obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o
+obj-$(CONFIG_DELL_WMI_SYSMAN) += dell-wmi-sysman/
diff --git a/drivers/platform/x86/dell/alienware-wmi-base.c b/drivers/platform/x86/dell/alienware-wmi-base.c
new file mode 100644
index 000000000000..64562b92314f
--- /dev/null
+++ b/drivers/platform/x86/dell/alienware-wmi-base.c
@@ -0,0 +1,491 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Alienware special feature control
+ *
+ * Copyright (C) 2014 Dell Inc <Dell.Client.Kernel@dell.com>
+ * Copyright (C) 2025 Kurt Borja <kuurtb@gmail.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
+#include <linux/cleanup.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/dmi.h>
+#include <linux/leds.h>
+#include "alienware-wmi.h"
+
+MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
+MODULE_AUTHOR("Kurt Borja <kuurtb@gmail.com>");
+MODULE_DESCRIPTION("Alienware special feature control");
+MODULE_LICENSE("GPL");
+
+struct alienfx_quirks *alienfx;
+
+static struct alienfx_quirks quirk_inspiron5675 = {
+ .num_zones = 2,
+ .hdmi_mux = false,
+ .amplifier = false,
+ .deepslp = false,
+};
+
+static struct alienfx_quirks quirk_unknown = {
+ .num_zones = 2,
+ .hdmi_mux = false,
+ .amplifier = false,
+ .deepslp = false,
+};
+
+static struct alienfx_quirks quirk_x51_r1_r2 = {
+ .num_zones = 3,
+ .hdmi_mux = false,
+ .amplifier = false,
+ .deepslp = false,
+};
+
+static struct alienfx_quirks quirk_x51_r3 = {
+ .num_zones = 4,
+ .hdmi_mux = false,
+ .amplifier = true,
+ .deepslp = false,
+};
+
+static struct alienfx_quirks quirk_asm100 = {
+ .num_zones = 2,
+ .hdmi_mux = true,
+ .amplifier = false,
+ .deepslp = false,
+};
+
+static struct alienfx_quirks quirk_asm200 = {
+ .num_zones = 2,
+ .hdmi_mux = true,
+ .amplifier = false,
+ .deepslp = true,
+};
+
+static struct alienfx_quirks quirk_asm201 = {
+ .num_zones = 2,
+ .hdmi_mux = true,
+ .amplifier = true,
+ .deepslp = true,
+};
+
+static int __init dmi_matched(const struct dmi_system_id *dmi)
+{
+ alienfx = dmi->driver_data;
+ return 1;
+}
+
+static const struct dmi_system_id alienware_quirks[] __initconst = {
+ {
+ .callback = dmi_matched,
+ .ident = "Alienware ASM100",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
+ },
+ .driver_data = &quirk_asm100,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Alienware ASM200",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
+ },
+ .driver_data = &quirk_asm200,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Alienware ASM201",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
+ },
+ .driver_data = &quirk_asm201,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Alienware X51 R1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
+ },
+ .driver_data = &quirk_x51_r1_r2,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Alienware X51 R2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
+ },
+ .driver_data = &quirk_x51_r1_r2,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Alienware X51 R3",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
+ },
+ .driver_data = &quirk_x51_r3,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Inc. Inspiron 5675",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
+ },
+ .driver_data = &quirk_inspiron5675,
+ },
+ {}
+};
+
+u8 alienware_interface;
+
+int alienware_wmi_command(struct wmi_device *wdev, u32 method_id,
+ void *in_args, size_t in_size, u32 *out_data)
+{
+ struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
+ struct acpi_buffer in = {in_size, in_args};
+ acpi_status ret;
+
+ ret = wmidev_evaluate_method(wdev, 0, method_id, &in, out_data ? &out : NULL);
+ if (ACPI_FAILURE(ret))
+ return -EIO;
+
+ union acpi_object *obj __free(kfree) = out.pointer;
+
+ if (out_data) {
+ if (obj && obj->type == ACPI_TYPE_INTEGER)
+ *out_data = (u32)obj->integer.value;
+ else
+ return -ENOMSG;
+ }
+
+ return 0;
+}
+
+/*
+ * Helpers used for zone control
+ */
+static int parse_rgb(const char *buf, struct color_platform *colors)
+{
+ long unsigned int rgb;
+ int ret;
+ union color_union {
+ struct color_platform cp;
+ int package;
+ } repackager;
+
+ ret = kstrtoul(buf, 16, &rgb);
+ if (ret)
+ return ret;
+
+ /* RGB triplet notation is 24-bit hexadecimal */
+ if (rgb > 0xFFFFFF)
+ return -EINVAL;
+
+ repackager.package = rgb & 0x0f0f0f0f;
+ pr_debug("alienware-wmi: r: %d g:%d b: %d\n",
+ repackager.cp.red, repackager.cp.green, repackager.cp.blue);
+ *colors = repackager.cp;
+ return 0;
+}
+
+/*
+ * Individual RGB zone control
+ */
+static ssize_t zone_show(struct device *dev, struct device_attribute *attr,
+ char *buf, u8 location)
+{
+ struct alienfx_priv *priv = dev_get_drvdata(dev);
+ struct color_platform *colors = &priv->colors[location];
+
+ return sprintf(buf, "red: %d, green: %d, blue: %d\n",
+ colors->red, colors->green, colors->blue);
+
+}
+
+static ssize_t zone_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count, u8 location)
+{
+ struct alienfx_priv *priv = dev_get_drvdata(dev);
+ struct color_platform *colors = &priv->colors[location];
+ struct alienfx_platdata *pdata = dev_get_platdata(dev);
+ int ret;
+
+ ret = parse_rgb(buf, colors);
+ if (ret)
+ return ret;
+
+ ret = pdata->ops.upd_led(priv, pdata->wdev, location);
+
+ return ret ? ret : count;
+}
+
+static ssize_t zone00_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return zone_show(dev, attr, buf, 0);
+}
+
+static ssize_t zone00_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return zone_store(dev, attr, buf, count, 0);
+}
+
+static DEVICE_ATTR_RW(zone00);
+
+static ssize_t zone01_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return zone_show(dev, attr, buf, 1);
+}
+
+static ssize_t zone01_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return zone_store(dev, attr, buf, count, 1);
+}
+
+static DEVICE_ATTR_RW(zone01);
+
+static ssize_t zone02_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return zone_show(dev, attr, buf, 2);
+}
+
+static ssize_t zone02_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return zone_store(dev, attr, buf, count, 2);
+}
+
+static DEVICE_ATTR_RW(zone02);
+
+static ssize_t zone03_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return zone_show(dev, attr, buf, 3);
+}
+
+static ssize_t zone03_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return zone_store(dev, attr, buf, count, 3);
+}
+
+static DEVICE_ATTR_RW(zone03);
+
+/*
+ * Lighting control state device attribute (Global)
+ */
+static ssize_t lighting_control_state_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct alienfx_priv *priv = dev_get_drvdata(dev);
+
+ if (priv->lighting_control_state == LEGACY_BOOTING)
+ return sysfs_emit(buf, "[booting] running suspend\n");
+ else if (priv->lighting_control_state == LEGACY_SUSPEND)
+ return sysfs_emit(buf, "booting running [suspend]\n");
+
+ return sysfs_emit(buf, "booting [running] suspend\n");
+}
+
+static ssize_t lighting_control_state_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct alienfx_priv *priv = dev_get_drvdata(dev);
+ u8 val;
+
+ if (strcmp(buf, "booting\n") == 0)
+ val = LEGACY_BOOTING;
+ else if (strcmp(buf, "suspend\n") == 0)
+ val = LEGACY_SUSPEND;
+ else if (alienware_interface == LEGACY)
+ val = LEGACY_RUNNING;
+ else
+ val = WMAX_RUNNING;
+
+ priv->lighting_control_state = val;
+ pr_debug("alienware-wmi: updated control state to %d\n",
+ priv->lighting_control_state);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(lighting_control_state);
+
+static umode_t zone_attr_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ if (n < alienfx->num_zones + 1)
+ return attr->mode;
+
+ return 0;
+}
+
+static bool zone_group_visible(struct kobject *kobj)
+{
+ return alienfx->num_zones > 0;
+}
+DEFINE_SYSFS_GROUP_VISIBLE(zone);
+
+static struct attribute *zone_attrs[] = {
+ &dev_attr_lighting_control_state.attr,
+ &dev_attr_zone00.attr,
+ &dev_attr_zone01.attr,
+ &dev_attr_zone02.attr,
+ &dev_attr_zone03.attr,
+ NULL
+};
+
+static struct attribute_group zone_attribute_group = {
+ .name = "rgb_zones",
+ .is_visible = SYSFS_GROUP_VISIBLE(zone),
+ .attrs = zone_attrs,
+};
+
+/*
+ * LED Brightness (Global)
+ */
+static void global_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct alienfx_priv *priv = container_of(led_cdev, struct alienfx_priv,
+ global_led);
+ struct alienfx_platdata *pdata = dev_get_platdata(&priv->pdev->dev);
+ int ret;
+
+ priv->global_brightness = brightness;
+
+ ret = pdata->ops.upd_brightness(priv, pdata->wdev, brightness);
+ if (ret)
+ pr_err("LED brightness update failed\n");
+}
+
+static enum led_brightness global_led_get(struct led_classdev *led_cdev)
+{
+ struct alienfx_priv *priv = container_of(led_cdev, struct alienfx_priv,
+ global_led);
+
+ return priv->global_brightness;
+}
+
+/*
+ * Platform Driver
+ */
+static int alienfx_probe(struct platform_device *pdev)
+{
+ struct alienfx_priv *priv;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ if (alienware_interface == WMAX)
+ priv->lighting_control_state = WMAX_RUNNING;
+ else
+ priv->lighting_control_state = LEGACY_RUNNING;
+
+ priv->pdev = pdev;
+ priv->global_led.name = "alienware::global_brightness";
+ priv->global_led.brightness_set = global_led_set;
+ priv->global_led.brightness_get = global_led_get;
+ priv->global_led.max_brightness = 0x0F;
+ priv->global_brightness = priv->global_led.max_brightness;
+ platform_set_drvdata(pdev, priv);
+
+ return devm_led_classdev_register(&pdev->dev, &priv->global_led);
+}
+
+static const struct attribute_group *alienfx_groups[] = {
+ &zone_attribute_group,
+ WMAX_DEV_GROUPS
+ NULL
+};
+
+static struct platform_driver platform_driver = {
+ .driver = {
+ .name = "alienware-wmi",
+ .dev_groups = alienfx_groups,
+ },
+ .probe = alienfx_probe,
+};
+
+static void alienware_alienfx_remove(void *data)
+{
+ struct platform_device *pdev = data;
+
+ platform_device_unregister(pdev);
+}
+
+int alienware_alienfx_setup(struct alienfx_platdata *pdata)
+{
+ struct device *dev = &pdata->wdev->dev;
+ struct platform_device *pdev;
+ int ret;
+
+ pdev = platform_device_register_data(NULL, "alienware-wmi",
+ PLATFORM_DEVID_NONE, pdata,
+ sizeof(*pdata));
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ dev_set_drvdata(dev, pdev);
+ ret = devm_add_action_or_reset(dev, alienware_alienfx_remove, pdev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int __init alienware_wmi_init(void)
+{
+ int ret;
+
+ dmi_check_system(alienware_quirks);
+ if (!alienfx)
+ alienfx = &quirk_unknown;
+
+ ret = platform_driver_register(&platform_driver);
+ if (ret < 0)
+ return ret;
+
+ if (wmi_has_guid(WMAX_CONTROL_GUID)) {
+ alienware_interface = WMAX;
+ ret = alienware_wmax_wmi_init();
+ } else {
+ alienware_interface = LEGACY;
+ ret = alienware_legacy_wmi_init();
+ }
+
+ if (ret < 0)
+ platform_driver_unregister(&platform_driver);
+
+ return ret;
+}
+
+module_init(alienware_wmi_init);
+
+static void __exit alienware_wmi_exit(void)
+{
+ if (alienware_interface == WMAX)
+ alienware_wmax_wmi_exit();
+ else
+ alienware_legacy_wmi_exit();
+
+ platform_driver_unregister(&platform_driver);
+}
+
+module_exit(alienware_wmi_exit);
diff --git a/drivers/platform/x86/dell/alienware-wmi-legacy.c b/drivers/platform/x86/dell/alienware-wmi-legacy.c
new file mode 100644
index 000000000000..4a84a2fe918b
--- /dev/null
+++ b/drivers/platform/x86/dell/alienware-wmi-legacy.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Alienware LEGACY WMI device driver
+ *
+ * Copyright (C) 2025 Kurt Borja <kuurtb@gmail.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/wmi.h>
+#include "alienware-wmi.h"
+
+struct legacy_led_args {
+ struct color_platform colors;
+ u8 brightness;
+ u8 state;
+} __packed;
+
+
+/*
+ * Legacy WMI driver
+ */
+static int legacy_wmi_update_led(struct alienfx_priv *priv,
+ struct wmi_device *wdev, u8 location)
+{
+ struct legacy_led_args legacy_args = {
+ .colors = priv->colors[location],
+ .brightness = priv->global_brightness,
+ .state = 0,
+ };
+ struct acpi_buffer input;
+ acpi_status status;
+
+ if (legacy_args.state != LEGACY_RUNNING) {
+ legacy_args.state = priv->lighting_control_state;
+
+ input.length = sizeof(legacy_args);
+ input.pointer = &legacy_args;
+
+ status = wmi_evaluate_method(LEGACY_POWER_CONTROL_GUID, 0,
+ location + 1, &input, NULL);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ return 0;
+ }
+
+ return alienware_wmi_command(wdev, location + 1, &legacy_args,
+ sizeof(legacy_args), NULL);
+}
+
+static int legacy_wmi_update_brightness(struct alienfx_priv *priv,
+ struct wmi_device *wdev, u8 brightness)
+{
+ return legacy_wmi_update_led(priv, wdev, 0);
+}
+
+static int legacy_wmi_probe(struct wmi_device *wdev, const void *context)
+{
+ struct alienfx_platdata pdata = {
+ .wdev = wdev,
+ .ops = {
+ .upd_led = legacy_wmi_update_led,
+ .upd_brightness = legacy_wmi_update_brightness,
+ },
+ };
+
+ return alienware_alienfx_setup(&pdata);
+}
+
+static const struct wmi_device_id alienware_legacy_device_id_table[] = {
+ { LEGACY_CONTROL_GUID, NULL },
+ { },
+};
+MODULE_DEVICE_TABLE(wmi, alienware_legacy_device_id_table);
+
+static struct wmi_driver alienware_legacy_wmi_driver = {
+ .driver = {
+ .name = "alienware-wmi-alienfx",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .id_table = alienware_legacy_device_id_table,
+ .probe = legacy_wmi_probe,
+ .no_singleton = true,
+};
+
+int __init alienware_legacy_wmi_init(void)
+{
+ return wmi_driver_register(&alienware_legacy_wmi_driver);
+}
+
+void __exit alienware_legacy_wmi_exit(void)
+{
+ wmi_driver_unregister(&alienware_legacy_wmi_driver);
+}
diff --git a/drivers/platform/x86/dell/alienware-wmi-wmax.c b/drivers/platform/x86/dell/alienware-wmi-wmax.c
new file mode 100644
index 000000000000..eb5cbe6ae9e9
--- /dev/null
+++ b/drivers/platform/x86/dell/alienware-wmi-wmax.c
@@ -0,0 +1,822 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Alienware WMAX WMI device driver
+ *
+ * Copyright (C) 2014 Dell Inc <Dell.Client.Kernel@dell.com>
+ * Copyright (C) 2025 Kurt Borja <kuurtb@gmail.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/dmi.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_profile.h>
+#include <linux/wmi.h>
+#include "alienware-wmi.h"
+
+#define WMAX_METHOD_HDMI_SOURCE 0x1
+#define WMAX_METHOD_HDMI_STATUS 0x2
+#define WMAX_METHOD_HDMI_CABLE 0x5
+#define WMAX_METHOD_AMPLIFIER_CABLE 0x6
+#define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B
+#define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C
+#define WMAX_METHOD_BRIGHTNESS 0x3
+#define WMAX_METHOD_ZONE_CONTROL 0x4
+#define WMAX_METHOD_THERMAL_INFORMATION 0x14
+#define WMAX_METHOD_THERMAL_CONTROL 0x15
+#define WMAX_METHOD_GAME_SHIFT_STATUS 0x25
+
+#define WMAX_THERMAL_MODE_GMODE 0xAB
+
+#define WMAX_FAILURE_CODE 0xFFFFFFFF
+#define WMAX_THERMAL_TABLE_MASK GENMASK(7, 4)
+#define WMAX_THERMAL_MODE_MASK GENMASK(3, 0)
+#define WMAX_SENSOR_ID_MASK BIT(8)
+
+static bool force_platform_profile;
+module_param_unsafe(force_platform_profile, bool, 0);
+MODULE_PARM_DESC(force_platform_profile, "Forces auto-detecting thermal profiles without checking if WMI thermal backend is available");
+
+static bool force_gmode;
+module_param_unsafe(force_gmode, bool, 0);
+MODULE_PARM_DESC(force_gmode, "Forces G-Mode when performance profile is selected");
+
+struct awcc_quirks {
+ bool pprof;
+ bool gmode;
+};
+
+static struct awcc_quirks g_series_quirks = {
+ .pprof = true,
+ .gmode = true,
+};
+
+static struct awcc_quirks generic_quirks = {
+ .pprof = true,
+ .gmode = false,
+};
+
+static struct awcc_quirks empty_quirks;
+
+static const struct dmi_system_id awcc_dmi_table[] __initconst = {
+ {
+ .ident = "Alienware Area-51m R2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware Area-51m R2"),
+ },
+ .driver_data = &generic_quirks,
+ },
+ {
+ .ident = "Alienware m15 R7",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m15 R7"),
+ },
+ .driver_data = &generic_quirks,
+ },
+ {
+ .ident = "Alienware m16 R1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m16 R1"),
+ },
+ .driver_data = &g_series_quirks,
+ },
+ {
+ .ident = "Alienware m16 R1 AMD",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m16 R1 AMD"),
+ },
+ .driver_data = &generic_quirks,
+ },
+ {
+ .ident = "Alienware m16 R2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m16 R2"),
+ },
+ .driver_data = &generic_quirks,
+ },
+ {
+ .ident = "Alienware m17 R5",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m17 R5 AMD"),
+ },
+ .driver_data = &generic_quirks,
+ },
+ {
+ .ident = "Alienware m18 R2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m18 R2"),
+ },
+ .driver_data = &generic_quirks,
+ },
+ {
+ .ident = "Alienware x15 R1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1"),
+ },
+ .driver_data = &generic_quirks,
+ },
+ {
+ .ident = "Alienware x15 R2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R2"),
+ },
+ .driver_data = &generic_quirks,
+ },
+ {
+ .ident = "Alienware x17 R2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x17 R2"),
+ },
+ .driver_data = &generic_quirks,
+ },
+ {
+ .ident = "Dell Inc. G15 5510",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5510"),
+ },
+ .driver_data = &g_series_quirks,
+ },
+ {
+ .ident = "Dell Inc. G15 5511",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5511"),
+ },
+ .driver_data = &g_series_quirks,
+ },
+ {
+ .ident = "Dell Inc. G15 5515",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5515"),
+ },
+ .driver_data = &g_series_quirks,
+ },
+ {
+ .ident = "Dell Inc. G16 7630",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Dell G16 7630"),
+ },
+ .driver_data = &g_series_quirks,
+ },
+ {
+ .ident = "Dell Inc. G3 3500",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "G3 3500"),
+ },
+ .driver_data = &g_series_quirks,
+ },
+ {
+ .ident = "Dell Inc. G3 3590",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "G3 3590"),
+ },
+ .driver_data = &g_series_quirks,
+ },
+ {
+ .ident = "Dell Inc. G5 5500",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "G5 5500"),
+ },
+ .driver_data = &g_series_quirks,
+ },
+ {
+ .ident = "Dell Inc. G5 5505",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "G5 5505"),
+ },
+ .driver_data = &g_series_quirks,
+ },
+};
+
+enum WMAX_THERMAL_INFORMATION_OPERATIONS {
+ WMAX_OPERATION_SYS_DESCRIPTION = 0x02,
+ WMAX_OPERATION_LIST_IDS = 0x03,
+ WMAX_OPERATION_CURRENT_PROFILE = 0x0B,
+};
+
+enum WMAX_THERMAL_CONTROL_OPERATIONS {
+ WMAX_OPERATION_ACTIVATE_PROFILE = 0x01,
+};
+
+enum WMAX_GAME_SHIFT_STATUS_OPERATIONS {
+ WMAX_OPERATION_TOGGLE_GAME_SHIFT = 0x01,
+ WMAX_OPERATION_GET_GAME_SHIFT_STATUS = 0x02,
+};
+
+enum WMAX_THERMAL_TABLES {
+ WMAX_THERMAL_TABLE_BASIC = 0x90,
+ WMAX_THERMAL_TABLE_USTT = 0xA0,
+};
+
+enum wmax_thermal_mode {
+ THERMAL_MODE_USTT_BALANCED,
+ THERMAL_MODE_USTT_BALANCED_PERFORMANCE,
+ THERMAL_MODE_USTT_COOL,
+ THERMAL_MODE_USTT_QUIET,
+ THERMAL_MODE_USTT_PERFORMANCE,
+ THERMAL_MODE_USTT_LOW_POWER,
+ THERMAL_MODE_BASIC_QUIET,
+ THERMAL_MODE_BASIC_BALANCED,
+ THERMAL_MODE_BASIC_BALANCED_PERFORMANCE,
+ THERMAL_MODE_BASIC_PERFORMANCE,
+ THERMAL_MODE_LAST,
+};
+
+struct wmax_led_args {
+ u32 led_mask;
+ struct color_platform colors;
+ u8 state;
+} __packed;
+
+struct wmax_brightness_args {
+ u32 led_mask;
+ u32 percentage;
+};
+
+struct wmax_basic_args {
+ u8 arg;
+};
+
+struct wmax_u32_args {
+ u8 operation;
+ u8 arg1;
+ u8 arg2;
+ u8 arg3;
+};
+
+struct awcc_priv {
+ struct wmi_device *wdev;
+ struct device *ppdev;
+ enum wmax_thermal_mode supported_thermal_profiles[PLATFORM_PROFILE_LAST];
+};
+
+static const enum platform_profile_option wmax_mode_to_platform_profile[THERMAL_MODE_LAST] = {
+ [THERMAL_MODE_USTT_BALANCED] = PLATFORM_PROFILE_BALANCED,
+ [THERMAL_MODE_USTT_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
+ [THERMAL_MODE_USTT_COOL] = PLATFORM_PROFILE_COOL,
+ [THERMAL_MODE_USTT_QUIET] = PLATFORM_PROFILE_QUIET,
+ [THERMAL_MODE_USTT_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
+ [THERMAL_MODE_USTT_LOW_POWER] = PLATFORM_PROFILE_LOW_POWER,
+ [THERMAL_MODE_BASIC_QUIET] = PLATFORM_PROFILE_QUIET,
+ [THERMAL_MODE_BASIC_BALANCED] = PLATFORM_PROFILE_BALANCED,
+ [THERMAL_MODE_BASIC_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
+ [THERMAL_MODE_BASIC_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
+};
+
+static struct awcc_quirks *awcc;
+
+/*
+ * The HDMI mux sysfs node indicates the status of the HDMI input mux.
+ * It can toggle between standard system GPU output and HDMI input.
+ */
+static ssize_t cable_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct alienfx_platdata *pdata = dev_get_platdata(dev);
+ struct wmax_basic_args in_args = {
+ .arg = 0,
+ };
+ u32 out_data;
+ int ret;
+
+ ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_CABLE,
+ &in_args, sizeof(in_args), &out_data);
+ if (!ret) {
+ if (out_data == 0)
+ return sysfs_emit(buf, "[unconnected] connected unknown\n");
+ else if (out_data == 1)
+ return sysfs_emit(buf, "unconnected [connected] unknown\n");
+ }
+
+ pr_err("alienware-wmi: unknown HDMI cable status: %d\n", ret);
+ return sysfs_emit(buf, "unconnected connected [unknown]\n");
+}
+
+static ssize_t source_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct alienfx_platdata *pdata = dev_get_platdata(dev);
+ struct wmax_basic_args in_args = {
+ .arg = 0,
+ };
+ u32 out_data;
+ int ret;
+
+ ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_STATUS,
+ &in_args, sizeof(in_args), &out_data);
+ if (!ret) {
+ if (out_data == 1)
+ return sysfs_emit(buf, "[input] gpu unknown\n");
+ else if (out_data == 2)
+ return sysfs_emit(buf, "input [gpu] unknown\n");
+ }
+
+ pr_err("alienware-wmi: unknown HDMI source status: %u\n", ret);
+ return sysfs_emit(buf, "input gpu [unknown]\n");
+}
+
+static ssize_t source_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct alienfx_platdata *pdata = dev_get_platdata(dev);
+ struct wmax_basic_args args;
+ int ret;
+
+ if (strcmp(buf, "gpu\n") == 0)
+ args.arg = 1;
+ else if (strcmp(buf, "input\n") == 0)
+ args.arg = 2;
+ else
+ args.arg = 3;
+ pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
+
+ ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_HDMI_SOURCE, &args,
+ sizeof(args), NULL);
+ if (ret < 0)
+ pr_err("alienware-wmi: HDMI toggle failed: results: %u\n", ret);
+
+ return count;
+}
+
+static DEVICE_ATTR_RO(cable);
+static DEVICE_ATTR_RW(source);
+
+static bool hdmi_group_visible(struct kobject *kobj)
+{
+ return alienware_interface == WMAX && alienfx->hdmi_mux;
+}
+DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(hdmi);
+
+static struct attribute *hdmi_attrs[] = {
+ &dev_attr_cable.attr,
+ &dev_attr_source.attr,
+ NULL,
+};
+
+const struct attribute_group wmax_hdmi_attribute_group = {
+ .name = "hdmi",
+ .is_visible = SYSFS_GROUP_VISIBLE(hdmi),
+ .attrs = hdmi_attrs,
+};
+
+/*
+ * Alienware GFX amplifier support
+ * - Currently supports reading cable status
+ * - Leaving expansion room to possibly support dock/undock events later
+ */
+static ssize_t status_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct alienfx_platdata *pdata = dev_get_platdata(dev);
+ struct wmax_basic_args in_args = {
+ .arg = 0,
+ };
+ u32 out_data;
+ int ret;
+
+ ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_AMPLIFIER_CABLE,
+ &in_args, sizeof(in_args), &out_data);
+ if (!ret) {
+ if (out_data == 0)
+ return sysfs_emit(buf, "[unconnected] connected unknown\n");
+ else if (out_data == 1)
+ return sysfs_emit(buf, "unconnected [connected] unknown\n");
+ }
+
+ pr_err("alienware-wmi: unknown amplifier cable status: %d\n", ret);
+ return sysfs_emit(buf, "unconnected connected [unknown]\n");
+}
+
+static DEVICE_ATTR_RO(status);
+
+static bool amplifier_group_visible(struct kobject *kobj)
+{
+ return alienware_interface == WMAX && alienfx->amplifier;
+}
+DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(amplifier);
+
+static struct attribute *amplifier_attrs[] = {
+ &dev_attr_status.attr,
+ NULL,
+};
+
+const struct attribute_group wmax_amplifier_attribute_group = {
+ .name = "amplifier",
+ .is_visible = SYSFS_GROUP_VISIBLE(amplifier),
+ .attrs = amplifier_attrs,
+};
+
+/*
+ * Deep Sleep Control support
+ * - Modifies BIOS setting for deep sleep control allowing extra wakeup events
+ */
+static ssize_t deepsleep_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct alienfx_platdata *pdata = dev_get_platdata(dev);
+ struct wmax_basic_args in_args = {
+ .arg = 0,
+ };
+ u32 out_data;
+ int ret;
+
+ ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_DEEP_SLEEP_STATUS,
+ &in_args, sizeof(in_args), &out_data);
+ if (!ret) {
+ if (out_data == 0)
+ return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
+ else if (out_data == 1)
+ return sysfs_emit(buf, "disabled [s5] s5_s4\n");
+ else if (out_data == 2)
+ return sysfs_emit(buf, "disabled s5 [s5_s4]\n");
+ }
+
+ pr_err("alienware-wmi: unknown deep sleep status: %d\n", ret);
+ return sysfs_emit(buf, "disabled s5 s5_s4 [unknown]\n");
+}
+
+static ssize_t deepsleep_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct alienfx_platdata *pdata = dev_get_platdata(dev);
+ struct wmax_basic_args args;
+ int ret;
+
+ if (strcmp(buf, "disabled\n") == 0)
+ args.arg = 0;
+ else if (strcmp(buf, "s5\n") == 0)
+ args.arg = 1;
+ else
+ args.arg = 2;
+ pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
+
+ ret = alienware_wmi_command(pdata->wdev, WMAX_METHOD_DEEP_SLEEP_CONTROL,
+ &args, sizeof(args), NULL);
+ if (!ret)
+ pr_err("alienware-wmi: deep sleep control failed: results: %u\n", ret);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(deepsleep);
+
+static bool deepsleep_group_visible(struct kobject *kobj)
+{
+ return alienware_interface == WMAX && alienfx->deepslp;
+}
+DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(deepsleep);
+
+static struct attribute *deepsleep_attrs[] = {
+ &dev_attr_deepsleep.attr,
+ NULL,
+};
+
+const struct attribute_group wmax_deepsleep_attribute_group = {
+ .name = "deepsleep",
+ .is_visible = SYSFS_GROUP_VISIBLE(deepsleep),
+ .attrs = deepsleep_attrs,
+};
+
+/*
+ * Thermal Profile control
+ * - Provides thermal profile control through the Platform Profile API
+ */
+static bool is_wmax_thermal_code(u32 code)
+{
+ if (code & WMAX_SENSOR_ID_MASK)
+ return false;
+
+ if ((code & WMAX_THERMAL_MODE_MASK) >= THERMAL_MODE_LAST)
+ return false;
+
+ if ((code & WMAX_THERMAL_TABLE_MASK) == WMAX_THERMAL_TABLE_BASIC &&
+ (code & WMAX_THERMAL_MODE_MASK) >= THERMAL_MODE_BASIC_QUIET)
+ return true;
+
+ if ((code & WMAX_THERMAL_TABLE_MASK) == WMAX_THERMAL_TABLE_USTT &&
+ (code & WMAX_THERMAL_MODE_MASK) <= THERMAL_MODE_USTT_LOW_POWER)
+ return true;
+
+ return false;
+}
+
+static int wmax_thermal_information(struct wmi_device *wdev, u8 operation,
+ u8 arg, u32 *out_data)
+{
+ struct wmax_u32_args in_args = {
+ .operation = operation,
+ .arg1 = arg,
+ .arg2 = 0,
+ .arg3 = 0,
+ };
+ int ret;
+
+ ret = alienware_wmi_command(wdev, WMAX_METHOD_THERMAL_INFORMATION,
+ &in_args, sizeof(in_args), out_data);
+ if (ret < 0)
+ return ret;
+
+ if (*out_data == WMAX_FAILURE_CODE)
+ return -EBADRQC;
+
+ return 0;
+}
+
+static int wmax_thermal_control(struct wmi_device *wdev, u8 profile)
+{
+ struct wmax_u32_args in_args = {
+ .operation = WMAX_OPERATION_ACTIVATE_PROFILE,
+ .arg1 = profile,
+ .arg2 = 0,
+ .arg3 = 0,
+ };
+ u32 out_data;
+ int ret;
+
+ ret = alienware_wmi_command(wdev, WMAX_METHOD_THERMAL_CONTROL,
+ &in_args, sizeof(in_args), &out_data);
+ if (ret)
+ return ret;
+
+ if (out_data == WMAX_FAILURE_CODE)
+ return -EBADRQC;
+
+ return 0;
+}
+
+static int wmax_game_shift_status(struct wmi_device *wdev, u8 operation,
+ u32 *out_data)
+{
+ struct wmax_u32_args in_args = {
+ .operation = operation,
+ .arg1 = 0,
+ .arg2 = 0,
+ .arg3 = 0,
+ };
+ int ret;
+
+ ret = alienware_wmi_command(wdev, WMAX_METHOD_GAME_SHIFT_STATUS,
+ &in_args, sizeof(in_args), out_data);
+ if (ret < 0)
+ return ret;
+
+ if (*out_data == WMAX_FAILURE_CODE)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static int thermal_profile_get(struct device *dev,
+ enum platform_profile_option *profile)
+{
+ struct awcc_priv *priv = dev_get_drvdata(dev);
+ u32 out_data;
+ int ret;
+
+ ret = wmax_thermal_information(priv->wdev, WMAX_OPERATION_CURRENT_PROFILE,
+ 0, &out_data);
+
+ if (ret < 0)
+ return ret;
+
+ if (out_data == WMAX_THERMAL_MODE_GMODE) {
+ *profile = PLATFORM_PROFILE_PERFORMANCE;
+ return 0;
+ }
+
+ if (!is_wmax_thermal_code(out_data))
+ return -ENODATA;
+
+ out_data &= WMAX_THERMAL_MODE_MASK;
+ *profile = wmax_mode_to_platform_profile[out_data];
+
+ return 0;
+}
+
+static int thermal_profile_set(struct device *dev,
+ enum platform_profile_option profile)
+{
+ struct awcc_priv *priv = dev_get_drvdata(dev);
+
+ if (awcc->gmode) {
+ u32 gmode_status;
+ int ret;
+
+ ret = wmax_game_shift_status(priv->wdev,
+ WMAX_OPERATION_GET_GAME_SHIFT_STATUS,
+ &gmode_status);
+
+ if (ret < 0)
+ return ret;
+
+ if ((profile == PLATFORM_PROFILE_PERFORMANCE && !gmode_status) ||
+ (profile != PLATFORM_PROFILE_PERFORMANCE && gmode_status)) {
+ ret = wmax_game_shift_status(priv->wdev,
+ WMAX_OPERATION_TOGGLE_GAME_SHIFT,
+ &gmode_status);
+
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return wmax_thermal_control(priv->wdev,
+ priv->supported_thermal_profiles[profile]);
+}
+
+static int thermal_profile_probe(void *drvdata, unsigned long *choices)
+{
+ enum platform_profile_option profile;
+ struct awcc_priv *priv = drvdata;
+ enum wmax_thermal_mode mode;
+ u8 sys_desc[4];
+ u32 first_mode;
+ u32 out_data;
+ int ret;
+
+ ret = wmax_thermal_information(priv->wdev, WMAX_OPERATION_SYS_DESCRIPTION,
+ 0, (u32 *) &sys_desc);
+ if (ret < 0)
+ return ret;
+
+ first_mode = sys_desc[0] + sys_desc[1];
+
+ for (u32 i = 0; i < sys_desc[3]; i++) {
+ ret = wmax_thermal_information(priv->wdev, WMAX_OPERATION_LIST_IDS,
+ i + first_mode, &out_data);
+ if (ret == -EBADRQC)
+ break;
+ if (ret)
+ return ret;
+
+ if (!is_wmax_thermal_code(out_data))
+ continue;
+
+ mode = out_data & WMAX_THERMAL_MODE_MASK;
+ profile = wmax_mode_to_platform_profile[mode];
+ priv->supported_thermal_profiles[profile] = out_data;
+
+ set_bit(profile, choices);
+ }
+
+ if (bitmap_empty(choices, PLATFORM_PROFILE_LAST))
+ return -ENODEV;
+
+ if (awcc->gmode) {
+ priv->supported_thermal_profiles[PLATFORM_PROFILE_PERFORMANCE] =
+ WMAX_THERMAL_MODE_GMODE;
+
+ set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
+ }
+
+ return 0;
+}
+
+static const struct platform_profile_ops awcc_platform_profile_ops = {
+ .probe = thermal_profile_probe,
+ .profile_get = thermal_profile_get,
+ .profile_set = thermal_profile_set,
+};
+
+static int awcc_platform_profile_init(struct wmi_device *wdev)
+{
+ struct awcc_priv *priv = dev_get_drvdata(&wdev->dev);
+
+ priv->ppdev = devm_platform_profile_register(&wdev->dev, "alienware-wmi",
+ priv, &awcc_platform_profile_ops);
+
+ return PTR_ERR_OR_ZERO(priv->ppdev);
+}
+
+static int alienware_awcc_setup(struct wmi_device *wdev)
+{
+ struct awcc_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->wdev = wdev;
+ dev_set_drvdata(&wdev->dev, priv);
+
+ if (awcc->pprof) {
+ ret = awcc_platform_profile_init(wdev);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * WMAX WMI driver
+ */
+static int wmax_wmi_update_led(struct alienfx_priv *priv,
+ struct wmi_device *wdev, u8 location)
+{
+ struct wmax_led_args in_args = {
+ .led_mask = 1 << location,
+ .colors = priv->colors[location],
+ .state = priv->lighting_control_state,
+ };
+
+ return alienware_wmi_command(wdev, WMAX_METHOD_ZONE_CONTROL, &in_args,
+ sizeof(in_args), NULL);
+}
+
+static int wmax_wmi_update_brightness(struct alienfx_priv *priv,
+ struct wmi_device *wdev, u8 brightness)
+{
+ struct wmax_brightness_args in_args = {
+ .led_mask = 0xFF,
+ .percentage = brightness,
+ };
+
+ return alienware_wmi_command(wdev, WMAX_METHOD_BRIGHTNESS, &in_args,
+ sizeof(in_args), NULL);
+}
+
+static int wmax_wmi_probe(struct wmi_device *wdev, const void *context)
+{
+ struct alienfx_platdata pdata = {
+ .wdev = wdev,
+ .ops = {
+ .upd_led = wmax_wmi_update_led,
+ .upd_brightness = wmax_wmi_update_brightness,
+ },
+ };
+ int ret;
+
+ if (awcc)
+ ret = alienware_awcc_setup(wdev);
+ else
+ ret = alienware_alienfx_setup(&pdata);
+
+ return ret;
+}
+
+static const struct wmi_device_id alienware_wmax_device_id_table[] = {
+ { WMAX_CONTROL_GUID, NULL },
+ { },
+};
+MODULE_DEVICE_TABLE(wmi, alienware_wmax_device_id_table);
+
+static struct wmi_driver alienware_wmax_wmi_driver = {
+ .driver = {
+ .name = "alienware-wmi-wmax",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .id_table = alienware_wmax_device_id_table,
+ .probe = wmax_wmi_probe,
+ .no_singleton = true,
+};
+
+int __init alienware_wmax_wmi_init(void)
+{
+ const struct dmi_system_id *id;
+
+ id = dmi_first_match(awcc_dmi_table);
+ if (id)
+ awcc = id->driver_data;
+
+ if (force_platform_profile) {
+ if (!awcc)
+ awcc = &empty_quirks;
+
+ awcc->pprof = true;
+ }
+
+ if (force_gmode) {
+ if (awcc)
+ awcc->gmode = true;
+ else
+ pr_warn("force_gmode requires platform profile support\n");
+ }
+
+ return wmi_driver_register(&alienware_wmax_wmi_driver);
+}
+
+void __exit alienware_wmax_wmi_exit(void)
+{
+ wmi_driver_unregister(&alienware_wmax_wmi_driver);
+}
diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
deleted file mode 100644
index 341d01d3e3e4..000000000000
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ /dev/null
@@ -1,1281 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Alienware AlienFX control
- *
- * Copyright (C) 2014 Dell Inc <Dell.Client.Kernel@dell.com>
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/acpi.h>
-#include <linux/bitfield.h>
-#include <linux/bits.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/platform_profile.h>
-#include <linux/dmi.h>
-#include <linux/leds.h>
-
-#define LEGACY_CONTROL_GUID "A90597CE-A997-11DA-B012-B622A1EF5492"
-#define LEGACY_POWER_CONTROL_GUID "A80593CE-A997-11DA-B012-B622A1EF5492"
-#define WMAX_CONTROL_GUID "A70591CE-A997-11DA-B012-B622A1EF5492"
-
-#define WMAX_METHOD_HDMI_SOURCE 0x1
-#define WMAX_METHOD_HDMI_STATUS 0x2
-#define WMAX_METHOD_BRIGHTNESS 0x3
-#define WMAX_METHOD_ZONE_CONTROL 0x4
-#define WMAX_METHOD_HDMI_CABLE 0x5
-#define WMAX_METHOD_AMPLIFIER_CABLE 0x6
-#define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B
-#define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C
-#define WMAX_METHOD_THERMAL_INFORMATION 0x14
-#define WMAX_METHOD_THERMAL_CONTROL 0x15
-#define WMAX_METHOD_GAME_SHIFT_STATUS 0x25
-
-#define WMAX_THERMAL_MODE_GMODE 0xAB
-
-#define WMAX_FAILURE_CODE 0xFFFFFFFF
-
-MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
-MODULE_DESCRIPTION("Alienware special feature control");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("wmi:" LEGACY_CONTROL_GUID);
-MODULE_ALIAS("wmi:" WMAX_CONTROL_GUID);
-
-static bool force_platform_profile;
-module_param_unsafe(force_platform_profile, bool, 0);
-MODULE_PARM_DESC(force_platform_profile, "Forces auto-detecting thermal profiles without checking if WMI thermal backend is available");
-
-static bool force_gmode;
-module_param_unsafe(force_gmode, bool, 0);
-MODULE_PARM_DESC(force_gmode, "Forces G-Mode when performance profile is selected");
-
-enum INTERFACE_FLAGS {
- LEGACY,
- WMAX,
-};
-
-enum LEGACY_CONTROL_STATES {
- LEGACY_RUNNING = 1,
- LEGACY_BOOTING = 0,
- LEGACY_SUSPEND = 3,
-};
-
-enum WMAX_CONTROL_STATES {
- WMAX_RUNNING = 0xFF,
- WMAX_BOOTING = 0,
- WMAX_SUSPEND = 3,
-};
-
-enum WMAX_THERMAL_INFORMATION_OPERATIONS {
- WMAX_OPERATION_SYS_DESCRIPTION = 0x02,
- WMAX_OPERATION_LIST_IDS = 0x03,
- WMAX_OPERATION_CURRENT_PROFILE = 0x0B,
-};
-
-enum WMAX_THERMAL_CONTROL_OPERATIONS {
- WMAX_OPERATION_ACTIVATE_PROFILE = 0x01,
-};
-
-enum WMAX_GAME_SHIFT_STATUS_OPERATIONS {
- WMAX_OPERATION_TOGGLE_GAME_SHIFT = 0x01,
- WMAX_OPERATION_GET_GAME_SHIFT_STATUS = 0x02,
-};
-
-enum WMAX_THERMAL_TABLES {
- WMAX_THERMAL_TABLE_BASIC = 0x90,
- WMAX_THERMAL_TABLE_USTT = 0xA0,
-};
-
-enum wmax_thermal_mode {
- THERMAL_MODE_USTT_BALANCED,
- THERMAL_MODE_USTT_BALANCED_PERFORMANCE,
- THERMAL_MODE_USTT_COOL,
- THERMAL_MODE_USTT_QUIET,
- THERMAL_MODE_USTT_PERFORMANCE,
- THERMAL_MODE_USTT_LOW_POWER,
- THERMAL_MODE_BASIC_QUIET,
- THERMAL_MODE_BASIC_BALANCED,
- THERMAL_MODE_BASIC_BALANCED_PERFORMANCE,
- THERMAL_MODE_BASIC_PERFORMANCE,
- THERMAL_MODE_LAST,
-};
-
-static const enum platform_profile_option wmax_mode_to_platform_profile[THERMAL_MODE_LAST] = {
- [THERMAL_MODE_USTT_BALANCED] = PLATFORM_PROFILE_BALANCED,
- [THERMAL_MODE_USTT_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
- [THERMAL_MODE_USTT_COOL] = PLATFORM_PROFILE_COOL,
- [THERMAL_MODE_USTT_QUIET] = PLATFORM_PROFILE_QUIET,
- [THERMAL_MODE_USTT_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
- [THERMAL_MODE_USTT_LOW_POWER] = PLATFORM_PROFILE_LOW_POWER,
- [THERMAL_MODE_BASIC_QUIET] = PLATFORM_PROFILE_QUIET,
- [THERMAL_MODE_BASIC_BALANCED] = PLATFORM_PROFILE_BALANCED,
- [THERMAL_MODE_BASIC_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE,
- [THERMAL_MODE_BASIC_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE,
-};
-
-struct quirk_entry {
- u8 num_zones;
- u8 hdmi_mux;
- u8 amplifier;
- u8 deepslp;
- bool thermal;
- bool gmode;
-};
-
-static struct quirk_entry *quirks;
-
-
-static struct quirk_entry quirk_inspiron5675 = {
- .num_zones = 2,
- .hdmi_mux = 0,
- .amplifier = 0,
- .deepslp = 0,
- .thermal = false,
- .gmode = false,
-};
-
-static struct quirk_entry quirk_unknown = {
- .num_zones = 2,
- .hdmi_mux = 0,
- .amplifier = 0,
- .deepslp = 0,
- .thermal = false,
- .gmode = false,
-};
-
-static struct quirk_entry quirk_x51_r1_r2 = {
- .num_zones = 3,
- .hdmi_mux = 0,
- .amplifier = 0,
- .deepslp = 0,
- .thermal = false,
- .gmode = false,
-};
-
-static struct quirk_entry quirk_x51_r3 = {
- .num_zones = 4,
- .hdmi_mux = 0,
- .amplifier = 1,
- .deepslp = 0,
- .thermal = false,
- .gmode = false,
-};
-
-static struct quirk_entry quirk_asm100 = {
- .num_zones = 2,
- .hdmi_mux = 1,
- .amplifier = 0,
- .deepslp = 0,
- .thermal = false,
- .gmode = false,
-};
-
-static struct quirk_entry quirk_asm200 = {
- .num_zones = 2,
- .hdmi_mux = 1,
- .amplifier = 0,
- .deepslp = 1,
- .thermal = false,
- .gmode = false,
-};
-
-static struct quirk_entry quirk_asm201 = {
- .num_zones = 2,
- .hdmi_mux = 1,
- .amplifier = 1,
- .deepslp = 1,
- .thermal = false,
- .gmode = false,
-};
-
-static struct quirk_entry quirk_g_series = {
- .num_zones = 0,
- .hdmi_mux = 0,
- .amplifier = 0,
- .deepslp = 0,
- .thermal = true,
- .gmode = true,
-};
-
-static struct quirk_entry quirk_x_series = {
- .num_zones = 0,
- .hdmi_mux = 0,
- .amplifier = 0,
- .deepslp = 0,
- .thermal = true,
- .gmode = false,
-};
-
-static int __init dmi_matched(const struct dmi_system_id *dmi)
-{
- quirks = dmi->driver_data;
- return 1;
-}
-
-static const struct dmi_system_id alienware_quirks[] __initconst = {
- {
- .callback = dmi_matched,
- .ident = "Alienware ASM100",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
- },
- .driver_data = &quirk_asm100,
- },
- {
- .callback = dmi_matched,
- .ident = "Alienware ASM200",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
- },
- .driver_data = &quirk_asm200,
- },
- {
- .callback = dmi_matched,
- .ident = "Alienware ASM201",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
- },
- .driver_data = &quirk_asm201,
- },
- {
- .callback = dmi_matched,
- .ident = "Alienware m16 R1 AMD",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m16 R1 AMD"),
- },
- .driver_data = &quirk_x_series,
- },
- {
- .callback = dmi_matched,
- .ident = "Alienware m17 R5",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m17 R5 AMD"),
- },
- .driver_data = &quirk_x_series,
- },
- {
- .callback = dmi_matched,
- .ident = "Alienware m18 R2",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m18 R2"),
- },
- .driver_data = &quirk_x_series,
- },
- {
- .callback = dmi_matched,
- .ident = "Alienware x15 R1",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1"),
- },
- .driver_data = &quirk_x_series,
- },
- {
- .callback = dmi_matched,
- .ident = "Alienware x17 R2",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x17 R2"),
- },
- .driver_data = &quirk_x_series,
- },
- {
- .callback = dmi_matched,
- .ident = "Alienware X51 R1",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
- },
- .driver_data = &quirk_x51_r1_r2,
- },
- {
- .callback = dmi_matched,
- .ident = "Alienware X51 R2",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
- },
- .driver_data = &quirk_x51_r1_r2,
- },
- {
- .callback = dmi_matched,
- .ident = "Alienware X51 R3",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
- },
- .driver_data = &quirk_x51_r3,
- },
- {
- .callback = dmi_matched,
- .ident = "Dell Inc. G15 5510",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5510"),
- },
- .driver_data = &quirk_g_series,
- },
- {
- .callback = dmi_matched,
- .ident = "Dell Inc. G15 5511",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5511"),
- },
- .driver_data = &quirk_g_series,
- },
- {
- .callback = dmi_matched,
- .ident = "Dell Inc. G15 5515",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5515"),
- },
- .driver_data = &quirk_g_series,
- },
- {
- .callback = dmi_matched,
- .ident = "Dell Inc. G3 3500",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "G3 3500"),
- },
- .driver_data = &quirk_g_series,
- },
- {
- .callback = dmi_matched,
- .ident = "Dell Inc. G3 3590",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "G3 3590"),
- },
- .driver_data = &quirk_g_series,
- },
- {
- .callback = dmi_matched,
- .ident = "Dell Inc. G5 5500",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "G5 5500"),
- },
- .driver_data = &quirk_g_series,
- },
- {
- .callback = dmi_matched,
- .ident = "Dell Inc. Inspiron 5675",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
- },
- .driver_data = &quirk_inspiron5675,
- },
- {}
-};
-
-struct color_platform {
- u8 blue;
- u8 green;
- u8 red;
-} __packed;
-
-struct platform_zone {
- u8 location;
- struct device_attribute *attr;
- struct color_platform colors;
-};
-
-struct wmax_brightness_args {
- u32 led_mask;
- u32 percentage;
-};
-
-struct wmax_basic_args {
- u8 arg;
-};
-
-struct legacy_led_args {
- struct color_platform colors;
- u8 brightness;
- u8 state;
-} __packed;
-
-struct wmax_led_args {
- u32 led_mask;
- struct color_platform colors;
- u8 state;
-} __packed;
-
-struct wmax_u32_args {
- u8 operation;
- u8 arg1;
- u8 arg2;
- u8 arg3;
-};
-
-static struct platform_device *platform_device;
-static struct device_attribute *zone_dev_attrs;
-static struct attribute **zone_attrs;
-static struct platform_zone *zone_data;
-static struct platform_profile_handler pp_handler;
-static enum wmax_thermal_mode supported_thermal_profiles[PLATFORM_PROFILE_LAST];
-
-static struct platform_driver platform_driver = {
- .driver = {
- .name = "alienware-wmi",
- }
-};
-
-static struct attribute_group zone_attribute_group = {
- .name = "rgb_zones",
-};
-
-static u8 interface;
-static u8 lighting_control_state;
-static u8 global_brightness;
-
-/*
- * Helpers used for zone control
- */
-static int parse_rgb(const char *buf, struct platform_zone *zone)
-{
- long unsigned int rgb;
- int ret;
- union color_union {
- struct color_platform cp;
- int package;
- } repackager;
-
- ret = kstrtoul(buf, 16, &rgb);
- if (ret)
- return ret;
-
- /* RGB triplet notation is 24-bit hexadecimal */
- if (rgb > 0xFFFFFF)
- return -EINVAL;
-
- repackager.package = rgb & 0x0f0f0f0f;
- pr_debug("alienware-wmi: r: %d g:%d b: %d\n",
- repackager.cp.red, repackager.cp.green, repackager.cp.blue);
- zone->colors = repackager.cp;
- return 0;
-}
-
-static struct platform_zone *match_zone(struct device_attribute *attr)
-{
- u8 zone;
-
- for (zone = 0; zone < quirks->num_zones; zone++) {
- if ((struct device_attribute *)zone_data[zone].attr == attr) {
- pr_debug("alienware-wmi: matched zone location: %d\n",
- zone_data[zone].location);
- return &zone_data[zone];
- }
- }
- return NULL;
-}
-
-/*
- * Individual RGB zone control
- */
-static int alienware_update_led(struct platform_zone *zone)
-{
- int method_id;
- acpi_status status;
- char *guid;
- struct acpi_buffer input;
- struct legacy_led_args legacy_args;
- struct wmax_led_args wmax_basic_args;
- if (interface == WMAX) {
- wmax_basic_args.led_mask = 1 << zone->location;
- wmax_basic_args.colors = zone->colors;
- wmax_basic_args.state = lighting_control_state;
- guid = WMAX_CONTROL_GUID;
- method_id = WMAX_METHOD_ZONE_CONTROL;
-
- input.length = sizeof(wmax_basic_args);
- input.pointer = &wmax_basic_args;
- } else {
- legacy_args.colors = zone->colors;
- legacy_args.brightness = global_brightness;
- legacy_args.state = 0;
- if (lighting_control_state == LEGACY_BOOTING ||
- lighting_control_state == LEGACY_SUSPEND) {
- guid = LEGACY_POWER_CONTROL_GUID;
- legacy_args.state = lighting_control_state;
- } else
- guid = LEGACY_CONTROL_GUID;
- method_id = zone->location + 1;
-
- input.length = sizeof(legacy_args);
- input.pointer = &legacy_args;
- }
- pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id);
-
- status = wmi_evaluate_method(guid, 0, method_id, &input, NULL);
- if (ACPI_FAILURE(status))
- pr_err("alienware-wmi: zone set failure: %u\n", status);
- return ACPI_FAILURE(status);
-}
-
-static ssize_t zone_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct platform_zone *target_zone;
- target_zone = match_zone(attr);
- if (target_zone == NULL)
- return sprintf(buf, "red: -1, green: -1, blue: -1\n");
- return sprintf(buf, "red: %d, green: %d, blue: %d\n",
- target_zone->colors.red,
- target_zone->colors.green, target_zone->colors.blue);
-
-}
-
-static ssize_t zone_set(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct platform_zone *target_zone;
- int ret;
- target_zone = match_zone(attr);
- if (target_zone == NULL) {
- pr_err("alienware-wmi: invalid target zone\n");
- return 1;
- }
- ret = parse_rgb(buf, target_zone);
- if (ret)
- return ret;
- ret = alienware_update_led(target_zone);
- return ret ? ret : count;
-}
-
-/*
- * LED Brightness (Global)
- */
-static int wmax_brightness(int brightness)
-{
- acpi_status status;
- struct acpi_buffer input;
- struct wmax_brightness_args args = {
- .led_mask = 0xFF,
- .percentage = brightness,
- };
- input.length = sizeof(args);
- input.pointer = &args;
- status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
- WMAX_METHOD_BRIGHTNESS, &input, NULL);
- if (ACPI_FAILURE(status))
- pr_err("alienware-wmi: brightness set failure: %u\n", status);
- return ACPI_FAILURE(status);
-}
-
-static void global_led_set(struct led_classdev *led_cdev,
- enum led_brightness brightness)
-{
- int ret;
- global_brightness = brightness;
- if (interface == WMAX)
- ret = wmax_brightness(brightness);
- else
- ret = alienware_update_led(&zone_data[0]);
- if (ret)
- pr_err("LED brightness update failed\n");
-}
-
-static enum led_brightness global_led_get(struct led_classdev *led_cdev)
-{
- return global_brightness;
-}
-
-static struct led_classdev global_led = {
- .brightness_set = global_led_set,
- .brightness_get = global_led_get,
- .name = "alienware::global_brightness",
-};
-
-/*
- * Lighting control state device attribute (Global)
- */
-static ssize_t show_control_state(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- if (lighting_control_state == LEGACY_BOOTING)
- return sysfs_emit(buf, "[booting] running suspend\n");
- else if (lighting_control_state == LEGACY_SUSPEND)
- return sysfs_emit(buf, "booting running [suspend]\n");
- return sysfs_emit(buf, "booting [running] suspend\n");
-}
-
-static ssize_t store_control_state(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- long unsigned int val;
- if (strcmp(buf, "booting\n") == 0)
- val = LEGACY_BOOTING;
- else if (strcmp(buf, "suspend\n") == 0)
- val = LEGACY_SUSPEND;
- else if (interface == LEGACY)
- val = LEGACY_RUNNING;
- else
- val = WMAX_RUNNING;
- lighting_control_state = val;
- pr_debug("alienware-wmi: updated control state to %d\n",
- lighting_control_state);
- return count;
-}
-
-static DEVICE_ATTR(lighting_control_state, 0644, show_control_state,
- store_control_state);
-
-static int alienware_zone_init(struct platform_device *dev)
-{
- u8 zone;
- char *name;
-
- if (interface == WMAX) {
- lighting_control_state = WMAX_RUNNING;
- } else if (interface == LEGACY) {
- lighting_control_state = LEGACY_RUNNING;
- }
- global_led.max_brightness = 0x0F;
- global_brightness = global_led.max_brightness;
-
- /*
- * - zone_dev_attrs num_zones + 1 is for individual zones and then
- * null terminated
- * - zone_attrs num_zones + 2 is for all attrs in zone_dev_attrs +
- * the lighting control + null terminated
- * - zone_data num_zones is for the distinct zones
- */
- zone_dev_attrs =
- kcalloc(quirks->num_zones + 1, sizeof(struct device_attribute),
- GFP_KERNEL);
- if (!zone_dev_attrs)
- return -ENOMEM;
-
- zone_attrs =
- kcalloc(quirks->num_zones + 2, sizeof(struct attribute *),
- GFP_KERNEL);
- if (!zone_attrs)
- return -ENOMEM;
-
- zone_data =
- kcalloc(quirks->num_zones, sizeof(struct platform_zone),
- GFP_KERNEL);
- if (!zone_data)
- return -ENOMEM;
-
- for (zone = 0; zone < quirks->num_zones; zone++) {
- name = kasprintf(GFP_KERNEL, "zone%02hhX", zone);
- if (name == NULL)
- return 1;
- sysfs_attr_init(&zone_dev_attrs[zone].attr);
- zone_dev_attrs[zone].attr.name = name;
- zone_dev_attrs[zone].attr.mode = 0644;
- zone_dev_attrs[zone].show = zone_show;
- zone_dev_attrs[zone].store = zone_set;
- zone_data[zone].location = zone;
- zone_attrs[zone] = &zone_dev_attrs[zone].attr;
- zone_data[zone].attr = &zone_dev_attrs[zone];
- }
- zone_attrs[quirks->num_zones] = &dev_attr_lighting_control_state.attr;
- zone_attribute_group.attrs = zone_attrs;
-
- led_classdev_register(&dev->dev, &global_led);
-
- return sysfs_create_group(&dev->dev.kobj, &zone_attribute_group);
-}
-
-static void alienware_zone_exit(struct platform_device *dev)
-{
- u8 zone;
-
- if (!quirks->num_zones)
- return;
-
- sysfs_remove_group(&dev->dev.kobj, &zone_attribute_group);
- led_classdev_unregister(&global_led);
- if (zone_dev_attrs) {
- for (zone = 0; zone < quirks->num_zones; zone++)
- kfree(zone_dev_attrs[zone].attr.name);
- }
- kfree(zone_dev_attrs);
- kfree(zone_data);
- kfree(zone_attrs);
-}
-
-static acpi_status alienware_wmax_command(void *in_args, size_t in_size,
- u32 command, u32 *out_data)
-{
- acpi_status status;
- union acpi_object *obj;
- struct acpi_buffer input;
- struct acpi_buffer output;
-
- input.length = in_size;
- input.pointer = in_args;
- if (out_data) {
- output.length = ACPI_ALLOCATE_BUFFER;
- output.pointer = NULL;
- status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
- command, &input, &output);
- if (ACPI_SUCCESS(status)) {
- obj = (union acpi_object *)output.pointer;
- if (obj && obj->type == ACPI_TYPE_INTEGER)
- *out_data = (u32)obj->integer.value;
- }
- kfree(output.pointer);
- } else {
- status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
- command, &input, NULL);
- }
- return status;
-}
-
-/*
- * The HDMI mux sysfs node indicates the status of the HDMI input mux.
- * It can toggle between standard system GPU output and HDMI input.
- */
-static ssize_t show_hdmi_cable(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- acpi_status status;
- u32 out_data;
- struct wmax_basic_args in_args = {
- .arg = 0,
- };
- status =
- alienware_wmax_command(&in_args, sizeof(in_args),
- WMAX_METHOD_HDMI_CABLE, &out_data);
- if (ACPI_SUCCESS(status)) {
- if (out_data == 0)
- return sysfs_emit(buf, "[unconnected] connected unknown\n");
- else if (out_data == 1)
- return sysfs_emit(buf, "unconnected [connected] unknown\n");
- }
- pr_err("alienware-wmi: unknown HDMI cable status: %d\n", status);
- return sysfs_emit(buf, "unconnected connected [unknown]\n");
-}
-
-static ssize_t show_hdmi_source(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- acpi_status status;
- u32 out_data;
- struct wmax_basic_args in_args = {
- .arg = 0,
- };
- status =
- alienware_wmax_command(&in_args, sizeof(in_args),
- WMAX_METHOD_HDMI_STATUS, &out_data);
-
- if (ACPI_SUCCESS(status)) {
- if (out_data == 1)
- return sysfs_emit(buf, "[input] gpu unknown\n");
- else if (out_data == 2)
- return sysfs_emit(buf, "input [gpu] unknown\n");
- }
- pr_err("alienware-wmi: unknown HDMI source status: %u\n", status);
- return sysfs_emit(buf, "input gpu [unknown]\n");
-}
-
-static ssize_t toggle_hdmi_source(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- acpi_status status;
- struct wmax_basic_args args;
- if (strcmp(buf, "gpu\n") == 0)
- args.arg = 1;
- else if (strcmp(buf, "input\n") == 0)
- args.arg = 2;
- else
- args.arg = 3;
- pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
-
- status = alienware_wmax_command(&args, sizeof(args),
- WMAX_METHOD_HDMI_SOURCE, NULL);
-
- if (ACPI_FAILURE(status))
- pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
- status);
- return count;
-}
-
-static DEVICE_ATTR(cable, S_IRUGO, show_hdmi_cable, NULL);
-static DEVICE_ATTR(source, S_IRUGO | S_IWUSR, show_hdmi_source,
- toggle_hdmi_source);
-
-static struct attribute *hdmi_attrs[] = {
- &dev_attr_cable.attr,
- &dev_attr_source.attr,
- NULL,
-};
-
-static const struct attribute_group hdmi_attribute_group = {
- .name = "hdmi",
- .attrs = hdmi_attrs,
-};
-
-static void remove_hdmi(struct platform_device *dev)
-{
- if (quirks->hdmi_mux > 0)
- sysfs_remove_group(&dev->dev.kobj, &hdmi_attribute_group);
-}
-
-static int create_hdmi(struct platform_device *dev)
-{
- int ret;
-
- ret = sysfs_create_group(&dev->dev.kobj, &hdmi_attribute_group);
- if (ret)
- remove_hdmi(dev);
- return ret;
-}
-
-/*
- * Alienware GFX amplifier support
- * - Currently supports reading cable status
- * - Leaving expansion room to possibly support dock/undock events later
- */
-static ssize_t show_amplifier_status(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- acpi_status status;
- u32 out_data;
- struct wmax_basic_args in_args = {
- .arg = 0,
- };
- status =
- alienware_wmax_command(&in_args, sizeof(in_args),
- WMAX_METHOD_AMPLIFIER_CABLE, &out_data);
- if (ACPI_SUCCESS(status)) {
- if (out_data == 0)
- return sysfs_emit(buf, "[unconnected] connected unknown\n");
- else if (out_data == 1)
- return sysfs_emit(buf, "unconnected [connected] unknown\n");
- }
- pr_err("alienware-wmi: unknown amplifier cable status: %d\n", status);
- return sysfs_emit(buf, "unconnected connected [unknown]\n");
-}
-
-static DEVICE_ATTR(status, S_IRUGO, show_amplifier_status, NULL);
-
-static struct attribute *amplifier_attrs[] = {
- &dev_attr_status.attr,
- NULL,
-};
-
-static const struct attribute_group amplifier_attribute_group = {
- .name = "amplifier",
- .attrs = amplifier_attrs,
-};
-
-static void remove_amplifier(struct platform_device *dev)
-{
- if (quirks->amplifier > 0)
- sysfs_remove_group(&dev->dev.kobj, &amplifier_attribute_group);
-}
-
-static int create_amplifier(struct platform_device *dev)
-{
- int ret;
-
- ret = sysfs_create_group(&dev->dev.kobj, &amplifier_attribute_group);
- if (ret)
- remove_amplifier(dev);
- return ret;
-}
-
-/*
- * Deep Sleep Control support
- * - Modifies BIOS setting for deep sleep control allowing extra wakeup events
- */
-static ssize_t show_deepsleep_status(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- acpi_status status;
- u32 out_data;
- struct wmax_basic_args in_args = {
- .arg = 0,
- };
- status = alienware_wmax_command(&in_args, sizeof(in_args),
- WMAX_METHOD_DEEP_SLEEP_STATUS, &out_data);
- if (ACPI_SUCCESS(status)) {
- if (out_data == 0)
- return sysfs_emit(buf, "[disabled] s5 s5_s4\n");
- else if (out_data == 1)
- return sysfs_emit(buf, "disabled [s5] s5_s4\n");
- else if (out_data == 2)
- return sysfs_emit(buf, "disabled s5 [s5_s4]\n");
- }
- pr_err("alienware-wmi: unknown deep sleep status: %d\n", status);
- return sysfs_emit(buf, "disabled s5 s5_s4 [unknown]\n");
-}
-
-static ssize_t toggle_deepsleep(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- acpi_status status;
- struct wmax_basic_args args;
-
- if (strcmp(buf, "disabled\n") == 0)
- args.arg = 0;
- else if (strcmp(buf, "s5\n") == 0)
- args.arg = 1;
- else
- args.arg = 2;
- pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
-
- status = alienware_wmax_command(&args, sizeof(args),
- WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL);
-
- if (ACPI_FAILURE(status))
- pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
- status);
- return count;
-}
-
-static DEVICE_ATTR(deepsleep, S_IRUGO | S_IWUSR, show_deepsleep_status, toggle_deepsleep);
-
-static struct attribute *deepsleep_attrs[] = {
- &dev_attr_deepsleep.attr,
- NULL,
-};
-
-static const struct attribute_group deepsleep_attribute_group = {
- .name = "deepsleep",
- .attrs = deepsleep_attrs,
-};
-
-static void remove_deepsleep(struct platform_device *dev)
-{
- if (quirks->deepslp > 0)
- sysfs_remove_group(&dev->dev.kobj, &deepsleep_attribute_group);
-}
-
-static int create_deepsleep(struct platform_device *dev)
-{
- int ret;
-
- ret = sysfs_create_group(&dev->dev.kobj, &deepsleep_attribute_group);
- if (ret)
- remove_deepsleep(dev);
- return ret;
-}
-
-/*
- * Thermal Profile control
- * - Provides thermal profile control through the Platform Profile API
- */
-#define WMAX_THERMAL_TABLE_MASK GENMASK(7, 4)
-#define WMAX_THERMAL_MODE_MASK GENMASK(3, 0)
-#define WMAX_SENSOR_ID_MASK BIT(8)
-
-static bool is_wmax_thermal_code(u32 code)
-{
- if (code & WMAX_SENSOR_ID_MASK)
- return false;
-
- if ((code & WMAX_THERMAL_MODE_MASK) >= THERMAL_MODE_LAST)
- return false;
-
- if ((code & WMAX_THERMAL_TABLE_MASK) == WMAX_THERMAL_TABLE_BASIC &&
- (code & WMAX_THERMAL_MODE_MASK) >= THERMAL_MODE_BASIC_QUIET)
- return true;
-
- if ((code & WMAX_THERMAL_TABLE_MASK) == WMAX_THERMAL_TABLE_USTT &&
- (code & WMAX_THERMAL_MODE_MASK) <= THERMAL_MODE_USTT_LOW_POWER)
- return true;
-
- return false;
-}
-
-static int wmax_thermal_information(u8 operation, u8 arg, u32 *out_data)
-{
- acpi_status status;
- struct wmax_u32_args in_args = {
- .operation = operation,
- .arg1 = arg,
- .arg2 = 0,
- .arg3 = 0,
- };
-
- status = alienware_wmax_command(&in_args, sizeof(in_args),
- WMAX_METHOD_THERMAL_INFORMATION,
- out_data);
-
- if (ACPI_FAILURE(status))
- return -EIO;
-
- if (*out_data == WMAX_FAILURE_CODE)
- return -EBADRQC;
-
- return 0;
-}
-
-static int wmax_thermal_control(u8 profile)
-{
- acpi_status status;
- struct wmax_u32_args in_args = {
- .operation = WMAX_OPERATION_ACTIVATE_PROFILE,
- .arg1 = profile,
- .arg2 = 0,
- .arg3 = 0,
- };
- u32 out_data;
-
- status = alienware_wmax_command(&in_args, sizeof(in_args),
- WMAX_METHOD_THERMAL_CONTROL,
- &out_data);
-
- if (ACPI_FAILURE(status))
- return -EIO;
-
- if (out_data == WMAX_FAILURE_CODE)
- return -EBADRQC;
-
- return 0;
-}
-
-static int wmax_game_shift_status(u8 operation, u32 *out_data)
-{
- acpi_status status;
- struct wmax_u32_args in_args = {
- .operation = operation,
- .arg1 = 0,
- .arg2 = 0,
- .arg3 = 0,
- };
-
- status = alienware_wmax_command(&in_args, sizeof(in_args),
- WMAX_METHOD_GAME_SHIFT_STATUS,
- out_data);
-
- if (ACPI_FAILURE(status))
- return -EIO;
-
- if (*out_data == WMAX_FAILURE_CODE)
- return -EOPNOTSUPP;
-
- return 0;
-}
-
-static int thermal_profile_get(struct platform_profile_handler *pprof,
- enum platform_profile_option *profile)
-{
- u32 out_data;
- int ret;
-
- ret = wmax_thermal_information(WMAX_OPERATION_CURRENT_PROFILE,
- 0, &out_data);
-
- if (ret < 0)
- return ret;
-
- if (out_data == WMAX_THERMAL_MODE_GMODE) {
- *profile = PLATFORM_PROFILE_PERFORMANCE;
- return 0;
- }
-
- if (!is_wmax_thermal_code(out_data))
- return -ENODATA;
-
- out_data &= WMAX_THERMAL_MODE_MASK;
- *profile = wmax_mode_to_platform_profile[out_data];
-
- return 0;
-}
-
-static int thermal_profile_set(struct platform_profile_handler *pprof,
- enum platform_profile_option profile)
-{
- if (quirks->gmode) {
- u32 gmode_status;
- int ret;
-
- ret = wmax_game_shift_status(WMAX_OPERATION_GET_GAME_SHIFT_STATUS,
- &gmode_status);
-
- if (ret < 0)
- return ret;
-
- if ((profile == PLATFORM_PROFILE_PERFORMANCE && !gmode_status) ||
- (profile != PLATFORM_PROFILE_PERFORMANCE && gmode_status)) {
- ret = wmax_game_shift_status(WMAX_OPERATION_TOGGLE_GAME_SHIFT,
- &gmode_status);
-
- if (ret < 0)
- return ret;
- }
- }
-
- return wmax_thermal_control(supported_thermal_profiles[profile]);
-}
-
-static int create_thermal_profile(void)
-{
- u32 out_data;
- u8 sys_desc[4];
- u32 first_mode;
- enum wmax_thermal_mode mode;
- enum platform_profile_option profile;
- int ret;
-
- ret = wmax_thermal_information(WMAX_OPERATION_SYS_DESCRIPTION,
- 0, (u32 *) &sys_desc);
- if (ret < 0)
- return ret;
-
- first_mode = sys_desc[0] + sys_desc[1];
-
- for (u32 i = 0; i < sys_desc[3]; i++) {
- ret = wmax_thermal_information(WMAX_OPERATION_LIST_IDS,
- i + first_mode, &out_data);
-
- if (ret == -EIO)
- return ret;
-
- if (ret == -EBADRQC)
- break;
-
- if (!is_wmax_thermal_code(out_data))
- continue;
-
- mode = out_data & WMAX_THERMAL_MODE_MASK;
- profile = wmax_mode_to_platform_profile[mode];
- supported_thermal_profiles[profile] = out_data;
-
- set_bit(profile, pp_handler.choices);
- }
-
- if (bitmap_empty(pp_handler.choices, PLATFORM_PROFILE_LAST))
- return -ENODEV;
-
- if (quirks->gmode) {
- supported_thermal_profiles[PLATFORM_PROFILE_PERFORMANCE] =
- WMAX_THERMAL_MODE_GMODE;
-
- set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices);
- }
-
- pp_handler.profile_get = thermal_profile_get;
- pp_handler.profile_set = thermal_profile_set;
-
- return platform_profile_register(&pp_handler);
-}
-
-static void remove_thermal_profile(void)
-{
- if (quirks->thermal)
- platform_profile_remove();
-}
-
-static int __init alienware_wmi_init(void)
-{
- int ret;
-
- if (wmi_has_guid(LEGACY_CONTROL_GUID))
- interface = LEGACY;
- else if (wmi_has_guid(WMAX_CONTROL_GUID))
- interface = WMAX;
- else {
- pr_warn("alienware-wmi: No known WMI GUID found\n");
- return -ENODEV;
- }
-
- dmi_check_system(alienware_quirks);
- if (quirks == NULL)
- quirks = &quirk_unknown;
-
- if (force_platform_profile)
- quirks->thermal = true;
-
- if (force_gmode) {
- if (quirks->thermal)
- quirks->gmode = true;
- else
- pr_warn("force_gmode requires platform profile support\n");
- }
-
- ret = platform_driver_register(&platform_driver);
- if (ret)
- goto fail_platform_driver;
- platform_device = platform_device_alloc("alienware-wmi", PLATFORM_DEVID_NONE);
- if (!platform_device) {
- ret = -ENOMEM;
- goto fail_platform_device1;
- }
- ret = platform_device_add(platform_device);
- if (ret)
- goto fail_platform_device2;
-
- if (quirks->hdmi_mux > 0) {
- ret = create_hdmi(platform_device);
- if (ret)
- goto fail_prep_hdmi;
- }
-
- if (quirks->amplifier > 0) {
- ret = create_amplifier(platform_device);
- if (ret)
- goto fail_prep_amplifier;
- }
-
- if (quirks->deepslp > 0) {
- ret = create_deepsleep(platform_device);
- if (ret)
- goto fail_prep_deepsleep;
- }
-
- if (quirks->thermal) {
- ret = create_thermal_profile();
- if (ret)
- goto fail_prep_thermal_profile;
- }
-
- if (quirks->num_zones > 0) {
- ret = alienware_zone_init(platform_device);
- if (ret)
- goto fail_prep_zones;
- }
-
- return 0;
-
-fail_prep_zones:
- alienware_zone_exit(platform_device);
- remove_thermal_profile();
-fail_prep_thermal_profile:
-fail_prep_deepsleep:
-fail_prep_amplifier:
-fail_prep_hdmi:
- platform_device_del(platform_device);
-fail_platform_device2:
- platform_device_put(platform_device);
-fail_platform_device1:
- platform_driver_unregister(&platform_driver);
-fail_platform_driver:
- return ret;
-}
-
-module_init(alienware_wmi_init);
-
-static void __exit alienware_wmi_exit(void)
-{
- if (platform_device) {
- alienware_zone_exit(platform_device);
- remove_hdmi(platform_device);
- remove_thermal_profile();
- platform_device_unregister(platform_device);
- platform_driver_unregister(&platform_driver);
- }
-}
-
-module_exit(alienware_wmi_exit);
diff --git a/drivers/platform/x86/dell/alienware-wmi.h b/drivers/platform/x86/dell/alienware-wmi.h
new file mode 100644
index 000000000000..68d4242211ae
--- /dev/null
+++ b/drivers/platform/x86/dell/alienware-wmi.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Alienware WMI special features driver
+ *
+ * Copyright (C) 2014 Dell Inc <Dell.Client.Kernel@dell.com>
+ * Copyright (C) 2024 Kurt Borja <kuurtb@gmail.com>
+ */
+
+#ifndef _ALIENWARE_WMI_H_
+#define _ALIENWARE_WMI_H_
+
+#include <linux/leds.h>
+#include <linux/platform_device.h>
+#include <linux/wmi.h>
+
+#define LEGACY_CONTROL_GUID "A90597CE-A997-11DA-B012-B622A1EF5492"
+#define LEGACY_POWER_CONTROL_GUID "A80593CE-A997-11DA-B012-B622A1EF5492"
+#define WMAX_CONTROL_GUID "A70591CE-A997-11DA-B012-B622A1EF5492"
+
+enum INTERFACE_FLAGS {
+ LEGACY,
+ WMAX,
+};
+
+enum LEGACY_CONTROL_STATES {
+ LEGACY_RUNNING = 1,
+ LEGACY_BOOTING = 0,
+ LEGACY_SUSPEND = 3,
+};
+
+enum WMAX_CONTROL_STATES {
+ WMAX_RUNNING = 0xFF,
+ WMAX_BOOTING = 0,
+ WMAX_SUSPEND = 3,
+};
+
+struct alienfx_quirks {
+ u8 num_zones;
+ bool hdmi_mux;
+ bool amplifier;
+ bool deepslp;
+};
+
+struct color_platform {
+ u8 blue;
+ u8 green;
+ u8 red;
+} __packed;
+
+struct alienfx_priv {
+ struct platform_device *pdev;
+ struct led_classdev global_led;
+ struct color_platform colors[4];
+ u8 global_brightness;
+ u8 lighting_control_state;
+};
+
+struct alienfx_ops {
+ int (*upd_led)(struct alienfx_priv *priv, struct wmi_device *wdev,
+ u8 location);
+ int (*upd_brightness)(struct alienfx_priv *priv, struct wmi_device *wdev,
+ u8 brightness);
+};
+
+struct alienfx_platdata {
+ struct wmi_device *wdev;
+ struct alienfx_ops ops;
+};
+
+extern u8 alienware_interface;
+extern struct alienfx_quirks *alienfx;
+
+int alienware_wmi_command(struct wmi_device *wdev, u32 method_id,
+ void *in_args, size_t in_size, u32 *out_data);
+
+int alienware_alienfx_setup(struct alienfx_platdata *pdata);
+
+#if IS_ENABLED(CONFIG_ALIENWARE_WMI_LEGACY)
+int __init alienware_legacy_wmi_init(void);
+void __exit alienware_legacy_wmi_exit(void);
+#else
+static inline int alienware_legacy_wmi_init(void)
+{
+ return -ENODEV;
+}
+
+static inline void alienware_legacy_wmi_exit(void)
+{
+}
+#endif
+
+#if IS_ENABLED(CONFIG_ALIENWARE_WMI_WMAX)
+extern const struct attribute_group wmax_hdmi_attribute_group;
+extern const struct attribute_group wmax_amplifier_attribute_group;
+extern const struct attribute_group wmax_deepsleep_attribute_group;
+
+#define WMAX_DEV_GROUPS &wmax_hdmi_attribute_group, \
+ &wmax_amplifier_attribute_group, \
+ &wmax_deepsleep_attribute_group,
+
+int __init alienware_wmax_wmi_init(void);
+void __exit alienware_wmax_wmi_exit(void);
+#else
+#define WMAX_DEV_GROUPS
+
+static inline int alienware_wmax_wmi_init(void)
+{
+ return -ENODEV;
+}
+
+
+static inline void alienware_wmax_wmi_exit(void)
+{
+}
+#endif
+
+#endif
diff --git a/drivers/platform/x86/dell/dcdbas.c b/drivers/platform/x86/dell/dcdbas.c
index 0aeb8149c16b..8149be25fa26 100644
--- a/drivers/platform/x86/dell/dcdbas.c
+++ b/drivers/platform/x86/dell/dcdbas.c
@@ -163,7 +163,7 @@ static ssize_t smi_data_buf_size_store(struct device *dev,
}
static ssize_t smi_data_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
+ const struct bin_attribute *bin_attr,
char *buf, loff_t pos, size_t count)
{
ssize_t ret;
@@ -176,7 +176,7 @@ static ssize_t smi_data_read(struct file *filp, struct kobject *kobj,
}
static ssize_t smi_data_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
+ const struct bin_attribute *bin_attr,
char *buf, loff_t pos, size_t count)
{
ssize_t ret;
@@ -636,9 +636,9 @@ static struct notifier_block dcdbas_reboot_nb = {
.priority = INT_MIN
};
-static DCDBAS_BIN_ATTR_RW(smi_data);
+static const BIN_ATTR_ADMIN_RW(smi_data, 0);
-static struct bin_attribute *dcdbas_bin_attrs[] = {
+static const struct bin_attribute *const dcdbas_bin_attrs[] = {
&bin_attr_smi_data,
NULL
};
@@ -662,7 +662,7 @@ static struct attribute *dcdbas_dev_attrs[] = {
static const struct attribute_group dcdbas_attr_group = {
.attrs = dcdbas_dev_attrs,
- .bin_attrs = dcdbas_bin_attrs,
+ .bin_attrs_new = dcdbas_bin_attrs,
};
static int dcdbas_probe(struct platform_device *dev)
diff --git a/drivers/platform/x86/dell/dcdbas.h b/drivers/platform/x86/dell/dcdbas.h
index 942a23ddded0..a05d7f667586 100644
--- a/drivers/platform/x86/dell/dcdbas.h
+++ b/drivers/platform/x86/dell/dcdbas.h
@@ -56,14 +56,6 @@
#define DCDBAS_DEV_ATTR_WO(_name) \
DEVICE_ATTR(_name,0200,NULL,_name##_store);
-#define DCDBAS_BIN_ATTR_RW(_name) \
-struct bin_attribute bin_attr_##_name = { \
- .attr = { .name = __stringify(_name), \
- .mode = 0600 }, \
- .read = _name##_read, \
- .write = _name##_write, \
-}
-
struct smi_cmd {
__u32 magic;
__u32 ebx;
diff --git a/drivers/platform/x86/dell/dell-laptop.c b/drivers/platform/x86/dell/dell-laptop.c
index 5671bd0deee7..57748c3ea24f 100644
--- a/drivers/platform/x86/dell/dell-laptop.c
+++ b/drivers/platform/x86/dell/dell-laptop.c
@@ -103,15 +103,15 @@ static bool mute_led_registered;
struct battery_mode_info {
int token;
- const char *label;
+ enum power_supply_charge_type charge_type;
};
static const struct battery_mode_info battery_modes[] = {
- { BAT_PRI_AC_MODE_TOKEN, "Trickle" },
- { BAT_EXPRESS_MODE_TOKEN, "Fast" },
- { BAT_STANDARD_MODE_TOKEN, "Standard" },
- { BAT_ADAPTIVE_MODE_TOKEN, "Adaptive" },
- { BAT_CUSTOM_MODE_TOKEN, "Custom" },
+ { BAT_PRI_AC_MODE_TOKEN, POWER_SUPPLY_CHARGE_TYPE_TRICKLE },
+ { BAT_EXPRESS_MODE_TOKEN, POWER_SUPPLY_CHARGE_TYPE_FAST },
+ { BAT_STANDARD_MODE_TOKEN, POWER_SUPPLY_CHARGE_TYPE_STANDARD },
+ { BAT_ADAPTIVE_MODE_TOKEN, POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE },
+ { BAT_CUSTOM_MODE_TOKEN, POWER_SUPPLY_CHARGE_TYPE_CUSTOM },
};
static u32 battery_supported_modes;
@@ -725,8 +725,8 @@ static void dell_update_rfkill(struct work_struct *ignored)
}
static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
-static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
- struct serio *port)
+static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str, struct serio *port,
+ void *context)
{
static bool extended;
@@ -884,7 +884,7 @@ static int __init dell_setup_rfkill(void)
pr_warn("Unable to register dell rbtn notifier\n");
goto err_filter;
} else {
- ret = i8042_install_filter(dell_laptop_i8042_filter);
+ ret = i8042_install_filter(dell_laptop_i8042_filter, NULL);
if (ret) {
pr_warn("Unable to install key filter\n");
goto err_filter;
@@ -2261,46 +2261,42 @@ static ssize_t charge_types_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- ssize_t count = 0;
+ enum power_supply_charge_type charge_type;
int i;
for (i = 0; i < ARRAY_SIZE(battery_modes); i++) {
- bool active;
+ charge_type = battery_modes[i].charge_type;
- if (!(battery_supported_modes & BIT(i)))
+ if (!(battery_supported_modes & BIT(charge_type)))
continue;
- active = dell_battery_mode_is_active(battery_modes[i].token);
- count += sysfs_emit_at(buf, count, active ? "[%s] " : "%s ",
- battery_modes[i].label);
- }
+ if (!dell_battery_mode_is_active(battery_modes[i].token))
+ continue;
- /* convert the last space to a newline */
- if (count > 0)
- count--;
- count += sysfs_emit_at(buf, count, "\n");
+ return power_supply_charge_types_show(dev, battery_supported_modes,
+ charge_type, buf);
+ }
- return count;
+ /* No active mode found */
+ return -EIO;
}
static ssize_t charge_types_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
- bool matched = false;
- int err, i;
+ int charge_type, err, i;
- for (i = 0; i < ARRAY_SIZE(battery_modes); i++) {
- if (!(battery_supported_modes & BIT(i)))
- continue;
+ charge_type = power_supply_charge_types_parse(battery_supported_modes, buf);
+ if (charge_type < 0)
+ return charge_type;
- if (sysfs_streq(battery_modes[i].label, buf)) {
- matched = true;
+ for (i = 0; i < ARRAY_SIZE(battery_modes); i++) {
+ if (battery_modes[i].charge_type == charge_type)
break;
- }
}
- if (!matched)
- return -EINVAL;
+ if (i == ARRAY_SIZE(battery_modes))
+ return -ENOENT;
err = dell_battery_set_mode(battery_modes[i].token);
if (err)
@@ -2430,7 +2426,7 @@ static u32 __init battery_get_supported_modes(void)
for (i = 0; i < ARRAY_SIZE(battery_modes); i++) {
if (dell_smbios_find_token(battery_modes[i].token))
- modes |= BIT(i);
+ modes |= BIT(battery_modes[i].charge_type);
}
return modes;
diff --git a/drivers/platform/x86/dell/dell-lis3lv02d.c b/drivers/platform/x86/dell/dell-lis3lv02d.c
new file mode 100644
index 000000000000..efe26d667973
--- /dev/null
+++ b/drivers/platform/x86/dell/dell-lis3lv02d.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * lis3lv02d i2c-client instantiation for ACPI SMO88xx devices without I2C resources.
+ *
+ * Copyright (C) 2024 Hans de Goede <hansg@kernel.org>
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/device/bus.h>
+#include <linux/dmi.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include "dell-smo8800-ids.h"
+
+#define LIS3_WHO_AM_I 0x0f
+
+#define DELL_LIS3LV02D_DMI_ENTRY(product_name, i2c_addr) \
+ { \
+ .matches = { \
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), \
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, product_name), \
+ }, \
+ .driver_data = (void *)(uintptr_t)(i2c_addr), \
+ }
+
+/*
+ * Accelerometer's I2C address is not specified in DMI nor ACPI,
+ * so it is needed to define mapping table based on DMI product names.
+ */
+static const struct dmi_system_id lis3lv02d_devices[] __initconst = {
+ /*
+ * Dell platform team told us that these Latitude devices have
+ * ST microelectronics accelerometer at I2C address 0x29.
+ */
+ DELL_LIS3LV02D_DMI_ENTRY("Latitude E5250", 0x29),
+ DELL_LIS3LV02D_DMI_ENTRY("Latitude E5450", 0x29),
+ DELL_LIS3LV02D_DMI_ENTRY("Latitude E5550", 0x29),
+ DELL_LIS3LV02D_DMI_ENTRY("Latitude E6440", 0x29),
+ DELL_LIS3LV02D_DMI_ENTRY("Latitude E6440 ATG", 0x29),
+ DELL_LIS3LV02D_DMI_ENTRY("Latitude E6540", 0x29),
+ /*
+ * Additional individual entries were added after verification.
+ */
+ DELL_LIS3LV02D_DMI_ENTRY("Latitude 5480", 0x29),
+ DELL_LIS3LV02D_DMI_ENTRY("Latitude E6330", 0x29),
+ DELL_LIS3LV02D_DMI_ENTRY("Latitude E6430", 0x29),
+ DELL_LIS3LV02D_DMI_ENTRY("Precision 3540", 0x29),
+ DELL_LIS3LV02D_DMI_ENTRY("Precision M6800", 0x29),
+ DELL_LIS3LV02D_DMI_ENTRY("Vostro V131", 0x1d),
+ DELL_LIS3LV02D_DMI_ENTRY("Vostro 5568", 0x29),
+ DELL_LIS3LV02D_DMI_ENTRY("XPS 15 7590", 0x29),
+ DELL_LIS3LV02D_DMI_ENTRY("XPS 15 9550", 0x29),
+ { }
+};
+
+static u8 i2c_addr;
+static struct i2c_client *i2c_dev;
+static bool notifier_registered;
+
+static bool probe_i2c_addr;
+module_param(probe_i2c_addr, bool, 0444);
+MODULE_PARM_DESC(probe_i2c_addr, "Probe the i801 I2C bus for the accelerometer on models where the address is unknown, this may be dangerous.");
+
+static int detect_lis3lv02d(struct i2c_adapter *adap, unsigned short addr)
+{
+ union i2c_smbus_data smbus_data;
+ int err;
+
+ dev_info(&adap->dev, "Probing for lis3lv02d on address 0x%02x\n", addr);
+
+ err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, LIS3_WHO_AM_I,
+ I2C_SMBUS_BYTE_DATA, &smbus_data);
+ if (err < 0)
+ return 0; /* Not found */
+
+ /* valid who-am-i values are from drivers/misc/lis3lv02d/lis3lv02d.c */
+ switch (smbus_data.byte) {
+ case 0x32:
+ case 0x33:
+ case 0x3a:
+ case 0x3b:
+ break;
+ default:
+ dev_warn(&adap->dev, "Unknown who-am-i register value 0x%02x\n",
+ smbus_data.byte);
+ return 0; /* Not found */
+ }
+
+ dev_info(&adap->dev,
+ "Detected lis3lv02d on address 0x%02x, please report this upstream to platform-driver-x86@vger.kernel.org so that a quirk can be added\n",
+ addr);
+
+ return 1; /* Found */
+}
+
+static bool i2c_adapter_is_main_i801(struct i2c_adapter *adap)
+{
+ /*
+ * Only match the main I801 adapter and reject secondary adapters
+ * which names start with "SMBus I801 IDF adapter".
+ */
+ return strstarts(adap->name, "SMBus I801 adapter");
+}
+
+static int find_i801(struct device *dev, void *data)
+{
+ struct i2c_adapter *adap, **adap_ret = data;
+
+ adap = i2c_verify_adapter(dev);
+ if (!adap)
+ return 0;
+
+ if (!i2c_adapter_is_main_i801(adap))
+ return 0;
+
+ *adap_ret = i2c_get_adapter(adap->nr);
+ return 1;
+}
+
+static void instantiate_i2c_client(struct work_struct *work)
+{
+ struct i2c_board_info info = { };
+ struct i2c_adapter *adap = NULL;
+
+ if (i2c_dev)
+ return;
+
+ /*
+ * bus_for_each_dev() and not i2c_for_each_dev() to avoid
+ * a deadlock when find_i801() calls i2c_get_adapter().
+ */
+ bus_for_each_dev(&i2c_bus_type, NULL, &adap, find_i801);
+ if (!adap)
+ return;
+
+ strscpy(info.type, "lis3lv02d", I2C_NAME_SIZE);
+
+ if (i2c_addr) {
+ info.addr = i2c_addr;
+ i2c_dev = i2c_new_client_device(adap, &info);
+ } else {
+ /* First try address 0x29 (most used) and then try 0x1d */
+ static const unsigned short addr_list[] = { 0x29, 0x1d, I2C_CLIENT_END };
+
+ i2c_dev = i2c_new_scanned_device(adap, &info, addr_list, detect_lis3lv02d);
+ }
+
+ if (IS_ERR(i2c_dev)) {
+ dev_err(&adap->dev, "error %ld registering i2c_client\n", PTR_ERR(i2c_dev));
+ i2c_dev = NULL;
+ } else {
+ dev_dbg(&adap->dev, "registered lis3lv02d on address 0x%02x\n", info.addr);
+ }
+
+ i2c_put_adapter(adap);
+}
+static DECLARE_WORK(i2c_work, instantiate_i2c_client);
+
+static int i2c_bus_notify(struct notifier_block *nb, unsigned long action, void *data)
+{
+ struct device *dev = data;
+ struct i2c_client *client;
+ struct i2c_adapter *adap;
+
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ adap = i2c_verify_adapter(dev);
+ if (!adap)
+ break;
+
+ if (i2c_adapter_is_main_i801(adap))
+ queue_work(system_long_wq, &i2c_work);
+ break;
+ case BUS_NOTIFY_REMOVED_DEVICE:
+ client = i2c_verify_client(dev);
+ if (!client)
+ break;
+
+ if (i2c_dev == client) {
+ dev_dbg(&client->adapter->dev, "lis3lv02d i2c_client removed\n");
+ i2c_dev = NULL;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+static struct notifier_block i2c_nb = { .notifier_call = i2c_bus_notify };
+
+static int __init match_acpi_device_ids(struct device *dev, const void *data)
+{
+ return acpi_match_device(data, dev) ? 1 : 0;
+}
+
+static int __init dell_lis3lv02d_init(void)
+{
+ const struct dmi_system_id *lis3lv02d_dmi_id;
+ struct device *dev;
+ int err;
+
+ /*
+ * First check for a matching platform_device. This protects against
+ * SMO88xx ACPI fwnodes which actually do have an I2C resource, which
+ * will already have an i2c_client instantiated (not a platform_device).
+ */
+ dev = bus_find_device(&platform_bus_type, NULL, smo8800_ids, match_acpi_device_ids);
+ if (!dev) {
+ pr_debug("No SMO88xx platform-device found\n");
+ return 0;
+ }
+ put_device(dev);
+
+ lis3lv02d_dmi_id = dmi_first_match(lis3lv02d_devices);
+ if (!lis3lv02d_dmi_id && !probe_i2c_addr) {
+ pr_warn("accelerometer is present on SMBus but its address is unknown, skipping registration\n");
+ pr_info("Pass dell_lis3lv02d.probe_i2c_addr=1 on the kernel command line to probe, this may be dangerous!\n");
+ return 0;
+ }
+
+ if (lis3lv02d_dmi_id)
+ i2c_addr = (long)lis3lv02d_dmi_id->driver_data;
+
+ /*
+ * Register i2c-bus notifier + queue initial scan for lis3lv02d
+ * i2c_client instantiation.
+ */
+ err = bus_register_notifier(&i2c_bus_type, &i2c_nb);
+ if (err)
+ return err;
+
+ notifier_registered = true;
+
+ queue_work(system_long_wq, &i2c_work);
+ return 0;
+}
+module_init(dell_lis3lv02d_init);
+
+static void __exit dell_lis3lv02d_module_exit(void)
+{
+ if (!notifier_registered)
+ return;
+
+ bus_unregister_notifier(&i2c_bus_type, &i2c_nb);
+ cancel_work_sync(&i2c_work);
+ i2c_unregister_device(i2c_dev);
+}
+module_exit(dell_lis3lv02d_module_exit);
+
+MODULE_DESCRIPTION("lis3lv02d i2c-client instantiation for ACPI SMO88xx devices");
+MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/dell/dell-pc.c b/drivers/platform/x86/dell/dell-pc.c
index 972385ca1990..483240bb36e7 100644
--- a/drivers/platform/x86/dell/dell-pc.c
+++ b/drivers/platform/x86/dell/dell-pc.c
@@ -18,10 +18,14 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_profile.h>
+#include <linux/platform_device.h>
#include <linux/slab.h>
#include "dell-smbios.h"
+static struct platform_device *platform_device;
+static int supported_modes;
+
static const struct dmi_system_id dell_device_table[] __initconst = {
{
.ident = "Dell Inc.",
@@ -105,8 +109,6 @@ MODULE_DEVICE_TABLE(dmi, dell_device_table);
#define DELL_ACC_SET_FIELD GENMASK(11, 8)
#define DELL_THERMAL_SUPPORTED GENMASK(3, 0)
-static struct platform_profile_handler *thermal_handler;
-
enum thermal_mode_bits {
DELL_BALANCED = BIT(0),
DELL_COOL_BOTTOM = BIT(1),
@@ -182,7 +184,7 @@ static int thermal_set_mode(enum thermal_mode_bits state)
return dell_send_request(&buffer, CLASS_INFO, SELECT_THERMAL_MANAGEMENT);
}
-static int thermal_platform_profile_set(struct platform_profile_handler *pprof,
+static int thermal_platform_profile_set(struct device *dev,
enum platform_profile_option profile)
{
switch (profile) {
@@ -199,7 +201,7 @@ static int thermal_platform_profile_set(struct platform_profile_handler *pprof,
}
}
-static int thermal_platform_profile_get(struct platform_profile_handler *pprof,
+static int thermal_platform_profile_get(struct device *dev,
enum platform_profile_option *profile)
{
int ret;
@@ -228,10 +230,30 @@ static int thermal_platform_profile_get(struct platform_profile_handler *pprof,
return 0;
}
+static int thermal_platform_profile_probe(void *drvdata, unsigned long *choices)
+{
+ if (supported_modes & DELL_QUIET)
+ set_bit(PLATFORM_PROFILE_QUIET, choices);
+ if (supported_modes & DELL_COOL_BOTTOM)
+ set_bit(PLATFORM_PROFILE_COOL, choices);
+ if (supported_modes & DELL_BALANCED)
+ set_bit(PLATFORM_PROFILE_BALANCED, choices);
+ if (supported_modes & DELL_PERFORMANCE)
+ set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
+
+ return 0;
+}
+
+static const struct platform_profile_ops dell_pc_platform_profile_ops = {
+ .probe = thermal_platform_profile_probe,
+ .profile_get = thermal_platform_profile_get,
+ .profile_set = thermal_platform_profile_set,
+};
+
static int thermal_init(void)
{
+ struct device *ppdev;
int ret;
- int supported_modes;
/* If thermal commands are not supported, exit without error */
if (!dell_smbios_class_is_supported(CLASS_INFO))
@@ -244,37 +266,28 @@ static int thermal_init(void)
if (!supported_modes)
return 0;
- thermal_handler = kzalloc(sizeof(*thermal_handler), GFP_KERNEL);
- if (!thermal_handler)
- return -ENOMEM;
- thermal_handler->profile_get = thermal_platform_profile_get;
- thermal_handler->profile_set = thermal_platform_profile_set;
+ platform_device = platform_device_register_simple("dell-pc", PLATFORM_DEVID_NONE, NULL, 0);
+ if (IS_ERR(platform_device))
+ return PTR_ERR(platform_device);
- if (supported_modes & DELL_QUIET)
- set_bit(PLATFORM_PROFILE_QUIET, thermal_handler->choices);
- if (supported_modes & DELL_COOL_BOTTOM)
- set_bit(PLATFORM_PROFILE_COOL, thermal_handler->choices);
- if (supported_modes & DELL_BALANCED)
- set_bit(PLATFORM_PROFILE_BALANCED, thermal_handler->choices);
- if (supported_modes & DELL_PERFORMANCE)
- set_bit(PLATFORM_PROFILE_PERFORMANCE, thermal_handler->choices);
-
- /* Clean up if failed */
- ret = platform_profile_register(thermal_handler);
- if (ret) {
- kfree(thermal_handler);
- thermal_handler = NULL;
+ ppdev = devm_platform_profile_register(&platform_device->dev, "dell-pc",
+ NULL, &dell_pc_platform_profile_ops);
+ if (IS_ERR(ppdev)) {
+ ret = PTR_ERR(ppdev);
+ goto cleanup_platform_device;
}
+ return 0;
+
+cleanup_platform_device:
+ platform_device_unregister(platform_device);
+
return ret;
}
static void thermal_cleanup(void)
{
- if (thermal_handler) {
- platform_profile_remove();
- kfree(thermal_handler);
- }
+ platform_device_unregister(platform_device);
}
static int __init dell_init(void)
diff --git a/drivers/platform/x86/dell/dell-smo8800-ids.h b/drivers/platform/x86/dell/dell-smo8800-ids.h
new file mode 100644
index 000000000000..ec58e229ba7a
--- /dev/null
+++ b/drivers/platform/x86/dell/dell-smo8800-ids.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * ACPI SMO88XX lis3lv02d freefall / accelerometer device-ids.
+ *
+ * Copyright (C) 2012 Sonal Santan <sonal.santan@gmail.com>
+ * Copyright (C) 2014 Pali Rohár <pali@kernel.org>
+ */
+#ifndef _DELL_SMO8800_IDS_H_
+#define _DELL_SMO8800_IDS_H_
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+
+static const struct acpi_device_id smo8800_ids[] = {
+ { "SMO8800" },
+ { "SMO8801" },
+ { "SMO8810" },
+ { "SMO8811" },
+ { "SMO8820" },
+ { "SMO8821" },
+ { "SMO8830" },
+ { "SMO8831" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, smo8800_ids);
+
+#endif
diff --git a/drivers/platform/x86/dell/dell-smo8800.c b/drivers/platform/x86/dell/dell-smo8800.c
index 87fe03f23f24..8872f9b57fce 100644
--- a/drivers/platform/x86/dell/dell-smo8800.c
+++ b/drivers/platform/x86/dell/dell-smo8800.c
@@ -14,10 +14,10 @@
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>
+#include "dell-smo8800-ids.h"
struct smo8800_device {
u32 irq; /* acpi device irq */
@@ -163,20 +163,6 @@ static void smo8800_remove(struct platform_device *device)
dev_dbg(&device->dev, "device /dev/freefall unregistered\n");
}
-/* NOTE: Keep this list in sync with drivers/i2c/busses/i2c-i801.c */
-static const struct acpi_device_id smo8800_ids[] = {
- { "SMO8800", 0 },
- { "SMO8801", 0 },
- { "SMO8810", 0 },
- { "SMO8811", 0 },
- { "SMO8820", 0 },
- { "SMO8821", 0 },
- { "SMO8830", 0 },
- { "SMO8831", 0 },
- { "", 0 },
-};
-MODULE_DEVICE_TABLE(acpi, smo8800_ids);
-
static struct platform_driver smo8800_driver = {
.probe = smo8800_probe,
.remove = smo8800_remove,
diff --git a/drivers/platform/x86/dell/dell-uart-backlight.c b/drivers/platform/x86/dell/dell-uart-backlight.c
index bcc5c0f3bb4d..8f868f845350 100644
--- a/drivers/platform/x86/dell/dell-uart-backlight.c
+++ b/drivers/platform/x86/dell/dell-uart-backlight.c
@@ -159,7 +159,7 @@ static int dell_uart_set_bl_power(struct dell_uart_backlight *dell_bl, int power
set_power[0] = DELL_SOF(SET_CMD_LEN);
set_power[1] = CMD_SET_BL_POWER;
- set_power[2] = (power == FB_BLANK_UNBLANK) ? 1 : 0;
+ set_power[2] = (power == BACKLIGHT_POWER_ON) ? 1 : 0;
set_power[3] = dell_uart_checksum(set_power, 3);
ret = dell_uart_bl_command(dell_bl, set_power, SET_CMD_LEN, resp, SET_RESP_LEN);
@@ -325,7 +325,7 @@ static int dell_uart_bl_serdev_probe(struct serdev_device *serdev)
return PTR_ERR_OR_ZERO(dell_bl->bl);
}
-struct serdev_device_driver dell_uart_bl_serdev_driver = {
+static struct serdev_device_driver dell_uart_bl_serdev_driver = {
.probe = dell_uart_bl_serdev_probe,
.driver = {
.name = KBUILD_MODNAME,
diff --git a/drivers/platform/x86/dell/dell-wmi-ddv.c b/drivers/platform/x86/dell/dell-wmi-ddv.c
index e75cd6e1efe6..f27739da380f 100644
--- a/drivers/platform/x86/dell/dell-wmi-ddv.c
+++ b/drivers/platform/x86/dell/dell-wmi-ddv.c
@@ -104,7 +104,6 @@ struct dell_wmi_ddv_sensors {
struct dell_wmi_ddv_data {
struct acpi_battery_hook hook;
- struct device_attribute temp_attr;
struct device_attribute eppid_attr;
struct dell_wmi_ddv_sensors fans;
struct dell_wmi_ddv_sensors temps;
@@ -651,24 +650,6 @@ static int dell_wmi_ddv_battery_index(struct acpi_device *acpi_dev, u32 *index)
return kstrtou32(uid_str, 10, index);
}
-static ssize_t temp_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct dell_wmi_ddv_data *data = container_of(attr, struct dell_wmi_ddv_data, temp_attr);
- u32 index, value;
- int ret;
-
- ret = dell_wmi_ddv_battery_index(to_acpi_device(dev->parent), &index);
- if (ret < 0)
- return ret;
-
- ret = dell_wmi_ddv_query_integer(data->wdev, DELL_DDV_BATTERY_TEMPERATURE, index, &value);
- if (ret < 0)
- return ret;
-
- /* Use 2731 instead of 2731.5 to avoid unnecessary rounding */
- return sysfs_emit(buf, "%d\n", value - 2731);
-}
-
static ssize_t eppid_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct dell_wmi_ddv_data *data = container_of(attr, struct dell_wmi_ddv_data, eppid_attr);
@@ -695,6 +676,46 @@ static ssize_t eppid_show(struct device *dev, struct device_attribute *attr, cha
return ret;
}
+static int dell_wmi_ddv_get_property(struct power_supply *psy, const struct power_supply_ext *ext,
+ void *drvdata, enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct dell_wmi_ddv_data *data = drvdata;
+ u32 index, value;
+ int ret;
+
+ ret = dell_wmi_ddv_battery_index(to_acpi_device(psy->dev.parent), &index);
+ if (ret < 0)
+ return ret;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_TEMP:
+ ret = dell_wmi_ddv_query_integer(data->wdev, DELL_DDV_BATTERY_TEMPERATURE, index,
+ &value);
+ if (ret < 0)
+ return ret;
+
+ /* Use 2732 instead of 2731.5 to avoid unnecessary rounding and to emulate
+ * the behaviour of the OEM application which seems to round down the result.
+ */
+ val->intval = value - 2732;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const enum power_supply_property dell_wmi_ddv_properties[] = {
+ POWER_SUPPLY_PROP_TEMP,
+};
+
+static const struct power_supply_ext dell_wmi_ddv_extension = {
+ .name = DRIVER_NAME,
+ .properties = dell_wmi_ddv_properties,
+ .num_properties = ARRAY_SIZE(dell_wmi_ddv_properties),
+ .get_property = dell_wmi_ddv_get_property,
+};
+
static int dell_wmi_ddv_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
{
struct dell_wmi_ddv_data *data = container_of(hook, struct dell_wmi_ddv_data, hook);
@@ -706,13 +727,14 @@ static int dell_wmi_ddv_add_battery(struct power_supply *battery, struct acpi_ba
if (ret < 0)
return 0;
- ret = device_create_file(&battery->dev, &data->temp_attr);
+ ret = device_create_file(&battery->dev, &data->eppid_attr);
if (ret < 0)
return ret;
- ret = device_create_file(&battery->dev, &data->eppid_attr);
+ ret = power_supply_register_extension(battery, &dell_wmi_ddv_extension, &data->wdev->dev,
+ data);
if (ret < 0) {
- device_remove_file(&battery->dev, &data->temp_attr);
+ device_remove_file(&battery->dev, &data->eppid_attr);
return ret;
}
@@ -724,38 +746,24 @@ static int dell_wmi_ddv_remove_battery(struct power_supply *battery, struct acpi
{
struct dell_wmi_ddv_data *data = container_of(hook, struct dell_wmi_ddv_data, hook);
- device_remove_file(&battery->dev, &data->temp_attr);
device_remove_file(&battery->dev, &data->eppid_attr);
+ power_supply_unregister_extension(battery, &dell_wmi_ddv_extension);
return 0;
}
-static void dell_wmi_ddv_battery_remove(void *data)
-{
- struct acpi_battery_hook *hook = data;
-
- battery_hook_unregister(hook);
-}
-
static int dell_wmi_ddv_battery_add(struct dell_wmi_ddv_data *data)
{
data->hook.name = "Dell DDV Battery Extension";
data->hook.add_battery = dell_wmi_ddv_add_battery;
data->hook.remove_battery = dell_wmi_ddv_remove_battery;
- sysfs_attr_init(&data->temp_attr.attr);
- data->temp_attr.attr.name = "temp";
- data->temp_attr.attr.mode = 0444;
- data->temp_attr.show = temp_show;
-
sysfs_attr_init(&data->eppid_attr.attr);
data->eppid_attr.attr.name = "eppid";
data->eppid_attr.attr.mode = 0444;
data->eppid_attr.show = eppid_show;
- battery_hook_register(&data->hook);
-
- return devm_add_action_or_reset(&data->wdev->dev, dell_wmi_ddv_battery_remove, &data->hook);
+ return devm_battery_hook_register(&data->wdev->dev, &data->hook);
}
static int dell_wmi_ddv_buffer_read(struct seq_file *seq, enum dell_ddv_method method)
diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/Makefile b/drivers/platform/x86/dell/dell-wmi-sysman/Makefile
index 825fb2fbeea8..0a6df449e222 100644
--- a/drivers/platform/x86/dell/dell-wmi-sysman/Makefile
+++ b/drivers/platform/x86/dell/dell-wmi-sysman/Makefile
@@ -1,5 +1,5 @@
obj-$(CONFIG_DELL_WMI_SYSMAN) += dell-wmi-sysman.o
-dell-wmi-sysman-objs := sysman.o \
+dell-wmi-sysman-y := sysman.o \
enum-attributes.o \
int-attributes.o \
string-attributes.o \
diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/passobj-attributes.c b/drivers/platform/x86/dell/dell-wmi-sysman/passobj-attributes.c
index 230e6ee96636..d8f1bf5e58a0 100644
--- a/drivers/platform/x86/dell/dell-wmi-sysman/passobj-attributes.c
+++ b/drivers/platform/x86/dell/dell-wmi-sysman/passobj-attributes.c
@@ -45,7 +45,7 @@ static ssize_t current_password_store(struct kobject *kobj,
int length;
length = strlen(buf);
- if (buf[length-1] == '\n')
+ if (length && buf[length - 1] == '\n')
length--;
/* firmware does verifiation of min/max password length,
diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c
index 40ddc6eb7562..d00389b860e4 100644
--- a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c
+++ b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c
@@ -25,7 +25,6 @@ struct wmi_sysman_priv wmi_priv = {
/* reset bios to defaults */
static const char * const reset_types[] = {"builtinsafe", "lastknowngood", "factory", "custom"};
static int reset_option = -1;
-static const struct class *fw_attr_class;
/**
@@ -541,15 +540,11 @@ static int __init sysman_init(void)
goto err_exit_bios_attr_pass_interface;
}
- ret = fw_attributes_class_get(&fw_attr_class);
- if (ret)
- goto err_exit_bios_attr_pass_interface;
-
- wmi_priv.class_dev = device_create(fw_attr_class, NULL, MKDEV(0, 0),
+ wmi_priv.class_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0),
NULL, "%s", DRIVER_NAME);
if (IS_ERR(wmi_priv.class_dev)) {
ret = PTR_ERR(wmi_priv.class_dev);
- goto err_unregister_class;
+ goto err_exit_bios_attr_pass_interface;
}
wmi_priv.main_dir_kset = kset_create_and_add("attributes", NULL,
@@ -602,10 +597,7 @@ err_release_attributes_data:
release_attributes_data();
err_destroy_classdev:
- device_destroy(fw_attr_class, MKDEV(0, 0));
-
-err_unregister_class:
- fw_attributes_class_put();
+ device_destroy(&firmware_attributes_class, MKDEV(0, 0));
err_exit_bios_attr_pass_interface:
exit_bios_attr_pass_interface();
@@ -619,8 +611,7 @@ err_exit_bios_attr_set_interface:
static void __exit sysman_exit(void)
{
release_attributes_data();
- device_destroy(fw_attr_class, MKDEV(0, 0));
- fw_attributes_class_put();
+ device_destroy(&firmware_attributes_class, MKDEV(0, 0));
exit_bios_attr_set_interface();
exit_bios_attr_pass_interface();
}
diff --git a/drivers/platform/x86/dell/dell_rbu.c b/drivers/platform/x86/dell/dell_rbu.c
index 9f51e0fcab04..8dea70b7f8c1 100644
--- a/drivers/platform/x86/dell/dell_rbu.c
+++ b/drivers/platform/x86/dell/dell_rbu.c
@@ -292,7 +292,7 @@ static int packet_read_list(char *data, size_t * pread_length)
remaining_bytes = *pread_length;
bytes_read = rbu_data.packet_read_count;
- list_for_each_entry(newpacket, (&packet_data_head.list)->next, list) {
+ list_for_each_entry(newpacket, &packet_data_head.list, list) {
bytes_copied = do_packet_read(pdest, newpacket,
remaining_bytes, bytes_read, &temp_count);
remaining_bytes -= bytes_copied;
@@ -315,14 +315,14 @@ static void packet_empty_list(void)
{
struct packet_data *newpacket, *tmp;
- list_for_each_entry_safe(newpacket, tmp, (&packet_data_head.list)->next, list) {
+ list_for_each_entry_safe(newpacket, tmp, &packet_data_head.list, list) {
list_del(&newpacket->list);
/*
* zero out the RBU packet memory before freeing
* to make sure there are no stale RBU packets left in memory
*/
- memset(newpacket->data, 0, rbu_data.packetsize);
+ memset(newpacket->data, 0, newpacket->length);
set_memory_wb((unsigned long)newpacket->data,
1 << newpacket->ordernum);
free_pages((unsigned long) newpacket->data,
@@ -475,7 +475,7 @@ static ssize_t read_rbu_mono_data(char *buffer, loff_t pos, size_t count)
}
static ssize_t data_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
+ const struct bin_attribute *bin_attr,
char *buffer, loff_t pos, size_t count)
{
ssize_t ret_count = 0;
@@ -492,7 +492,7 @@ static ssize_t data_read(struct file *filp, struct kobject *kobj,
spin_unlock(&rbu_data.lock);
return ret_count;
}
-static BIN_ATTR_RO(data, 0);
+static const BIN_ATTR_RO(data, 0);
static void callbackfn_rbu(const struct firmware *fw, void *context)
{
@@ -530,7 +530,7 @@ static void callbackfn_rbu(const struct firmware *fw, void *context)
}
static ssize_t image_type_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
+ const struct bin_attribute *bin_attr,
char *buffer, loff_t pos, size_t count)
{
int size = 0;
@@ -540,7 +540,7 @@ static ssize_t image_type_read(struct file *filp, struct kobject *kobj,
}
static ssize_t image_type_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
+ const struct bin_attribute *bin_attr,
char *buffer, loff_t pos, size_t count)
{
int rc = count;
@@ -597,10 +597,10 @@ static ssize_t image_type_write(struct file *filp, struct kobject *kobj,
return rc;
}
-static BIN_ATTR_RW(image_type, 0);
+static const BIN_ATTR_RW(image_type, 0);
static ssize_t packet_size_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
+ const struct bin_attribute *bin_attr,
char *buffer, loff_t pos, size_t count)
{
int size = 0;
@@ -613,7 +613,7 @@ static ssize_t packet_size_read(struct file *filp, struct kobject *kobj,
}
static ssize_t packet_size_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
+ const struct bin_attribute *bin_attr,
char *buffer, loff_t pos, size_t count)
{
unsigned long temp;
@@ -626,9 +626,9 @@ static ssize_t packet_size_write(struct file *filp, struct kobject *kobj,
spin_unlock(&rbu_data.lock);
return count;
}
-static BIN_ATTR_RW(packet_size, 0);
+static const BIN_ATTR_RW(packet_size, 0);
-static struct bin_attribute *rbu_bin_attrs[] = {
+static const struct bin_attribute *const rbu_bin_attrs[] = {
&bin_attr_data,
&bin_attr_image_type,
&bin_attr_packet_size,
@@ -636,7 +636,7 @@ static struct bin_attribute *rbu_bin_attrs[] = {
};
static const struct attribute_group rbu_group = {
- .bin_attrs = rbu_bin_attrs,
+ .bin_attrs_new = rbu_bin_attrs,
};
static int __init dcdrbu_init(void)
diff --git a/drivers/platform/x86/firmware_attributes_class.c b/drivers/platform/x86/firmware_attributes_class.c
index 182a07d8ae3d..736e96c186d9 100644
--- a/drivers/platform/x86/firmware_attributes_class.c
+++ b/drivers/platform/x86/firmware_attributes_class.c
@@ -2,51 +2,25 @@
/* Firmware attributes class helper module */
-#include <linux/mutex.h>
-#include <linux/device/class.h>
#include <linux/module.h>
#include "firmware_attributes_class.h"
-static DEFINE_MUTEX(fw_attr_lock);
-static int fw_attr_inuse;
-
-static const struct class firmware_attributes_class = {
+const struct class firmware_attributes_class = {
.name = "firmware-attributes",
};
+EXPORT_SYMBOL_GPL(firmware_attributes_class);
-int fw_attributes_class_get(const struct class **fw_attr_class)
+static __init int fw_attributes_class_init(void)
{
- int err;
-
- mutex_lock(&fw_attr_lock);
- if (!fw_attr_inuse) { /*first time class is being used*/
- err = class_register(&firmware_attributes_class);
- if (err) {
- mutex_unlock(&fw_attr_lock);
- return err;
- }
- }
- fw_attr_inuse++;
- *fw_attr_class = &firmware_attributes_class;
- mutex_unlock(&fw_attr_lock);
- return 0;
+ return class_register(&firmware_attributes_class);
}
-EXPORT_SYMBOL_GPL(fw_attributes_class_get);
+module_init(fw_attributes_class_init);
-int fw_attributes_class_put(void)
+static __exit void fw_attributes_class_exit(void)
{
- mutex_lock(&fw_attr_lock);
- if (!fw_attr_inuse) {
- mutex_unlock(&fw_attr_lock);
- return -EINVAL;
- }
- fw_attr_inuse--;
- if (!fw_attr_inuse) /* No more consumers */
- class_unregister(&firmware_attributes_class);
- mutex_unlock(&fw_attr_lock);
- return 0;
+ class_unregister(&firmware_attributes_class);
}
-EXPORT_SYMBOL_GPL(fw_attributes_class_put);
+module_exit(fw_attributes_class_exit);
MODULE_AUTHOR("Mark Pearson <markpearson@lenovo.com>");
MODULE_DESCRIPTION("Firmware attributes class helper module");
diff --git a/drivers/platform/x86/firmware_attributes_class.h b/drivers/platform/x86/firmware_attributes_class.h
index 363c75f1ac1b..d27abe54fcf9 100644
--- a/drivers/platform/x86/firmware_attributes_class.h
+++ b/drivers/platform/x86/firmware_attributes_class.h
@@ -5,7 +5,8 @@
#ifndef FW_ATTR_CLASS_H
#define FW_ATTR_CLASS_H
-int fw_attributes_class_get(const struct class **fw_attr_class);
-int fw_attributes_class_put(void);
+#include <linux/device/class.h>
+
+extern const struct class firmware_attributes_class;
#endif /* FW_ATTR_CLASS_H */
diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c
index ae992ac1ab4a..162809140f68 100644
--- a/drivers/platform/x86/fujitsu-laptop.c
+++ b/drivers/platform/x86/fujitsu-laptop.c
@@ -17,13 +17,13 @@
/*
* fujitsu-laptop.c - Fujitsu laptop support, providing access to additional
* features made available on a range of Fujitsu laptops including the
- * P2xxx/P5xxx/S6xxx/S7xxx series.
+ * P2xxx/P5xxx/S2xxx/S6xxx/S7xxx series.
*
* This driver implements a vendor-specific backlight control interface for
* Fujitsu laptops and provides support for hotkeys present on certain Fujitsu
* laptops.
*
- * This driver has been tested on a Fujitsu Lifebook S6410, S7020 and
+ * This driver has been tested on a Fujitsu Lifebook S2110, S6410, S7020 and
* P8010. It should work on most P-series and S-series Lifebooks, but
* YMMV.
*
@@ -107,7 +107,11 @@
#define KEY2_CODE 0x411
#define KEY3_CODE 0x412
#define KEY4_CODE 0x413
-#define KEY5_CODE 0x420
+#define KEY5_CODE 0x414
+#define KEY6_CODE 0x415
+#define KEY7_CODE 0x416
+#define KEY8_CODE 0x417
+#define KEY9_CODE 0x420
/* Hotkey ringbuffer limits */
#define MAX_HOTKEY_RINGBUFFER_SIZE 100
@@ -505,8 +509,8 @@ static int acpi_fujitsu_bl_add(struct acpi_device *device)
return -ENOMEM;
fujitsu_bl = priv;
- strcpy(acpi_device_name(device), ACPI_FUJITSU_BL_DEVICE_NAME);
- strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS);
+ strscpy(acpi_device_name(device), ACPI_FUJITSU_BL_DEVICE_NAME);
+ strscpy(acpi_device_class(device), ACPI_FUJITSU_CLASS);
device->driver_data = priv;
pr_info("ACPI: %s [%s]\n",
@@ -560,7 +564,7 @@ static const struct key_entry keymap_default[] = {
{ KE_KEY, KEY2_CODE, { KEY_PROG2 } },
{ KE_KEY, KEY3_CODE, { KEY_PROG3 } },
{ KE_KEY, KEY4_CODE, { KEY_PROG4 } },
- { KE_KEY, KEY5_CODE, { KEY_RFKILL } },
+ { KE_KEY, KEY9_CODE, { KEY_RFKILL } },
/* Soft keys read from status flags */
{ KE_KEY, FLAG_RFKILL, { KEY_RFKILL } },
{ KE_KEY, FLAG_TOUCHPAD_TOGGLE, { KEY_TOUCHPAD_TOGGLE } },
@@ -584,6 +588,18 @@ static const struct key_entry keymap_p8010[] = {
{ KE_END, 0 }
};
+static const struct key_entry keymap_s2110[] = {
+ { KE_KEY, KEY1_CODE, { KEY_PROG1 } }, /* "A" */
+ { KE_KEY, KEY2_CODE, { KEY_PROG2 } }, /* "B" */
+ { KE_KEY, KEY3_CODE, { KEY_WWW } }, /* "Internet" */
+ { KE_KEY, KEY4_CODE, { KEY_EMAIL } }, /* "E-mail" */
+ { KE_KEY, KEY5_CODE, { KEY_STOPCD } },
+ { KE_KEY, KEY6_CODE, { KEY_PLAYPAUSE } },
+ { KE_KEY, KEY7_CODE, { KEY_PREVIOUSSONG } },
+ { KE_KEY, KEY8_CODE, { KEY_NEXTSONG } },
+ { KE_END, 0 }
+};
+
static const struct key_entry *keymap = keymap_default;
static int fujitsu_laptop_dmi_keymap_override(const struct dmi_system_id *id)
@@ -621,6 +637,15 @@ static const struct dmi_system_id fujitsu_laptop_dmi_table[] = {
},
.driver_data = (void *)keymap_p8010
},
+ {
+ .callback = fujitsu_laptop_dmi_keymap_override,
+ .ident = "Fujitsu LifeBook S2110",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S2110"),
+ },
+ .driver_data = (void *)keymap_s2110
+ },
{}
};
@@ -891,8 +916,8 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device)
WARN_ONCE(fext, "More than one FUJ02E3 ACPI device was found. Driver may not work as intended.");
fext = device;
- strcpy(acpi_device_name(device), ACPI_FUJITSU_LAPTOP_DEVICE_NAME);
- strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS);
+ strscpy(acpi_device_name(device), ACPI_FUJITSU_LAPTOP_DEVICE_NAME);
+ strscpy(acpi_device_class(device), ACPI_FUJITSU_CLASS);
device->driver_data = priv;
/* kfifo */
diff --git a/drivers/platform/x86/gigabyte-wmi.c b/drivers/platform/x86/gigabyte-wmi.c
index f6ba88baee4d..f42c85607a6b 100644
--- a/drivers/platform/x86/gigabyte-wmi.c
+++ b/drivers/platform/x86/gigabyte-wmi.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * Copyright (C) 2021 Thomas WeiĂźschuh <thomas@weissschuh.net>
+ * Copyright (C) 2021 Thomas WeiĂźschuh <linux@weissschuh.net>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -159,6 +159,6 @@ static struct wmi_driver gigabyte_wmi_driver = {
module_wmi_driver(gigabyte_wmi_driver);
MODULE_DEVICE_TABLE(wmi, gigabyte_wmi_id_table);
-MODULE_AUTHOR("Thomas WeiĂźschuh <thomas@weissschuh.net>");
+MODULE_AUTHOR("Thomas WeiĂźschuh <linux@weissschuh.net>");
MODULE_DESCRIPTION("Gigabyte WMI temperature driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/hp/hp-bioscfg/Makefile b/drivers/platform/x86/hp/hp-bioscfg/Makefile
index 67be0d917753..7d23649b34dc 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/Makefile
+++ b/drivers/platform/x86/hp/hp-bioscfg/Makefile
@@ -1,6 +1,6 @@
obj-$(CONFIG_HP_BIOSCFG) := hp-bioscfg.o
-hp-bioscfg-objs := bioscfg.o \
+hp-bioscfg-y := bioscfg.o \
biosattr-interface.o \
enum-attributes.o \
int-attributes.o \
diff --git a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
index 2dc50152158a..13237890fc92 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
+++ b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
@@ -24,8 +24,6 @@ struct bioscfg_priv bioscfg_drv = {
.mutex = __MUTEX_INITIALIZER(bioscfg_drv.mutex),
};
-static const struct class *fw_attr_class;
-
ssize_t display_name_language_code_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
@@ -390,16 +388,13 @@ union acpi_object *hp_get_wmiobj_pointer(int instance_id, const char *guid_strin
*/
int hp_get_instance_count(const char *guid_string)
{
- union acpi_object *wmi_obj = NULL;
- int i = 0;
+ int ret;
- do {
- kfree(wmi_obj);
- wmi_obj = hp_get_wmiobj_pointer(i, guid_string);
- i++;
- } while (wmi_obj);
+ ret = wmi_instance_count(guid_string);
+ if (ret < 0)
+ return 0;
- return i - 1;
+ return ret;
}
/**
@@ -450,7 +445,7 @@ int hp_convert_hexstr_to_str(const char *input, u32 input_len, char **str, int *
return -ENOMEM;
for (i = 0; i < input_len; i += 5) {
- strncpy(tmp, input + i, strlen(tmp));
+ strscpy(tmp, input + i);
if (kstrtol(tmp, 16, &ch) == 0) {
// escape char
if (ch == '\\' ||
@@ -972,11 +967,7 @@ static int __init hp_init(void)
if (ret)
return ret;
- ret = fw_attributes_class_get(&fw_attr_class);
- if (ret)
- goto err_unregister_class;
-
- bioscfg_drv.class_dev = device_create(fw_attr_class, NULL, MKDEV(0, 0),
+ bioscfg_drv.class_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0),
NULL, "%s", DRIVER_NAME);
if (IS_ERR(bioscfg_drv.class_dev)) {
ret = PTR_ERR(bioscfg_drv.class_dev);
@@ -1043,10 +1034,9 @@ err_release_attributes_data:
release_attributes_data();
err_destroy_classdev:
- device_destroy(fw_attr_class, MKDEV(0, 0));
+ device_destroy(&firmware_attributes_class, MKDEV(0, 0));
err_unregister_class:
- fw_attributes_class_put();
hp_exit_attr_set_interface();
return ret;
@@ -1055,9 +1045,8 @@ err_unregister_class:
static void __exit hp_exit(void)
{
release_attributes_data();
- device_destroy(fw_attr_class, MKDEV(0, 0));
+ device_destroy(&firmware_attributes_class, MKDEV(0, 0));
- fw_attributes_class_put();
hp_exit_attr_set_interface();
}
diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c
index 20c55bab3b8c..db5fdee2109c 100644
--- a/drivers/platform/x86/hp/hp-wmi.c
+++ b/drivers/platform/x86/hp/hp-wmi.c
@@ -45,6 +45,10 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45E9-BE91-3D44E2C707E4");
#define HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET 0x63
#define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95
+#define HP_FAN_SPEED_AUTOMATIC 0x00
+#define HP_POWER_LIMIT_DEFAULT 0x00
+#define HP_POWER_LIMIT_NO_CHANGE 0xFF
+
#define ACPI_AC_CLASS "ac_adapter"
#define zero_if_sup(tmp) (zero_insize_support?0:sizeof(tmp)) // use when zero insize is required
@@ -83,11 +87,16 @@ static const char * const omen_timed_thermal_profile_boards[] = {
"8BAD", "8A42", "8A15"
};
-/* DMI Board names of Victus laptops */
+/* DMI Board names of Victus 16-d1xxx laptops */
static const char * const victus_thermal_profile_boards[] = {
"8A25"
};
+/* DMI Board names of Victus 16-s1000 laptops */
+static const char * const victus_s_thermal_profile_boards[] = {
+ "8C9C"
+};
+
enum hp_wmi_radio {
HPWMI_WIFI = 0x0,
HPWMI_BLUETOOTH = 0x1,
@@ -147,12 +156,32 @@ enum hp_wmi_commandtype {
HPWMI_THERMAL_PROFILE_QUERY = 0x4c,
};
+struct victus_power_limits {
+ u8 pl1;
+ u8 pl2;
+ u8 pl4;
+ u8 cpu_gpu_concurrent_limit;
+};
+
+struct victus_gpu_power_modes {
+ u8 ctgp_enable;
+ u8 ppab_enable;
+ u8 dstate;
+ u8 gpu_slowdown_temp;
+};
+
enum hp_wmi_gm_commandtype {
- HPWMI_FAN_SPEED_GET_QUERY = 0x11,
- HPWMI_SET_PERFORMANCE_MODE = 0x1A,
- HPWMI_FAN_SPEED_MAX_GET_QUERY = 0x26,
- HPWMI_FAN_SPEED_MAX_SET_QUERY = 0x27,
- HPWMI_GET_SYSTEM_DESIGN_DATA = 0x28,
+ HPWMI_FAN_SPEED_GET_QUERY = 0x11,
+ HPWMI_SET_PERFORMANCE_MODE = 0x1A,
+ HPWMI_FAN_SPEED_MAX_GET_QUERY = 0x26,
+ HPWMI_FAN_SPEED_MAX_SET_QUERY = 0x27,
+ HPWMI_GET_SYSTEM_DESIGN_DATA = 0x28,
+ HPWMI_FAN_COUNT_GET_QUERY = 0x10,
+ HPWMI_GET_GPU_THERMAL_MODES_QUERY = 0x21,
+ HPWMI_SET_GPU_THERMAL_MODES_QUERY = 0x22,
+ HPWMI_SET_POWER_LIMITS_QUERY = 0x29,
+ HPWMI_VICTUS_S_FAN_SPEED_GET_QUERY = 0x2D,
+ HPWMI_FAN_SPEED_SET_QUERY = 0x2E,
};
enum hp_wmi_command {
@@ -211,6 +240,11 @@ enum hp_thermal_profile_victus {
HP_VICTUS_THERMAL_PROFILE_QUIET = 0x03,
};
+enum hp_thermal_profile_victus_s {
+ HP_VICTUS_S_THERMAL_PROFILE_DEFAULT = 0x00,
+ HP_VICTUS_S_THERMAL_PROFILE_PERFORMANCE = 0x01,
+};
+
enum hp_thermal_profile {
HP_THERMAL_PROFILE_PERFORMANCE = 0x00,
HP_THERMAL_PROFILE_DEFAULT = 0x01,
@@ -273,7 +307,7 @@ static DEFINE_MUTEX(active_platform_profile_lock);
static struct input_dev *hp_wmi_input_dev;
static struct input_dev *camera_shutter_input_dev;
static struct platform_device *hp_wmi_platform_dev;
-static struct platform_profile_handler platform_profile_handler;
+static struct device *platform_profile_device;
static struct notifier_block platform_power_source_nb;
static enum platform_profile_option active_platform_profile;
static bool platform_profile_support;
@@ -411,6 +445,26 @@ out_free:
return ret;
}
+/*
+ * Calling this hp_wmi_get_fan_count_userdefine_trigger function also enables
+ * and/or maintains the laptop in user defined thermal and fan states, instead
+ * of using a fallback state. After a 120 seconds timeout however, the laptop
+ * goes back to its fallback state.
+ */
+static int hp_wmi_get_fan_count_userdefine_trigger(void)
+{
+ u8 fan_data[4] = {};
+ int ret;
+
+ ret = hp_wmi_perform_query(HPWMI_FAN_COUNT_GET_QUERY, HPWMI_GM,
+ &fan_data, sizeof(u8),
+ sizeof(fan_data));
+ if (ret != 0)
+ return -EINVAL;
+
+ return fan_data[0]; /* Others bytes aren't providing fan count */
+}
+
static int hp_wmi_get_fan_speed(int fan)
{
u8 fsh, fsl;
@@ -429,6 +483,23 @@ static int hp_wmi_get_fan_speed(int fan)
return (fsh << 8) | fsl;
}
+static int hp_wmi_get_fan_speed_victus_s(int fan)
+{
+ u8 fan_data[128] = {};
+ int ret;
+
+ if (fan < 0 || fan >= sizeof(fan_data))
+ return -EINVAL;
+
+ ret = hp_wmi_perform_query(HPWMI_VICTUS_S_FAN_SPEED_GET_QUERY,
+ HPWMI_GM, &fan_data, sizeof(u8),
+ sizeof(fan_data));
+ if (ret != 0)
+ return -EINVAL;
+
+ return fan_data[fan] * 100;
+}
+
static int hp_wmi_read_int(int query)
{
int val = 0, ret;
@@ -557,6 +628,30 @@ static int hp_wmi_fan_speed_max_set(int enabled)
return enabled;
}
+static int hp_wmi_fan_speed_reset(void)
+{
+ u8 fan_speed[2] = { HP_FAN_SPEED_AUTOMATIC, HP_FAN_SPEED_AUTOMATIC };
+ int ret;
+
+ ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_SET_QUERY, HPWMI_GM,
+ &fan_speed, sizeof(fan_speed), 0);
+
+ return ret;
+}
+
+static int hp_wmi_fan_speed_max_reset(void)
+{
+ int ret;
+
+ ret = hp_wmi_fan_speed_max_set(0);
+ if (ret)
+ return ret;
+
+ /* Disabling max fan speed on Victus s1xxx laptops needs a 2nd step: */
+ ret = hp_wmi_fan_speed_reset();
+ return ret;
+}
+
static int hp_wmi_fan_speed_max_get(void)
{
int val = 0, ret;
@@ -1221,7 +1316,7 @@ static int platform_profile_omen_get_ec(enum platform_profile_option *profile)
return 0;
}
-static int platform_profile_omen_get(struct platform_profile_handler *pprof,
+static int platform_profile_omen_get(struct device *dev,
enum platform_profile_option *profile)
{
/*
@@ -1318,7 +1413,7 @@ static int platform_profile_omen_set_ec(enum platform_profile_option profile)
return 0;
}
-static int platform_profile_omen_set(struct platform_profile_handler *pprof,
+static int platform_profile_omen_set(struct device *dev,
enum platform_profile_option profile)
{
int err;
@@ -1345,7 +1440,7 @@ static int thermal_profile_set(int thermal_profile)
sizeof(thermal_profile), 0);
}
-static int hp_wmi_platform_profile_get(struct platform_profile_handler *pprof,
+static int hp_wmi_platform_profile_get(struct device *dev,
enum platform_profile_option *profile)
{
int tp;
@@ -1374,7 +1469,7 @@ static int hp_wmi_platform_profile_get(struct platform_profile_handler *pprof,
return 0;
}
-static int hp_wmi_platform_profile_set(struct platform_profile_handler *pprof,
+static int hp_wmi_platform_profile_set(struct device *dev,
enum platform_profile_option profile)
{
int err, tp;
@@ -1440,11 +1535,11 @@ static int platform_profile_victus_get_ec(enum platform_profile_option *profile)
return 0;
}
-static int platform_profile_victus_get(struct platform_profile_handler *pprof,
+static int platform_profile_victus_get(struct device *dev,
enum platform_profile_option *profile)
{
/* Same behaviour as platform_profile_omen_get */
- return platform_profile_omen_get(pprof, profile);
+ return platform_profile_omen_get(dev, profile);
}
static int platform_profile_victus_set_ec(enum platform_profile_option profile)
@@ -1472,7 +1567,162 @@ static int platform_profile_victus_set_ec(enum platform_profile_option profile)
return 0;
}
-static int platform_profile_victus_set(struct platform_profile_handler *pprof,
+static bool is_victus_s_thermal_profile(void)
+{
+ const char *board_name;
+
+ board_name = dmi_get_system_info(DMI_BOARD_NAME);
+ if (!board_name)
+ return false;
+
+ return match_string(victus_s_thermal_profile_boards,
+ ARRAY_SIZE(victus_s_thermal_profile_boards),
+ board_name) >= 0;
+}
+
+static int victus_s_gpu_thermal_profile_get(bool *ctgp_enable,
+ bool *ppab_enable,
+ u8 *dstate,
+ u8 *gpu_slowdown_temp)
+{
+ struct victus_gpu_power_modes gpu_power_modes;
+ int ret;
+
+ ret = hp_wmi_perform_query(HPWMI_GET_GPU_THERMAL_MODES_QUERY, HPWMI_GM,
+ &gpu_power_modes, sizeof(gpu_power_modes),
+ sizeof(gpu_power_modes));
+ if (ret == 0) {
+ *ctgp_enable = gpu_power_modes.ctgp_enable ? true : false;
+ *ppab_enable = gpu_power_modes.ppab_enable ? true : false;
+ *dstate = gpu_power_modes.dstate;
+ *gpu_slowdown_temp = gpu_power_modes.gpu_slowdown_temp;
+ }
+
+ return ret;
+}
+
+static int victus_s_gpu_thermal_profile_set(bool ctgp_enable,
+ bool ppab_enable,
+ u8 dstate)
+{
+ struct victus_gpu_power_modes gpu_power_modes;
+ int ret;
+
+ bool current_ctgp_state, current_ppab_state;
+ u8 current_dstate, current_gpu_slowdown_temp;
+
+ /* Retrieving GPU slowdown temperature, in order to keep it unchanged */
+ ret = victus_s_gpu_thermal_profile_get(&current_ctgp_state,
+ &current_ppab_state,
+ &current_dstate,
+ &current_gpu_slowdown_temp);
+ if (ret < 0) {
+ pr_warn("GPU modes not updated, unable to get slowdown temp\n");
+ return ret;
+ }
+
+ gpu_power_modes.ctgp_enable = ctgp_enable ? 0x01 : 0x00;
+ gpu_power_modes.ppab_enable = ppab_enable ? 0x01 : 0x00;
+ gpu_power_modes.dstate = dstate;
+ gpu_power_modes.gpu_slowdown_temp = current_gpu_slowdown_temp;
+
+
+ ret = hp_wmi_perform_query(HPWMI_SET_GPU_THERMAL_MODES_QUERY, HPWMI_GM,
+ &gpu_power_modes, sizeof(gpu_power_modes), 0);
+
+ return ret;
+}
+
+/* Note: HP_POWER_LIMIT_DEFAULT can be used to restore default PL1 and PL2 */
+static int victus_s_set_cpu_pl1_pl2(u8 pl1, u8 pl2)
+{
+ struct victus_power_limits power_limits;
+ int ret;
+
+ /* We need to know both PL1 and PL2 values in order to check them */
+ if (pl1 == HP_POWER_LIMIT_NO_CHANGE || pl2 == HP_POWER_LIMIT_NO_CHANGE)
+ return -EINVAL;
+
+ /* PL2 is not supposed to be lower than PL1 */
+ if (pl2 < pl1)
+ return -EINVAL;
+
+ power_limits.pl1 = pl1;
+ power_limits.pl2 = pl2;
+ power_limits.pl4 = HP_POWER_LIMIT_NO_CHANGE;
+ power_limits.cpu_gpu_concurrent_limit = HP_POWER_LIMIT_NO_CHANGE;
+
+ ret = hp_wmi_perform_query(HPWMI_SET_POWER_LIMITS_QUERY, HPWMI_GM,
+ &power_limits, sizeof(power_limits), 0);
+
+ return ret;
+}
+
+static int platform_profile_victus_s_set_ec(enum platform_profile_option profile)
+{
+ bool gpu_ctgp_enable, gpu_ppab_enable;
+ u8 gpu_dstate; /* Test shows 1 = 100%, 2 = 50%, 3 = 25%, 4 = 12.5% */
+ int err, tp;
+
+ switch (profile) {
+ case PLATFORM_PROFILE_PERFORMANCE:
+ tp = HP_VICTUS_S_THERMAL_PROFILE_PERFORMANCE;
+ gpu_ctgp_enable = true;
+ gpu_ppab_enable = true;
+ gpu_dstate = 1;
+ break;
+ case PLATFORM_PROFILE_BALANCED:
+ tp = HP_VICTUS_S_THERMAL_PROFILE_DEFAULT;
+ gpu_ctgp_enable = false;
+ gpu_ppab_enable = true;
+ gpu_dstate = 1;
+ break;
+ case PLATFORM_PROFILE_LOW_POWER:
+ tp = HP_VICTUS_S_THERMAL_PROFILE_DEFAULT;
+ gpu_ctgp_enable = false;
+ gpu_ppab_enable = false;
+ gpu_dstate = 1;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ hp_wmi_get_fan_count_userdefine_trigger();
+
+ err = omen_thermal_profile_set(tp);
+ if (err < 0) {
+ pr_err("Failed to set platform profile %d: %d\n", profile, err);
+ return err;
+ }
+
+ err = victus_s_gpu_thermal_profile_set(gpu_ctgp_enable,
+ gpu_ppab_enable,
+ gpu_dstate);
+ if (err < 0) {
+ pr_err("Failed to set GPU profile %d: %d\n", profile, err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int platform_profile_victus_s_set(struct device *dev,
+ enum platform_profile_option profile)
+{
+ int err;
+
+ guard(mutex)(&active_platform_profile_lock);
+
+ err = platform_profile_victus_s_set_ec(profile);
+ if (err < 0)
+ return err;
+
+ active_platform_profile = profile;
+
+ return 0;
+}
+
+static int platform_profile_victus_set(struct device *dev,
enum platform_profile_option profile)
{
int err;
@@ -1488,6 +1738,26 @@ static int platform_profile_victus_set(struct platform_profile_handler *pprof,
return 0;
}
+static int hp_wmi_platform_profile_probe(void *drvdata, unsigned long *choices)
+{
+ if (is_omen_thermal_profile()) {
+ set_bit(PLATFORM_PROFILE_COOL, choices);
+ } else if (is_victus_thermal_profile()) {
+ set_bit(PLATFORM_PROFILE_QUIET, choices);
+ } else if (is_victus_s_thermal_profile()) {
+ /* Adding an equivalent to HP Omen software ECO mode: */
+ set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
+ } else {
+ set_bit(PLATFORM_PROFILE_QUIET, choices);
+ set_bit(PLATFORM_PROFILE_COOL, choices);
+ }
+
+ set_bit(PLATFORM_PROFILE_BALANCED, choices);
+ set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
+
+ return 0;
+}
+
static int omen_powersource_event(struct notifier_block *nb,
unsigned long value,
void *data)
@@ -1545,6 +1815,39 @@ static int omen_powersource_event(struct notifier_block *nb,
return NOTIFY_OK;
}
+static int victus_s_powersource_event(struct notifier_block *nb,
+ unsigned long value,
+ void *data)
+{
+ struct acpi_bus_event *event_entry = data;
+ int err;
+
+ if (strcmp(event_entry->device_class, ACPI_AC_CLASS) != 0)
+ return NOTIFY_DONE;
+
+ pr_debug("Received power source device event\n");
+
+ /*
+ * Switching to battery power source while Performance mode is active
+ * needs manual triggering of CPU power limits. Same goes when switching
+ * to AC power source while Performance mode is active. Other modes
+ * however are automatically behaving without any manual action.
+ * Seen on HP 16-s1034nf (board 8C9C) with F.11 and F.13 BIOS versions.
+ */
+
+ if (active_platform_profile == PLATFORM_PROFILE_PERFORMANCE) {
+ pr_debug("Triggering CPU PL1/PL2 actualization\n");
+ err = victus_s_set_cpu_pl1_pl2(HP_POWER_LIMIT_DEFAULT,
+ HP_POWER_LIMIT_DEFAULT);
+ if (err)
+ pr_warn("Failed to actualize power limits: %d\n", err);
+
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
static int omen_register_powersource_event_handler(void)
{
int err;
@@ -1560,13 +1863,57 @@ static int omen_register_powersource_event_handler(void)
return 0;
}
+static int victus_s_register_powersource_event_handler(void)
+{
+ int err;
+
+ platform_power_source_nb.notifier_call = victus_s_powersource_event;
+ err = register_acpi_notifier(&platform_power_source_nb);
+ if (err < 0) {
+ pr_warn("Failed to install ACPI power source notify handler\n");
+ return err;
+ }
+
+ return 0;
+}
+
static inline void omen_unregister_powersource_event_handler(void)
{
unregister_acpi_notifier(&platform_power_source_nb);
}
-static int thermal_profile_setup(void)
+static inline void victus_s_unregister_powersource_event_handler(void)
{
+ unregister_acpi_notifier(&platform_power_source_nb);
+}
+
+static const struct platform_profile_ops platform_profile_omen_ops = {
+ .probe = hp_wmi_platform_profile_probe,
+ .profile_get = platform_profile_omen_get,
+ .profile_set = platform_profile_omen_set,
+};
+
+static const struct platform_profile_ops platform_profile_victus_ops = {
+ .probe = hp_wmi_platform_profile_probe,
+ .profile_get = platform_profile_victus_get,
+ .profile_set = platform_profile_victus_set,
+};
+
+static const struct platform_profile_ops platform_profile_victus_s_ops = {
+ .probe = hp_wmi_platform_profile_probe,
+ .profile_get = platform_profile_omen_get,
+ .profile_set = platform_profile_victus_s_set,
+};
+
+static const struct platform_profile_ops hp_wmi_platform_profile_ops = {
+ .probe = hp_wmi_platform_profile_probe,
+ .profile_get = hp_wmi_platform_profile_get,
+ .profile_set = hp_wmi_platform_profile_set,
+};
+
+static int thermal_profile_setup(struct platform_device *device)
+{
+ const struct platform_profile_ops *ops;
int err, tp;
if (is_omen_thermal_profile()) {
@@ -1582,10 +1929,7 @@ static int thermal_profile_setup(void)
if (err < 0)
return err;
- platform_profile_handler.profile_get = platform_profile_omen_get;
- platform_profile_handler.profile_set = platform_profile_omen_set;
-
- set_bit(PLATFORM_PROFILE_COOL, platform_profile_handler.choices);
+ ops = &platform_profile_omen_ops;
} else if (is_victus_thermal_profile()) {
err = platform_profile_victus_get_ec(&active_platform_profile);
if (err < 0)
@@ -1599,10 +1943,19 @@ static int thermal_profile_setup(void)
if (err < 0)
return err;
- platform_profile_handler.profile_get = platform_profile_victus_get;
- platform_profile_handler.profile_set = platform_profile_victus_set;
+ ops = &platform_profile_victus_ops;
+ } else if (is_victus_s_thermal_profile()) {
+ /*
+ * Being unable to retrieve laptop's current thermal profile,
+ * during this setup, we set it to Balanced by default.
+ */
+ active_platform_profile = PLATFORM_PROFILE_BALANCED;
+
+ err = platform_profile_victus_s_set_ec(active_platform_profile);
+ if (err < 0)
+ return err;
- set_bit(PLATFORM_PROFILE_QUIET, platform_profile_handler.choices);
+ ops = &platform_profile_victus_s_ops;
} else {
tp = thermal_profile_get();
@@ -1617,20 +1970,15 @@ static int thermal_profile_setup(void)
if (err)
return err;
- platform_profile_handler.profile_get = hp_wmi_platform_profile_get;
- platform_profile_handler.profile_set = hp_wmi_platform_profile_set;
-
- set_bit(PLATFORM_PROFILE_QUIET, platform_profile_handler.choices);
- set_bit(PLATFORM_PROFILE_COOL, platform_profile_handler.choices);
+ ops = &hp_wmi_platform_profile_ops;
}
- set_bit(PLATFORM_PROFILE_BALANCED, platform_profile_handler.choices);
- set_bit(PLATFORM_PROFILE_PERFORMANCE, platform_profile_handler.choices);
-
- err = platform_profile_register(&platform_profile_handler);
- if (err)
- return err;
+ platform_profile_device = devm_platform_profile_register(&device->dev, "hp-wmi",
+ NULL, ops);
+ if (IS_ERR(platform_profile_device))
+ return PTR_ERR(platform_profile_device);
+ pr_info("Registered as platform profile handler\n");
platform_profile_support = true;
return 0;
@@ -1663,7 +2011,7 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
if (err < 0)
return err;
- thermal_profile_setup();
+ thermal_profile_setup(device);
return 0;
}
@@ -1689,9 +2037,6 @@ static void __exit hp_wmi_bios_remove(struct platform_device *device)
rfkill_unregister(wwan_rfkill);
rfkill_destroy(wwan_rfkill);
}
-
- if (platform_profile_support)
- platform_profile_remove();
}
static int hp_wmi_resume_handler(struct device *device)
@@ -1759,8 +2104,13 @@ static umode_t hp_wmi_hwmon_is_visible(const void *data,
case hwmon_pwm:
return 0644;
case hwmon_fan:
- if (hp_wmi_get_fan_speed(channel) >= 0)
- return 0444;
+ if (is_victus_s_thermal_profile()) {
+ if (hp_wmi_get_fan_speed_victus_s(channel) >= 0)
+ return 0444;
+ } else {
+ if (hp_wmi_get_fan_speed(channel) >= 0)
+ return 0444;
+ }
break;
default:
return 0;
@@ -1776,8 +2126,10 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
switch (type) {
case hwmon_fan:
- ret = hp_wmi_get_fan_speed(channel);
-
+ if (is_victus_s_thermal_profile())
+ ret = hp_wmi_get_fan_speed_victus_s(channel);
+ else
+ ret = hp_wmi_get_fan_speed(channel);
if (ret < 0)
return ret;
*val = ret;
@@ -1810,11 +2162,17 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
case hwmon_pwm:
switch (val) {
case 0:
+ if (is_victus_s_thermal_profile())
+ hp_wmi_get_fan_count_userdefine_trigger();
/* 0 is no fan speed control (max), which is 1 for us */
return hp_wmi_fan_speed_max_set(1);
case 2:
/* 2 is automatic speed control, which is 0 for us */
- return hp_wmi_fan_speed_max_set(0);
+ if (is_victus_s_thermal_profile()) {
+ hp_wmi_get_fan_count_userdefine_trigger();
+ return hp_wmi_fan_speed_max_reset();
+ } else
+ return hp_wmi_fan_speed_max_set(0);
default:
/* we don't support manual fan speed control */
return -EINVAL;
@@ -1893,6 +2251,10 @@ static int __init hp_wmi_init(void)
err = omen_register_powersource_event_handler();
if (err)
goto err_unregister_device;
+ } else if (is_victus_s_thermal_profile()) {
+ err = victus_s_register_powersource_event_handler();
+ if (err)
+ goto err_unregister_device;
}
return 0;
@@ -1912,6 +2274,9 @@ static void __exit hp_wmi_exit(void)
if (is_omen_thermal_profile() || is_victus_thermal_profile())
omen_unregister_powersource_event_handler();
+ if (is_victus_s_thermal_profile())
+ victus_s_unregister_powersource_event_handler();
+
if (wmi_has_guid(HPWMI_EVENT_GUID))
hp_wmi_input_destroy();
diff --git a/drivers/platform/x86/hp/hp_accel.c b/drivers/platform/x86/hp/hp_accel.c
index 39a6530f5072..10d5af18d639 100644
--- a/drivers/platform/x86/hp/hp_accel.c
+++ b/drivers/platform/x86/hp/hp_accel.c
@@ -267,7 +267,7 @@ static struct delayed_led_classdev hpled_led = {
};
static bool hp_accel_i8042_filter(unsigned char data, unsigned char str,
- struct serio *port)
+ struct serio *port, void *context)
{
static bool extended;
@@ -326,7 +326,7 @@ static int lis3lv02d_probe(struct platform_device *device)
/* filter to remove HPQ6000 accelerometer data
* from keyboard bus stream */
if (strstr(dev_name(&device->dev), "HPQ6000"))
- i8042_install_filter(hp_accel_i8042_filter);
+ i8042_install_filter(hp_accel_i8042_filter, NULL);
INIT_WORK(&hpled_led.work, delayed_set_status_worker);
ret = led_classdev_register(NULL, &hpled_led.led_classdev);
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
index e980dd18e5f6..b5e4da6a6779 100644
--- a/drivers/platform/x86/ideapad-laptop.c
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -15,6 +15,7 @@
#include <linux/bug.h>
#include <linux/cleanup.h>
#include <linux/debugfs.h>
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dmi.h>
#include <linux/i8042.h>
@@ -142,7 +143,7 @@ enum {
struct ideapad_dytc_priv {
enum platform_profile_option current_profile;
- struct platform_profile_handler pprof;
+ struct device *ppdev; /* platform profile device */
struct mutex mutex; /* protects the DYTC interface */
struct ideapad_private *priv;
};
@@ -267,6 +268,20 @@ static void ideapad_shared_exit(struct ideapad_private *priv)
*/
#define IDEAPAD_EC_TIMEOUT 200 /* in ms */
+/*
+ * Some models (e.g., ThinkBook since 2024) have a low tolerance for being
+ * polled too frequently. Doing so may break the state machine in the EC,
+ * resulting in a hard shutdown.
+ *
+ * It is also observed that frequent polls may disturb the ongoing operation
+ * and notably delay the availability of EC response.
+ *
+ * These values are used as the delay before the first poll and the interval
+ * between subsequent polls to solve the above issues.
+ */
+#define IDEAPAD_EC_POLL_MIN_US 150
+#define IDEAPAD_EC_POLL_MAX_US 300
+
static int eval_int(acpi_handle handle, const char *name, unsigned long *res)
{
unsigned long long result;
@@ -383,7 +398,7 @@ static int read_ec_data(acpi_handle handle, unsigned long cmd, unsigned long *da
end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1;
while (time_before(jiffies, end_jiffies)) {
- schedule();
+ usleep_range(IDEAPAD_EC_POLL_MIN_US, IDEAPAD_EC_POLL_MAX_US);
err = eval_vpcr(handle, 1, &val);
if (err)
@@ -414,7 +429,7 @@ static int write_ec_cmd(acpi_handle handle, unsigned long cmd, unsigned long dat
end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1;
while (time_before(jiffies, end_jiffies)) {
- schedule();
+ usleep_range(IDEAPAD_EC_POLL_MIN_US, IDEAPAD_EC_POLL_MAX_US);
err = eval_vpcr(handle, 1, &val);
if (err)
@@ -854,6 +869,7 @@ static const struct attribute_group ideapad_attribute_group = {
.is_visible = ideapad_is_visible,
.attrs = ideapad_attributes
};
+__ATTRIBUTE_GROUPS(ideapad_attribute);
/*
* DYTC Platform profile
@@ -933,10 +949,10 @@ static int convert_profile_to_dytc(enum platform_profile_option profile, int *pe
* dytc_profile_get: Function to register with platform_profile
* handler. Returns current platform profile.
*/
-static int dytc_profile_get(struct platform_profile_handler *pprof,
+static int dytc_profile_get(struct device *dev,
enum platform_profile_option *profile)
{
- struct ideapad_dytc_priv *dytc = container_of(pprof, struct ideapad_dytc_priv, pprof);
+ struct ideapad_dytc_priv *dytc = dev_get_drvdata(dev);
*profile = dytc->current_profile;
return 0;
@@ -986,10 +1002,10 @@ static int dytc_cql_command(struct ideapad_private *priv, unsigned long cmd,
* dytc_profile_set: Function to register with platform_profile
* handler. Sets current platform profile.
*/
-static int dytc_profile_set(struct platform_profile_handler *pprof,
+static int dytc_profile_set(struct device *dev,
enum platform_profile_option profile)
{
- struct ideapad_dytc_priv *dytc = container_of(pprof, struct ideapad_dytc_priv, pprof);
+ struct ideapad_dytc_priv *dytc = dev_get_drvdata(dev);
struct ideapad_private *priv = dytc->priv;
unsigned long output;
int err;
@@ -1023,6 +1039,15 @@ static int dytc_profile_set(struct platform_profile_handler *pprof,
return -EINTR;
}
+static int dytc_profile_probe(void *drvdata, unsigned long *choices)
+{
+ set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
+ set_bit(PLATFORM_PROFILE_BALANCED, choices);
+ set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
+
+ return 0;
+}
+
static void dytc_profile_refresh(struct ideapad_private *priv)
{
enum platform_profile_option profile;
@@ -1041,7 +1066,7 @@ static void dytc_profile_refresh(struct ideapad_private *priv)
if (profile != priv->dytc->current_profile) {
priv->dytc->current_profile = profile;
- platform_profile_notify();
+ platform_profile_notify(priv->dytc->ppdev);
}
}
@@ -1063,6 +1088,12 @@ static const struct dmi_system_id ideapad_dytc_v4_allow_table[] = {
{}
};
+static const struct platform_profile_ops dytc_profile_ops = {
+ .probe = dytc_profile_probe,
+ .profile_get = dytc_profile_get,
+ .profile_set = dytc_profile_set,
+};
+
static int ideapad_dytc_profile_init(struct ideapad_private *priv)
{
int err, dytc_version;
@@ -1103,18 +1134,15 @@ static int ideapad_dytc_profile_init(struct ideapad_private *priv)
mutex_init(&priv->dytc->mutex);
priv->dytc->priv = priv;
- priv->dytc->pprof.profile_get = dytc_profile_get;
- priv->dytc->pprof.profile_set = dytc_profile_set;
-
- /* Setup supported modes */
- set_bit(PLATFORM_PROFILE_LOW_POWER, priv->dytc->pprof.choices);
- set_bit(PLATFORM_PROFILE_BALANCED, priv->dytc->pprof.choices);
- set_bit(PLATFORM_PROFILE_PERFORMANCE, priv->dytc->pprof.choices);
/* Create platform_profile structure and register */
- err = platform_profile_register(&priv->dytc->pprof);
- if (err)
+ priv->dytc->ppdev = devm_platform_profile_register(&priv->platform_device->dev,
+ "ideapad-laptop", priv->dytc,
+ &dytc_profile_ops);
+ if (IS_ERR(priv->dytc->ppdev)) {
+ err = PTR_ERR(priv->dytc->ppdev);
goto pp_reg_failed;
+ }
/* Ensure initial values are correct */
dytc_profile_refresh(priv);
@@ -1134,7 +1162,6 @@ static void ideapad_dytc_profile_exit(struct ideapad_private *priv)
if (!priv->dytc)
return;
- platform_profile_remove();
mutex_destroy(&priv->dytc->mutex);
kfree(priv->dytc);
@@ -1234,21 +1261,6 @@ static void ideapad_unregister_rfkill(struct ideapad_private *priv, int dev)
}
/*
- * Platform device
- */
-static int ideapad_sysfs_init(struct ideapad_private *priv)
-{
- return device_add_group(&priv->platform_device->dev,
- &ideapad_attribute_group);
-}
-
-static void ideapad_sysfs_exit(struct ideapad_private *priv)
-{
- device_remove_group(&priv->platform_device->dev,
- &ideapad_attribute_group);
-}
-
-/*
* input device
*/
#define IDEAPAD_WMI_KEY 0x100
@@ -1297,6 +1309,16 @@ static const struct key_entry ideapad_keymap[] = {
/* Specific to some newer models */
{ KE_KEY, 0x3e | IDEAPAD_WMI_KEY, { KEY_MICMUTE } },
{ KE_KEY, 0x3f | IDEAPAD_WMI_KEY, { KEY_RFKILL } },
+ /* Star- (User Assignable Key) */
+ { KE_KEY, 0x44 | IDEAPAD_WMI_KEY, { KEY_PROG1 } },
+ /* Eye */
+ { KE_KEY, 0x45 | IDEAPAD_WMI_KEY, { KEY_PROG3 } },
+ /* Performance toggle also Fn+Q, handled inside ideapad_wmi_notify() */
+ { KE_KEY, 0x3d | IDEAPAD_WMI_KEY, { KEY_PROG4 } },
+ /* shift + prtsc */
+ { KE_KEY, 0x2d | IDEAPAD_WMI_KEY, { KEY_CUT } },
+ { KE_KEY, 0x29 | IDEAPAD_WMI_KEY, { KEY_TOUCHPAD_TOGGLE } },
+ { KE_KEY, 0x2a | IDEAPAD_WMI_KEY, { KEY_ROOT_MENU } },
{ KE_END },
};
@@ -2083,6 +2105,12 @@ static void ideapad_wmi_notify(struct wmi_device *wdev, union acpi_object *data)
dev_dbg(&wdev->dev, "WMI fn-key event: 0x%llx\n",
data->integer.value);
+ /* performance button triggered by 0x3d */
+ if (data->integer.value == 0x3d && priv->dytc) {
+ platform_profile_cycle();
+ break;
+ }
+
/* 0x02 FnLock, 0x03 Esc */
if (data->integer.value == 0x02 || data->integer.value == 0x03)
ideapad_fn_lock_led_notify(priv, data->integer.value == 0x02);
@@ -2164,10 +2192,6 @@ static int ideapad_acpi_add(struct platform_device *pdev)
ideapad_check_features(priv);
- err = ideapad_sysfs_init(priv);
- if (err)
- return err;
-
ideapad_debugfs_init(priv);
err = ideapad_input_init(priv);
@@ -2254,7 +2278,6 @@ backlight_failed:
input_failed:
ideapad_debugfs_exit(priv);
- ideapad_sysfs_exit(priv);
return err;
}
@@ -2282,7 +2305,6 @@ static void ideapad_acpi_remove(struct platform_device *pdev)
ideapad_kbd_bl_exit(priv);
ideapad_input_exit(priv);
ideapad_debugfs_exit(priv);
- ideapad_sysfs_exit(priv);
}
#ifdef CONFIG_PM_SLEEP
@@ -2314,6 +2336,7 @@ static struct platform_driver ideapad_acpi_driver = {
.name = "ideapad_acpi",
.pm = &ideapad_pm,
.acpi_match_table = ACPI_PTR(ideapad_device_ids),
+ .dev_groups = ideapad_attribute_groups,
},
};
diff --git a/drivers/platform/x86/inspur_platform_profile.c b/drivers/platform/x86/inspur_platform_profile.c
index 8440defa6788..e02f5a55a6c5 100644
--- a/drivers/platform/x86/inspur_platform_profile.c
+++ b/drivers/platform/x86/inspur_platform_profile.c
@@ -32,7 +32,7 @@ enum inspur_tmp_profile {
struct inspur_wmi_priv {
struct wmi_device *wdev;
- struct platform_profile_handler handler;
+ struct device *ppdev;
};
static int inspur_wmi_perform_query(struct wmi_device *wdev,
@@ -84,11 +84,10 @@ out_free:
* 0x0: No Error
* 0x1: Error
*/
-static int inspur_platform_profile_set(struct platform_profile_handler *pprof,
+static int inspur_platform_profile_set(struct device *dev,
enum platform_profile_option profile)
{
- struct inspur_wmi_priv *priv = container_of(pprof, struct inspur_wmi_priv,
- handler);
+ struct inspur_wmi_priv *priv = dev_get_drvdata(dev);
u8 ret_code[4] = {0, 0, 0, 0};
int ret;
@@ -132,11 +131,10 @@ static int inspur_platform_profile_set(struct platform_profile_handler *pprof,
* 0x1: Performance Mode
* 0x2: Power Saver Mode
*/
-static int inspur_platform_profile_get(struct platform_profile_handler *pprof,
+static int inspur_platform_profile_get(struct device *dev,
enum platform_profile_option *profile)
{
- struct inspur_wmi_priv *priv = container_of(pprof, struct inspur_wmi_priv,
- handler);
+ struct inspur_wmi_priv *priv = dev_get_drvdata(dev);
u8 ret_code[4] = {0, 0, 0, 0};
int ret;
@@ -166,6 +164,21 @@ static int inspur_platform_profile_get(struct platform_profile_handler *pprof,
return 0;
}
+static int inspur_platform_profile_probe(void *drvdata, unsigned long *choices)
+{
+ set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
+ set_bit(PLATFORM_PROFILE_BALANCED, choices);
+ set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
+
+ return 0;
+}
+
+static const struct platform_profile_ops inspur_platform_profile_ops = {
+ .probe = inspur_platform_profile_probe,
+ .profile_get = inspur_platform_profile_get,
+ .profile_set = inspur_platform_profile_set,
+};
+
static int inspur_wmi_probe(struct wmi_device *wdev, const void *context)
{
struct inspur_wmi_priv *priv;
@@ -177,19 +190,10 @@ static int inspur_wmi_probe(struct wmi_device *wdev, const void *context)
priv->wdev = wdev;
dev_set_drvdata(&wdev->dev, priv);
- priv->handler.profile_get = inspur_platform_profile_get;
- priv->handler.profile_set = inspur_platform_profile_set;
-
- set_bit(PLATFORM_PROFILE_LOW_POWER, priv->handler.choices);
- set_bit(PLATFORM_PROFILE_BALANCED, priv->handler.choices);
- set_bit(PLATFORM_PROFILE_PERFORMANCE, priv->handler.choices);
+ priv->ppdev = devm_platform_profile_register(&wdev->dev, "inspur-wmi", priv,
+ &inspur_platform_profile_ops);
- return platform_profile_register(&priv->handler);
-}
-
-static void inspur_wmi_remove(struct wmi_device *wdev)
-{
- platform_profile_remove();
+ return PTR_ERR_OR_ZERO(priv->ppdev);
}
static const struct wmi_device_id inspur_wmi_id_table[] = {
@@ -206,7 +210,6 @@ static struct wmi_driver inspur_wmi_driver = {
},
.id_table = inspur_wmi_id_table,
.probe = inspur_wmi_probe,
- .remove = inspur_wmi_remove,
.no_singleton = true,
};
diff --git a/drivers/platform/x86/intel/Kconfig b/drivers/platform/x86/intel/Kconfig
index eb698dcb9af9..19a2246f2770 100644
--- a/drivers/platform/x86/intel/Kconfig
+++ b/drivers/platform/x86/intel/Kconfig
@@ -83,6 +83,7 @@ config INTEL_BXTWC_PMIC_TMU
config INTEL_BYTCRC_PWRSRC
tristate "Intel Bay Trail Crystal Cove power source driver"
depends on INTEL_SOC_PMIC
+ depends on POWER_SUPPLY
help
This option adds a power source driver for Crystal Cove PMICs
on Intel Bay Trail devices.
diff --git a/drivers/platform/x86/intel/bytcrc_pwrsrc.c b/drivers/platform/x86/intel/bytcrc_pwrsrc.c
index 3edc2a9dab38..68ac040082df 100644
--- a/drivers/platform/x86/intel/bytcrc_pwrsrc.c
+++ b/drivers/platform/x86/intel/bytcrc_pwrsrc.c
@@ -8,13 +8,22 @@
* Copyright (C) 2013 Intel Corporation
*/
+#include <linux/array_size.h>
+#include <linux/bits.h>
#include <linux/debugfs.h>
+#include <linux/interrupt.h>
#include <linux/mfd/intel_soc_pmic.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/property.h>
#include <linux/regmap.h>
+#define CRYSTALCOVE_PWRSRC_IRQ 0x03
#define CRYSTALCOVE_SPWRSRC_REG 0x1E
+#define CRYSTALCOVE_SPWRSRC_USB BIT(0)
+#define CRYSTALCOVE_SPWRSRC_DC BIT(1)
+#define CRYSTALCOVE_SPWRSRC_BATTERY BIT(2)
#define CRYSTALCOVE_RESETSRC0_REG 0x20
#define CRYSTALCOVE_RESETSRC1_REG 0x21
#define CRYSTALCOVE_WAKESRC_REG 0x22
@@ -22,6 +31,7 @@
struct crc_pwrsrc_data {
struct regmap *regmap;
struct dentry *debug_dentry;
+ struct power_supply *psy;
unsigned int resetsrc0;
unsigned int resetsrc1;
unsigned int wakesrc;
@@ -118,13 +128,60 @@ static int crc_pwrsrc_read_and_clear(struct crc_pwrsrc_data *data,
return regmap_write(data->regmap, reg, *val);
}
+static irqreturn_t crc_pwrsrc_irq_handler(int irq, void *_data)
+{
+ struct crc_pwrsrc_data *data = _data;
+ unsigned int irq_mask;
+
+ if (regmap_read(data->regmap, CRYSTALCOVE_PWRSRC_IRQ, &irq_mask))
+ return IRQ_NONE;
+
+ regmap_write(data->regmap, CRYSTALCOVE_PWRSRC_IRQ, irq_mask);
+
+ power_supply_changed(data->psy);
+ return IRQ_HANDLED;
+}
+
+static int crc_pwrsrc_psy_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct crc_pwrsrc_data *data = power_supply_get_drvdata(psy);
+ unsigned int pwrsrc;
+ int ret;
+
+ if (psp != POWER_SUPPLY_PROP_ONLINE)
+ return -EINVAL;
+
+ ret = regmap_read(data->regmap, CRYSTALCOVE_SPWRSRC_REG, &pwrsrc);
+ if (ret)
+ return ret;
+
+ val->intval = !!(pwrsrc & (CRYSTALCOVE_SPWRSRC_USB |
+ CRYSTALCOVE_SPWRSRC_DC));
+ return 0;
+}
+
+static const enum power_supply_property crc_pwrsrc_psy_props[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static const struct power_supply_desc crc_pwrsrc_psy_desc = {
+ .name = "crystal_cove_pwrsrc",
+ .type = POWER_SUPPLY_TYPE_MAINS,
+ .properties = crc_pwrsrc_psy_props,
+ .num_properties = ARRAY_SIZE(crc_pwrsrc_psy_props),
+ .get_property = crc_pwrsrc_psy_get_property,
+};
+
static int crc_pwrsrc_probe(struct platform_device *pdev)
{
struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
struct crc_pwrsrc_data *data;
- int ret;
+ int irq, ret;
- data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -149,6 +206,24 @@ static int crc_pwrsrc_probe(struct platform_device *pdev)
if (ret)
return ret;
+ if (device_property_read_bool(dev->parent, "linux,register-pwrsrc-power_supply")) {
+ struct power_supply_config psy_cfg = { .drv_data = data };
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ data->psy = devm_power_supply_register(dev, &crc_pwrsrc_psy_desc, &psy_cfg);
+ if (IS_ERR(data->psy))
+ return dev_err_probe(dev, PTR_ERR(data->psy), "registering power-supply\n");
+
+ ret = devm_request_threaded_irq(dev, irq, NULL,
+ crc_pwrsrc_irq_handler,
+ IRQF_ONESHOT, KBUILD_MODNAME, data);
+ if (ret)
+ return dev_err_probe(dev, ret, "requesting IRQ\n");
+ }
+
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);
diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c
index 927a2993f616..0b5e43444ed6 100644
--- a/drivers/platform/x86/intel/hid.c
+++ b/drivers/platform/x86/intel/hid.c
@@ -44,16 +44,17 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alex Hung");
static const struct acpi_device_id intel_hid_ids[] = {
- {"INT33D5", 0},
- {"INTC1051", 0},
- {"INTC1054", 0},
- {"INTC1070", 0},
- {"INTC1076", 0},
- {"INTC1077", 0},
- {"INTC1078", 0},
- {"INTC107B", 0},
- {"INTC10CB", 0},
- {"", 0},
+ { "INT33D5" },
+ { "INTC1051" },
+ { "INTC1054" },
+ { "INTC1070" },
+ { "INTC1076" },
+ { "INTC1077" },
+ { "INTC1078" },
+ { "INTC107B" },
+ { "INTC10CB" },
+ { "INTC10CC" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, intel_hid_ids);
@@ -139,6 +140,13 @@ static const struct dmi_system_id button_array_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Surface Go 3"),
},
},
+ {
+ .ident = "Microsoft Surface Go 4",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Surface Go 4"),
+ },
+ },
{ }
};
diff --git a/drivers/platform/x86/intel/ifs/Makefile b/drivers/platform/x86/intel/ifs/Makefile
index 30f035ef5581..c3e417bce9b6 100644
--- a/drivers/platform/x86/intel/ifs/Makefile
+++ b/drivers/platform/x86/intel/ifs/Makefile
@@ -1,3 +1,3 @@
obj-$(CONFIG_INTEL_IFS) += intel_ifs.o
-intel_ifs-objs := core.o load.o runtest.o sysfs.o
+intel_ifs-y := core.o load.o runtest.o sysfs.o
diff --git a/drivers/platform/x86/intel/ifs/ifs.h b/drivers/platform/x86/intel/ifs/ifs.h
index 5c3c0dfa1bf8..f369fb0d3d82 100644
--- a/drivers/platform/x86/intel/ifs/ifs.h
+++ b/drivers/platform/x86/intel/ifs/ifs.h
@@ -23,12 +23,14 @@
* IFS Image
* ---------
*
- * Intel provides a firmware file containing the scan tests via
- * github [#f1]_. Similar to microcode there is a separate file for each
+ * Intel provides firmware files containing the scan tests via the webpage [#f1]_.
+ * Look under "In-Field Scan Test Images Download" section towards the
+ * end of the page. Similar to microcode, there are separate files for each
* 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.
*
+ * .. [#f1] https://intel.com/InFieldScan
*
* IFS Image Loading
* -----------------
@@ -125,9 +127,6 @@
* 2) Hardware allows for some number of cores to be tested in parallel.
* The driver does not make use of this, it only tests one core at a time.
*
- * .. [#f1] https://github.com/intel/TBD
- *
- *
* Structural Based Functional Test at Field (SBAF):
* -------------------------------------------------
*
diff --git a/drivers/platform/x86/intel/int0002_vgpio.c b/drivers/platform/x86/intel/int0002_vgpio.c
index 0cc80603a8a9..3b48cd7a4075 100644
--- a/drivers/platform/x86/intel/int0002_vgpio.c
+++ b/drivers/platform/x86/intel/int0002_vgpio.c
@@ -83,8 +83,12 @@ static void int0002_irq_ack(struct irq_data *data)
static void int0002_irq_unmask(struct irq_data *data)
{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
+ irq_hw_number_t hwirq = irqd_to_hwirq(data);
u32 gpe_en_reg;
+ gpiochip_enable_irq(gc, hwirq);
+
gpe_en_reg = inl(GPE0A_EN_PORT);
gpe_en_reg |= GPE0A_PME_B0_EN_BIT;
outl(gpe_en_reg, GPE0A_EN_PORT);
@@ -92,11 +96,15 @@ static void int0002_irq_unmask(struct irq_data *data)
static void int0002_irq_mask(struct irq_data *data)
{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
+ irq_hw_number_t hwirq = irqd_to_hwirq(data);
u32 gpe_en_reg;
gpe_en_reg = inl(GPE0A_EN_PORT);
gpe_en_reg &= ~GPE0A_PME_B0_EN_BIT;
outl(gpe_en_reg, GPE0A_EN_PORT);
+
+ gpiochip_disable_irq(gc, hwirq);
}
static int int0002_irq_set_wake(struct irq_data *data, unsigned int on)
@@ -140,12 +148,14 @@ static bool int0002_check_wake(void *data)
return (gpe_sts_reg & GPE0A_PME_B0_STS_BIT);
}
-static struct irq_chip int0002_irqchip = {
+static const struct irq_chip int0002_irqchip = {
.name = DRV_NAME,
.irq_ack = int0002_irq_ack,
.irq_mask = int0002_irq_mask,
.irq_unmask = int0002_irq_unmask,
.irq_set_wake = int0002_irq_set_wake,
+ .flags = IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
static void int0002_init_irq_valid_mask(struct gpio_chip *chip,
@@ -203,7 +213,7 @@ static int int0002_probe(struct platform_device *pdev)
}
girq = &chip->irq;
- girq->chip = &int0002_irqchip;
+ gpio_irq_chip_set_chip(girq, &int0002_irqchip);
/* This let us handle the parent IRQ in the driver */
girq->parent_handler = NULL;
girq->num_parents = 0;
diff --git a/drivers/platform/x86/intel/int3472/common.c b/drivers/platform/x86/intel/int3472/common.c
index b3a2578e06c1..1638be8fa71e 100644
--- a/drivers/platform/x86/intel/int3472/common.c
+++ b/drivers/platform/x86/intel/int3472/common.c
@@ -70,6 +70,8 @@ int skl_int3472_get_sensor_adev_and_name(struct device *dev,
return -ENODEV;
}
+ dev_dbg(dev, "Sensor name %s\n", acpi_dev_name(sensor));
+
*name_ret = devm_kasprintf(dev, GFP_KERNEL, I2C_DEV_NAME_FORMAT,
acpi_dev_name(sensor));
if (!*name_ret)
diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c
index 09fff213b091..30ff8f3ea1f5 100644
--- a/drivers/platform/x86/intel/int3472/discrete.c
+++ b/drivers/platform/x86/intel/int3472/discrete.c
@@ -2,6 +2,7 @@
/* Author: Dan Scally <djrscally@gmail.com> */
#include <linux/acpi.h>
+#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
@@ -55,7 +56,7 @@ static void skl_int3472_log_sensor_module_name(struct int3472_discrete_device *i
static int skl_int3472_fill_gpiod_lookup(struct gpiod_lookup *table_entry,
struct acpi_resource_gpio *agpio,
- const char *func, u32 polarity)
+ const char *con_id, unsigned long gpio_flags)
{
char *path = agpio->resource_source.string_ptr;
struct acpi_device *adev;
@@ -70,14 +71,14 @@ static int skl_int3472_fill_gpiod_lookup(struct gpiod_lookup *table_entry,
if (!adev)
return -ENODEV;
- *table_entry = GPIO_LOOKUP(acpi_dev_name(adev), agpio->pin_table[0], func, polarity);
+ *table_entry = GPIO_LOOKUP(acpi_dev_name(adev), agpio->pin_table[0], con_id, gpio_flags);
return 0;
}
static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int3472,
struct acpi_resource_gpio *agpio,
- const char *func, u32 polarity)
+ const char *con_id, unsigned long gpio_flags)
{
int ret;
@@ -87,7 +88,7 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347
}
ret = skl_int3472_fill_gpiod_lookup(&int3472->gpios.table[int3472->n_sensor_gpios],
- agpio, func, polarity);
+ agpio, con_id, gpio_flags);
if (ret)
return ret;
@@ -100,7 +101,7 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347
static struct gpio_desc *
skl_int3472_gpiod_get_from_temp_lookup(struct int3472_discrete_device *int3472,
struct acpi_resource_gpio *agpio,
- const char *func, u32 polarity)
+ const char *con_id, unsigned long gpio_flags)
{
struct gpio_desc *desc;
int ret;
@@ -111,43 +112,87 @@ skl_int3472_gpiod_get_from_temp_lookup(struct int3472_discrete_device *int3472,
return ERR_PTR(-ENOMEM);
lookup->dev_id = dev_name(int3472->dev);
- ret = skl_int3472_fill_gpiod_lookup(&lookup->table[0], agpio, func, polarity);
+ ret = skl_int3472_fill_gpiod_lookup(&lookup->table[0], agpio, con_id, gpio_flags);
if (ret)
return ERR_PTR(ret);
gpiod_add_lookup_table(lookup);
- desc = devm_gpiod_get(int3472->dev, func, GPIOD_OUT_LOW);
+ desc = devm_gpiod_get(int3472->dev, con_id, GPIOD_OUT_LOW);
gpiod_remove_lookup_table(lookup);
return desc;
}
-static void int3472_get_func_and_polarity(u8 type, const char **func, u32 *polarity)
+/**
+ * struct int3472_gpio_map - Map GPIOs to whatever is expected by the
+ * sensor driver (as in DT bindings)
+ * @hid: The ACPI HID of the device without the instance number e.g. INT347E
+ * @type_from: The GPIO type from ACPI ?SDT
+ * @type_to: The assigned GPIO type, typically same as @type_from
+ * @con_id: The name of the GPIO for the device
+ * @polarity_low: GPIO_ACTIVE_LOW true if the @polarity_low is true,
+ * GPIO_ACTIVE_HIGH otherwise
+ */
+struct int3472_gpio_map {
+ const char *hid;
+ u8 type_from;
+ u8 type_to;
+ bool polarity_low;
+ const char *con_id;
+};
+
+static const struct int3472_gpio_map int3472_gpio_map[] = {
+ { "INT347E", INT3472_GPIO_TYPE_RESET, INT3472_GPIO_TYPE_RESET, false, "enable" },
+};
+
+static void int3472_get_con_id_and_polarity(struct acpi_device *adev, u8 *type,
+ const char **con_id, unsigned long *gpio_flags)
{
- switch (type) {
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(int3472_gpio_map); i++) {
+ /*
+ * Map the firmware-provided GPIO to whatever a driver expects
+ * (as in DT bindings). First check if the type matches with the
+ * GPIO map, then further check that the device _HID matches.
+ */
+ if (*type != int3472_gpio_map[i].type_from)
+ continue;
+
+ if (!acpi_dev_hid_uid_match(adev, int3472_gpio_map[i].hid, NULL))
+ continue;
+
+ *type = int3472_gpio_map[i].type_to;
+ *gpio_flags = int3472_gpio_map[i].polarity_low ?
+ GPIO_ACTIVE_LOW : GPIO_ACTIVE_HIGH;
+ *con_id = int3472_gpio_map[i].con_id;
+ return;
+ }
+
+ switch (*type) {
case INT3472_GPIO_TYPE_RESET:
- *func = "reset";
- *polarity = GPIO_ACTIVE_LOW;
+ *con_id = "reset";
+ *gpio_flags = GPIO_ACTIVE_LOW;
break;
case INT3472_GPIO_TYPE_POWERDOWN:
- *func = "powerdown";
- *polarity = GPIO_ACTIVE_LOW;
+ *con_id = "powerdown";
+ *gpio_flags = GPIO_ACTIVE_LOW;
break;
case INT3472_GPIO_TYPE_CLK_ENABLE:
- *func = "clk-enable";
- *polarity = GPIO_ACTIVE_HIGH;
+ *con_id = "clk-enable";
+ *gpio_flags = GPIO_ACTIVE_HIGH;
break;
case INT3472_GPIO_TYPE_PRIVACY_LED:
- *func = "privacy-led";
- *polarity = GPIO_ACTIVE_HIGH;
+ *con_id = "privacy-led";
+ *gpio_flags = GPIO_ACTIVE_HIGH;
break;
case INT3472_GPIO_TYPE_POWER_ENABLE:
- *func = "power-enable";
- *polarity = GPIO_ACTIVE_HIGH;
+ *con_id = "power-enable";
+ *gpio_flags = GPIO_ACTIVE_HIGH;
break;
default:
- *func = "unknown";
- *polarity = GPIO_ACTIVE_HIGH;
+ *con_id = "unknown";
+ *gpio_flags = GPIO_ACTIVE_HIGH;
break;
}
}
@@ -178,11 +223,11 @@ static void int3472_get_func_and_polarity(u8 type, const char **func, u32 *polar
* to create clocks and regulators via the usual frameworks.
*
* Return:
- * * 1 - To continue the loop
- * * 0 - When all resources found are handled properly.
- * * -EINVAL - If the resource is not a GPIO IO resource
- * * -ENODEV - If the resource has no corresponding _DSM entry
- * * -Other - Errors propagated from one of the sub-functions.
+ * * 1 - Continue the loop without adding a copy of the resource to
+ * * the list passed to acpi_dev_get_resources()
+ * * 0 - Continue the loop after adding a copy of the resource to
+ * * the list passed to acpi_dev_get_resources()
+ * * -errno - Error, break loop
*/
static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
void *data)
@@ -193,8 +238,8 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
union acpi_object *obj;
struct gpio_desc *gpio;
const char *err_msg;
- const char *func;
- u32 polarity;
+ const char *con_id;
+ unsigned long gpio_flags;
int ret;
if (!acpi_gpio_get_io_resource(ares, &agpio))
@@ -217,26 +262,26 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
type = FIELD_GET(INT3472_GPIO_DSM_TYPE, obj->integer.value);
- int3472_get_func_and_polarity(type, &func, &polarity);
+ int3472_get_con_id_and_polarity(int3472->sensor, &type, &con_id, &gpio_flags);
pin = FIELD_GET(INT3472_GPIO_DSM_PIN, obj->integer.value);
- if (pin != agpio->pin_table[0])
- dev_warn(int3472->dev, "%s %s pin number mismatch _DSM %d resource %d\n",
- func, agpio->resource_source.string_ptr, pin,
- agpio->pin_table[0]);
+ /* Pin field is not really used under Windows and wraps around at 8 bits */
+ if (pin != (agpio->pin_table[0] & 0xff))
+ dev_dbg(int3472->dev, FW_BUG "%s %s pin number mismatch _DSM %d resource %d\n",
+ con_id, agpio->resource_source.string_ptr, pin, agpio->pin_table[0]);
active_value = FIELD_GET(INT3472_GPIO_DSM_SENSOR_ON_VAL, obj->integer.value);
if (!active_value)
- polarity ^= GPIO_ACTIVE_LOW;
+ gpio_flags ^= GPIO_ACTIVE_LOW;
- dev_dbg(int3472->dev, "%s %s pin %d active-%s\n", func,
+ dev_dbg(int3472->dev, "%s %s pin %d active-%s\n", con_id,
agpio->resource_source.string_ptr, agpio->pin_table[0],
- str_high_low(polarity == GPIO_ACTIVE_HIGH));
+ str_high_low(gpio_flags == GPIO_ACTIVE_HIGH));
switch (type) {
case INT3472_GPIO_TYPE_RESET:
case INT3472_GPIO_TYPE_POWERDOWN:
- ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, func, polarity);
+ ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, con_id, gpio_flags);
if (ret)
err_msg = "Failed to map GPIO pin to sensor\n";
@@ -244,7 +289,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
case INT3472_GPIO_TYPE_CLK_ENABLE:
case INT3472_GPIO_TYPE_PRIVACY_LED:
case INT3472_GPIO_TYPE_POWER_ENABLE:
- gpio = skl_int3472_gpiod_get_from_temp_lookup(int3472, agpio, func, polarity);
+ gpio = skl_int3472_gpiod_get_from_temp_lookup(int3472, agpio, con_id, gpio_flags);
if (IS_ERR(gpio)) {
ret = PTR_ERR(gpio);
err_msg = "Failed to get GPIO\n";
@@ -289,7 +334,8 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
if (ret < 0)
return dev_err_probe(int3472->dev, ret, err_msg);
- return ret;
+ /* Tell acpi_dev_get_resources() to not make a copy of the resource */
+ return 1;
}
static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472)
diff --git a/drivers/platform/x86/intel/plr_tpmi.c b/drivers/platform/x86/intel/plr_tpmi.c
index 691d43c3592c..2b55347a5a93 100644
--- a/drivers/platform/x86/intel/plr_tpmi.c
+++ b/drivers/platform/x86/intel/plr_tpmi.c
@@ -262,7 +262,7 @@ static int intel_plr_probe(struct auxiliary_device *auxdev, const struct auxilia
struct resource *res;
struct tpmi_plr *plr;
void __iomem *base;
- char name[16];
+ char name[17];
int err;
plat_info = tpmi_get_platform_data(auxdev);
diff --git a/drivers/platform/x86/intel/pmc/Makefile b/drivers/platform/x86/intel/pmc/Makefile
index 389e5419dadf..b148b40d09f5 100644
--- a/drivers/platform/x86/intel/pmc/Makefile
+++ b/drivers/platform/x86/intel/pmc/Makefile
@@ -4,7 +4,7 @@
#
intel_pmc_core-y := core.o core_ssram.o spt.o cnp.o \
- icl.o tgl.o adl.o mtl.o arl.o lnl.o
+ icl.o tgl.o adl.o mtl.o arl.o lnl.o ptl.o
obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o
intel_pmc_core_pltdrv-y := pltdrv.o
obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core_pltdrv.o
diff --git a/drivers/platform/x86/intel/pmc/adl.c b/drivers/platform/x86/intel/pmc/adl.c
index e7878558fd90..9e7dfd6e3310 100644
--- a/drivers/platform/x86/intel/pmc/adl.c
+++ b/drivers/platform/x86/intel/pmc/adl.c
@@ -11,7 +11,7 @@
#include "core.h"
/* Alder Lake: PGD PFET Enable Ack Status Register(s) bitmap */
-const struct pmc_bit_map adl_pfear_map[] = {
+static const struct pmc_bit_map adl_pfear_map[] = {
{"SPI/eSPI", BIT(2)},
{"XHCI", BIT(3)},
{"SPA", BIT(4)},
@@ -54,7 +54,7 @@ const struct pmc_bit_map adl_pfear_map[] = {
{}
};
-const struct pmc_bit_map *ext_adl_pfear_map[] = {
+static const struct pmc_bit_map *ext_adl_pfear_map[] = {
/*
* Check intel_pmc_core_ids[] users of cnp_reg_map for
* a list of core SoCs using this.
@@ -63,7 +63,7 @@ const struct pmc_bit_map *ext_adl_pfear_map[] = {
NULL
};
-const struct pmc_bit_map adl_ltr_show_map[] = {
+static const struct pmc_bit_map adl_ltr_show_map[] = {
{"SOUTHPORT_A", CNP_PMC_LTR_SPA},
{"SOUTHPORT_B", CNP_PMC_LTR_SPB},
{"SATA", CNP_PMC_LTR_SATA},
@@ -100,7 +100,7 @@ const struct pmc_bit_map adl_ltr_show_map[] = {
{}
};
-const struct pmc_bit_map adl_clocksource_status_map[] = {
+static const struct pmc_bit_map adl_clocksource_status_map[] = {
{"CLKPART1_OFF_STS", BIT(0)},
{"CLKPART2_OFF_STS", BIT(1)},
{"CLKPART3_OFF_STS", BIT(2)},
@@ -128,7 +128,7 @@ const struct pmc_bit_map adl_clocksource_status_map[] = {
{}
};
-const struct pmc_bit_map adl_power_gating_status_0_map[] = {
+static const struct pmc_bit_map adl_power_gating_status_0_map[] = {
{"PMC_PGD0_PG_STS", BIT(0)},
{"DMI_PGD0_PG_STS", BIT(1)},
{"ESPISPI_PGD0_PG_STS", BIT(2)},
@@ -158,7 +158,7 @@ const struct pmc_bit_map adl_power_gating_status_0_map[] = {
{}
};
-const struct pmc_bit_map adl_power_gating_status_1_map[] = {
+static const struct pmc_bit_map adl_power_gating_status_1_map[] = {
{"USBR0_PGD0_PG_STS", BIT(0)},
{"SMT1_PGD0_PG_STS", BIT(2)},
{"CSMERTC_PGD0_PG_STS", BIT(6)},
@@ -170,14 +170,14 @@ const struct pmc_bit_map adl_power_gating_status_1_map[] = {
{}
};
-const struct pmc_bit_map adl_power_gating_status_2_map[] = {
+static const struct pmc_bit_map adl_power_gating_status_2_map[] = {
{"THC0_PGD0_PG_STS", BIT(7)},
{"THC1_PGD0_PG_STS", BIT(8)},
{"SPF_PGD0_PG_STS", BIT(14)},
{}
};
-const struct pmc_bit_map adl_d3_status_0_map[] = {
+static const struct pmc_bit_map adl_d3_status_0_map[] = {
{"ISH_D3_STS", BIT(2)},
{"LPSS_D3_STS", BIT(3)},
{"XDCI_D3_STS", BIT(4)},
@@ -193,13 +193,13 @@ const struct pmc_bit_map adl_d3_status_0_map[] = {
{}
};
-const struct pmc_bit_map adl_d3_status_1_map[] = {
+static const struct pmc_bit_map adl_d3_status_1_map[] = {
{"GBE_D3_STS", BIT(19)},
{"CNVI_D3_STS", BIT(27)},
{}
};
-const struct pmc_bit_map adl_d3_status_2_map[] = {
+static const struct pmc_bit_map adl_d3_status_2_map[] = {
{"CSMERTC_D3_STS", BIT(1)},
{"CSE_D3_STS", BIT(4)},
{"KVMCC_D3_STS", BIT(5)},
@@ -210,20 +210,20 @@ const struct pmc_bit_map adl_d3_status_2_map[] = {
{}
};
-const struct pmc_bit_map adl_d3_status_3_map[] = {
+static const struct pmc_bit_map adl_d3_status_3_map[] = {
{"THC0_D3_STS", BIT(14)},
{"THC1_D3_STS", BIT(15)},
{}
};
-const struct pmc_bit_map adl_vnn_req_status_0_map[] = {
+static const struct pmc_bit_map adl_vnn_req_status_0_map[] = {
{"ISH_VNN_REQ_STS", BIT(2)},
{"ESPISPI_VNN_REQ_STS", BIT(18)},
{"DSP_VNN_REQ_STS", BIT(19)},
{}
};
-const struct pmc_bit_map adl_vnn_req_status_1_map[] = {
+static const struct pmc_bit_map adl_vnn_req_status_1_map[] = {
{"NPK_VNN_REQ_STS", BIT(4)},
{"EXI_VNN_REQ_STS", BIT(9)},
{"GBE_VNN_REQ_STS", BIT(19)},
@@ -232,7 +232,7 @@ const struct pmc_bit_map adl_vnn_req_status_1_map[] = {
{}
};
-const struct pmc_bit_map adl_vnn_req_status_2_map[] = {
+static const struct pmc_bit_map adl_vnn_req_status_2_map[] = {
{"CSMERTC_VNN_REQ_STS", BIT(1)},
{"CSE_VNN_REQ_STS", BIT(4)},
{"SMT1_VNN_REQ_STS", BIT(8)},
@@ -245,12 +245,12 @@ const struct pmc_bit_map adl_vnn_req_status_2_map[] = {
{}
};
-const struct pmc_bit_map adl_vnn_req_status_3_map[] = {
+static const struct pmc_bit_map adl_vnn_req_status_3_map[] = {
{"GPIOCOM5_VNN_REQ_STS", BIT(11)},
{}
};
-const struct pmc_bit_map adl_vnn_misc_status_map[] = {
+static const struct pmc_bit_map adl_vnn_misc_status_map[] = {
{"CPU_C10_REQ_STS", BIT(0)},
{"PCIe_LPM_En_REQ_STS", BIT(3)},
{"ITH_REQ_STS", BIT(5)},
@@ -265,7 +265,7 @@ const struct pmc_bit_map adl_vnn_misc_status_map[] = {
{}
};
-const struct pmc_bit_map *adl_lpm_maps[] = {
+static const struct pmc_bit_map *adl_lpm_maps[] = {
adl_clocksource_status_map,
adl_power_gating_status_0_map,
adl_power_gating_status_1_map,
@@ -311,20 +311,8 @@ const struct pmc_reg_map adl_reg_map = {
.pson_residency_counter_step = TGL_PSON_RES_COUNTER_STEP,
};
-int adl_core_init(struct pmc_dev *pmcdev)
-{
- struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
- int ret;
-
- pmcdev->suspend = cnl_suspend;
- pmcdev->resume = cnl_resume;
-
- pmc->map = &adl_reg_map;
- ret = get_primary_reg_base(pmc);
- if (ret)
- return ret;
-
- pmc_core_get_low_power_modes(pmcdev);
-
- return 0;
-}
+struct pmc_dev_info adl_pmc_dev = {
+ .map = &adl_reg_map,
+ .suspend = cnl_suspend,
+ .resume = cnl_resume,
+};
diff --git a/drivers/platform/x86/intel/pmc/arl.c b/drivers/platform/x86/intel/pmc/arl.c
index 05dec4f5019f..f9c48738b853 100644
--- a/drivers/platform/x86/intel/pmc/arl.c
+++ b/drivers/platform/x86/intel/pmc/arl.c
@@ -16,10 +16,11 @@
#define IOEP_LPM_REQ_GUID 0x5077612
#define SOCS_LPM_REQ_GUID 0x8478657
#define PCHS_LPM_REQ_GUID 0x9684572
+#define SOCM_LPM_REQ_GUID 0x2625030
static const u8 ARL_LPM_REG_INDEX[] = {0, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20};
-const struct pmc_bit_map arl_socs_ltr_show_map[] = {
+static const struct pmc_bit_map arl_socs_ltr_show_map[] = {
{"SOUTHPORT_A", CNP_PMC_LTR_SPA},
{"SOUTHPORT_B", CNP_PMC_LTR_SPB},
{"SATA", CNP_PMC_LTR_SATA},
@@ -59,7 +60,7 @@ const struct pmc_bit_map arl_socs_ltr_show_map[] = {
{}
};
-const struct pmc_bit_map arl_socs_clocksource_status_map[] = {
+static const struct pmc_bit_map arl_socs_clocksource_status_map[] = {
{"AON2_OFF_STS", BIT(0)},
{"AON3_OFF_STS", BIT(1)},
{"AON4_OFF_STS", BIT(2)},
@@ -87,7 +88,7 @@ const struct pmc_bit_map arl_socs_clocksource_status_map[] = {
{}
};
-const struct pmc_bit_map arl_socs_power_gating_status_0_map[] = {
+static const struct pmc_bit_map arl_socs_power_gating_status_0_map[] = {
{"PMC_PGD0_PG_STS", BIT(0)},
{"DMI_PGD0_PG_STS", BIT(1)},
{"ESPISPI_PGD0_PG_STS", BIT(2)},
@@ -123,7 +124,7 @@ const struct pmc_bit_map arl_socs_power_gating_status_0_map[] = {
{}
};
-const struct pmc_bit_map arl_socs_power_gating_status_1_map[] = {
+static const struct pmc_bit_map arl_socs_power_gating_status_1_map[] = {
{"USBR0_PGD0_PG_STS", BIT(0)},
{"SUSRAM_PGD0_PG_STS", BIT(1)},
{"SMT1_PGD0_PG_STS", BIT(2)},
@@ -159,7 +160,7 @@ const struct pmc_bit_map arl_socs_power_gating_status_1_map[] = {
{}
};
-const struct pmc_bit_map arl_socs_power_gating_status_2_map[] = {
+static const struct pmc_bit_map arl_socs_power_gating_status_2_map[] = {
{"PSF8_PGD0_PG_STS", BIT(0)},
{"FIA_PGD0_PG_STS", BIT(1)},
{"SOC_D2D_PGD3_PG_STS", BIT(2)},
@@ -187,7 +188,7 @@ const struct pmc_bit_map arl_socs_power_gating_status_2_map[] = {
{}
};
-const struct pmc_bit_map arl_socs_d3_status_2_map[] = {
+static const struct pmc_bit_map arl_socs_d3_status_2_map[] = {
{"CSMERTC_D3_STS", BIT(1)},
{"SUSRAM_D3_STS", BIT(2)},
{"CSE_D3_STS", BIT(4)},
@@ -206,7 +207,7 @@ const struct pmc_bit_map arl_socs_d3_status_2_map[] = {
{}
};
-const struct pmc_bit_map arl_socs_d3_status_3_map[] = {
+static const struct pmc_bit_map arl_socs_d3_status_3_map[] = {
{"GBETSN_D3_STS", BIT(13)},
{"THC0_D3_STS", BIT(14)},
{"THC1_D3_STS", BIT(15)},
@@ -214,13 +215,13 @@ const struct pmc_bit_map arl_socs_d3_status_3_map[] = {
{}
};
-const struct pmc_bit_map arl_socs_vnn_req_status_3_map[] = {
+static const struct pmc_bit_map arl_socs_vnn_req_status_3_map[] = {
{"DTS0_VNN_REQ_STS", BIT(7)},
{"GPIOCOM5_VNN_REQ_STS", BIT(11)},
{}
};
-const struct pmc_bit_map *arl_socs_lpm_maps[] = {
+static const struct pmc_bit_map *arl_socs_lpm_maps[] = {
arl_socs_clocksource_status_map,
arl_socs_power_gating_status_0_map,
arl_socs_power_gating_status_1_map,
@@ -238,7 +239,7 @@ const struct pmc_bit_map *arl_socs_lpm_maps[] = {
NULL
};
-const struct pmc_bit_map arl_socs_pfear_map[] = {
+static const struct pmc_bit_map arl_socs_pfear_map[] = {
{"RSVD64", BIT(0)},
{"RSVD65", BIT(1)},
{"RSVD66", BIT(2)},
@@ -249,13 +250,13 @@ const struct pmc_bit_map arl_socs_pfear_map[] = {
{}
};
-const struct pmc_bit_map *ext_arl_socs_pfear_map[] = {
+static const struct pmc_bit_map *ext_arl_socs_pfear_map[] = {
mtl_socm_pfear_map,
arl_socs_pfear_map,
NULL
};
-const struct pmc_reg_map arl_socs_reg_map = {
+static const struct pmc_reg_map arl_socs_reg_map = {
.pfear_sts = ext_arl_socs_pfear_map,
.ppfear_buckets = ARL_SOCS_PPFEAR_NUM_ENTRIES,
.pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT,
@@ -283,7 +284,7 @@ const struct pmc_reg_map arl_socs_reg_map = {
.pson_residency_counter_step = TGL_PSON_RES_COUNTER_STEP,
};
-const struct pmc_bit_map arl_pchs_ltr_show_map[] = {
+static const struct pmc_bit_map arl_pchs_ltr_show_map[] = {
{"SOUTHPORT_A", CNP_PMC_LTR_SPA},
{"SOUTHPORT_B", CNP_PMC_LTR_SPB},
{"SATA", CNP_PMC_LTR_SATA},
@@ -323,7 +324,7 @@ const struct pmc_bit_map arl_pchs_ltr_show_map[] = {
{}
};
-const struct pmc_bit_map arl_pchs_clocksource_status_map[] = {
+static const struct pmc_bit_map arl_pchs_clocksource_status_map[] = {
{"AON2_OFF_STS", BIT(0)},
{"AON3_OFF_STS", BIT(1)},
{"AON4_OFF_STS", BIT(2)},
@@ -358,7 +359,7 @@ const struct pmc_bit_map arl_pchs_clocksource_status_map[] = {
{}
};
-const struct pmc_bit_map arl_pchs_power_gating_status_0_map[] = {
+static const struct pmc_bit_map arl_pchs_power_gating_status_0_map[] = {
{"PMC_PGD0_PG_STS", BIT(0)},
{"DMI_PGD0_PG_STS", BIT(1)},
{"ESPISPI_PGD0_PG_STS", BIT(2)},
@@ -394,7 +395,7 @@ const struct pmc_bit_map arl_pchs_power_gating_status_0_map[] = {
{}
};
-const struct pmc_bit_map arl_pchs_power_gating_status_1_map[] = {
+static const struct pmc_bit_map arl_pchs_power_gating_status_1_map[] = {
{"USBR0_PGD0_PG_STS", BIT(0)},
{"SUSRAM_PGD0_PG_STS", BIT(1)},
{"SMT1_PGD0_PG_STS", BIT(2)},
@@ -430,7 +431,7 @@ const struct pmc_bit_map arl_pchs_power_gating_status_1_map[] = {
{}
};
-const struct pmc_bit_map arl_pchs_power_gating_status_2_map[] = {
+static const struct pmc_bit_map arl_pchs_power_gating_status_2_map[] = {
{"U3FPW2_PGD0_PG_STS", BIT(0)},
{"FIA_PGD0_PG_STS", BIT(1)},
{"FIACPCB_X_PGD0_PG_STS", BIT(2)},
@@ -457,7 +458,7 @@ const struct pmc_bit_map arl_pchs_power_gating_status_2_map[] = {
{}
};
-const struct pmc_bit_map arl_pchs_d3_status_0_map[] = {
+static const struct pmc_bit_map arl_pchs_d3_status_0_map[] = {
{"SPF_D3_STS", BIT(0)},
{"LPSS_D3_STS", BIT(3)},
{"XDCI_D3_STS", BIT(4)},
@@ -474,7 +475,7 @@ const struct pmc_bit_map arl_pchs_d3_status_0_map[] = {
{}
};
-const struct pmc_bit_map arl_pchs_d3_status_1_map[] = {
+static const struct pmc_bit_map arl_pchs_d3_status_1_map[] = {
{"GBETSN1_D3_STS", BIT(14)},
{"GBE_D3_STS", BIT(19)},
{"ITSS_D3_STS", BIT(23)},
@@ -483,7 +484,7 @@ const struct pmc_bit_map arl_pchs_d3_status_1_map[] = {
{}
};
-const struct pmc_bit_map arl_pchs_d3_status_2_map[] = {
+static const struct pmc_bit_map arl_pchs_d3_status_2_map[] = {
{"CSMERTC_D3_STS", BIT(1)},
{"SUSRAM_D3_STS", BIT(2)},
{"CSE_D3_STS", BIT(4)},
@@ -504,7 +505,7 @@ const struct pmc_bit_map arl_pchs_d3_status_2_map[] = {
{}
};
-const struct pmc_bit_map arl_pchs_d3_status_3_map[] = {
+static const struct pmc_bit_map arl_pchs_d3_status_3_map[] = {
{"ESE_D3_STS", BIT(3)},
{"GBETSN_D3_STS", BIT(13)},
{"THC0_D3_STS", BIT(14)},
@@ -513,13 +514,13 @@ const struct pmc_bit_map arl_pchs_d3_status_3_map[] = {
{}
};
-const struct pmc_bit_map arl_pchs_vnn_req_status_0_map[] = {
+static const struct pmc_bit_map arl_pchs_vnn_req_status_0_map[] = {
{"FIA_VNN_REQ_STS", BIT(17)},
{"ESPISPI_VNN_REQ_STS", BIT(18)},
{}
};
-const struct pmc_bit_map arl_pchs_vnn_req_status_1_map[] = {
+static const struct pmc_bit_map arl_pchs_vnn_req_status_1_map[] = {
{"NPK_VNN_REQ_STS", BIT(4)},
{"DFXAGG_VNN_REQ_STS", BIT(8)},
{"EXI_VNN_REQ_STS", BIT(9)},
@@ -530,7 +531,7 @@ const struct pmc_bit_map arl_pchs_vnn_req_status_1_map[] = {
{}
};
-const struct pmc_bit_map arl_pchs_vnn_req_status_2_map[] = {
+static const struct pmc_bit_map arl_pchs_vnn_req_status_2_map[] = {
{"FIA2_VNN_REQ_STS", BIT(0)},
{"CSMERTC_VNN_REQ_STS", BIT(1)},
{"CSE_VNN_REQ_STS", BIT(4)},
@@ -548,7 +549,7 @@ const struct pmc_bit_map arl_pchs_vnn_req_status_2_map[] = {
{}
};
-const struct pmc_bit_map arl_pchs_vnn_req_status_3_map[] = {
+static const struct pmc_bit_map arl_pchs_vnn_req_status_3_map[] = {
{"ESE_VNN_REQ_STS", BIT(3)},
{"DTS0_VNN_REQ_STS", BIT(7)},
{"GPIOCOM5_VNN_REQ_STS", BIT(11)},
@@ -556,7 +557,7 @@ const struct pmc_bit_map arl_pchs_vnn_req_status_3_map[] = {
{}
};
-const struct pmc_bit_map arl_pchs_vnn_misc_status_map[] = {
+static const struct pmc_bit_map arl_pchs_vnn_misc_status_map[] = {
{"CPU_C10_REQ_STS", BIT(0)},
{"TS_OFF_REQ_STS", BIT(1)},
{"PNDE_MET_REQ_STS", BIT(2)},
@@ -586,7 +587,7 @@ const struct pmc_bit_map arl_pchs_vnn_misc_status_map[] = {
{}
};
-const struct pmc_bit_map arl_pchs_signal_status_map[] = {
+static const struct pmc_bit_map arl_pchs_signal_status_map[] = {
{"LSX_Wake0_STS", BIT(0)},
{"LSX_Wake1_STS", BIT(1)},
{"LSX_Wake2_STS", BIT(2)},
@@ -606,7 +607,7 @@ const struct pmc_bit_map arl_pchs_signal_status_map[] = {
{}
};
-const struct pmc_bit_map *arl_pchs_lpm_maps[] = {
+static const struct pmc_bit_map *arl_pchs_lpm_maps[] = {
arl_pchs_clocksource_status_map,
arl_pchs_power_gating_status_0_map,
arl_pchs_power_gating_status_1_map,
@@ -624,7 +625,7 @@ const struct pmc_bit_map *arl_pchs_lpm_maps[] = {
NULL
};
-const struct pmc_reg_map arl_pchs_reg_map = {
+static const struct pmc_reg_map arl_pchs_reg_map = {
.pfear_sts = ext_arl_socs_pfear_map,
.ppfear_buckets = ARL_SOCS_PPFEAR_NUM_ENTRIES,
.pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT,
@@ -650,6 +651,7 @@ const struct pmc_reg_map arl_pchs_reg_map = {
.etr3_offset = ETR3_OFFSET,
};
+#define PMC_DEVID_SOCM 0x777f
#define PMC_DEVID_SOCS 0xae7f
#define PMC_DEVID_IOEP 0x7ecf
#define PMC_DEVID_PCHS 0x7f27
@@ -669,11 +671,18 @@ static struct pmc_info arl_pmc_info_list[] = {
.devid = PMC_DEVID_PCHS,
.map = &arl_pchs_reg_map,
},
+ {
+ .guid = SOCM_LPM_REQ_GUID,
+ .devid = PMC_DEVID_SOCM,
+ .map = &mtl_socm_reg_map,
+ },
{}
};
#define ARL_NPU_PCI_DEV 0xad1d
#define ARL_GNA_PCI_DEV 0xae4c
+#define ARL_H_NPU_PCI_DEV 0x7d1d
+#define ARL_H_GNA_PCI_DEV 0x774c
/*
* Set power state of select devices that do not have drivers to D3
* so that they do not block Package C entry.
@@ -684,6 +693,12 @@ static void arl_d3_fixup(void)
pmc_core_set_device_d3(ARL_GNA_PCI_DEV);
}
+static void arl_h_d3_fixup(void)
+{
+ pmc_core_set_device_d3(ARL_H_NPU_PCI_DEV);
+ pmc_core_set_device_d3(ARL_H_GNA_PCI_DEV);
+}
+
static int arl_resume(struct pmc_dev *pmcdev)
{
arl_d3_fixup();
@@ -691,40 +706,41 @@ static int arl_resume(struct pmc_dev *pmcdev)
return cnl_resume(pmcdev);
}
-int arl_core_init(struct pmc_dev *pmcdev)
+static int arl_h_resume(struct pmc_dev *pmcdev)
{
- struct pmc *pmc = pmcdev->pmcs[PMC_IDX_SOC];
- int ret;
- int func = 0;
- bool ssram_init = true;
+ arl_h_d3_fixup();
+ return cnl_resume(pmcdev);
+}
+
+static int arl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
+{
arl_d3_fixup();
- pmcdev->suspend = cnl_suspend;
- pmcdev->resume = arl_resume;
- pmcdev->regmap_list = arl_pmc_info_list;
+ return generic_core_init(pmcdev, pmc_dev_info);
+}
- /*
- * If ssram init fails use legacy method to at least get the
- * primary PMC
- */
- ret = pmc_core_ssram_init(pmcdev, func);
- if (ret) {
- ssram_init = false;
- pmc->map = &arl_socs_reg_map;
-
- ret = get_primary_reg_base(pmc);
- if (ret)
- return ret;
- }
-
- pmc_core_get_low_power_modes(pmcdev);
- pmc_core_punit_pmt_init(pmcdev, ARL_PMT_DMU_GUID);
-
- if (ssram_init) {
- ret = pmc_core_ssram_get_lpm_reqs(pmcdev);
- if (ret)
- return ret;
- }
-
- return 0;
+static int arl_h_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
+{
+ arl_h_d3_fixup();
+ return generic_core_init(pmcdev, pmc_dev_info);
}
+
+struct pmc_dev_info arl_pmc_dev = {
+ .pci_func = 0,
+ .dmu_guid = ARL_PMT_DMU_GUID,
+ .regmap_list = arl_pmc_info_list,
+ .map = &arl_socs_reg_map,
+ .suspend = cnl_suspend,
+ .resume = arl_resume,
+ .init = arl_core_init,
+};
+
+struct pmc_dev_info arl_h_pmc_dev = {
+ .pci_func = 2,
+ .dmu_guid = ARL_PMT_DMU_GUID,
+ .regmap_list = arl_pmc_info_list,
+ .map = &mtl_socm_reg_map,
+ .suspend = cnl_suspend,
+ .resume = arl_h_resume,
+ .init = arl_h_core_init,
+};
diff --git a/drivers/platform/x86/intel/pmc/cnp.c b/drivers/platform/x86/intel/pmc/cnp.c
index fc5193fdf8a8..2c5af158bbe2 100644
--- a/drivers/platform/x86/intel/pmc/cnp.c
+++ b/drivers/platform/x86/intel/pmc/cnp.c
@@ -88,7 +88,7 @@ const struct pmc_bit_map cnp_pfear_map[] = {
{}
};
-const struct pmc_bit_map *ext_cnp_pfear_map[] = {
+static const struct pmc_bit_map *ext_cnp_pfear_map[] = {
/*
* Check intel_pmc_core_ids[] users of cnp_reg_map for
* a list of core SoCs using this.
@@ -97,7 +97,7 @@ const struct pmc_bit_map *ext_cnp_pfear_map[] = {
NULL
};
-const struct pmc_bit_map cnp_slps0_dbg0_map[] = {
+static const struct pmc_bit_map cnp_slps0_dbg0_map[] = {
{"AUDIO_D3", BIT(0)},
{"OTG_D3", BIT(1)},
{"XHCI_D3", BIT(2)},
@@ -110,7 +110,7 @@ const struct pmc_bit_map cnp_slps0_dbg0_map[] = {
{}
};
-const struct pmc_bit_map cnp_slps0_dbg1_map[] = {
+static const struct pmc_bit_map cnp_slps0_dbg1_map[] = {
{"SDIO_PLL_OFF", BIT(0)},
{"USB2_PLL_OFF", BIT(1)},
{"AUDIO_PLL_OFF", BIT(2)},
@@ -127,7 +127,7 @@ const struct pmc_bit_map cnp_slps0_dbg1_map[] = {
{}
};
-const struct pmc_bit_map cnp_slps0_dbg2_map[] = {
+static const struct pmc_bit_map cnp_slps0_dbg2_map[] = {
{"MPHY_CORE_GATED", BIT(0)},
{"CSME_GATED", BIT(1)},
{"USB2_SUS_GATED", BIT(2)},
@@ -274,20 +274,9 @@ int cnl_resume(struct pmc_dev *pmcdev)
return pmc_core_resume_common(pmcdev);
}
-int cnp_core_init(struct pmc_dev *pmcdev)
-{
- struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
- int ret;
-
- pmcdev->suspend = cnl_suspend;
- pmcdev->resume = cnl_resume;
-
- pmc->map = &cnp_reg_map;
- ret = get_primary_reg_base(pmc);
- if (ret)
- return ret;
-
- pmc_core_get_low_power_modes(pmcdev);
+struct pmc_dev_info cnp_pmc_dev = {
+ .map = &cnp_reg_map,
+ .suspend = cnl_suspend,
+ .resume = cnl_resume,
+};
- return 0;
-}
diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c
index 3e7f99ac8c94..7a1d11f2914f 100644
--- a/drivers/platform/x86/intel/pmc/core.c
+++ b/drivers/platform/x86/intel/pmc/core.c
@@ -22,6 +22,7 @@
#include <linux/suspend.h>
#include <linux/units.h>
+#include <asm/cpuid.h>
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
#include <asm/msr.h>
@@ -625,8 +626,8 @@ static u32 convert_ltr_scale(u32 val)
static int pmc_core_ltr_show(struct seq_file *s, void *unused)
{
struct pmc_dev *pmcdev = s->private;
- u64 decoded_snoop_ltr, decoded_non_snoop_ltr;
- u32 ltr_raw_data, scale, val;
+ u64 decoded_snoop_ltr, decoded_non_snoop_ltr, val;
+ u32 ltr_raw_data, scale;
u16 snoop_ltr, nonsnoop_ltr;
unsigned int i, index, ltr_index = 0;
@@ -935,13 +936,13 @@ static unsigned int pmc_core_get_crystal_freq(void)
{
unsigned int eax_denominator, ebx_numerator, ecx_hz, edx;
- if (boot_cpu_data.cpuid_level < 0x15)
+ if (boot_cpu_data.cpuid_level < CPUID_LEAF_TSC)
return 0;
eax_denominator = ebx_numerator = ecx_hz = edx = 0;
- /* CPUID 15H TSC/Crystal ratio, plus optionally Crystal Hz */
- cpuid(0x15, &eax_denominator, &ebx_numerator, &ecx_hz, &edx);
+ /* TSC/Crystal ratio, plus optionally Crystal Hz */
+ cpuid(CPUID_LEAF_TSC, &eax_denominator, &ebx_numerator, &ecx_hz, &edx);
if (ebx_numerator == 0 || eax_denominator == 0)
return 0;
@@ -1344,40 +1345,80 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
}
}
+/*
+ * When supported, ssram init is used to achieve all available PMCs.
+ * If ssram init fails, this function uses legacy method to at least get the
+ * primary PMC.
+ */
+int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
+{
+ struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
+ bool ssram;
+ int ret;
+
+ pmcdev->suspend = pmc_dev_info->suspend;
+ pmcdev->resume = pmc_dev_info->resume;
+
+ ssram = pmc_dev_info->regmap_list != NULL;
+ if (ssram) {
+ pmcdev->regmap_list = pmc_dev_info->regmap_list;
+ ret = pmc_core_ssram_init(pmcdev, pmc_dev_info->pci_func);
+ if (ret) {
+ dev_warn(&pmcdev->pdev->dev,
+ "ssram init failed, %d, using legacy init\n", ret);
+ ssram = false;
+ }
+ }
+
+ if (!ssram) {
+ pmc->map = pmc_dev_info->map;
+ ret = get_primary_reg_base(pmc);
+ if (ret)
+ return ret;
+ }
+
+ pmc_core_get_low_power_modes(pmcdev);
+ if (pmc_dev_info->dmu_guid)
+ pmc_core_punit_pmt_init(pmcdev, pmc_dev_info->dmu_guid);
+
+ if (ssram)
+ return pmc_core_ssram_get_lpm_reqs(pmcdev);
+
+ return 0;
+}
+
static const struct x86_cpu_id intel_pmc_core_ids[] = {
- X86_MATCH_VFM(INTEL_SKYLAKE_L, spt_core_init),
- X86_MATCH_VFM(INTEL_SKYLAKE, spt_core_init),
- X86_MATCH_VFM(INTEL_KABYLAKE_L, spt_core_init),
- X86_MATCH_VFM(INTEL_KABYLAKE, spt_core_init),
- X86_MATCH_VFM(INTEL_CANNONLAKE_L, cnp_core_init),
- X86_MATCH_VFM(INTEL_ICELAKE_L, icl_core_init),
- X86_MATCH_VFM(INTEL_ICELAKE_NNPI, icl_core_init),
- X86_MATCH_VFM(INTEL_COMETLAKE, cnp_core_init),
- X86_MATCH_VFM(INTEL_COMETLAKE_L, cnp_core_init),
- X86_MATCH_VFM(INTEL_TIGERLAKE_L, tgl_l_core_init),
- X86_MATCH_VFM(INTEL_TIGERLAKE, tgl_core_init),
- X86_MATCH_VFM(INTEL_ATOM_TREMONT, tgl_l_core_init),
- X86_MATCH_VFM(INTEL_ATOM_TREMONT_L, icl_core_init),
- X86_MATCH_VFM(INTEL_ROCKETLAKE, tgl_core_init),
- X86_MATCH_VFM(INTEL_ALDERLAKE_L, tgl_l_core_init),
- X86_MATCH_VFM(INTEL_ATOM_GRACEMONT, tgl_l_core_init),
- X86_MATCH_VFM(INTEL_ALDERLAKE, adl_core_init),
- X86_MATCH_VFM(INTEL_RAPTORLAKE_P, tgl_l_core_init),
- X86_MATCH_VFM(INTEL_RAPTORLAKE, adl_core_init),
- X86_MATCH_VFM(INTEL_RAPTORLAKE_S, adl_core_init),
- X86_MATCH_VFM(INTEL_METEORLAKE_L, mtl_core_init),
- X86_MATCH_VFM(INTEL_ARROWLAKE, arl_core_init),
- X86_MATCH_VFM(INTEL_LUNARLAKE_M, lnl_core_init),
+ X86_MATCH_VFM(INTEL_SKYLAKE_L, &spt_pmc_dev),
+ X86_MATCH_VFM(INTEL_SKYLAKE, &spt_pmc_dev),
+ X86_MATCH_VFM(INTEL_KABYLAKE_L, &spt_pmc_dev),
+ X86_MATCH_VFM(INTEL_KABYLAKE, &spt_pmc_dev),
+ X86_MATCH_VFM(INTEL_CANNONLAKE_L, &cnp_pmc_dev),
+ X86_MATCH_VFM(INTEL_ICELAKE_L, &icl_pmc_dev),
+ X86_MATCH_VFM(INTEL_ICELAKE_NNPI, &icl_pmc_dev),
+ X86_MATCH_VFM(INTEL_COMETLAKE, &cnp_pmc_dev),
+ X86_MATCH_VFM(INTEL_COMETLAKE_L, &cnp_pmc_dev),
+ X86_MATCH_VFM(INTEL_TIGERLAKE_L, &tgl_l_pmc_dev),
+ X86_MATCH_VFM(INTEL_TIGERLAKE, &tgl_pmc_dev),
+ X86_MATCH_VFM(INTEL_ATOM_TREMONT, &tgl_l_pmc_dev),
+ X86_MATCH_VFM(INTEL_ATOM_TREMONT_L, &icl_pmc_dev),
+ X86_MATCH_VFM(INTEL_ROCKETLAKE, &tgl_pmc_dev),
+ X86_MATCH_VFM(INTEL_ALDERLAKE_L, &tgl_l_pmc_dev),
+ X86_MATCH_VFM(INTEL_ATOM_GRACEMONT, &tgl_l_pmc_dev),
+ X86_MATCH_VFM(INTEL_ALDERLAKE, &adl_pmc_dev),
+ X86_MATCH_VFM(INTEL_RAPTORLAKE_P, &tgl_l_pmc_dev),
+ X86_MATCH_VFM(INTEL_RAPTORLAKE, &adl_pmc_dev),
+ X86_MATCH_VFM(INTEL_RAPTORLAKE_S, &adl_pmc_dev),
+ X86_MATCH_VFM(INTEL_METEORLAKE_L, &mtl_pmc_dev),
+ X86_MATCH_VFM(INTEL_ARROWLAKE, &arl_pmc_dev),
+ X86_MATCH_VFM(INTEL_ARROWLAKE_H, &arl_h_pmc_dev),
+ X86_MATCH_VFM(INTEL_ARROWLAKE_U, &arl_h_pmc_dev),
+ X86_MATCH_VFM(INTEL_LUNARLAKE_M, &lnl_pmc_dev),
+ X86_MATCH_VFM(INTEL_PANTHERLAKE_L, &ptl_pmc_dev),
{}
};
MODULE_DEVICE_TABLE(x86cpu, intel_pmc_core_ids);
-static const struct pci_device_id pmc_pci_ids[] = {
- { PCI_VDEVICE(INTEL, SPT_PMC_PCI_DEVICE_ID) },
- { }
-};
-
/*
* This quirk can be used on those platforms where
* the platform BIOS enforces 24Mhz crystal to shutdown
@@ -1451,7 +1492,7 @@ static int pmc_core_probe(struct platform_device *pdev)
static bool device_initialized;
struct pmc_dev *pmcdev;
const struct x86_cpu_id *cpu_id;
- int (*core_init)(struct pmc_dev *pmcdev);
+ struct pmc_dev_info *pmc_dev_info;
struct pmc *primary_pmc;
int ret;
@@ -1471,7 +1512,7 @@ static int pmc_core_probe(struct platform_device *pdev)
if (!cpu_id)
return -ENODEV;
- core_init = (int (*)(struct pmc_dev *))cpu_id->driver_data;
+ pmc_dev_info = (struct pmc_dev_info *)cpu_id->driver_data;
/* Primary PMC */
primary_pmc = devm_kzalloc(&pdev->dev, sizeof(*primary_pmc), GFP_KERNEL);
@@ -1488,16 +1529,13 @@ static int pmc_core_probe(struct platform_device *pdev)
if (!pmcdev->pkgc_res_cnt)
return -ENOMEM;
- /*
- * Coffee Lake has CPU ID of Kaby Lake and Cannon Lake PCH. So here
- * Sunrisepoint PCH regmap can't be used. Use Cannon Lake PCH regmap
- * in this case.
- */
- if (core_init == spt_core_init && !pci_dev_present(pmc_pci_ids))
- core_init = cnp_core_init;
-
mutex_init(&pmcdev->lock);
- ret = core_init(pmcdev);
+
+ if (pmc_dev_info->init)
+ ret = pmc_dev_info->init(pmcdev, pmc_dev_info);
+ else
+ ret = generic_core_init(pmcdev, pmc_dev_info);
+
if (ret) {
pmc_core_clean_structure(pdev);
return ret;
diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h
index b9d3291d0bf2..945a1c440cca 100644
--- a/drivers/platform/x86/intel/pmc/core.h
+++ b/drivers/platform/x86/intel/pmc/core.h
@@ -285,6 +285,14 @@ enum ppfear_regs {
#define LNL_PPFEAR_NUM_ENTRIES 12
#define LNL_S0IX_BLOCKER_OFFSET 0x2004
+/* Panther Lake Power Management Controller register offsets */
+#define PTL_LPM_NUM_MAPS 14
+#define PTL_PMC_LTR_SATA2 0x1B90
+#define PTL_PMC_LTR_PMC 0x1BA8
+#define PTL_PMC_LTR_CUR_ASLT 0x1C28
+#define PTL_PMC_LTR_CUR_PLT 0x1C2C
+#define PTL_PCD_PMC_MMIO_REG_LEN 0x31A8
+
extern const char *pmc_lpm_modes[];
struct pmc_bit_map {
@@ -430,178 +438,77 @@ struct pmc_dev {
enum pmc_index {
PMC_IDX_MAIN,
- PMC_IDX_SOC = PMC_IDX_MAIN,
PMC_IDX_IOE,
PMC_IDX_PCH,
PMC_IDX_MAX
};
+/**
+ * struct pmc_dev_info - Structure to keep PMC device info
+ * @pci_func: Function number of the primary PMC
+ * @dmu_guid: Die Management Unit GUID
+ * @regmap_list: Pointer to a list of pmc_info structure that could be
+ * available for the platform. When set, this field implies
+ * SSRAM support.
+ * @map: Pointer to a pmc_reg_map struct that contains platform
+ * specific attributes of the primary PMC
+ * @suspend: Function to perform platform specific suspend
+ * @resume: Function to perform platform specific resume
+ * @init: Function to perform platform specific init action
+ */
+struct pmc_dev_info {
+ u8 pci_func;
+ u32 dmu_guid;
+ struct pmc_info *regmap_list;
+ const struct pmc_reg_map *map;
+ void (*suspend)(struct pmc_dev *pmcdev);
+ int (*resume)(struct pmc_dev *pmcdev);
+ int (*init)(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info);
+};
+
extern const struct pmc_bit_map msr_map[];
-extern const struct pmc_bit_map spt_pll_map[];
-extern const struct pmc_bit_map spt_mphy_map[];
-extern const struct pmc_bit_map spt_pfear_map[];
-extern const struct pmc_bit_map *ext_spt_pfear_map[];
-extern const struct pmc_bit_map spt_ltr_show_map[];
-extern const struct pmc_reg_map spt_reg_map;
extern const struct pmc_bit_map cnp_pfear_map[];
-extern const struct pmc_bit_map *ext_cnp_pfear_map[];
-extern const struct pmc_bit_map cnp_slps0_dbg0_map[];
-extern const struct pmc_bit_map cnp_slps0_dbg1_map[];
-extern const struct pmc_bit_map cnp_slps0_dbg2_map[];
extern const struct pmc_bit_map *cnp_slps0_dbg_maps[];
extern const struct pmc_bit_map cnp_ltr_show_map[];
extern const struct pmc_reg_map cnp_reg_map;
-extern const struct pmc_bit_map icl_pfear_map[];
-extern const struct pmc_bit_map *ext_icl_pfear_map[];
-extern const struct pmc_reg_map icl_reg_map;
-extern const struct pmc_bit_map tgl_pfear_map[];
-extern const struct pmc_bit_map *ext_tgl_pfear_map[];
-extern const struct pmc_bit_map tgl_clocksource_status_map[];
-extern const struct pmc_bit_map tgl_power_gating_status_map[];
-extern const struct pmc_bit_map tgl_d3_status_map[];
-extern const struct pmc_bit_map tgl_vnn_req_status_map[];
-extern const struct pmc_bit_map tgl_vnn_misc_status_map[];
extern const struct pmc_bit_map tgl_signal_status_map[];
-extern const struct pmc_bit_map *tgl_lpm_maps[];
-extern const struct pmc_reg_map tgl_reg_map;
-extern const struct pmc_reg_map tgl_h_reg_map;
-extern const struct pmc_bit_map adl_pfear_map[];
-extern const struct pmc_bit_map *ext_adl_pfear_map[];
-extern const struct pmc_bit_map adl_ltr_show_map[];
-extern const struct pmc_bit_map adl_clocksource_status_map[];
-extern const struct pmc_bit_map adl_power_gating_status_0_map[];
-extern const struct pmc_bit_map adl_power_gating_status_1_map[];
-extern const struct pmc_bit_map adl_power_gating_status_2_map[];
-extern const struct pmc_bit_map adl_d3_status_0_map[];
-extern const struct pmc_bit_map adl_d3_status_1_map[];
-extern const struct pmc_bit_map adl_d3_status_2_map[];
-extern const struct pmc_bit_map adl_d3_status_3_map[];
-extern const struct pmc_bit_map adl_vnn_req_status_0_map[];
-extern const struct pmc_bit_map adl_vnn_req_status_1_map[];
-extern const struct pmc_bit_map adl_vnn_req_status_2_map[];
-extern const struct pmc_bit_map adl_vnn_req_status_3_map[];
-extern const struct pmc_bit_map adl_vnn_misc_status_map[];
-extern const struct pmc_bit_map *adl_lpm_maps[];
extern const struct pmc_reg_map adl_reg_map;
extern const struct pmc_bit_map mtl_socm_pfear_map[];
-extern const struct pmc_bit_map *ext_mtl_socm_pfear_map[];
-extern const struct pmc_bit_map mtl_socm_ltr_show_map[];
-extern const struct pmc_bit_map mtl_socm_clocksource_status_map[];
-extern const struct pmc_bit_map mtl_socm_power_gating_status_0_map[];
-extern const struct pmc_bit_map mtl_socm_power_gating_status_1_map[];
-extern const struct pmc_bit_map mtl_socm_power_gating_status_2_map[];
extern const struct pmc_bit_map mtl_socm_d3_status_0_map[];
extern const struct pmc_bit_map mtl_socm_d3_status_1_map[];
-extern const struct pmc_bit_map mtl_socm_d3_status_2_map[];
-extern const struct pmc_bit_map mtl_socm_d3_status_3_map[];
extern const struct pmc_bit_map mtl_socm_vnn_req_status_0_map[];
extern const struct pmc_bit_map mtl_socm_vnn_req_status_1_map[];
extern const struct pmc_bit_map mtl_socm_vnn_req_status_2_map[];
-extern const struct pmc_bit_map mtl_socm_vnn_req_status_3_map[];
extern const struct pmc_bit_map mtl_socm_vnn_misc_status_map[];
extern const struct pmc_bit_map mtl_socm_signal_status_map[];
-extern const struct pmc_bit_map *mtl_socm_lpm_maps[];
extern const struct pmc_reg_map mtl_socm_reg_map;
-extern const struct pmc_bit_map mtl_ioep_pfear_map[];
-extern const struct pmc_bit_map *ext_mtl_ioep_pfear_map[];
-extern const struct pmc_bit_map mtl_ioep_ltr_show_map[];
-extern const struct pmc_bit_map mtl_ioep_clocksource_status_map[];
-extern const struct pmc_bit_map mtl_ioep_power_gating_status_0_map[];
-extern const struct pmc_bit_map mtl_ioep_power_gating_status_1_map[];
-extern const struct pmc_bit_map mtl_ioep_power_gating_status_2_map[];
-extern const struct pmc_bit_map mtl_ioep_d3_status_0_map[];
-extern const struct pmc_bit_map mtl_ioep_d3_status_1_map[];
-extern const struct pmc_bit_map mtl_ioep_d3_status_2_map[];
-extern const struct pmc_bit_map mtl_ioep_d3_status_3_map[];
-extern const struct pmc_bit_map mtl_ioep_vnn_req_status_0_map[];
-extern const struct pmc_bit_map mtl_ioep_vnn_req_status_1_map[];
-extern const struct pmc_bit_map mtl_ioep_vnn_req_status_2_map[];
-extern const struct pmc_bit_map mtl_ioep_vnn_req_status_3_map[];
-extern const struct pmc_bit_map mtl_ioep_vnn_misc_status_map[];
-extern const struct pmc_bit_map *mtl_ioep_lpm_maps[];
extern const struct pmc_reg_map mtl_ioep_reg_map;
-extern const struct pmc_bit_map mtl_ioem_pfear_map[];
-extern const struct pmc_bit_map *ext_mtl_ioem_pfear_map[];
-extern const struct pmc_bit_map mtl_ioem_power_gating_status_1_map[];
-extern const struct pmc_bit_map mtl_ioem_vnn_req_status_1_map[];
-extern const struct pmc_bit_map *mtl_ioem_lpm_maps[];
-extern const struct pmc_reg_map mtl_ioem_reg_map;
-extern const struct pmc_reg_map lnl_socm_reg_map;
-
-/* LNL */
-extern const struct pmc_bit_map lnl_ltr_show_map[];
-extern const struct pmc_bit_map lnl_clocksource_status_map[];
-extern const struct pmc_bit_map lnl_power_gating_status_0_map[];
-extern const struct pmc_bit_map lnl_power_gating_status_1_map[];
-extern const struct pmc_bit_map lnl_power_gating_status_2_map[];
-extern const struct pmc_bit_map lnl_d3_status_0_map[];
-extern const struct pmc_bit_map lnl_d3_status_1_map[];
-extern const struct pmc_bit_map lnl_d3_status_2_map[];
-extern const struct pmc_bit_map lnl_d3_status_3_map[];
-extern const struct pmc_bit_map lnl_vnn_req_status_0_map[];
-extern const struct pmc_bit_map lnl_vnn_req_status_1_map[];
-extern const struct pmc_bit_map lnl_vnn_req_status_2_map[];
-extern const struct pmc_bit_map lnl_vnn_req_status_3_map[];
-extern const struct pmc_bit_map lnl_vnn_misc_status_map[];
-extern const struct pmc_bit_map *lnl_lpm_maps[];
-extern const struct pmc_bit_map *lnl_blk_maps[];
-extern const struct pmc_bit_map lnl_pfear_map[];
-extern const struct pmc_bit_map *ext_lnl_pfear_map[];
-extern const struct pmc_bit_map lnl_signal_status_map[];
-
-/* ARL */
-extern const struct pmc_bit_map arl_socs_ltr_show_map[];
-extern const struct pmc_bit_map arl_socs_clocksource_status_map[];
-extern const struct pmc_bit_map arl_socs_power_gating_status_0_map[];
-extern const struct pmc_bit_map arl_socs_power_gating_status_1_map[];
-extern const struct pmc_bit_map arl_socs_power_gating_status_2_map[];
-extern const struct pmc_bit_map arl_socs_d3_status_2_map[];
-extern const struct pmc_bit_map arl_socs_d3_status_3_map[];
-extern const struct pmc_bit_map arl_socs_vnn_req_status_3_map[];
-extern const struct pmc_bit_map *arl_socs_lpm_maps[];
-extern const struct pmc_bit_map arl_socs_pfear_map[];
-extern const struct pmc_bit_map *ext_arl_socs_pfear_map[];
-extern const struct pmc_reg_map arl_socs_reg_map;
-extern const struct pmc_bit_map arl_pchs_ltr_show_map[];
-extern const struct pmc_bit_map arl_pchs_clocksource_status_map[];
-extern const struct pmc_bit_map arl_pchs_power_gating_status_0_map[];
-extern const struct pmc_bit_map arl_pchs_power_gating_status_1_map[];
-extern const struct pmc_bit_map arl_pchs_power_gating_status_2_map[];
-extern const struct pmc_bit_map arl_pchs_d3_status_0_map[];
-extern const struct pmc_bit_map arl_pchs_d3_status_1_map[];
-extern const struct pmc_bit_map arl_pchs_d3_status_2_map[];
-extern const struct pmc_bit_map arl_pchs_d3_status_3_map[];
-extern const struct pmc_bit_map arl_pchs_vnn_req_status_0_map[];
-extern const struct pmc_bit_map arl_pchs_vnn_req_status_1_map[];
-extern const struct pmc_bit_map arl_pchs_vnn_req_status_2_map[];
-extern const struct pmc_bit_map arl_pchs_vnn_req_status_3_map[];
-extern const struct pmc_bit_map arl_pchs_vnn_misc_status_map[];
-extern const struct pmc_bit_map arl_pchs_signal_status_map[];
-extern const struct pmc_bit_map *arl_pchs_lpm_maps[];
-extern const struct pmc_reg_map arl_pchs_reg_map;
-
-extern void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev);
-extern int pmc_core_ssram_get_lpm_reqs(struct pmc_dev *pmcdev);
+
+void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev);
+int pmc_core_ssram_get_lpm_reqs(struct pmc_dev *pmcdev);
int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore);
int pmc_core_resume_common(struct pmc_dev *pmcdev);
int get_primary_reg_base(struct pmc *pmc);
-extern void pmc_core_get_low_power_modes(struct pmc_dev *pmcdev);
-extern void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 guid);
-extern void pmc_core_set_device_d3(unsigned int device);
-
-extern int pmc_core_ssram_init(struct pmc_dev *pmcdev, int func);
-
-int spt_core_init(struct pmc_dev *pmcdev);
-int cnp_core_init(struct pmc_dev *pmcdev);
-int icl_core_init(struct pmc_dev *pmcdev);
-int tgl_core_init(struct pmc_dev *pmcdev);
-int tgl_l_core_init(struct pmc_dev *pmcdev);
-int tgl_core_generic_init(struct pmc_dev *pmcdev, int pch_tp);
-int adl_core_init(struct pmc_dev *pmcdev);
-int mtl_core_init(struct pmc_dev *pmcdev);
-int arl_core_init(struct pmc_dev *pmcdev);
-int lnl_core_init(struct pmc_dev *pmcdev);
+void pmc_core_get_low_power_modes(struct pmc_dev *pmcdev);
+void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 guid);
+void pmc_core_set_device_d3(unsigned int device);
+
+int pmc_core_ssram_init(struct pmc_dev *pmcdev, int func);
+
+int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info);
+
+extern struct pmc_dev_info spt_pmc_dev;
+extern struct pmc_dev_info cnp_pmc_dev;
+extern struct pmc_dev_info icl_pmc_dev;
+extern struct pmc_dev_info tgl_l_pmc_dev;
+extern struct pmc_dev_info tgl_pmc_dev;
+extern struct pmc_dev_info adl_pmc_dev;
+extern struct pmc_dev_info mtl_pmc_dev;
+extern struct pmc_dev_info arl_pmc_dev;
+extern struct pmc_dev_info arl_h_pmc_dev;
+extern struct pmc_dev_info lnl_pmc_dev;
+extern struct pmc_dev_info ptl_pmc_dev;
void cnl_suspend(struct pmc_dev *pmcdev);
int cnl_resume(struct pmc_dev *pmcdev);
diff --git a/drivers/platform/x86/intel/pmc/icl.c b/drivers/platform/x86/intel/pmc/icl.c
index 71b0fd6cb7d8..db7ed15bf863 100644
--- a/drivers/platform/x86/intel/pmc/icl.c
+++ b/drivers/platform/x86/intel/pmc/icl.c
@@ -10,7 +10,7 @@
#include "core.h"
-const struct pmc_bit_map icl_pfear_map[] = {
+static const struct pmc_bit_map icl_pfear_map[] = {
{"RES_65", BIT(0)},
{"RES_66", BIT(1)},
{"RES_67", BIT(2)},
@@ -22,7 +22,7 @@ const struct pmc_bit_map icl_pfear_map[] = {
{}
};
-const struct pmc_bit_map *ext_icl_pfear_map[] = {
+static const struct pmc_bit_map *ext_icl_pfear_map[] = {
/*
* Check intel_pmc_core_ids[] users of icl_reg_map for
* a list of core SoCs using this.
@@ -32,7 +32,7 @@ const struct pmc_bit_map *ext_icl_pfear_map[] = {
NULL
};
-const struct pmc_reg_map icl_reg_map = {
+static const struct pmc_reg_map icl_reg_map = {
.pfear_sts = ext_icl_pfear_map,
.slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET,
.slp_s0_res_counter_step = ICL_PMC_SLP_S0_RES_COUNTER_STEP,
@@ -50,18 +50,6 @@ const struct pmc_reg_map icl_reg_map = {
.etr3_offset = ETR3_OFFSET,
};
-int icl_core_init(struct pmc_dev *pmcdev)
-{
- struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
- int ret;
-
- pmc->map = &icl_reg_map;
-
- ret = get_primary_reg_base(pmc);
- if (ret)
- return ret;
-
- pmc_core_get_low_power_modes(pmcdev);
-
- return ret;
-}
+struct pmc_dev_info icl_pmc_dev = {
+ .map = &icl_reg_map,
+};
diff --git a/drivers/platform/x86/intel/pmc/lnl.c b/drivers/platform/x86/intel/pmc/lnl.c
index be029f12cdf4..da513c234714 100644
--- a/drivers/platform/x86/intel/pmc/lnl.c
+++ b/drivers/platform/x86/intel/pmc/lnl.c
@@ -13,7 +13,7 @@
#include "core.h"
-const struct pmc_bit_map lnl_ltr_show_map[] = {
+static const struct pmc_bit_map lnl_ltr_show_map[] = {
{"SOUTHPORT_A", CNP_PMC_LTR_SPA},
{"SOUTHPORT_B", CNP_PMC_LTR_SPB},
{"SATA", CNP_PMC_LTR_SATA},
@@ -55,7 +55,7 @@ const struct pmc_bit_map lnl_ltr_show_map[] = {
{}
};
-const struct pmc_bit_map lnl_power_gating_status_0_map[] = {
+static const struct pmc_bit_map lnl_power_gating_status_0_map[] = {
{"PMC_PGD0_PG_STS", BIT(0), 0},
{"FUSE_OSSE_PGD0_PG_STS", BIT(1), 0},
{"ESPISPI_PGD0_PG_STS", BIT(2), 0},
@@ -91,7 +91,7 @@ const struct pmc_bit_map lnl_power_gating_status_0_map[] = {
{}
};
-const struct pmc_bit_map lnl_power_gating_status_1_map[] = {
+static const struct pmc_bit_map lnl_power_gating_status_1_map[] = {
{"USBR0_PGD0_PG_STS", BIT(0), 1},
{"SUSRAM_PGD0_PG_STS", BIT(1), 1},
{"SMT1_PGD0_PG_STS", BIT(2), 1},
@@ -127,7 +127,7 @@ const struct pmc_bit_map lnl_power_gating_status_1_map[] = {
{}
};
-const struct pmc_bit_map lnl_power_gating_status_2_map[] = {
+static const struct pmc_bit_map lnl_power_gating_status_2_map[] = {
{"PSF8_PGD0_PG_STS", BIT(0), 0},
{"SBR16B2_PGD0_PG_STS", BIT(1), 0},
{"D2D_IPU_PGD0_PG_STS", BIT(2), 1},
@@ -163,7 +163,7 @@ const struct pmc_bit_map lnl_power_gating_status_2_map[] = {
{}
};
-const struct pmc_bit_map lnl_d3_status_0_map[] = {
+static const struct pmc_bit_map lnl_d3_status_0_map[] = {
{"LPSS_D3_STS", BIT(3), 1},
{"XDCI_D3_STS", BIT(4), 1},
{"XHCI_D3_STS", BIT(5), 1},
@@ -175,7 +175,7 @@ const struct pmc_bit_map lnl_d3_status_0_map[] = {
{}
};
-const struct pmc_bit_map lnl_d3_status_1_map[] = {
+static const struct pmc_bit_map lnl_d3_status_1_map[] = {
{"OSSE_SMT1_D3_STS", BIT(7), 0},
{"GBE_D3_STS", BIT(19), 0},
{"ITSS_D3_STS", BIT(23), 0},
@@ -185,7 +185,7 @@ const struct pmc_bit_map lnl_d3_status_1_map[] = {
{}
};
-const struct pmc_bit_map lnl_d3_status_2_map[] = {
+static const struct pmc_bit_map lnl_d3_status_2_map[] = {
{"ESE_D3_STS", BIT(0), 0},
{"CSMERTC_D3_STS", BIT(1), 0},
{"SUSRAM_D3_STS", BIT(2), 0},
@@ -205,7 +205,7 @@ const struct pmc_bit_map lnl_d3_status_2_map[] = {
{}
};
-const struct pmc_bit_map lnl_d3_status_3_map[] = {
+static const struct pmc_bit_map lnl_d3_status_3_map[] = {
{"THC0_D3_STS", BIT(14), 1},
{"THC1_D3_STS", BIT(15), 1},
{"OSSE_SMT3_D3_STS", BIT(21), 0},
@@ -213,14 +213,14 @@ const struct pmc_bit_map lnl_d3_status_3_map[] = {
{}
};
-const struct pmc_bit_map lnl_vnn_req_status_0_map[] = {
+static const struct pmc_bit_map lnl_vnn_req_status_0_map[] = {
{"LPSS_VNN_REQ_STS", BIT(3), 1},
{"OSSE_VNN_REQ_STS", BIT(15), 1},
{"ESPISPI_VNN_REQ_STS", BIT(18), 1},
{}
};
-const struct pmc_bit_map lnl_vnn_req_status_1_map[] = {
+static const struct pmc_bit_map lnl_vnn_req_status_1_map[] = {
{"NPK_VNN_REQ_STS", BIT(4), 1},
{"OSSE_SMT1_VNN_REQ_STS", BIT(7), 1},
{"DFXAGG_VNN_REQ_STS", BIT(8), 0},
@@ -232,7 +232,7 @@ const struct pmc_bit_map lnl_vnn_req_status_1_map[] = {
{}
};
-const struct pmc_bit_map lnl_vnn_req_status_2_map[] = {
+static const struct pmc_bit_map lnl_vnn_req_status_2_map[] = {
{"eSE_VNN_REQ_STS", BIT(0), 1},
{"CSMERTC_VNN_REQ_STS", BIT(1), 1},
{"CSE_VNN_REQ_STS", BIT(4), 1},
@@ -249,14 +249,14 @@ const struct pmc_bit_map lnl_vnn_req_status_2_map[] = {
{}
};
-const struct pmc_bit_map lnl_vnn_req_status_3_map[] = {
+static const struct pmc_bit_map lnl_vnn_req_status_3_map[] = {
{"DISP_SHIM_VNN_REQ_STS", BIT(2), 0},
{"DTS0_VNN_REQ_STS", BIT(7), 0},
{"GPIOCOM5_VNN_REQ_STS", BIT(11), 2},
{}
};
-const struct pmc_bit_map lnl_vnn_misc_status_map[] = {
+static const struct pmc_bit_map lnl_vnn_misc_status_map[] = {
{"CPU_C10_REQ_STS", BIT(0), 0},
{"TS_OFF_REQ_STS", BIT(1), 0},
{"PNDE_MET_REQ_STS", BIT(2), 1},
@@ -292,7 +292,7 @@ const struct pmc_bit_map lnl_vnn_misc_status_map[] = {
{}
};
-const struct pmc_bit_map lnl_clocksource_status_map[] = {
+static const struct pmc_bit_map lnl_clocksource_status_map[] = {
{"AON2_OFF_STS", BIT(0), 0},
{"AON3_OFF_STS", BIT(1), 1},
{"AON4_OFF_STS", BIT(2), 1},
@@ -317,7 +317,7 @@ const struct pmc_bit_map lnl_clocksource_status_map[] = {
{}
};
-const struct pmc_bit_map lnl_signal_status_map[] = {
+static const struct pmc_bit_map lnl_signal_status_map[] = {
{"LSX_Wake0_STS", BIT(0), 0},
{"LSX_Wake1_STS", BIT(1), 0},
{"LSX_Wake2_STS", BIT(2), 0},
@@ -337,7 +337,7 @@ const struct pmc_bit_map lnl_signal_status_map[] = {
{}
};
-const struct pmc_bit_map lnl_rsc_status_map[] = {
+static const struct pmc_bit_map lnl_rsc_status_map[] = {
{"Memory", 0, 1},
{"PSF0", 0, 1},
{"PSF4", 0, 1},
@@ -349,7 +349,7 @@ const struct pmc_bit_map lnl_rsc_status_map[] = {
{}
};
-const struct pmc_bit_map *lnl_lpm_maps[] = {
+static const struct pmc_bit_map *lnl_lpm_maps[] = {
lnl_clocksource_status_map,
lnl_power_gating_status_0_map,
lnl_power_gating_status_1_map,
@@ -367,7 +367,7 @@ const struct pmc_bit_map *lnl_lpm_maps[] = {
NULL
};
-const struct pmc_bit_map *lnl_blk_maps[] = {
+static const struct pmc_bit_map *lnl_blk_maps[] = {
lnl_power_gating_status_0_map,
lnl_power_gating_status_1_map,
lnl_power_gating_status_2_map,
@@ -386,7 +386,7 @@ const struct pmc_bit_map *lnl_blk_maps[] = {
NULL
};
-const struct pmc_bit_map lnl_pfear_map[] = {
+static const struct pmc_bit_map lnl_pfear_map[] = {
{"PMC_0", BIT(0)},
{"FUSE_OSSE", BIT(1)},
{"ESPISPI", BIT(2)},
@@ -498,12 +498,12 @@ const struct pmc_bit_map lnl_pfear_map[] = {
{}
};
-const struct pmc_bit_map *ext_lnl_pfear_map[] = {
+static const struct pmc_bit_map *ext_lnl_pfear_map[] = {
lnl_pfear_map,
NULL
};
-const struct pmc_reg_map lnl_socm_reg_map = {
+static const struct pmc_reg_map lnl_socm_reg_map = {
.pfear_sts = ext_lnl_pfear_map,
.slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET,
.slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP,
@@ -550,22 +550,15 @@ static int lnl_resume(struct pmc_dev *pmcdev)
return cnl_resume(pmcdev);
}
-int lnl_core_init(struct pmc_dev *pmcdev)
+static int lnl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
{
- int ret;
- struct pmc *pmc = pmcdev->pmcs[PMC_IDX_SOC];
-
lnl_d3_fixup();
-
- pmcdev->suspend = cnl_suspend;
- pmcdev->resume = lnl_resume;
-
- pmc->map = &lnl_socm_reg_map;
- ret = get_primary_reg_base(pmc);
- if (ret)
- return ret;
-
- pmc_core_get_low_power_modes(pmcdev);
-
- return 0;
+ return generic_core_init(pmcdev, pmc_dev_info);
}
+
+struct pmc_dev_info lnl_pmc_dev = {
+ .map = &lnl_socm_reg_map,
+ .suspend = cnl_suspend,
+ .resume = lnl_resume,
+ .init = lnl_core_init,
+};
diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c
index 02949fed76e9..8862829694a7 100644
--- a/drivers/platform/x86/intel/pmc/mtl.c
+++ b/drivers/platform/x86/intel/pmc/mtl.c
@@ -102,12 +102,12 @@ const struct pmc_bit_map mtl_socm_pfear_map[] = {
{}
};
-const struct pmc_bit_map *ext_mtl_socm_pfear_map[] = {
+static const struct pmc_bit_map *ext_mtl_socm_pfear_map[] = {
mtl_socm_pfear_map,
NULL
};
-const struct pmc_bit_map mtl_socm_ltr_show_map[] = {
+static const struct pmc_bit_map mtl_socm_ltr_show_map[] = {
{"SOUTHPORT_A", CNP_PMC_LTR_SPA},
{"SOUTHPORT_B", CNP_PMC_LTR_SPB},
{"SATA", CNP_PMC_LTR_SATA},
@@ -141,7 +141,7 @@ const struct pmc_bit_map mtl_socm_ltr_show_map[] = {
{}
};
-const struct pmc_bit_map mtl_socm_clocksource_status_map[] = {
+static const struct pmc_bit_map mtl_socm_clocksource_status_map[] = {
{"AON2_OFF_STS", BIT(0)},
{"AON3_OFF_STS", BIT(1)},
{"AON4_OFF_STS", BIT(2)},
@@ -167,7 +167,7 @@ const struct pmc_bit_map mtl_socm_clocksource_status_map[] = {
{}
};
-const struct pmc_bit_map mtl_socm_power_gating_status_0_map[] = {
+static const struct pmc_bit_map mtl_socm_power_gating_status_0_map[] = {
{"PMC_PGD0_PG_STS", BIT(0)},
{"DMI_PGD0_PG_STS", BIT(1)},
{"ESPISPI_PGD0_PG_STS", BIT(2)},
@@ -203,7 +203,7 @@ const struct pmc_bit_map mtl_socm_power_gating_status_0_map[] = {
{}
};
-const struct pmc_bit_map mtl_socm_power_gating_status_1_map[] = {
+static const struct pmc_bit_map mtl_socm_power_gating_status_1_map[] = {
{"USBR0_PGD0_PG_STS", BIT(0)},
{"SUSRAM_PGD0_PG_STS", BIT(1)},
{"SMT1_PGD0_PG_STS", BIT(2)},
@@ -239,7 +239,7 @@ const struct pmc_bit_map mtl_socm_power_gating_status_1_map[] = {
{}
};
-const struct pmc_bit_map mtl_socm_power_gating_status_2_map[] = {
+static const struct pmc_bit_map mtl_socm_power_gating_status_2_map[] = {
{"PSF8_PGD0_PG_STS", BIT(0)},
{"FIA_PGD0_PG_STS", BIT(1)},
{"SOC_D2D_PGD1_PG_STS", BIT(2)},
@@ -291,7 +291,7 @@ const struct pmc_bit_map mtl_socm_d3_status_1_map[] = {
{}
};
-const struct pmc_bit_map mtl_socm_d3_status_2_map[] = {
+static const struct pmc_bit_map mtl_socm_d3_status_2_map[] = {
{"GNA_D3_STS", BIT(0)},
{"CSMERTC_D3_STS", BIT(1)},
{"SUSRAM_D3_STS", BIT(2)},
@@ -310,7 +310,7 @@ const struct pmc_bit_map mtl_socm_d3_status_2_map[] = {
{}
};
-const struct pmc_bit_map mtl_socm_d3_status_3_map[] = {
+static const struct pmc_bit_map mtl_socm_d3_status_3_map[] = {
{"ESE_D3_STS", BIT(2)},
{"GBETSN_D3_STS", BIT(13)},
{"THC0_D3_STS", BIT(14)},
@@ -353,7 +353,7 @@ const struct pmc_bit_map mtl_socm_vnn_req_status_2_map[] = {
{}
};
-const struct pmc_bit_map mtl_socm_vnn_req_status_3_map[] = {
+static const struct pmc_bit_map mtl_socm_vnn_req_status_3_map[] = {
{"ESE_VNN_REQ_STS", BIT(2)},
{"DTS0_VNN_REQ_STS", BIT(7)},
{"GPIOCOM5_VNN_REQ_STS", BIT(11)},
@@ -432,7 +432,7 @@ const struct pmc_bit_map mtl_socm_signal_status_map[] = {
{}
};
-const struct pmc_bit_map *mtl_socm_lpm_maps[] = {
+static const struct pmc_bit_map *mtl_socm_lpm_maps[] = {
mtl_socm_clocksource_status_map,
mtl_socm_power_gating_status_0_map,
mtl_socm_power_gating_status_1_map,
@@ -476,7 +476,7 @@ const struct pmc_reg_map mtl_socm_reg_map = {
.lpm_reg_index = MTL_LPM_REG_INDEX,
};
-const struct pmc_bit_map mtl_ioep_pfear_map[] = {
+static const struct pmc_bit_map mtl_ioep_pfear_map[] = {
{"PMC_0", BIT(0)},
{"OPI", BIT(1)},
{"TCSS", BIT(2)},
@@ -563,12 +563,12 @@ const struct pmc_bit_map mtl_ioep_pfear_map[] = {
{}
};
-const struct pmc_bit_map *ext_mtl_ioep_pfear_map[] = {
+static const struct pmc_bit_map *ext_mtl_ioep_pfear_map[] = {
mtl_ioep_pfear_map,
NULL
};
-const struct pmc_bit_map mtl_ioep_ltr_show_map[] = {
+static const struct pmc_bit_map mtl_ioep_ltr_show_map[] = {
{"SOUTHPORT_A", CNP_PMC_LTR_SPA},
{"SOUTHPORT_B", CNP_PMC_LTR_SPB},
{"SATA", CNP_PMC_LTR_SATA},
@@ -600,7 +600,7 @@ const struct pmc_bit_map mtl_ioep_ltr_show_map[] = {
{}
};
-const struct pmc_bit_map mtl_ioep_clocksource_status_map[] = {
+static const struct pmc_bit_map mtl_ioep_clocksource_status_map[] = {
{"AON2_OFF_STS", BIT(0)},
{"AON3_OFF_STS", BIT(1)},
{"AON4_OFF_STS", BIT(2)},
@@ -623,7 +623,7 @@ const struct pmc_bit_map mtl_ioep_clocksource_status_map[] = {
{}
};
-const struct pmc_bit_map mtl_ioep_power_gating_status_0_map[] = {
+static const struct pmc_bit_map mtl_ioep_power_gating_status_0_map[] = {
{"PMC_PGD0_PG_STS", BIT(0)},
{"DMI_PGD0_PG_STS", BIT(1)},
{"TCSS_PGD0_PG_STS", BIT(2)},
@@ -650,7 +650,7 @@ const struct pmc_bit_map mtl_ioep_power_gating_status_0_map[] = {
{}
};
-const struct pmc_bit_map mtl_ioep_power_gating_status_1_map[] = {
+static const struct pmc_bit_map mtl_ioep_power_gating_status_1_map[] = {
{"PSF9_PGD0_PG_STS", BIT(0)},
{"MPFPW4_PGD0_PG_STS", BIT(1)},
{"SBR0_PGD0_PG_STS", BIT(8)},
@@ -668,7 +668,7 @@ const struct pmc_bit_map mtl_ioep_power_gating_status_1_map[] = {
{}
};
-const struct pmc_bit_map mtl_ioep_power_gating_status_2_map[] = {
+static const struct pmc_bit_map mtl_ioep_power_gating_status_2_map[] = {
{"FIA_PGD0_PG_STS", BIT(1)},
{"FIA_P_PGD0_PG_STS", BIT(3)},
{"TAM_PGD0_PG_STS", BIT(4)},
@@ -680,7 +680,7 @@ const struct pmc_bit_map mtl_ioep_power_gating_status_2_map[] = {
{}
};
-const struct pmc_bit_map mtl_ioep_d3_status_0_map[] = {
+static const struct pmc_bit_map mtl_ioep_d3_status_0_map[] = {
{"SPF_D3_STS", BIT(0)},
{"SPA_D3_STS", BIT(12)},
{"SPB_D3_STS", BIT(13)},
@@ -691,43 +691,43 @@ const struct pmc_bit_map mtl_ioep_d3_status_0_map[] = {
{}
};
-const struct pmc_bit_map mtl_ioep_d3_status_1_map[] = {
+static const struct pmc_bit_map mtl_ioep_d3_status_1_map[] = {
{"GBETSN1_D3_STS", BIT(14)},
{"P2S_D3_STS", BIT(24)},
{}
};
-const struct pmc_bit_map mtl_ioep_d3_status_2_map[] = {
+static const struct pmc_bit_map mtl_ioep_d3_status_2_map[] = {
{}
};
-const struct pmc_bit_map mtl_ioep_d3_status_3_map[] = {
+static const struct pmc_bit_map mtl_ioep_d3_status_3_map[] = {
{"GBETSN_D3_STS", BIT(13)},
{"ACE_D3_STS", BIT(23)},
{}
};
-const struct pmc_bit_map mtl_ioep_vnn_req_status_0_map[] = {
+static const struct pmc_bit_map mtl_ioep_vnn_req_status_0_map[] = {
{"FIA_VNN_REQ_STS", BIT(17)},
{}
};
-const struct pmc_bit_map mtl_ioep_vnn_req_status_1_map[] = {
+static const struct pmc_bit_map mtl_ioep_vnn_req_status_1_map[] = {
{"DFXAGG_VNN_REQ_STS", BIT(8)},
{}
};
-const struct pmc_bit_map mtl_ioep_vnn_req_status_2_map[] = {
+static const struct pmc_bit_map mtl_ioep_vnn_req_status_2_map[] = {
{}
};
-const struct pmc_bit_map mtl_ioep_vnn_req_status_3_map[] = {
+static const struct pmc_bit_map mtl_ioep_vnn_req_status_3_map[] = {
{"DTS0_VNN_REQ_STS", BIT(7)},
{"DISP_VNN_REQ_STS", BIT(19)},
{}
};
-const struct pmc_bit_map mtl_ioep_vnn_misc_status_map[] = {
+static const struct pmc_bit_map mtl_ioep_vnn_misc_status_map[] = {
{"CPU_C10_REQ_STS", BIT(0)},
{"TS_OFF_REQ_STS", BIT(1)},
{"PNDE_MET_REQ_STS", BIT(2)},
@@ -762,7 +762,7 @@ const struct pmc_bit_map mtl_ioep_vnn_misc_status_map[] = {
{}
};
-const struct pmc_bit_map *mtl_ioep_lpm_maps[] = {
+static const struct pmc_bit_map *mtl_ioep_lpm_maps[] = {
mtl_ioep_clocksource_status_map,
mtl_ioep_power_gating_status_0_map,
mtl_ioep_power_gating_status_1_map,
@@ -800,7 +800,7 @@ const struct pmc_reg_map mtl_ioep_reg_map = {
.lpm_reg_index = MTL_LPM_REG_INDEX,
};
-const struct pmc_bit_map mtl_ioem_pfear_map[] = {
+static const struct pmc_bit_map mtl_ioem_pfear_map[] = {
{"PMC_0", BIT(0)},
{"OPI", BIT(1)},
{"TCSS", BIT(2)},
@@ -887,12 +887,12 @@ const struct pmc_bit_map mtl_ioem_pfear_map[] = {
{}
};
-const struct pmc_bit_map *ext_mtl_ioem_pfear_map[] = {
+static const struct pmc_bit_map *ext_mtl_ioem_pfear_map[] = {
mtl_ioem_pfear_map,
NULL
};
-const struct pmc_bit_map mtl_ioem_power_gating_status_1_map[] = {
+static const struct pmc_bit_map mtl_ioem_power_gating_status_1_map[] = {
{"PSF9_PGD0_PG_STS", BIT(0)},
{"MPFPW4_PGD0_PG_STS", BIT(1)},
{"SBR0_PGD0_PG_STS", BIT(8)},
@@ -909,7 +909,7 @@ const struct pmc_bit_map mtl_ioem_power_gating_status_1_map[] = {
{}
};
-const struct pmc_bit_map *mtl_ioem_lpm_maps[] = {
+static const struct pmc_bit_map *mtl_ioem_lpm_maps[] = {
mtl_ioep_clocksource_status_map,
mtl_ioep_power_gating_status_0_map,
mtl_ioem_power_gating_status_1_map,
@@ -927,7 +927,7 @@ const struct pmc_bit_map *mtl_ioem_lpm_maps[] = {
NULL
};
-const struct pmc_reg_map mtl_ioem_reg_map = {
+static const struct pmc_reg_map mtl_ioem_reg_map = {
.regmap_length = MTL_IOE_PMC_MMIO_REG_LEN,
.pfear_sts = ext_mtl_ioem_pfear_map,
.ppfear0_offset = CNP_PMC_HOST_PPFEAR0A,
@@ -990,39 +990,18 @@ static int mtl_resume(struct pmc_dev *pmcdev)
return cnl_resume(pmcdev);
}
-int mtl_core_init(struct pmc_dev *pmcdev)
+static int mtl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
{
- struct pmc *pmc = pmcdev->pmcs[PMC_IDX_SOC];
- int ret;
- int func = 2;
- bool ssram_init = true;
-
mtl_d3_fixup();
-
- pmcdev->suspend = cnl_suspend;
- pmcdev->resume = mtl_resume;
- pmcdev->regmap_list = mtl_pmc_info_list;
-
- /*
- * If ssram init fails use legacy method to at least get the
- * primary PMC
- */
- ret = pmc_core_ssram_init(pmcdev, func);
- if (ret) {
- ssram_init = false;
- dev_warn(&pmcdev->pdev->dev,
- "ssram init failed, %d, using legacy init\n", ret);
- pmc->map = &mtl_socm_reg_map;
- ret = get_primary_reg_base(pmc);
- if (ret)
- return ret;
- }
-
- pmc_core_get_low_power_modes(pmcdev);
- pmc_core_punit_pmt_init(pmcdev, MTL_PMT_DMU_GUID);
-
- if (ssram_init)
- return pmc_core_ssram_get_lpm_reqs(pmcdev);
-
- return 0;
+ return generic_core_init(pmcdev, pmc_dev_info);
}
+
+struct pmc_dev_info mtl_pmc_dev = {
+ .pci_func = 2,
+ .dmu_guid = MTL_PMT_DMU_GUID,
+ .regmap_list = mtl_pmc_info_list,
+ .map = &mtl_socm_reg_map,
+ .suspend = cnl_suspend,
+ .resume = mtl_resume,
+ .init = mtl_core_init,
+};
diff --git a/drivers/platform/x86/intel/pmc/ptl.c b/drivers/platform/x86/intel/pmc/ptl.c
new file mode 100644
index 000000000000..394515af60d6
--- /dev/null
+++ b/drivers/platform/x86/intel/pmc/ptl.c
@@ -0,0 +1,550 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file contains platform specific structure definitions
+ * and init function used by Panther Lake PCH.
+ *
+ * Copyright (c) 2025, Intel Corporation.
+ */
+
+#include <linux/pci.h>
+
+#include "core.h"
+
+static const struct pmc_bit_map ptl_pcdp_pfear_map[] = {
+ {"PMC_0", BIT(0)},
+ {"FUSE_OSSE", BIT(1)},
+ {"ESPISPI", BIT(2)},
+ {"XHCI", BIT(3)},
+ {"SPA", BIT(4)},
+ {"SPB", BIT(5)},
+ {"MPFPW2", BIT(6)},
+ {"GBE", BIT(7)},
+
+ {"SBR16B20", BIT(0)},
+ {"SBR8B20", BIT(1)},
+ {"SBR16B21", BIT(2)},
+ {"DBG_SBR16B", BIT(3)},
+ {"OSSE_HOTHAM", BIT(4)},
+ {"D2D_DISP_1", BIT(5)},
+ {"LPSS", BIT(6)},
+ {"LPC", BIT(7)},
+
+ {"SMB", BIT(0)},
+ {"ISH", BIT(1)},
+ {"SBR16B2", BIT(2)},
+ {"NPK_0", BIT(3)},
+ {"D2D_NOC_1", BIT(4)},
+ {"SBR8B2", BIT(5)},
+ {"FUSE", BIT(6)},
+ {"SBR16B0", BIT(7)},
+
+ {"PSF0", BIT(0)},
+ {"XDCI", BIT(1)},
+ {"EXI", BIT(2)},
+ {"CSE", BIT(3)},
+ {"KVMCC", BIT(4)},
+ {"PMT", BIT(5)},
+ {"CLINK", BIT(6)},
+ {"PTIO", BIT(7)},
+
+ {"USBR0", BIT(0)},
+ {"SUSRAM", BIT(1)},
+ {"SMT1", BIT(2)},
+ {"MPFPW1", BIT(3)},
+ {"SMS2", BIT(4)},
+ {"SMS1", BIT(5)},
+ {"CSMERTC", BIT(6)},
+ {"CSMEPSF", BIT(7)},
+
+ {"D2D_NOC_0", BIT(0)},
+ {"ESE", BIT(1)},
+ {"P2SB8B", BIT(2)},
+ {"SBR16B7", BIT(3)},
+ {"SBR16B3", BIT(4)},
+ {"OSSE_SMT1", BIT(5)},
+ {"D2D_DISP", BIT(6)},
+ {"DBG_SBR", BIT(7)},
+
+ {"U3FPW1", BIT(0)},
+ {"FIA_X", BIT(1)},
+ {"PSF4", BIT(2)},
+ {"CNVI", BIT(3)},
+ {"UFSX2", BIT(4)},
+ {"ENDBG", BIT(5)},
+ {"DBC", BIT(6)},
+ {"FIA_PG", BIT(7)},
+
+ {"D2D_IPU", BIT(0)},
+ {"NPK1", BIT(1)},
+ {"FIACPCB_X", BIT(2)},
+ {"SBR8B4", BIT(3)},
+ {"DBG_PSF", BIT(4)},
+ {"PSF6", BIT(5)},
+ {"UFSPW1", BIT(6)},
+ {"FIA_U", BIT(7)},
+
+ {"PSF8", BIT(0)},
+ {"SBR16B4", BIT(1)},
+ {"SBR16B5", BIT(2)},
+ {"FIACPCB_U", BIT(3)},
+ {"TAM", BIT(4)},
+ {"D2D_NOC_2", BIT(5)},
+ {"TBTLSX", BIT(6)},
+ {"THC0", BIT(7)},
+
+ {"THC1", BIT(0)},
+ {"PMC_1", BIT(1)},
+ {"SBR8B1", BIT(2)},
+ {"TCSS", BIT(3)},
+ {"DISP_PGA", BIT(4)},
+ {"SBR16B1", BIT(5)},
+ {"SBRG", BIT(6)},
+ {"PSF5", BIT(7)},
+
+ {"P2SB16B", BIT(0)},
+ {"ACE_0", BIT(1)},
+ {"ACE_1", BIT(2)},
+ {"ACE_2", BIT(3)},
+ {"ACE_3", BIT(4)},
+ {"ACE_4", BIT(5)},
+ {"ACE_5", BIT(6)},
+ {"ACE_6", BIT(7)},
+
+ {"ACE_7", BIT(0)},
+ {"ACE_8", BIT(1)},
+ {"ACE_9", BIT(2)},
+ {"ACE_10", BIT(3)},
+ {"FIACPCB_PG", BIT(4)},
+ {"SBR16B6", BIT(5)},
+ {"OSSE", BIT(6)},
+ {"SBR8B0", BIT(7)},
+ {}
+};
+
+static const struct pmc_bit_map *ext_ptl_pcdp_pfear_map[] = {
+ ptl_pcdp_pfear_map,
+ NULL
+};
+
+static const struct pmc_bit_map ptl_pcdp_ltr_show_map[] = {
+ {"SOUTHPORT_A", CNP_PMC_LTR_SPA},
+ {"SOUTHPORT_B", CNP_PMC_LTR_SPB},
+ {"SATA", CNP_PMC_LTR_SATA},
+ {"GIGABIT_ETHERNET", CNP_PMC_LTR_GBE},
+ {"XHCI", CNP_PMC_LTR_XHCI},
+ {"SOUTHPORT_F", ADL_PMC_LTR_SPF},
+ {"ME", CNP_PMC_LTR_ME},
+ {"SATA1", CNP_PMC_LTR_EVA},
+ {"SOUTHPORT_C", CNP_PMC_LTR_SPC},
+ {"HD_AUDIO", CNP_PMC_LTR_AZ},
+ {"CNV", CNP_PMC_LTR_CNV},
+ {"LPSS", CNP_PMC_LTR_LPSS},
+ {"SOUTHPORT_D", CNP_PMC_LTR_SPD},
+ {"SOUTHPORT_E", CNP_PMC_LTR_SPE},
+ {"SATA2", PTL_PMC_LTR_SATA2},
+ {"ESPI", CNP_PMC_LTR_ESPI},
+ {"SCC", CNP_PMC_LTR_SCC},
+ {"ISH", CNP_PMC_LTR_ISH},
+ {"UFSX2", CNP_PMC_LTR_UFSX2},
+ {"EMMC", CNP_PMC_LTR_EMMC},
+ {"WIGIG", ICL_PMC_LTR_WIGIG},
+ {"THC0", TGL_PMC_LTR_THC0},
+ {"THC1", TGL_PMC_LTR_THC1},
+ {"SOUTHPORT_G", MTL_PMC_LTR_SPG},
+ {"ESE", MTL_PMC_LTR_ESE},
+ {"IOE_PMC", MTL_PMC_LTR_IOE_PMC},
+ {"DMI3", ARL_PMC_LTR_DMI3},
+ {"OSSE", LNL_PMC_LTR_OSSE},
+
+ /* Below two cannot be used for LTR_IGNORE */
+ {"CURRENT_PLATFORM", PTL_PMC_LTR_CUR_PLT},
+ {"AGGREGATED_SYSTEM", PTL_PMC_LTR_CUR_ASLT},
+ {}
+};
+
+static const struct pmc_bit_map ptl_pcdp_clocksource_status_map[] = {
+ {"AON2_OFF_STS", BIT(0), 1},
+ {"AON3_OFF_STS", BIT(1), 0},
+ {"AON4_OFF_STS", BIT(2), 1},
+ {"AON5_OFF_STS", BIT(3), 1},
+ {"AON1_OFF_STS", BIT(4), 0},
+ {"XTAL_LVM_OFF_STS", BIT(5), 0},
+ {"MPFPW1_0_PLL_OFF_STS", BIT(6), 1},
+ {"USB3_PLL_OFF_STS", BIT(8), 1},
+ {"AON3_SPL_OFF_STS", BIT(9), 1},
+ {"MPFPW2_0_PLL_OFF_STS", BIT(12), 1},
+ {"XTAL_AGGR_OFF_STS", BIT(17), 1},
+ {"USB2_PLL_OFF_STS", BIT(18), 0},
+ {"SAF_PLL_OFF_STS", BIT(19), 1},
+ {"SE_TCSS_PLL_OFF_STS", BIT(20), 1},
+ {"DDI_PLL_OFF_STS", BIT(21), 1},
+ {"FILTER_PLL_OFF_STS", BIT(22), 1},
+ {"ACE_PLL_OFF_STS", BIT(24), 0},
+ {"FABRIC_PLL_OFF_STS", BIT(25), 1},
+ {"SOC_PLL_OFF_STS", BIT(26), 1},
+ {"REF_PLL_OFF_STS", BIT(28), 1},
+ {"IMG_PLL_OFF_STS", BIT(29), 1},
+ {"RTC_PLL_OFF_STS", BIT(31), 0},
+ {}
+};
+
+static const struct pmc_bit_map ptl_pcdp_power_gating_status_0_map[] = {
+ {"PMC_PGD0_PG_STS", BIT(0), 0},
+ {"FUSE_OSSE_PGD0_PG_STS", BIT(1), 0},
+ {"ESPISPI_PGD0_PG_STS", BIT(2), 0},
+ {"XHCI_PGD0_PG_STS", BIT(3), 1},
+ {"SPA_PGD0_PG_STS", BIT(4), 1},
+ {"SPB_PGD0_PG_STS", BIT(5), 1},
+ {"MPFPW2_PGD0_PG_STS", BIT(6), 0},
+ {"GBE_PGD0_PG_STS", BIT(7), 1},
+ {"SBR16B20_PGD0_PG_STS", BIT(8), 0},
+ {"SBR8B20_PGD0_PG_STS", BIT(9), 0},
+ {"SBR16B21_PGD0_PG_STS", BIT(10), 0},
+ {"DBG_PGD0_PG_STS", BIT(11), 0},
+ {"OSSE_HOTHAM_PGD0_PG_STS", BIT(12), 1},
+ {"D2D_DISP_PGD1_PG_STS", BIT(13), 1},
+ {"LPSS_PGD0_PG_STS", BIT(14), 1},
+ {"LPC_PGD0_PG_STS", BIT(15), 0},
+ {"SMB_PGD0_PG_STS", BIT(16), 0},
+ {"ISH_PGD0_PG_STS", BIT(17), 0},
+ {"SBR16B2_PGD0_PG_STS", BIT(18), 0},
+ {"NPK_PGD0_PG_STS", BIT(19), 0},
+ {"D2D_NOC_PGD1_PG_STS", BIT(20), 1},
+ {"SBR8B2_PGD0_PG_STS", BIT(21), 0},
+ {"FUSE_PGD0_PG_STS", BIT(22), 0},
+ {"SBR16B0_PGD0_PG_STS", BIT(23), 0},
+ {"PSF0_PGD0_PG_STS", BIT(24), 0},
+ {"XDCI_PGD0_PG_STS", BIT(25), 1},
+ {"EXI_PGD0_PG_STS", BIT(26), 0},
+ {"CSE_PGD0_PG_STS", BIT(27), 1},
+ {"KVMCC_PGD0_PG_STS", BIT(28), 1},
+ {"PMT_PGD0_PG_STS", BIT(29), 1},
+ {"CLINK_PGD0_PG_STS", BIT(30), 1},
+ {"PTIO_PGD0_PG_STS", BIT(31), 1},
+ {}
+};
+
+static const struct pmc_bit_map ptl_pcdp_power_gating_status_1_map[] = {
+ {"USBR0_PGD0_PG_STS", BIT(0), 1},
+ {"SUSRAM_PGD0_PG_STS", BIT(1), 1},
+ {"SMT1_PGD0_PG_STS", BIT(2), 1},
+ {"MPFPW1_PGD0_PG_STS", BIT(3), 0},
+ {"SMS2_PGD0_PG_STS", BIT(4), 1},
+ {"SMS1_PGD0_PG_STS", BIT(5), 1},
+ {"CSMERTC_PGD0_PG_STS", BIT(6), 0},
+ {"CSMEPSF_PGD0_PG_STS", BIT(7), 0},
+ {"D2D_NOC_PGD0_PG_STS", BIT(8), 0},
+ {"ESE_PGD0_PG_STS", BIT(9), 1},
+ {"P2SB8B_PGD0_PG_STS", BIT(10), 1},
+ {"SBR16B7_PGD0_PG_STS", BIT(11), 0},
+ {"SBR16B3_PGD0_PG_STS", BIT(12), 0},
+ {"OSSE_SMT1_PGD0_PG_STS", BIT(13), 1},
+ {"D2D_DISP_PGD0_PG_STS", BIT(14), 1},
+ {"DBG_SBR_PGD0_PG_STS", BIT(15), 0},
+ {"U3FPW1_PGD0_PG_STS", BIT(16), 0},
+ {"FIA_X_PGD0_PG_STS", BIT(17), 0},
+ {"PSF4_PGD0_PG_STS", BIT(18), 0},
+ {"CNVI_PGD0_PG_STS", BIT(19), 0},
+ {"UFSX2_PGD0_PG_STS", BIT(20), 1},
+ {"ENDBG_PGD0_PG_STS", BIT(21), 0},
+ {"DBC_PGD0_PG_STS", BIT(22), 0},
+ {"FIA_PG_PGD0_PG_STS", BIT(23), 0},
+ {"D2D_IPU_PGD0_PG_STS", BIT(24), 1},
+ {"NPK_PGD1_PG_STS", BIT(25), 0},
+ {"FIACPCB_X_PGD0_PG_STS", BIT(26), 0},
+ {"SBR8B4_PGD0_PG_STS", BIT(27), 0},
+ {"DBG_PSF_PGD0_PG_STS", BIT(28), 0},
+ {"PSF6_PGD0_PG_STS", BIT(29), 0},
+ {"UFSPW1_PGD0_PG_STS", BIT(30), 0},
+ {"FIA_U_PGD0_PG_STS", BIT(31), 0},
+ {}
+};
+
+static const struct pmc_bit_map ptl_pcdp_power_gating_status_2_map[] = {
+ {"PSF8_PGD0_PG_STS", BIT(0), 0},
+ {"SBR16B4_PGD0_PG_STS", BIT(1), 0},
+ {"SBR16B5_PGD0_PG_STS", BIT(2), 0},
+ {"FIACPCB_U_PGD0_PG_STS", BIT(3), 0},
+ {"TAM_PGD0_PG_STS", BIT(4), 1},
+ {"D2D_NOC_PGD0_PG_STS", BIT(5), 1},
+ {"TBTLSX_PGD0_PG_STS", BIT(6), 1},
+ {"THC0_PGD0_PG_STS", BIT(7), 1},
+ {"THC1_PGD0_PG_STS", BIT(8), 1},
+ {"PMC_PGD1_PG_STS", BIT(9), 0},
+ {"SBR8B1_PGD0_PG_STS", BIT(10), 0},
+ {"TCSS_PGD0_PG_STS", BIT(11), 0},
+ {"DISP_PGA_PGD0_PG_STS", BIT(12), 0},
+ {"SBR16B1_PGD0_PG_STS", BIT(13), 0},
+ {"SBRG_PGD0_PG_STS", BIT(14), 0},
+ {"PSF5_PGD0_PG_STS", BIT(15), 0},
+ {"P2SB16B_PGD0_PG_STS", BIT(16), 1},
+ {"ACE_PGD0_PG_STS", BIT(17), 0},
+ {"ACE_PGD1_PG_STS", BIT(18), 0},
+ {"ACE_PGD2_PG_STS", BIT(19), 0},
+ {"ACE_PGD3_PG_STS", BIT(20), 0},
+ {"ACE_PGD4_PG_STS", BIT(21), 0},
+ {"ACE_PGD5_PG_STS", BIT(22), 0},
+ {"ACE_PGD6_PG_STS", BIT(23), 0},
+ {"ACE_PGD7_PG_STS", BIT(24), 0},
+ {"ACE_PGD8_PG_STS", BIT(25), 0},
+ {"ACE_PGD9_PG_STS", BIT(26), 0},
+ {"ACE_PGD10_PG_STS", BIT(27), 0},
+ {"FIACPCB_PG_PGD0_PG_STS", BIT(28), 0},
+ {"SBR16B6_PGD0_PG_STS", BIT(29), 0},
+ {"OSSE_PGD0_PG_STS", BIT(30), 1},
+ {"SBR8B0_PGD0_PG_STS", BIT(31), 0},
+ {}
+};
+
+static const struct pmc_bit_map ptl_pcdp_d3_status_0_map[] = {
+ {"LPSS_D3_STS", BIT(3), 1},
+ {"XDCI_D3_STS", BIT(4), 1},
+ {"XHCI_D3_STS", BIT(5), 1},
+ {"OSSE_D3_STS", BIT(6), 0},
+ {"SPA_D3_STS", BIT(12), 0},
+ {"SPB_D3_STS", BIT(13), 0},
+ {"ESPISPI_D3_STS", BIT(18), 0},
+ {"PSTH_D3_STS", BIT(21), 0},
+ {"OSSE_SMT1_D3_STS", BIT(30), 0},
+ {}
+};
+
+static const struct pmc_bit_map ptl_pcdp_d3_status_1_map[] = {
+ {"GBE_D3_STS", BIT(19), 0},
+ {"ITSS_D3_STS", BIT(23), 0},
+ {"CNVI_D3_STS", BIT(27), 0},
+ {"UFSX2_D3_STS", BIT(28), 1},
+ {"OSSE_HOTHAM_D3_STS", BIT(29), 0},
+ {"ESE_D3_STS", BIT(30), 0},
+ {}
+};
+
+static const struct pmc_bit_map ptl_pcdp_d3_status_2_map[] = {
+ {"CSMERTC_D3_STS", BIT(1), 0},
+ {"SUSRAM_D3_STS", BIT(2), 0},
+ {"CSE_D3_STS", BIT(4), 0},
+ {"KVMCC_D3_STS", BIT(5), 0},
+ {"USBR0_D3_STS", BIT(6), 0},
+ {"ISH_D3_STS", BIT(7), 0},
+ {"SMT1_D3_STS", BIT(8), 0},
+ {"SMT2_D3_STS", BIT(9), 0},
+ {"SMT3_D3_STS", BIT(10), 0},
+ {"OSSE_SMT2_D3_STS", BIT(12), 0},
+ {"CLINK_D3_STS", BIT(14), 0},
+ {"PTIO_D3_STS", BIT(16), 0},
+ {"PMT_D3_STS", BIT(17), 0},
+ {"SMS1_D3_STS", BIT(18), 0},
+ {"SMS2_D3_STS", BIT(19), 0},
+ {}
+};
+
+static const struct pmc_bit_map ptl_pcdp_d3_status_3_map[] = {
+ {"THC0_D3_STS", BIT(14), 1},
+ {"THC1_D3_STS", BIT(15), 1},
+ {"OSSE_SMT3_D3_STS", BIT(18), 0},
+ {"ACE_D3_STS", BIT(23), 0},
+ {}
+};
+
+static const struct pmc_bit_map ptl_pcdp_vnn_req_status_0_map[] = {
+ {"LPSS_VNN_REQ_STS", BIT(3), 1},
+ {"OSSE_VNN_REQ_STS", BIT(6), 1},
+ {"ESPISPI_VNN_REQ_STS", BIT(18), 1},
+ {"OSSE_SMT1_VNN_REQ_STS", BIT(30), 1},
+ {}
+};
+
+static const struct pmc_bit_map ptl_pcdp_vnn_req_status_1_map[] = {
+ {"NPK_VNN_REQ_STS", BIT(4), 1},
+ {"DFXAGG_VNN_REQ_STS", BIT(8), 0},
+ {"EXI_VNN_REQ_STS", BIT(9), 1},
+ {"P2D_VNN_REQ_STS", BIT(18), 1},
+ {"GBE_VNN_REQ_STS", BIT(19), 1},
+ {"SMB_VNN_REQ_STS", BIT(25), 1},
+ {"LPC_VNN_REQ_STS", BIT(26), 0},
+ {"ESE_VNN_REQ_STS", BIT(30), 1},
+ {}
+};
+
+static const struct pmc_bit_map ptl_pcdp_vnn_req_status_2_map[] = {
+ {"CSMERTC_VNN_REQ_STS", BIT(1), 1},
+ {"CSE_VNN_REQ_STS", BIT(4), 1},
+ {"ISH_VNN_REQ_STS", BIT(7), 1},
+ {"SMT1_VNN_REQ_STS", BIT(8), 1},
+ {"CLINK_VNN_REQ_STS", BIT(14), 1},
+ {"SMS1_VNN_REQ_STS", BIT(18), 1},
+ {"SMS2_VNN_REQ_STS", BIT(19), 1},
+ {"GPIOCOM4_VNN_REQ_STS", BIT(20), 1},
+ {"GPIOCOM3_VNN_REQ_STS", BIT(21), 1},
+ {"GPIOCOM1_VNN_REQ_STS", BIT(23), 1},
+ {"GPIOCOM0_VNN_REQ_STS", BIT(24), 1},
+ {"DISP_SHIM_VNN_REQ_STS", BIT(26), 1},
+ {}
+};
+
+static const struct pmc_bit_map ptl_pcdp_vnn_req_status_3_map[] = {
+ {"DTS0_VNN_REQ_STS", BIT(7), 0},
+ {"GPIOCOM5_VNN_REQ_STS", BIT(11), 1},
+ {}
+};
+
+static const struct pmc_bit_map ptl_pcdp_vnn_misc_status_map[] = {
+ {"CPU_C10_REQ_STS", BIT(0), 0},
+ {"TS_OFF_REQ_STS", BIT(1), 0},
+ {"PNDE_MET_REQ_STS", BIT(2), 1},
+ {"PG5_PMA0_REQ_STS", BIT(3), 0},
+ {"FW_THROTTLE_ALLOWED_REQ_STS", BIT(4), 0},
+ {"VNN_SOC_REQ_STS", BIT(6), 1},
+ {"ISH_VNNAON_REQ_STS", BIT(7), 0},
+ {"D2D_NOC_CFI_QACTIVE_REQ_STS", BIT(8), 1},
+ {"D2D_NOC_GPSB_QACTIVE_REQ_STS", BIT(9), 1},
+ {"D2D_IPU_QACTIVE_REQ_STS", BIT(10), 1},
+ {"PLT_GREATER_REQ_STS", BIT(11), 1},
+ {"ALL_SBR_IDLE_REQ_STS", BIT(12), 0},
+ {"PMC_IDLE_FB_OCP_REQ_STS", BIT(13), 0},
+ {"PM_SYNC_STATES_REQ_STS", BIT(14), 0},
+ {"EA_REQ_STS", BIT(15), 0},
+ {"MPHY_CORE_OFF_REQ_STS", BIT(16), 0},
+ {"BRK_EV_EN_REQ_STS", BIT(17), 0},
+ {"AUTO_DEMO_EN_REQ_STS", BIT(18), 0},
+ {"ITSS_CLK_SRC_REQ_STS", BIT(19), 1},
+ {"ARC_IDLE_REQ_STS", BIT(21), 0},
+ {"PG5_PMA1_REQ_STS", BIT(22), 0},
+ {"FIA_DEEP_PM_REQ_STS", BIT(23), 0},
+ {"XDCI_ATTACHED_REQ_STS", BIT(24), 1},
+ {"ARC_INTERRUPT_WAKE_REQ_STS", BIT(25), 0},
+ {"D2D_DISP_DDI_QACTIVE_REQ_STS", BIT(26), 1},
+ {"PRE_WAKE0_REQ_STS", BIT(27), 1},
+ {"PRE_WAKE1_REQ_STS", BIT(28), 1},
+ {"PRE_WAKE2_REQ_STS", BIT(29), 1},
+ {"D2D_DISP_EDP_QACTIVE_REQ_STS", BIT(31), 1},
+ {}
+};
+
+static const struct pmc_bit_map ptl_pcdp_signal_status_map[] = {
+ {"LSX_Wake0_STS", BIT(0), 0},
+ {"LSX_Wake1_STS", BIT(1), 0},
+ {"LSX_Wake2_STS", BIT(2), 0},
+ {"LSX_Wake3_STS", BIT(3), 0},
+ {"LSX_Wake4_STS", BIT(4), 0},
+ {"LSX_Wake5_STS", BIT(5), 0},
+ {"LSX_Wake6_STS", BIT(6), 0},
+ {"LSX_Wake7_STS", BIT(7), 0},
+ {"LPSS_Wake0_STS", BIT(8), 1},
+ {"LPSS_Wake1_STS", BIT(9), 1},
+ {"Int_Timer_SS_Wake0_STS", BIT(10), 1},
+ {"Int_Timer_SS_Wake1_STS", BIT(11), 1},
+ {"Int_Timer_SS_Wake2_STS", BIT(12), 1},
+ {"Int_Timer_SS_Wake3_STS", BIT(13), 1},
+ {"Int_Timer_SS_Wake4_STS", BIT(14), 1},
+ {"Int_Timer_SS_Wake5_STS", BIT(15), 1},
+ {}
+};
+
+static const struct pmc_bit_map ptl_pcdp_rsc_status_map[] = {
+ {"Memory", 0, 1},
+ {"PSF0", 0, 1},
+ {"PSF4", 0, 1},
+ {"PSF5", 0, 1},
+ {"PSF6", 0, 1},
+ {"PSF8", 0, 1},
+ {"SAF_CFI_LINK", 0, 1},
+ {"SB", 0, 1},
+ {}
+};
+
+static const struct pmc_bit_map *ptl_pcdp_lpm_maps[] = {
+ ptl_pcdp_clocksource_status_map,
+ ptl_pcdp_power_gating_status_0_map,
+ ptl_pcdp_power_gating_status_1_map,
+ ptl_pcdp_power_gating_status_2_map,
+ ptl_pcdp_d3_status_0_map,
+ ptl_pcdp_d3_status_1_map,
+ ptl_pcdp_d3_status_2_map,
+ ptl_pcdp_d3_status_3_map,
+ ptl_pcdp_vnn_req_status_0_map,
+ ptl_pcdp_vnn_req_status_1_map,
+ ptl_pcdp_vnn_req_status_2_map,
+ ptl_pcdp_vnn_req_status_3_map,
+ ptl_pcdp_vnn_misc_status_map,
+ ptl_pcdp_signal_status_map,
+ NULL
+};
+
+static const struct pmc_bit_map *ptl_pcdp_blk_maps[] = {
+ ptl_pcdp_power_gating_status_0_map,
+ ptl_pcdp_power_gating_status_1_map,
+ ptl_pcdp_power_gating_status_2_map,
+ ptl_pcdp_rsc_status_map,
+ ptl_pcdp_vnn_req_status_0_map,
+ ptl_pcdp_vnn_req_status_1_map,
+ ptl_pcdp_vnn_req_status_2_map,
+ ptl_pcdp_vnn_req_status_3_map,
+ ptl_pcdp_d3_status_0_map,
+ ptl_pcdp_d3_status_1_map,
+ ptl_pcdp_d3_status_2_map,
+ ptl_pcdp_d3_status_3_map,
+ ptl_pcdp_clocksource_status_map,
+ ptl_pcdp_vnn_misc_status_map,
+ ptl_pcdp_signal_status_map,
+ NULL
+};
+
+static const struct pmc_reg_map ptl_pcdp_reg_map = {
+ .pfear_sts = ext_ptl_pcdp_pfear_map,
+ .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET,
+ .slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP,
+ .ltr_show_sts = ptl_pcdp_ltr_show_map,
+ .msr_sts = msr_map,
+ .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET,
+ .regmap_length = PTL_PCD_PMC_MMIO_REG_LEN,
+ .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A,
+ .ppfear_buckets = LNL_PPFEAR_NUM_ENTRIES,
+ .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET,
+ .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT,
+ .lpm_num_maps = PTL_LPM_NUM_MAPS,
+ .ltr_ignore_max = LNL_NUM_IP_IGN_ALLOWED,
+ .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2,
+ .etr3_offset = ETR3_OFFSET,
+ .lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET,
+ .lpm_priority_offset = MTL_LPM_PRI_OFFSET,
+ .lpm_en_offset = MTL_LPM_EN_OFFSET,
+ .lpm_residency_offset = MTL_LPM_RESIDENCY_OFFSET,
+ .lpm_sts = ptl_pcdp_lpm_maps,
+ .lpm_status_offset = MTL_LPM_STATUS_OFFSET,
+ .lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET,
+ .s0ix_blocker_maps = ptl_pcdp_blk_maps,
+ .s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET,
+};
+
+#define PTL_NPU_PCI_DEV 0xb03e
+#define PTL_IPU_PCI_DEV 0xb05d
+
+/*
+ * Set power state of select devices that do not have drivers to D3
+ * so that they do not block Package C entry.
+ */
+static void ptl_d3_fixup(void)
+{
+ pmc_core_set_device_d3(PTL_IPU_PCI_DEV);
+ pmc_core_set_device_d3(PTL_NPU_PCI_DEV);
+}
+
+static int ptl_resume(struct pmc_dev *pmcdev)
+{
+ ptl_d3_fixup();
+ return cnl_resume(pmcdev);
+}
+
+static int ptl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
+{
+ ptl_d3_fixup();
+ return generic_core_init(pmcdev, pmc_dev_info);
+}
+
+struct pmc_dev_info ptl_pmc_dev = {
+ .map = &ptl_pcdp_reg_map,
+ .suspend = cnl_suspend,
+ .resume = ptl_resume,
+ .init = ptl_core_init,
+};
diff --git a/drivers/platform/x86/intel/pmc/spt.c b/drivers/platform/x86/intel/pmc/spt.c
index ab993a69e33e..b50534aa2cba 100644
--- a/drivers/platform/x86/intel/pmc/spt.c
+++ b/drivers/platform/x86/intel/pmc/spt.c
@@ -8,9 +8,11 @@
*
*/
+#include <linux/pci.h>
+
#include "core.h"
-const struct pmc_bit_map spt_pll_map[] = {
+static const struct pmc_bit_map spt_pll_map[] = {
{"MIPI PLL", SPT_PMC_BIT_MPHY_CMN_LANE0},
{"GEN2 USB2PCIE2 PLL", SPT_PMC_BIT_MPHY_CMN_LANE1},
{"DMIPCIE3 PLL", SPT_PMC_BIT_MPHY_CMN_LANE2},
@@ -18,7 +20,7 @@ const struct pmc_bit_map spt_pll_map[] = {
{}
};
-const struct pmc_bit_map spt_mphy_map[] = {
+static const struct pmc_bit_map spt_mphy_map[] = {
{"MPHY CORE LANE 0", SPT_PMC_BIT_MPHY_LANE0},
{"MPHY CORE LANE 1", SPT_PMC_BIT_MPHY_LANE1},
{"MPHY CORE LANE 2", SPT_PMC_BIT_MPHY_LANE2},
@@ -38,7 +40,7 @@ const struct pmc_bit_map spt_mphy_map[] = {
{}
};
-const struct pmc_bit_map spt_pfear_map[] = {
+static const struct pmc_bit_map spt_pfear_map[] = {
{"PMC", SPT_PMC_BIT_PMC},
{"OPI-DMI", SPT_PMC_BIT_OPI},
{"SPI / eSPI", SPT_PMC_BIT_SPI},
@@ -82,7 +84,7 @@ const struct pmc_bit_map spt_pfear_map[] = {
{}
};
-const struct pmc_bit_map *ext_spt_pfear_map[] = {
+static const struct pmc_bit_map *ext_spt_pfear_map[] = {
/*
* Check intel_pmc_core_ids[] users of spt_reg_map for
* a list of core SoCs using this.
@@ -91,7 +93,7 @@ const struct pmc_bit_map *ext_spt_pfear_map[] = {
NULL
};
-const struct pmc_bit_map spt_ltr_show_map[] = {
+static const struct pmc_bit_map spt_ltr_show_map[] = {
{"SOUTHPORT_A", SPT_PMC_LTR_SPA},
{"SOUTHPORT_B", SPT_PMC_LTR_SPB},
{"SATA", SPT_PMC_LTR_SATA},
@@ -116,7 +118,7 @@ const struct pmc_bit_map spt_ltr_show_map[] = {
{}
};
-const struct pmc_reg_map spt_reg_map = {
+static const struct pmc_reg_map spt_reg_map = {
.pfear_sts = ext_spt_pfear_map,
.mphy_sts = spt_mphy_map,
.pll_sts = spt_pll_map,
@@ -134,18 +136,25 @@ const struct pmc_reg_map spt_reg_map = {
.pm_vric1_offset = SPT_PMC_VRIC1_OFFSET,
};
-int spt_core_init(struct pmc_dev *pmcdev)
-{
- struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
- int ret;
-
- pmc->map = &spt_reg_map;
-
- ret = get_primary_reg_base(pmc);
- if (ret)
- return ret;
+static const struct pci_device_id spt_pmc_pci_id[] = {
+ { PCI_VDEVICE(INTEL, SPT_PMC_PCI_DEVICE_ID) },
+ { }
+};
- pmc_core_get_low_power_modes(pmcdev);
+static int spt_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
+{
+ /*
+ * Coffee Lake has CPU ID of Kaby Lake and Cannon Lake PCH. So here
+ * Sunrisepoint PCH regmap can't be used. Use Cannon Lake PCH regmap
+ * in this case.
+ */
+ if (!pci_dev_present(spt_pmc_pci_id))
+ return generic_core_init(pmcdev, &cnp_pmc_dev);
- return ret;
+ return generic_core_init(pmcdev, pmc_dev_info);
}
+
+struct pmc_dev_info spt_pmc_dev = {
+ .map = &spt_reg_map,
+ .init = spt_core_init,
+};
diff --git a/drivers/platform/x86/intel/pmc/tgl.c b/drivers/platform/x86/intel/pmc/tgl.c
index e0580de18077..02e731ed3391 100644
--- a/drivers/platform/x86/intel/pmc/tgl.c
+++ b/drivers/platform/x86/intel/pmc/tgl.c
@@ -18,7 +18,7 @@ enum pch_type {
PCH_LP
};
-const struct pmc_bit_map tgl_pfear_map[] = {
+static const struct pmc_bit_map tgl_pfear_map[] = {
{"PSF9", BIT(0)},
{"RES_66", BIT(1)},
{"RES_67", BIT(2)},
@@ -29,7 +29,7 @@ const struct pmc_bit_map tgl_pfear_map[] = {
{}
};
-const struct pmc_bit_map *ext_tgl_pfear_map[] = {
+static const struct pmc_bit_map *ext_tgl_pfear_map[] = {
/*
* Check intel_pmc_core_ids[] users of tgl_reg_map for
* a list of core SoCs using this.
@@ -39,7 +39,7 @@ const struct pmc_bit_map *ext_tgl_pfear_map[] = {
NULL
};
-const struct pmc_bit_map tgl_clocksource_status_map[] = {
+static const struct pmc_bit_map tgl_clocksource_status_map[] = {
{"USB2PLL_OFF_STS", BIT(18)},
{"PCIe/USB3.1_Gen2PLL_OFF_STS", BIT(19)},
{"PCIe_Gen3PLL_OFF_STS", BIT(20)},
@@ -55,7 +55,7 @@ const struct pmc_bit_map tgl_clocksource_status_map[] = {
{}
};
-const struct pmc_bit_map tgl_power_gating_status_map[] = {
+static const struct pmc_bit_map tgl_power_gating_status_map[] = {
{"CSME_PG_STS", BIT(0)},
{"SATA_PG_STS", BIT(1)},
{"xHCI_PG_STS", BIT(2)},
@@ -83,7 +83,7 @@ const struct pmc_bit_map tgl_power_gating_status_map[] = {
{}
};
-const struct pmc_bit_map tgl_d3_status_map[] = {
+static const struct pmc_bit_map tgl_d3_status_map[] = {
{"ADSP_D3_STS", BIT(0)},
{"SATA_D3_STS", BIT(1)},
{"xHCI0_D3_STS", BIT(2)},
@@ -98,7 +98,7 @@ const struct pmc_bit_map tgl_d3_status_map[] = {
{}
};
-const struct pmc_bit_map tgl_vnn_req_status_map[] = {
+static const struct pmc_bit_map tgl_vnn_req_status_map[] = {
{"GPIO_COM0_VNN_REQ_STS", BIT(1)},
{"GPIO_COM1_VNN_REQ_STS", BIT(2)},
{"GPIO_COM2_VNN_REQ_STS", BIT(3)},
@@ -123,7 +123,7 @@ const struct pmc_bit_map tgl_vnn_req_status_map[] = {
{}
};
-const struct pmc_bit_map tgl_vnn_misc_status_map[] = {
+static const struct pmc_bit_map tgl_vnn_misc_status_map[] = {
{"CPU_C10_REQ_STS_0", BIT(0)},
{"PCIe_LPM_En_REQ_STS_3", BIT(3)},
{"ITH_REQ_STS_5", BIT(5)},
@@ -175,7 +175,7 @@ const struct pmc_bit_map tgl_signal_status_map[] = {
{}
};
-const struct pmc_bit_map *tgl_lpm_maps[] = {
+static const struct pmc_bit_map *tgl_lpm_maps[] = {
tgl_clocksource_status_map,
tgl_power_gating_status_map,
tgl_d3_status_map,
@@ -185,7 +185,7 @@ const struct pmc_bit_map *tgl_lpm_maps[] = {
NULL
};
-const struct pmc_reg_map tgl_reg_map = {
+static const struct pmc_reg_map tgl_reg_map = {
.pfear_sts = ext_tgl_pfear_map,
.slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET,
.slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP,
@@ -210,7 +210,7 @@ const struct pmc_reg_map tgl_reg_map = {
.etr3_offset = ETR3_OFFSET,
};
-const struct pmc_reg_map tgl_h_reg_map = {
+static const struct pmc_reg_map tgl_h_reg_map = {
.pfear_sts = ext_tgl_pfear_map,
.slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET,
.slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP,
@@ -285,35 +285,28 @@ free_acpi_obj:
ACPI_FREE(out_obj);
}
-int tgl_l_core_init(struct pmc_dev *pmcdev)
+static int tgl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info)
{
- return tgl_core_generic_init(pmcdev, PCH_LP);
-}
-
-int tgl_core_init(struct pmc_dev *pmcdev)
-{
- return tgl_core_generic_init(pmcdev, PCH_H);
-}
-
-int tgl_core_generic_init(struct pmc_dev *pmcdev, int pch_tp)
-{
- struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
int ret;
- if (pch_tp == PCH_H)
- pmc->map = &tgl_h_reg_map;
- else
- pmc->map = &tgl_reg_map;
-
- pmcdev->suspend = cnl_suspend;
- pmcdev->resume = cnl_resume;
-
- ret = get_primary_reg_base(pmc);
+ ret = generic_core_init(pmcdev, pmc_dev_info);
if (ret)
return ret;
- pmc_core_get_low_power_modes(pmcdev);
pmc_core_get_tgl_lpm_reqs(pmcdev->pdev);
-
return 0;
}
+
+struct pmc_dev_info tgl_l_pmc_dev = {
+ .map = &tgl_reg_map,
+ .suspend = cnl_suspend,
+ .resume = cnl_resume,
+ .init = tgl_core_init,
+};
+
+struct pmc_dev_info tgl_pmc_dev = {
+ .map = &tgl_h_reg_map,
+ .suspend = cnl_suspend,
+ .resume = cnl_resume,
+ .init = tgl_core_init,
+};
diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c
index 8ed54b7a3333..7233b654bbad 100644
--- a/drivers/platform/x86/intel/pmt/class.c
+++ b/drivers/platform/x86/intel/pmt/class.c
@@ -81,7 +81,7 @@ EXPORT_SYMBOL_NS_GPL(pmt_telem_read_mmio, "INTEL_PMT");
*/
static ssize_t
intel_pmt_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *attr, char *buf, loff_t off,
+ const struct bin_attribute *attr, char *buf, loff_t off,
size_t count)
{
struct intel_pmt_entry *entry = container_of(attr,
@@ -308,7 +308,7 @@ static int intel_pmt_dev_register(struct intel_pmt_entry *entry,
entry->pmt_bin_attr.attr.name = ns->name;
entry->pmt_bin_attr.attr.mode = 0440;
entry->pmt_bin_attr.mmap = intel_pmt_mmap;
- entry->pmt_bin_attr.read = intel_pmt_read;
+ entry->pmt_bin_attr.read_new = intel_pmt_read;
entry->pmt_bin_attr.size = entry->size;
ret = sysfs_create_bin_file(&dev->kobj, &entry->pmt_bin_attr);
diff --git a/drivers/platform/x86/intel/punit_ipc.c b/drivers/platform/x86/intel/punit_ipc.c
index cd0ba84cc8e4..bafac8aa2baf 100644
--- a/drivers/platform/x86/intel/punit_ipc.c
+++ b/drivers/platform/x86/intel/punit_ipc.c
@@ -131,39 +131,6 @@ static int intel_punit_ipc_check_status(IPC_DEV *ipcdev, IPC_TYPE type)
}
/**
- * intel_punit_ipc_simple_command() - Simple IPC command
- * @cmd: IPC command code.
- * @para1: First 8bit parameter, set 0 if not used.
- * @para2: Second 8bit parameter, set 0 if not used.
- *
- * Send a IPC command to P-Unit when there is no data transaction
- *
- * Return: IPC error code or 0 on success.
- */
-int intel_punit_ipc_simple_command(int cmd, int para1, int para2)
-{
- IPC_DEV *ipcdev = punit_ipcdev;
- IPC_TYPE type;
- u32 val;
- int ret;
-
- mutex_lock(&ipcdev->lock);
-
- reinit_completion(&ipcdev->cmd_complete);
- type = (cmd & IPC_PUNIT_CMD_TYPE_MASK) >> IPC_TYPE_OFFSET;
-
- val = cmd & ~IPC_PUNIT_CMD_TYPE_MASK;
- val |= CMD_RUN | para2 << CMD_PARA2_SHIFT | para1 << CMD_PARA1_SHIFT;
- ipc_write_cmd(ipcdev, type, val);
- ret = intel_punit_ipc_check_status(ipcdev, type);
-
- mutex_unlock(&ipcdev->lock);
-
- return ret;
-}
-EXPORT_SYMBOL(intel_punit_ipc_simple_command);
-
-/**
* intel_punit_ipc_command() - IPC command with data and pointers
* @cmd: IPC command code.
* @para1: First 8bit parameter, set 0 if not used.
diff --git a/drivers/platform/x86/intel/sdsi.c b/drivers/platform/x86/intel/sdsi.c
index 33f33b1070fd..30d1c2caf984 100644
--- a/drivers/platform/x86/intel/sdsi.c
+++ b/drivers/platform/x86/intel/sdsi.c
@@ -398,8 +398,8 @@ free_payload:
}
static ssize_t provision_akc_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *attr, char *buf, loff_t off,
- size_t count)
+ const struct bin_attribute *attr, char *buf,
+ loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
struct sdsi_priv *priv = dev_get_drvdata(dev);
@@ -409,11 +409,11 @@ static ssize_t provision_akc_write(struct file *filp, struct kobject *kobj,
return sdsi_provision(priv, buf, count, SDSI_CMD_PROVISION_AKC);
}
-static BIN_ATTR_WO(provision_akc, SDSI_SIZE_WRITE_MSG);
+static const BIN_ATTR_WO(provision_akc, SDSI_SIZE_WRITE_MSG);
static ssize_t provision_cap_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *attr, char *buf, loff_t off,
- size_t count)
+ const struct bin_attribute *attr, char *buf,
+ loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
struct sdsi_priv *priv = dev_get_drvdata(dev);
@@ -423,7 +423,7 @@ static ssize_t provision_cap_write(struct file *filp, struct kobject *kobj,
return sdsi_provision(priv, buf, count, SDSI_CMD_PROVISION_CAP);
}
-static BIN_ATTR_WO(provision_cap, SDSI_SIZE_WRITE_MSG);
+static const BIN_ATTR_WO(provision_cap, SDSI_SIZE_WRITE_MSG);
static ssize_t
certificate_read(u64 command, u64 control_flags, struct sdsi_priv *priv,
@@ -469,7 +469,7 @@ free_buffer:
static ssize_t
state_certificate_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *attr, char *buf, loff_t off,
+ const struct bin_attribute *attr, char *buf, loff_t off,
size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@@ -477,11 +477,11 @@ state_certificate_read(struct file *filp, struct kobject *kobj,
return certificate_read(SDSI_CMD_READ_STATE, 0, priv, buf, off, count);
}
-static BIN_ATTR_ADMIN_RO(state_certificate, SDSI_SIZE_READ_MSG);
+static const BIN_ATTR_ADMIN_RO(state_certificate, SDSI_SIZE_READ_MSG);
static ssize_t
meter_certificate_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *attr, char *buf, loff_t off,
+ const struct bin_attribute *attr, char *buf, loff_t off,
size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@@ -489,11 +489,11 @@ meter_certificate_read(struct file *filp, struct kobject *kobj,
return certificate_read(SDSI_CMD_READ_METER, 0, priv, buf, off, count);
}
-static BIN_ATTR_ADMIN_RO(meter_certificate, SDSI_SIZE_READ_MSG);
+static const BIN_ATTR_ADMIN_RO(meter_certificate, SDSI_SIZE_READ_MSG);
static ssize_t
meter_current_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *attr, char *buf, loff_t off,
+ const struct bin_attribute *attr, char *buf, loff_t off,
size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@@ -502,11 +502,11 @@ meter_current_read(struct file *filp, struct kobject *kobj,
return certificate_read(SDSI_CMD_READ_METER, CTRL_METER_ENABLE_DRAM,
priv, buf, off, count);
}
-static BIN_ATTR_ADMIN_RO(meter_current, SDSI_SIZE_READ_MSG);
+static const BIN_ATTR_ADMIN_RO(meter_current, SDSI_SIZE_READ_MSG);
static ssize_t registers_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *attr, char *buf, loff_t off,
- size_t count)
+ const struct bin_attribute *attr, char *buf,
+ loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
struct sdsi_priv *priv = dev_get_drvdata(dev);
@@ -528,9 +528,9 @@ static ssize_t registers_read(struct file *filp, struct kobject *kobj,
return count;
}
-static BIN_ATTR_ADMIN_RO(registers, SDSI_SIZE_REGS);
+static const BIN_ATTR_ADMIN_RO(registers, SDSI_SIZE_REGS);
-static struct bin_attribute *sdsi_bin_attrs[] = {
+static const struct bin_attribute *const sdsi_bin_attrs[] = {
&bin_attr_registers,
&bin_attr_state_certificate,
&bin_attr_meter_certificate,
@@ -576,7 +576,7 @@ static struct attribute *sdsi_attrs[] = {
static const struct attribute_group sdsi_group = {
.attrs = sdsi_attrs,
- .bin_attrs = sdsi_bin_attrs,
+ .bin_attrs_new = sdsi_bin_attrs,
.is_bin_visible = sdsi_battr_is_visible,
};
__ATTRIBUTE_GROUPS(sdsi);
diff --git a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c
index dbcd3087aaa4..31239a93dd71 100644
--- a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c
+++ b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c
@@ -84,7 +84,7 @@ static DECLARE_HASHTABLE(isst_hash, 8);
static DEFINE_MUTEX(isst_hash_lock);
static int isst_store_new_cmd(int cmd, u32 cpu, int mbox_cmd_type, u32 param,
- u32 data)
+ u64 data)
{
struct isst_cmd *sst_cmd;
diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c
index 4aa6c227ec82..b67bf85532ae 100644
--- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c
+++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c
@@ -467,10 +467,13 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_
/* Get the package ID from the TPMI core */
plat_info = tpmi_get_platform_data(auxdev);
- if (plat_info)
- pkg = plat_info->package_id;
- else
+ if (unlikely(!plat_info)) {
dev_info(&auxdev->dev, "Platform information is NULL\n");
+ ret = -ENODEV;
+ goto err_rem_common;
+ }
+
+ pkg = plat_info->package_id;
for (i = 0; i < num_resources; ++i) {
struct tpmi_uncore_power_domain_info *pd_info;
diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c
index 40bbf8e45fa4..bdee5d00f30b 100644
--- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c
+++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c
@@ -146,15 +146,13 @@ static int uncore_event_cpu_online(unsigned int cpu)
{
struct uncore_data *data;
int target;
+ int ret;
/* Check if there is an online cpu in the package for uncore MSR */
target = cpumask_any_and(&uncore_cpu_mask, topology_die_cpumask(cpu));
if (target < nr_cpu_ids)
return 0;
- /* Use this CPU on this die as a control CPU */
- cpumask_set_cpu(cpu, &uncore_cpu_mask);
-
data = uncore_get_instance(cpu);
if (!data)
return 0;
@@ -163,7 +161,14 @@ static int uncore_event_cpu_online(unsigned int cpu)
data->die_id = topology_die_id(cpu);
data->domain_id = UNCORE_DOMAIN_ID_INVALID;
- return uncore_freq_add_entry(data, cpu);
+ ret = uncore_freq_add_entry(data, cpu);
+ if (ret)
+ return ret;
+
+ /* Use this CPU on this die as a control CPU */
+ cpumask_set_cpu(cpu, &uncore_cpu_mask);
+
+ return 0;
}
static int uncore_event_cpu_offline(unsigned int cpu)
diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c
index 8272f1dd0fbc..db3c031d1757 100644
--- a/drivers/platform/x86/intel/vsec.c
+++ b/drivers/platform/x86/intel/vsec.c
@@ -404,6 +404,11 @@ static const struct intel_vsec_platform_info oobmsm_info = {
.caps = VSEC_CAP_TELEMETRY | VSEC_CAP_SDSI | VSEC_CAP_TPMI,
};
+/* DMR OOBMSM info */
+static const struct intel_vsec_platform_info dmr_oobmsm_info = {
+ .caps = VSEC_CAP_TELEMETRY | VSEC_CAP_TPMI,
+};
+
/* TGL info */
static const struct intel_vsec_platform_info tgl_info = {
.caps = VSEC_CAP_TELEMETRY,
@@ -420,6 +425,7 @@ static const struct intel_vsec_platform_info lnl_info = {
#define PCI_DEVICE_ID_INTEL_VSEC_MTL_M 0x7d0d
#define PCI_DEVICE_ID_INTEL_VSEC_MTL_S 0xad0d
#define PCI_DEVICE_ID_INTEL_VSEC_OOBMSM 0x09a7
+#define PCI_DEVICE_ID_INTEL_VSEC_OOBMSM_DMR 0x09a1
#define PCI_DEVICE_ID_INTEL_VSEC_RPL 0xa77d
#define PCI_DEVICE_ID_INTEL_VSEC_TGL 0x9a0d
#define PCI_DEVICE_ID_INTEL_VSEC_LNL_M 0x647d
@@ -430,6 +436,7 @@ static const struct pci_device_id intel_vsec_pci_ids[] = {
{ PCI_DEVICE_DATA(INTEL, VSEC_MTL_M, &mtl_info) },
{ PCI_DEVICE_DATA(INTEL, VSEC_MTL_S, &mtl_info) },
{ PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM, &oobmsm_info) },
+ { PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM_DMR, &dmr_oobmsm_info) },
{ PCI_DEVICE_DATA(INTEL, VSEC_RPL, &tgl_info) },
{ PCI_DEVICE_DATA(INTEL, VSEC_TGL, &tgl_info) },
{ PCI_DEVICE_DATA(INTEL, VSEC_LNL_M, &lnl_info) },
diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c
index 79a7b68c7373..5d717b1c23cf 100644
--- a/drivers/platform/x86/intel_ips.c
+++ b/drivers/platform/x86/intel_ips.c
@@ -1108,7 +1108,7 @@ static int ips_monitor(void *data)
last_sample_period = 1;
} while (!kthread_should_stop());
- del_timer_sync(&ips->timer);
+ timer_delete_sync(&ips->timer);
dev_dbg(ips->dev, "ips-monitor thread stopped\n");
diff --git a/drivers/platform/x86/lenovo-wmi-camera.c b/drivers/platform/x86/lenovo-wmi-camera.c
index 0c0bedaf7407..eb60fb9a5b3f 100644
--- a/drivers/platform/x86/lenovo-wmi-camera.c
+++ b/drivers/platform/x86/lenovo-wmi-camera.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/wmi.h>
+#include <linux/cleanup.h>
#define WMI_LENOVO_CAMERABUTTON_EVENT_GUID "50C76F1F-D8E4-D895-0A3D-62F4EA400013"
@@ -26,10 +27,38 @@ enum {
SW_CAMERA_ON = 1,
};
+static int camera_shutter_input_setup(struct wmi_device *wdev, u8 camera_mode)
+{
+ struct lenovo_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
+ int err;
+
+ priv->idev = input_allocate_device();
+ if (!priv->idev)
+ return -ENOMEM;
+
+ priv->idev->name = "Lenovo WMI Camera Button";
+ priv->idev->phys = "wmi/input0";
+ priv->idev->id.bustype = BUS_HOST;
+ priv->idev->dev.parent = &wdev->dev;
+
+ input_set_capability(priv->idev, EV_SW, SW_CAMERA_LENS_COVER);
+
+ input_report_switch(priv->idev, SW_CAMERA_LENS_COVER,
+ camera_mode == SW_CAMERA_ON ? 0 : 1);
+ input_sync(priv->idev);
+
+ err = input_register_device(priv->idev);
+ if (err) {
+ input_free_device(priv->idev);
+ priv->idev = NULL;
+ }
+
+ return err;
+}
+
static void lenovo_wmi_notify(struct wmi_device *wdev, union acpi_object *obj)
{
struct lenovo_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
- unsigned int keycode;
u8 camera_mode;
if (obj->type != ACPI_TYPE_BUFFER) {
@@ -53,22 +82,24 @@ static void lenovo_wmi_notify(struct wmi_device *wdev, union acpi_object *obj)
return;
}
- mutex_lock(&priv->notify_lock);
+ guard(mutex)(&priv->notify_lock);
- keycode = camera_mode == SW_CAMERA_ON ?
- KEY_CAMERA_ACCESS_ENABLE : KEY_CAMERA_ACCESS_DISABLE;
- input_report_key(priv->idev, keycode, 1);
- input_sync(priv->idev);
- input_report_key(priv->idev, keycode, 0);
- input_sync(priv->idev);
+ if (!priv->idev) {
+ if (camera_shutter_input_setup(wdev, camera_mode))
+ dev_warn(&wdev->dev, "Failed to register input device\n");
+ return;
+ }
- mutex_unlock(&priv->notify_lock);
+ if (camera_mode == SW_CAMERA_ON)
+ input_report_switch(priv->idev, SW_CAMERA_LENS_COVER, 0);
+ else
+ input_report_switch(priv->idev, SW_CAMERA_LENS_COVER, 1);
+ input_sync(priv->idev);
}
static int lenovo_wmi_probe(struct wmi_device *wdev, const void *context)
{
struct lenovo_wmi_priv *priv;
- int ret;
priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -76,21 +107,6 @@ static int lenovo_wmi_probe(struct wmi_device *wdev, const void *context)
dev_set_drvdata(&wdev->dev, priv);
- priv->idev = devm_input_allocate_device(&wdev->dev);
- if (!priv->idev)
- return -ENOMEM;
-
- priv->idev->name = "Lenovo WMI Camera Button";
- priv->idev->phys = "wmi/input0";
- priv->idev->id.bustype = BUS_HOST;
- priv->idev->dev.parent = &wdev->dev;
- input_set_capability(priv->idev, EV_KEY, KEY_CAMERA_ACCESS_ENABLE);
- input_set_capability(priv->idev, EV_KEY, KEY_CAMERA_ACCESS_DISABLE);
-
- ret = input_register_device(priv->idev);
- if (ret)
- return ret;
-
mutex_init(&priv->notify_lock);
return 0;
@@ -100,6 +116,9 @@ static void lenovo_wmi_remove(struct wmi_device *wdev)
{
struct lenovo_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
+ if (priv->idev)
+ input_unregister_device(priv->idev);
+
mutex_destroy(&priv->notify_lock);
}
diff --git a/drivers/platform/x86/lenovo-wmi-hotkey-utilities.c b/drivers/platform/x86/lenovo-wmi-hotkey-utilities.c
new file mode 100644
index 000000000000..89153afd7015
--- /dev/null
+++ b/drivers/platform/x86/lenovo-wmi-hotkey-utilities.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Lenovo Super Hotkey Utility WMI extras driver for Ideapad laptop
+ *
+ * Copyright (C) 2025 Lenovo
+ */
+
+#include <linux/cleanup.h>
+#include <linux/dev_printk.h>
+#include <linux/device.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/wmi.h>
+
+/* Lenovo Super Hotkey WMI GUIDs */
+#define LUD_WMI_METHOD_GUID "CE6C0974-0407-4F50-88BA-4FC3B6559AD8"
+
+/* Lenovo Utility Data WMI method_id */
+#define WMI_LUD_GET_SUPPORT 1
+#define WMI_LUD_SET_FEATURE 2
+
+#define WMI_LUD_GET_MICMUTE_LED_VER 20
+#define WMI_LUD_GET_AUDIOMUTE_LED_VER 26
+
+#define WMI_LUD_SUPPORT_MICMUTE_LED_VER 25
+#define WMI_LUD_SUPPORT_AUDIOMUTE_LED_VER 27
+
+/* Input parameters to mute/unmute audio LED and Mic LED */
+struct wmi_led_args {
+ u8 id;
+ u8 subid;
+ u16 value;
+};
+
+/* Values of input parameters to SetFeature of audio LED and Mic LED */
+enum hotkey_set_feature {
+ MIC_MUTE_LED_ON = 1,
+ MIC_MUTE_LED_OFF = 2,
+ AUDIO_MUTE_LED_ON = 4,
+ AUDIO_MUTE_LED_OFF = 5,
+};
+
+#define LSH_ACPI_LED_MAX 2
+
+struct lenovo_super_hotkey_wmi_private {
+ struct led_classdev cdev[LSH_ACPI_LED_MAX];
+ struct wmi_device *led_wdev;
+};
+
+enum mute_led_type {
+ MIC_MUTE,
+ AUDIO_MUTE,
+};
+
+static int lsh_wmi_mute_led_set(enum mute_led_type led_type, struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+
+{
+ struct lenovo_super_hotkey_wmi_private *wpriv = container_of(led_cdev,
+ struct lenovo_super_hotkey_wmi_private, cdev[led_type]);
+ struct wmi_led_args led_arg = {0, 0, 0};
+ struct acpi_buffer input;
+ acpi_status status;
+
+ switch (led_type) {
+ case MIC_MUTE:
+ led_arg.id = brightness == LED_ON ? MIC_MUTE_LED_ON : MIC_MUTE_LED_OFF;
+ break;
+ case AUDIO_MUTE:
+ led_arg.id = brightness == LED_ON ? AUDIO_MUTE_LED_ON : AUDIO_MUTE_LED_OFF;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ input.length = sizeof(led_arg);
+ input.pointer = &led_arg;
+ status = wmidev_evaluate_method(wpriv->led_wdev, 0, WMI_LUD_SET_FEATURE, &input, NULL);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ return 0;
+}
+
+static int lsh_wmi_audiomute_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+
+{
+ return lsh_wmi_mute_led_set(AUDIO_MUTE, led_cdev, brightness);
+}
+
+static int lsh_wmi_micmute_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ return lsh_wmi_mute_led_set(MIC_MUTE, led_cdev, brightness);
+}
+
+static int lenovo_super_hotkey_wmi_led_init(enum mute_led_type led_type, struct device *dev)
+{
+ struct lenovo_super_hotkey_wmi_private *wpriv = dev_get_drvdata(dev);
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_buffer input;
+ int led_version, err = 0;
+ unsigned int wmiarg;
+ acpi_status status;
+
+ switch (led_type) {
+ case MIC_MUTE:
+ wmiarg = WMI_LUD_GET_MICMUTE_LED_VER;
+ break;
+ case AUDIO_MUTE:
+ wmiarg = WMI_LUD_GET_AUDIOMUTE_LED_VER;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ input.length = sizeof(wmiarg);
+ input.pointer = &wmiarg;
+ status = wmidev_evaluate_method(wpriv->led_wdev, 0, WMI_LUD_GET_SUPPORT, &input, &output);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ union acpi_object *obj __free(kfree) = output.pointer;
+ if (obj && obj->type == ACPI_TYPE_INTEGER)
+ led_version = obj->integer.value;
+ else
+ return -EIO;
+
+ wpriv->cdev[led_type].max_brightness = LED_ON;
+ wpriv->cdev[led_type].flags = LED_CORE_SUSPENDRESUME;
+
+ switch (led_type) {
+ case MIC_MUTE:
+ if (led_version != WMI_LUD_SUPPORT_MICMUTE_LED_VER)
+ return -EIO;
+
+ wpriv->cdev[led_type].name = "platform::micmute";
+ wpriv->cdev[led_type].brightness_set_blocking = &lsh_wmi_micmute_led_set;
+ wpriv->cdev[led_type].default_trigger = "audio-micmute";
+ break;
+ case AUDIO_MUTE:
+ if (led_version != WMI_LUD_SUPPORT_AUDIOMUTE_LED_VER)
+ return -EIO;
+
+ wpriv->cdev[led_type].name = "platform::mute";
+ wpriv->cdev[led_type].brightness_set_blocking = &lsh_wmi_audiomute_led_set;
+ wpriv->cdev[led_type].default_trigger = "audio-mute";
+ break;
+ default:
+ dev_err(dev, "Unknown LED type %d\n", led_type);
+ return -EINVAL;
+ }
+
+ err = devm_led_classdev_register(dev, &wpriv->cdev[led_type]);
+ if (err < 0) {
+ dev_err(dev, "Could not register mute LED %d : %d\n", led_type, err);
+ return err;
+ }
+ return 0;
+}
+
+static int lenovo_super_hotkey_wmi_leds_setup(struct device *dev)
+{
+ int err;
+
+ err = lenovo_super_hotkey_wmi_led_init(MIC_MUTE, dev);
+ if (err)
+ return err;
+
+ err = lenovo_super_hotkey_wmi_led_init(AUDIO_MUTE, dev);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int lenovo_super_hotkey_wmi_probe(struct wmi_device *wdev, const void *context)
+{
+ struct lenovo_super_hotkey_wmi_private *wpriv;
+
+ wpriv = devm_kzalloc(&wdev->dev, sizeof(*wpriv), GFP_KERNEL);
+ if (!wpriv)
+ return -ENOMEM;
+
+ dev_set_drvdata(&wdev->dev, wpriv);
+ wpriv->led_wdev = wdev;
+ return lenovo_super_hotkey_wmi_leds_setup(&wdev->dev);
+}
+
+static const struct wmi_device_id lenovo_super_hotkey_wmi_id_table[] = {
+ { LUD_WMI_METHOD_GUID, NULL }, /* Utility data */
+ { }
+};
+
+MODULE_DEVICE_TABLE(wmi, lenovo_super_hotkey_wmi_id_table);
+
+static struct wmi_driver lenovo_wmi_hotkey_utilities_driver = {
+ .driver = {
+ .name = "lenovo_wmi_hotkey_utilities",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS
+ },
+ .id_table = lenovo_super_hotkey_wmi_id_table,
+ .probe = lenovo_super_hotkey_wmi_probe,
+ .no_singleton = true,
+};
+
+module_wmi_driver(lenovo_wmi_hotkey_utilities_driver);
+
+MODULE_AUTHOR("Jackie Dong <dongeg1@lenovo.com>");
+MODULE_DESCRIPTION("Lenovo Super Hotkey Utility WMI extras driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c b/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c
index a96b215cd2c5..25933cd018d1 100644
--- a/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c
+++ b/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c
@@ -219,7 +219,7 @@ static int yt2_1380_fc_serdev_probe(struct serdev_device *serdev)
return 0;
}
-struct serdev_device_driver yt2_1380_fc_serdev_driver = {
+static struct serdev_device_driver yt2_1380_fc_serdev_driver = {
.probe = yt2_1380_fc_serdev_probe,
.driver = {
.name = KBUILD_MODNAME,
diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c
index e5391a37014d..c4b150fa093f 100644
--- a/drivers/platform/x86/msi-laptop.c
+++ b/drivers/platform/x86/msi-laptop.c
@@ -806,8 +806,8 @@ static void msi_send_touchpad_key(struct work_struct *ignored)
}
static DECLARE_DELAYED_WORK(msi_touchpad_dwork, msi_send_touchpad_key);
-static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
- struct serio *port)
+static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, struct serio *port,
+ void *context)
{
static bool extended;
@@ -996,7 +996,7 @@ static int __init load_scm_model_init(struct platform_device *sdev)
if (result)
goto fail_input;
- result = i8042_install_filter(msi_laptop_i8042_filter);
+ result = i8042_install_filter(msi_laptop_i8042_filter, NULL);
if (result) {
pr_err("Unable to install key filter\n");
goto fail_filter;
diff --git a/drivers/platform/x86/msi-wmi-platform.c b/drivers/platform/x86/msi-wmi-platform.c
index 9b5c7f8c79b0..dc5e9878cb68 100644
--- a/drivers/platform/x86/msi-wmi-platform.c
+++ b/drivers/platform/x86/msi-wmi-platform.c
@@ -10,6 +10,7 @@
#include <linux/acpi.h>
#include <linux/bits.h>
#include <linux/bitfield.h>
+#include <linux/cleanup.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/device/driver.h>
@@ -17,6 +18,7 @@
#include <linux/hwmon.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/printk.h>
#include <linux/rwsem.h>
#include <linux/types.h>
@@ -76,8 +78,13 @@ enum msi_wmi_platform_method {
MSI_PLATFORM_GET_WMI = 0x1d,
};
-struct msi_wmi_platform_debugfs_data {
+struct msi_wmi_platform_data {
struct wmi_device *wdev;
+ struct mutex wmi_lock; /* Necessary when calling WMI methods */
+};
+
+struct msi_wmi_platform_debugfs_data {
+ struct msi_wmi_platform_data *data;
enum msi_wmi_platform_method method;
struct rw_semaphore buffer_lock; /* Protects debugfs buffer */
size_t length;
@@ -132,8 +139,9 @@ static int msi_wmi_platform_parse_buffer(union acpi_object *obj, u8 *output, siz
return 0;
}
-static int msi_wmi_platform_query(struct wmi_device *wdev, enum msi_wmi_platform_method method,
- u8 *input, size_t input_length, u8 *output, size_t output_length)
+static int msi_wmi_platform_query(struct msi_wmi_platform_data *data,
+ enum msi_wmi_platform_method method, u8 *input,
+ size_t input_length, u8 *output, size_t output_length)
{
struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
struct acpi_buffer in = {
@@ -147,9 +155,15 @@ static int msi_wmi_platform_query(struct wmi_device *wdev, enum msi_wmi_platform
if (!input_length || !output_length)
return -EINVAL;
- status = wmidev_evaluate_method(wdev, 0x0, method, &in, &out);
- if (ACPI_FAILURE(status))
- return -EIO;
+ /*
+ * The ACPI control method responsible for handling the WMI method calls
+ * is not thread-safe. Because of this we have to do the locking ourself.
+ */
+ scoped_guard(mutex, &data->wmi_lock) {
+ status = wmidev_evaluate_method(data->wdev, 0x0, method, &in, &out);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+ }
obj = out.pointer;
if (!obj)
@@ -170,22 +184,22 @@ static umode_t msi_wmi_platform_is_visible(const void *drvdata, enum hwmon_senso
static int msi_wmi_platform_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
int channel, long *val)
{
- struct wmi_device *wdev = dev_get_drvdata(dev);
+ struct msi_wmi_platform_data *data = dev_get_drvdata(dev);
u8 input[32] = { 0 };
u8 output[32];
- u16 data;
+ u16 value;
int ret;
- ret = msi_wmi_platform_query(wdev, MSI_PLATFORM_GET_FAN, input, sizeof(input), output,
+ ret = msi_wmi_platform_query(data, MSI_PLATFORM_GET_FAN, input, sizeof(input), output,
sizeof(output));
if (ret < 0)
return ret;
- data = get_unaligned_be16(&output[channel * 2 + 1]);
- if (!data)
+ value = get_unaligned_be16(&output[channel * 2 + 1]);
+ if (!value)
*val = 0;
else
- *val = 480000 / data;
+ *val = 480000 / value;
return 0;
}
@@ -231,7 +245,7 @@ static ssize_t msi_wmi_platform_write(struct file *fp, const char __user *input,
return ret;
down_write(&data->buffer_lock);
- ret = msi_wmi_platform_query(data->wdev, data->method, payload, data->length, data->buffer,
+ ret = msi_wmi_platform_query(data->data, data->method, payload, data->length, data->buffer,
data->length);
up_write(&data->buffer_lock);
@@ -277,17 +291,17 @@ static void msi_wmi_platform_debugfs_remove(void *data)
debugfs_remove_recursive(dir);
}
-static void msi_wmi_platform_debugfs_add(struct wmi_device *wdev, struct dentry *dir,
+static void msi_wmi_platform_debugfs_add(struct msi_wmi_platform_data *drvdata, struct dentry *dir,
const char *name, enum msi_wmi_platform_method method)
{
struct msi_wmi_platform_debugfs_data *data;
struct dentry *entry;
- data = devm_kzalloc(&wdev->dev, sizeof(*data), GFP_KERNEL);
+ data = devm_kzalloc(&drvdata->wdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return;
- data->wdev = wdev;
+ data->data = drvdata;
data->method = method;
init_rwsem(&data->buffer_lock);
@@ -298,82 +312,82 @@ static void msi_wmi_platform_debugfs_add(struct wmi_device *wdev, struct dentry
entry = debugfs_create_file(name, 0600, dir, data, &msi_wmi_platform_debugfs_fops);
if (IS_ERR(entry))
- devm_kfree(&wdev->dev, data);
+ devm_kfree(&drvdata->wdev->dev, data);
}
-static void msi_wmi_platform_debugfs_init(struct wmi_device *wdev)
+static void msi_wmi_platform_debugfs_init(struct msi_wmi_platform_data *data)
{
struct dentry *dir;
char dir_name[64];
int ret, method;
- scnprintf(dir_name, ARRAY_SIZE(dir_name), "%s-%s", DRIVER_NAME, dev_name(&wdev->dev));
+ scnprintf(dir_name, ARRAY_SIZE(dir_name), "%s-%s", DRIVER_NAME, dev_name(&data->wdev->dev));
dir = debugfs_create_dir(dir_name, NULL);
if (IS_ERR(dir))
return;
- ret = devm_add_action_or_reset(&wdev->dev, msi_wmi_platform_debugfs_remove, dir);
+ ret = devm_add_action_or_reset(&data->wdev->dev, msi_wmi_platform_debugfs_remove, dir);
if (ret < 0)
return;
for (method = MSI_PLATFORM_GET_PACKAGE; method <= MSI_PLATFORM_GET_WMI; method++)
- msi_wmi_platform_debugfs_add(wdev, dir, msi_wmi_platform_debugfs_names[method - 1],
+ msi_wmi_platform_debugfs_add(data, dir, msi_wmi_platform_debugfs_names[method - 1],
method);
}
-static int msi_wmi_platform_hwmon_init(struct wmi_device *wdev)
+static int msi_wmi_platform_hwmon_init(struct msi_wmi_platform_data *data)
{
struct device *hdev;
- hdev = devm_hwmon_device_register_with_info(&wdev->dev, "msi_wmi_platform", wdev,
+ hdev = devm_hwmon_device_register_with_info(&data->wdev->dev, "msi_wmi_platform", data,
&msi_wmi_platform_chip_info, NULL);
return PTR_ERR_OR_ZERO(hdev);
}
-static int msi_wmi_platform_ec_init(struct wmi_device *wdev)
+static int msi_wmi_platform_ec_init(struct msi_wmi_platform_data *data)
{
u8 input[32] = { 0 };
u8 output[32];
u8 flags;
int ret;
- ret = msi_wmi_platform_query(wdev, MSI_PLATFORM_GET_EC, input, sizeof(input), output,
+ ret = msi_wmi_platform_query(data, MSI_PLATFORM_GET_EC, input, sizeof(input), output,
sizeof(output));
if (ret < 0)
return ret;
flags = output[MSI_PLATFORM_EC_FLAGS_OFFSET];
- dev_dbg(&wdev->dev, "EC RAM version %lu.%lu\n",
+ dev_dbg(&data->wdev->dev, "EC RAM version %lu.%lu\n",
FIELD_GET(MSI_PLATFORM_EC_MAJOR_MASK, flags),
FIELD_GET(MSI_PLATFORM_EC_MINOR_MASK, flags));
- dev_dbg(&wdev->dev, "EC firmware version %.28s\n",
+ dev_dbg(&data->wdev->dev, "EC firmware version %.28s\n",
&output[MSI_PLATFORM_EC_VERSION_OFFSET]);
if (!(flags & MSI_PLATFORM_EC_IS_TIGERLAKE)) {
if (!force)
return -ENODEV;
- dev_warn(&wdev->dev, "Loading on a non-Tigerlake platform\n");
+ dev_warn(&data->wdev->dev, "Loading on a non-Tigerlake platform\n");
}
return 0;
}
-static int msi_wmi_platform_init(struct wmi_device *wdev)
+static int msi_wmi_platform_init(struct msi_wmi_platform_data *data)
{
u8 input[32] = { 0 };
u8 output[32];
int ret;
- ret = msi_wmi_platform_query(wdev, MSI_PLATFORM_GET_WMI, input, sizeof(input), output,
+ ret = msi_wmi_platform_query(data, MSI_PLATFORM_GET_WMI, input, sizeof(input), output,
sizeof(output));
if (ret < 0)
return ret;
- dev_dbg(&wdev->dev, "WMI interface version %u.%u\n",
+ dev_dbg(&data->wdev->dev, "WMI interface version %u.%u\n",
output[MSI_PLATFORM_WMI_MAJOR_OFFSET],
output[MSI_PLATFORM_WMI_MINOR_OFFSET]);
@@ -381,7 +395,8 @@ static int msi_wmi_platform_init(struct wmi_device *wdev)
if (!force)
return -ENODEV;
- dev_warn(&wdev->dev, "Loading despite unsupported WMI interface version (%u.%u)\n",
+ dev_warn(&data->wdev->dev,
+ "Loading despite unsupported WMI interface version (%u.%u)\n",
output[MSI_PLATFORM_WMI_MAJOR_OFFSET],
output[MSI_PLATFORM_WMI_MINOR_OFFSET]);
}
@@ -391,19 +406,31 @@ static int msi_wmi_platform_init(struct wmi_device *wdev)
static int msi_wmi_platform_probe(struct wmi_device *wdev, const void *context)
{
+ struct msi_wmi_platform_data *data;
int ret;
- ret = msi_wmi_platform_init(wdev);
+ data = devm_kzalloc(&wdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->wdev = wdev;
+ dev_set_drvdata(&wdev->dev, data);
+
+ ret = devm_mutex_init(&wdev->dev, &data->wmi_lock);
+ if (ret < 0)
+ return ret;
+
+ ret = msi_wmi_platform_init(data);
if (ret < 0)
return ret;
- ret = msi_wmi_platform_ec_init(wdev);
+ ret = msi_wmi_platform_ec_init(data);
if (ret < 0)
return ret;
- msi_wmi_platform_debugfs_init(wdev);
+ msi_wmi_platform_debugfs_init(data);
- return msi_wmi_platform_hwmon_init(wdev);
+ return msi_wmi_platform_hwmon_init(data);
}
static const struct wmi_device_id msi_wmi_platform_id_table[] = {
diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c
index 22ca70eb8227..2987b4db6009 100644
--- a/drivers/platform/x86/panasonic-laptop.c
+++ b/drivers/platform/x86/panasonic-laptop.c
@@ -260,7 +260,7 @@ struct pcc_acpi {
* keypress events over the PS/2 kbd interface, filter these out.
*/
static bool panasonic_i8042_filter(unsigned char data, unsigned char str,
- struct serio *port)
+ struct serio *port, void *context)
{
static bool extended;
@@ -1100,7 +1100,7 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
pcc->platform = NULL;
}
- i8042_install_filter(panasonic_i8042_filter);
+ i8042_install_filter(panasonic_i8042_filter, NULL);
return 0;
out_platform:
diff --git a/drivers/platform/x86/quickstart.c b/drivers/platform/x86/quickstart.c
index 8d540a1c8602..c332c7cdaff5 100644
--- a/drivers/platform/x86/quickstart.c
+++ b/drivers/platform/x86/quickstart.c
@@ -20,7 +20,6 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
-#include <linux/pm_wakeup.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
diff --git a/drivers/platform/x86/samsung-galaxybook.c b/drivers/platform/x86/samsung-galaxybook.c
new file mode 100644
index 000000000000..5878a351993e
--- /dev/null
+++ b/drivers/platform/x86/samsung-galaxybook.c
@@ -0,0 +1,1425 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Samsung Galaxy Book driver
+ *
+ * Copyright (c) 2025 Joshua Grisham <josh@joshuagrisham.com>
+ *
+ * With contributions to the SCAI ACPI device interface:
+ * Copyright (c) 2024 Giulio Girardi <giulio.girardi@protechgroup.it>
+ *
+ * Implementation inspired by existing x86 platform drivers.
+ * Thank you to the authors!
+ */
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/err.h>
+#include <linux/i8042.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/platform_profile.h>
+#include <linux/serio.h>
+#include <linux/sysfs.h>
+#include <linux/uuid.h>
+#include <linux/workqueue.h>
+#include <acpi/battery.h>
+#include "firmware_attributes_class.h"
+
+#define DRIVER_NAME "samsung-galaxybook"
+
+struct samsung_galaxybook {
+ struct platform_device *platform;
+ struct acpi_device *acpi;
+
+ struct device *fw_attrs_dev;
+ struct kset *fw_attrs_kset;
+ /* block in case firmware attributes are updated in multiple threads */
+ struct mutex fw_attr_lock;
+
+ bool has_kbd_backlight;
+ bool has_block_recording;
+ bool has_performance_mode;
+
+ struct led_classdev kbd_backlight;
+ struct work_struct kbd_backlight_hotkey_work;
+ /* block in case brightness updated using hotkey and another thread */
+ struct mutex kbd_backlight_lock;
+
+ void *i8042_filter_ptr;
+
+ struct work_struct block_recording_hotkey_work;
+ struct input_dev *camera_lens_cover_switch;
+
+ struct acpi_battery_hook battery_hook;
+
+ u8 profile_performance_modes[PLATFORM_PROFILE_LAST];
+};
+
+enum galaxybook_fw_attr_id {
+ GB_ATTR_POWER_ON_LID_OPEN,
+ GB_ATTR_USB_CHARGING,
+ GB_ATTR_BLOCK_RECORDING,
+};
+
+static const char * const galaxybook_fw_attr_name[] = {
+ [GB_ATTR_POWER_ON_LID_OPEN] = "power_on_lid_open",
+ [GB_ATTR_USB_CHARGING] = "usb_charging",
+ [GB_ATTR_BLOCK_RECORDING] = "block_recording",
+};
+
+static const char * const galaxybook_fw_attr_desc[] = {
+ [GB_ATTR_POWER_ON_LID_OPEN] = "Power On Lid Open",
+ [GB_ATTR_USB_CHARGING] = "USB Charging",
+ [GB_ATTR_BLOCK_RECORDING] = "Block Recording",
+};
+
+#define GB_ATTR_LANGUAGE_CODE "en_US.UTF-8"
+
+struct galaxybook_fw_attr {
+ struct samsung_galaxybook *galaxybook;
+ enum galaxybook_fw_attr_id fw_attr_id;
+ struct attribute_group attr_group;
+ struct kobj_attribute display_name;
+ struct kobj_attribute current_value;
+ int (*get_value)(struct samsung_galaxybook *galaxybook, bool *value);
+ int (*set_value)(struct samsung_galaxybook *galaxybook, const bool value);
+};
+
+struct sawb {
+ u16 safn;
+ u16 sasb;
+ u8 rflg;
+ union {
+ struct {
+ u8 gunm;
+ u8 guds[250];
+ } __packed;
+ struct {
+ u8 caid[16];
+ u8 fncn;
+ u8 subn;
+ u8 iob0;
+ u8 iob1;
+ u8 iob2;
+ u8 iob3;
+ u8 iob4;
+ u8 iob5;
+ u8 iob6;
+ u8 iob7;
+ u8 iob8;
+ u8 iob9;
+ } __packed;
+ struct {
+ u8 iob_prefix[18];
+ u8 iobs[10];
+ } __packed;
+ } __packed;
+} __packed;
+
+#define GB_SAWB_LEN_SETTINGS 0x15
+#define GB_SAWB_LEN_PERFORMANCE_MODE 0x100
+
+#define GB_SAFN 0x5843
+
+#define GB_SASB_KBD_BACKLIGHT 0x78
+#define GB_SASB_POWER_MANAGEMENT 0x7a
+#define GB_SASB_USB_CHARGING_GET 0x67
+#define GB_SASB_USB_CHARGING_SET 0x68
+#define GB_SASB_NOTIFICATIONS 0x86
+#define GB_SASB_BLOCK_RECORDING 0x8a
+#define GB_SASB_PERFORMANCE_MODE 0x91
+
+#define GB_SAWB_RFLG_POS 4
+#define GB_SAWB_GB_GUNM_POS 5
+
+#define GB_RFLG_SUCCESS 0xaa
+#define GB_GUNM_FAIL 0xff
+
+#define GB_GUNM_FEATURE_ENABLE 0xbb
+#define GB_GUNM_FEATURE_ENABLE_SUCCESS 0xdd
+#define GB_GUDS_FEATURE_ENABLE 0xaa
+#define GB_GUDS_FEATURE_ENABLE_SUCCESS 0xcc
+
+#define GB_GUNM_GET 0x81
+#define GB_GUNM_SET 0x82
+
+#define GB_GUNM_POWER_MANAGEMENT 0x82
+
+#define GB_GUNM_USB_CHARGING_GET 0x80
+#define GB_GUNM_USB_CHARGING_ON 0x81
+#define GB_GUNM_USB_CHARGING_OFF 0x80
+#define GB_GUDS_POWER_ON_LID_OPEN 0xa3
+#define GB_GUDS_POWER_ON_LID_OPEN_GET 0x81
+#define GB_GUDS_POWER_ON_LID_OPEN_SET 0x80
+#define GB_GUDS_BATTERY_CHARGE_CONTROL 0xe9
+#define GB_GUDS_BATTERY_CHARGE_CONTROL_GET 0x91
+#define GB_GUDS_BATTERY_CHARGE_CONTROL_SET 0x90
+#define GB_GUNM_ACPI_NOTIFY_ENABLE 0x80
+#define GB_GUDS_ACPI_NOTIFY_ENABLE 0x02
+
+#define GB_BLOCK_RECORDING_ON 0x0
+#define GB_BLOCK_RECORDING_OFF 0x1
+
+#define GB_FNCN_PERFORMANCE_MODE 0x51
+#define GB_SUBN_PERFORMANCE_MODE_LIST 0x01
+#define GB_SUBN_PERFORMANCE_MODE_GET 0x02
+#define GB_SUBN_PERFORMANCE_MODE_SET 0x03
+
+/* guid 8246028d-8bca-4a55-ba0f-6f1e6b921b8f */
+static const guid_t performance_mode_guid =
+ GUID_INIT(0x8246028d, 0x8bca, 0x4a55, 0xba, 0x0f, 0x6f, 0x1e, 0x6b, 0x92, 0x1b, 0x8f);
+#define GB_PERFORMANCE_MODE_GUID performance_mode_guid
+
+#define GB_PERFORMANCE_MODE_FANOFF 0xb
+#define GB_PERFORMANCE_MODE_LOWNOISE 0xa
+#define GB_PERFORMANCE_MODE_OPTIMIZED 0x0
+#define GB_PERFORMANCE_MODE_OPTIMIZED_V2 0x2
+#define GB_PERFORMANCE_MODE_PERFORMANCE 0x1
+#define GB_PERFORMANCE_MODE_PERFORMANCE_V2 0x15
+#define GB_PERFORMANCE_MODE_ULTRA 0x16
+#define GB_PERFORMANCE_MODE_IGNORE1 0x14
+#define GB_PERFORMANCE_MODE_IGNORE2 0xc
+
+#define GB_ACPI_METHOD_ENABLE "SDLS"
+#define GB_ACPI_METHOD_ENABLE_ON 1
+#define GB_ACPI_METHOD_ENABLE_OFF 0
+#define GB_ACPI_METHOD_SETTINGS "CSFI"
+#define GB_ACPI_METHOD_PERFORMANCE_MODE "CSXI"
+
+#define GB_KBD_BACKLIGHT_MAX_BRIGHTNESS 3
+
+#define GB_ACPI_NOTIFY_BATTERY_STATE_CHANGED 0x61
+#define GB_ACPI_NOTIFY_DEVICE_ON_TABLE 0x6c
+#define GB_ACPI_NOTIFY_DEVICE_OFF_TABLE 0x6d
+#define GB_ACPI_NOTIFY_HOTKEY_PERFORMANCE_MODE 0x70
+
+#define GB_KEY_KBD_BACKLIGHT_KEYDOWN 0x2c
+#define GB_KEY_KBD_BACKLIGHT_KEYUP 0xac
+#define GB_KEY_BLOCK_RECORDING_KEYDOWN 0x1f
+#define GB_KEY_BLOCK_RECORDING_KEYUP 0x9f
+#define GB_KEY_BATTERY_NOTIFY_KEYUP 0xf
+#define GB_KEY_BATTERY_NOTIFY_KEYDOWN 0x8f
+
+/*
+ * Optional features which have been determined as not supported on a particular
+ * device will return GB_NOT_SUPPORTED from their init function. Positive
+ * EOPNOTSUPP is used as the underlying value instead of negative to
+ * differentiate this return code from valid upstream failures.
+ */
+#define GB_NOT_SUPPORTED EOPNOTSUPP /* Galaxy Book feature not supported */
+
+/*
+ * ACPI method handling
+ */
+
+static int galaxybook_acpi_method(struct samsung_galaxybook *galaxybook, acpi_string method,
+ struct sawb *buf, size_t len)
+{
+ struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
+ union acpi_object in_obj, *out_obj;
+ struct acpi_object_list input;
+ acpi_status status;
+ int err;
+
+ in_obj.type = ACPI_TYPE_BUFFER;
+ in_obj.buffer.length = len;
+ in_obj.buffer.pointer = (u8 *)buf;
+
+ input.count = 1;
+ input.pointer = &in_obj;
+
+ status = acpi_evaluate_object_typed(galaxybook->acpi->handle, method, &input, &output,
+ ACPI_TYPE_BUFFER);
+
+ if (ACPI_FAILURE(status)) {
+ dev_err(&galaxybook->acpi->dev, "failed to execute method %s; got %s\n",
+ method, acpi_format_exception(status));
+ return -EIO;
+ }
+
+ out_obj = output.pointer;
+
+ if (out_obj->buffer.length != len || out_obj->buffer.length < GB_SAWB_GB_GUNM_POS + 1) {
+ dev_err(&galaxybook->acpi->dev,
+ "failed to execute %s; response length mismatch\n",
+ method);
+ err = -EPROTO;
+ goto out_free;
+ }
+ if (out_obj->buffer.pointer[GB_SAWB_RFLG_POS] != GB_RFLG_SUCCESS) {
+ dev_err(&galaxybook->acpi->dev,
+ "failed to execute %s; device did not respond with success code 0x%x\n",
+ method, GB_RFLG_SUCCESS);
+ err = -ENXIO;
+ goto out_free;
+ }
+ if (out_obj->buffer.pointer[GB_SAWB_GB_GUNM_POS] == GB_GUNM_FAIL) {
+ dev_err(&galaxybook->acpi->dev,
+ "failed to execute %s; device responded with failure code 0x%x\n",
+ method, GB_GUNM_FAIL);
+ err = -ENXIO;
+ goto out_free;
+ }
+
+ memcpy(buf, out_obj->buffer.pointer, len);
+ err = 0;
+
+out_free:
+ kfree(out_obj);
+ return err;
+}
+
+static int galaxybook_enable_acpi_feature(struct samsung_galaxybook *galaxybook, const u16 sasb)
+{
+ struct sawb buf = {};
+ int err;
+
+ buf.safn = GB_SAFN;
+ buf.sasb = sasb;
+ buf.gunm = GB_GUNM_FEATURE_ENABLE;
+ buf.guds[0] = GB_GUDS_FEATURE_ENABLE;
+
+ err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
+ &buf, GB_SAWB_LEN_SETTINGS);
+ if (err)
+ return err;
+
+ if (buf.gunm != GB_GUNM_FEATURE_ENABLE_SUCCESS &&
+ buf.guds[0] != GB_GUDS_FEATURE_ENABLE_SUCCESS)
+ return -ENODEV;
+
+ return 0;
+}
+
+/*
+ * Keyboard Backlight
+ */
+
+static int kbd_backlight_acpi_get(struct samsung_galaxybook *galaxybook,
+ enum led_brightness *brightness)
+{
+ struct sawb buf = {};
+ int err;
+
+ buf.safn = GB_SAFN;
+ buf.sasb = GB_SASB_KBD_BACKLIGHT;
+ buf.gunm = GB_GUNM_GET;
+
+ err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
+ &buf, GB_SAWB_LEN_SETTINGS);
+ if (err)
+ return err;
+
+ *brightness = buf.gunm;
+
+ return 0;
+}
+
+static int kbd_backlight_acpi_set(struct samsung_galaxybook *galaxybook,
+ const enum led_brightness brightness)
+{
+ struct sawb buf = {};
+
+ buf.safn = GB_SAFN;
+ buf.sasb = GB_SASB_KBD_BACKLIGHT;
+ buf.gunm = GB_GUNM_SET;
+
+ buf.guds[0] = brightness;
+
+ return galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
+ &buf, GB_SAWB_LEN_SETTINGS);
+}
+
+static enum led_brightness kbd_backlight_show(struct led_classdev *led)
+{
+ struct samsung_galaxybook *galaxybook =
+ container_of(led, struct samsung_galaxybook, kbd_backlight);
+ enum led_brightness brightness;
+ int err;
+
+ err = kbd_backlight_acpi_get(galaxybook, &brightness);
+ if (err)
+ return err;
+
+ return brightness;
+}
+
+static int kbd_backlight_store(struct led_classdev *led,
+ const enum led_brightness brightness)
+{
+ struct samsung_galaxybook *galaxybook =
+ container_of_const(led, struct samsung_galaxybook, kbd_backlight);
+
+ return kbd_backlight_acpi_set(galaxybook, brightness);
+}
+
+static int galaxybook_kbd_backlight_init(struct samsung_galaxybook *galaxybook)
+{
+ struct led_init_data init_data = {};
+ enum led_brightness brightness;
+ int err;
+
+ err = devm_mutex_init(&galaxybook->platform->dev, &galaxybook->kbd_backlight_lock);
+ if (err)
+ return err;
+
+ err = galaxybook_enable_acpi_feature(galaxybook, GB_SASB_KBD_BACKLIGHT);
+ if (err) {
+ dev_dbg(&galaxybook->platform->dev,
+ "failed to enable kbd_backlight feature, error %d\n", err);
+ return GB_NOT_SUPPORTED;
+ }
+
+ err = kbd_backlight_acpi_get(galaxybook, &brightness);
+ if (err) {
+ dev_dbg(&galaxybook->platform->dev,
+ "failed to get initial kbd_backlight brightness, error %d\n", err);
+ return GB_NOT_SUPPORTED;
+ }
+
+ init_data.devicename = DRIVER_NAME;
+ init_data.default_label = ":" LED_FUNCTION_KBD_BACKLIGHT;
+ init_data.devname_mandatory = true;
+
+ galaxybook->kbd_backlight.brightness_get = kbd_backlight_show;
+ galaxybook->kbd_backlight.brightness_set_blocking = kbd_backlight_store;
+ galaxybook->kbd_backlight.flags = LED_BRIGHT_HW_CHANGED;
+ galaxybook->kbd_backlight.max_brightness = GB_KBD_BACKLIGHT_MAX_BRIGHTNESS;
+
+ return devm_led_classdev_register_ext(&galaxybook->platform->dev,
+ &galaxybook->kbd_backlight, &init_data);
+}
+
+/*
+ * Battery Extension (adds charge_control_end_threshold to the battery device)
+ */
+
+static int charge_control_end_threshold_acpi_get(struct samsung_galaxybook *galaxybook, u8 *value)
+{
+ struct sawb buf = {};
+ int err;
+
+ buf.safn = GB_SAFN;
+ buf.sasb = GB_SASB_POWER_MANAGEMENT;
+ buf.gunm = GB_GUNM_POWER_MANAGEMENT;
+ buf.guds[0] = GB_GUDS_BATTERY_CHARGE_CONTROL;
+ buf.guds[1] = GB_GUDS_BATTERY_CHARGE_CONTROL_GET;
+
+ err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
+ &buf, GB_SAWB_LEN_SETTINGS);
+ if (err)
+ return err;
+
+ *value = buf.guds[1];
+
+ return 0;
+}
+
+static int charge_control_end_threshold_acpi_set(struct samsung_galaxybook *galaxybook, u8 value)
+{
+ struct sawb buf = {};
+
+ buf.safn = GB_SAFN;
+ buf.sasb = GB_SASB_POWER_MANAGEMENT;
+ buf.gunm = GB_GUNM_POWER_MANAGEMENT;
+ buf.guds[0] = GB_GUDS_BATTERY_CHARGE_CONTROL;
+ buf.guds[1] = GB_GUDS_BATTERY_CHARGE_CONTROL_SET;
+ buf.guds[2] = value;
+
+ return galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
+ &buf, GB_SAWB_LEN_SETTINGS);
+}
+
+static int galaxybook_battery_ext_property_get(struct power_supply *psy,
+ const struct power_supply_ext *ext,
+ void *ext_data,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct samsung_galaxybook *galaxybook = ext_data;
+ int err;
+
+ if (psp != POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD)
+ return -EINVAL;
+
+ err = charge_control_end_threshold_acpi_get(galaxybook, (u8 *)&val->intval);
+ if (err)
+ return err;
+
+ /*
+ * device stores "no end threshold" as 0 instead of 100;
+ * if device has 0, report 100
+ */
+ if (val->intval == 0)
+ val->intval = 100;
+
+ return 0;
+}
+
+static int galaxybook_battery_ext_property_set(struct power_supply *psy,
+ const struct power_supply_ext *ext,
+ void *ext_data,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct samsung_galaxybook *galaxybook = ext_data;
+ u8 value;
+
+ if (psp != POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD)
+ return -EINVAL;
+
+ value = val->intval;
+
+ if (value < 1 || value > 100)
+ return -EINVAL;
+
+ /*
+ * device stores "no end threshold" as 0 instead of 100;
+ * if setting to 100, send 0
+ */
+ if (value == 100)
+ value = 0;
+
+ return charge_control_end_threshold_acpi_set(galaxybook, value);
+}
+
+static int galaxybook_battery_ext_property_is_writeable(struct power_supply *psy,
+ const struct power_supply_ext *ext,
+ void *ext_data,
+ enum power_supply_property psp)
+{
+ if (psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD)
+ return true;
+
+ return false;
+}
+
+static const enum power_supply_property galaxybook_battery_properties[] = {
+ POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD,
+};
+
+static const struct power_supply_ext galaxybook_battery_ext = {
+ .name = DRIVER_NAME,
+ .properties = galaxybook_battery_properties,
+ .num_properties = ARRAY_SIZE(galaxybook_battery_properties),
+ .get_property = galaxybook_battery_ext_property_get,
+ .set_property = galaxybook_battery_ext_property_set,
+ .property_is_writeable = galaxybook_battery_ext_property_is_writeable,
+};
+
+static int galaxybook_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook)
+{
+ struct samsung_galaxybook *galaxybook =
+ container_of(hook, struct samsung_galaxybook, battery_hook);
+
+ return power_supply_register_extension(battery, &galaxybook_battery_ext,
+ &battery->dev, galaxybook);
+}
+
+static int galaxybook_battery_remove(struct power_supply *battery, struct acpi_battery_hook *hook)
+{
+ power_supply_unregister_extension(battery, &galaxybook_battery_ext);
+ return 0;
+}
+
+static int galaxybook_battery_threshold_init(struct samsung_galaxybook *galaxybook)
+{
+ u8 value;
+ int err;
+
+ err = charge_control_end_threshold_acpi_get(galaxybook, &value);
+ if (err) {
+ dev_dbg(&galaxybook->platform->dev,
+ "failed to get initial battery charge end threshold, error %d\n", err);
+ return 0;
+ }
+
+ galaxybook->battery_hook.add_battery = galaxybook_battery_add;
+ galaxybook->battery_hook.remove_battery = galaxybook_battery_remove;
+ galaxybook->battery_hook.name = "Samsung Galaxy Book Battery Extension";
+
+ return devm_battery_hook_register(&galaxybook->platform->dev, &galaxybook->battery_hook);
+}
+
+/*
+ * Platform Profile / Performance mode
+ */
+
+static int performance_mode_acpi_get(struct samsung_galaxybook *galaxybook, u8 *performance_mode)
+{
+ struct sawb buf = {};
+ int err;
+
+ buf.safn = GB_SAFN;
+ buf.sasb = GB_SASB_PERFORMANCE_MODE;
+ export_guid(buf.caid, &GB_PERFORMANCE_MODE_GUID);
+ buf.fncn = GB_FNCN_PERFORMANCE_MODE;
+ buf.subn = GB_SUBN_PERFORMANCE_MODE_GET;
+
+ err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_PERFORMANCE_MODE,
+ &buf, GB_SAWB_LEN_PERFORMANCE_MODE);
+ if (err)
+ return err;
+
+ *performance_mode = buf.iob0;
+
+ return 0;
+}
+
+static int performance_mode_acpi_set(struct samsung_galaxybook *galaxybook,
+ const u8 performance_mode)
+{
+ struct sawb buf = {};
+
+ buf.safn = GB_SAFN;
+ buf.sasb = GB_SASB_PERFORMANCE_MODE;
+ export_guid(buf.caid, &GB_PERFORMANCE_MODE_GUID);
+ buf.fncn = GB_FNCN_PERFORMANCE_MODE;
+ buf.subn = GB_SUBN_PERFORMANCE_MODE_SET;
+ buf.iob0 = performance_mode;
+
+ return galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_PERFORMANCE_MODE,
+ &buf, GB_SAWB_LEN_PERFORMANCE_MODE);
+}
+
+static int get_performance_mode_profile(struct samsung_galaxybook *galaxybook,
+ const u8 performance_mode,
+ enum platform_profile_option *profile)
+{
+ switch (performance_mode) {
+ case GB_PERFORMANCE_MODE_FANOFF:
+ *profile = PLATFORM_PROFILE_LOW_POWER;
+ break;
+ case GB_PERFORMANCE_MODE_LOWNOISE:
+ *profile = PLATFORM_PROFILE_QUIET;
+ break;
+ case GB_PERFORMANCE_MODE_OPTIMIZED:
+ case GB_PERFORMANCE_MODE_OPTIMIZED_V2:
+ *profile = PLATFORM_PROFILE_BALANCED;
+ break;
+ case GB_PERFORMANCE_MODE_PERFORMANCE:
+ case GB_PERFORMANCE_MODE_PERFORMANCE_V2:
+ case GB_PERFORMANCE_MODE_ULTRA:
+ *profile = PLATFORM_PROFILE_PERFORMANCE;
+ break;
+ case GB_PERFORMANCE_MODE_IGNORE1:
+ case GB_PERFORMANCE_MODE_IGNORE2:
+ return -EOPNOTSUPP;
+ default:
+ dev_warn(&galaxybook->platform->dev,
+ "unrecognized performance mode 0x%x\n", performance_mode);
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int galaxybook_platform_profile_get(struct device *dev,
+ enum platform_profile_option *profile)
+{
+ struct samsung_galaxybook *galaxybook = dev_get_drvdata(dev);
+ u8 performance_mode;
+ int err;
+
+ err = performance_mode_acpi_get(galaxybook, &performance_mode);
+ if (err)
+ return err;
+
+ return get_performance_mode_profile(galaxybook, performance_mode, profile);
+}
+
+static int galaxybook_platform_profile_set(struct device *dev,
+ enum platform_profile_option profile)
+{
+ struct samsung_galaxybook *galaxybook = dev_get_drvdata(dev);
+
+ return performance_mode_acpi_set(galaxybook,
+ galaxybook->profile_performance_modes[profile]);
+}
+
+static int galaxybook_platform_profile_probe(void *drvdata, unsigned long *choices)
+{
+ struct samsung_galaxybook *galaxybook = drvdata;
+ u8 *perfmodes = galaxybook->profile_performance_modes;
+ enum platform_profile_option profile;
+ struct sawb buf = {};
+ unsigned int i;
+ int err;
+
+ buf.safn = GB_SAFN;
+ buf.sasb = GB_SASB_PERFORMANCE_MODE;
+ export_guid(buf.caid, &GB_PERFORMANCE_MODE_GUID);
+ buf.fncn = GB_FNCN_PERFORMANCE_MODE;
+ buf.subn = GB_SUBN_PERFORMANCE_MODE_LIST;
+
+ err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_PERFORMANCE_MODE,
+ &buf, GB_SAWB_LEN_PERFORMANCE_MODE);
+ if (err) {
+ dev_dbg(&galaxybook->platform->dev,
+ "failed to get supported performance modes, error %d\n", err);
+ return err;
+ }
+
+ /* set initial default profile performance mode values */
+ perfmodes[PLATFORM_PROFILE_LOW_POWER] = GB_PERFORMANCE_MODE_FANOFF;
+ perfmodes[PLATFORM_PROFILE_QUIET] = GB_PERFORMANCE_MODE_LOWNOISE;
+ perfmodes[PLATFORM_PROFILE_BALANCED] = GB_PERFORMANCE_MODE_OPTIMIZED;
+ perfmodes[PLATFORM_PROFILE_PERFORMANCE] = GB_PERFORMANCE_MODE_PERFORMANCE;
+
+ /*
+ * Value returned in iob0 will have the number of supported performance
+ * modes per device. The performance mode values will then be given as a
+ * list after this (iob1-iobX). Loop through the supported values and
+ * enable their mapped platform_profile choice, overriding "legacy"
+ * values along the way if a non-legacy value exists.
+ */
+ for (i = 1; i <= buf.iob0; i++) {
+ err = get_performance_mode_profile(galaxybook, buf.iobs[i], &profile);
+ if (err) {
+ dev_dbg(&galaxybook->platform->dev,
+ "ignoring unmapped performance mode 0x%x\n", buf.iobs[i]);
+ continue;
+ }
+ switch (buf.iobs[i]) {
+ case GB_PERFORMANCE_MODE_OPTIMIZED_V2:
+ perfmodes[profile] = GB_PERFORMANCE_MODE_OPTIMIZED_V2;
+ break;
+ case GB_PERFORMANCE_MODE_PERFORMANCE_V2:
+ /* only update if not already overwritten by Ultra */
+ if (perfmodes[profile] != GB_PERFORMANCE_MODE_ULTRA)
+ perfmodes[profile] = GB_PERFORMANCE_MODE_PERFORMANCE_V2;
+ break;
+ case GB_PERFORMANCE_MODE_ULTRA:
+ perfmodes[profile] = GB_PERFORMANCE_MODE_ULTRA;
+ break;
+ default:
+ break;
+ }
+ set_bit(profile, choices);
+ dev_dbg(&galaxybook->platform->dev,
+ "setting platform profile %d to use performance mode 0x%x\n",
+ profile, perfmodes[profile]);
+ }
+
+ /* initialize performance_mode using balanced's mapped value */
+ if (test_bit(PLATFORM_PROFILE_BALANCED, choices))
+ return performance_mode_acpi_set(galaxybook, perfmodes[PLATFORM_PROFILE_BALANCED]);
+
+ return 0;
+}
+
+static const struct platform_profile_ops galaxybook_platform_profile_ops = {
+ .probe = galaxybook_platform_profile_probe,
+ .profile_get = galaxybook_platform_profile_get,
+ .profile_set = galaxybook_platform_profile_set,
+};
+
+static int galaxybook_platform_profile_init(struct samsung_galaxybook *galaxybook)
+{
+ struct device *platform_profile_dev;
+ u8 performance_mode;
+ int err;
+
+ err = performance_mode_acpi_get(galaxybook, &performance_mode);
+ if (err) {
+ dev_dbg(&galaxybook->platform->dev,
+ "failed to get initial performance mode, error %d\n", err);
+ return GB_NOT_SUPPORTED;
+ }
+
+ platform_profile_dev = devm_platform_profile_register(&galaxybook->platform->dev,
+ DRIVER_NAME, galaxybook,
+ &galaxybook_platform_profile_ops);
+
+ return PTR_ERR_OR_ZERO(platform_profile_dev);
+}
+
+/*
+ * Firmware Attributes
+ */
+
+/* Power on lid open (device should power on when lid is opened) */
+
+static int power_on_lid_open_acpi_get(struct samsung_galaxybook *galaxybook, bool *value)
+{
+ struct sawb buf = {};
+ int err;
+
+ buf.safn = GB_SAFN;
+ buf.sasb = GB_SASB_POWER_MANAGEMENT;
+ buf.gunm = GB_GUNM_POWER_MANAGEMENT;
+ buf.guds[0] = GB_GUDS_POWER_ON_LID_OPEN;
+ buf.guds[1] = GB_GUDS_POWER_ON_LID_OPEN_GET;
+
+ err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
+ &buf, GB_SAWB_LEN_SETTINGS);
+ if (err)
+ return err;
+
+ *value = buf.guds[1];
+
+ return 0;
+}
+
+static int power_on_lid_open_acpi_set(struct samsung_galaxybook *galaxybook, const bool value)
+{
+ struct sawb buf = {};
+
+ lockdep_assert_held(&galaxybook->fw_attr_lock);
+
+ buf.safn = GB_SAFN;
+ buf.sasb = GB_SASB_POWER_MANAGEMENT;
+ buf.gunm = GB_GUNM_POWER_MANAGEMENT;
+ buf.guds[0] = GB_GUDS_POWER_ON_LID_OPEN;
+ buf.guds[1] = GB_GUDS_POWER_ON_LID_OPEN_SET;
+ buf.guds[2] = value ? 1 : 0;
+
+ return galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
+ &buf, GB_SAWB_LEN_SETTINGS);
+}
+
+/* USB Charging (USB ports can provide power when device is powered off) */
+
+static int usb_charging_acpi_get(struct samsung_galaxybook *galaxybook, bool *value)
+{
+ struct sawb buf = {};
+ int err;
+
+ buf.safn = GB_SAFN;
+ buf.sasb = GB_SASB_USB_CHARGING_GET;
+ buf.gunm = GB_GUNM_USB_CHARGING_GET;
+
+ err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
+ &buf, GB_SAWB_LEN_SETTINGS);
+ if (err)
+ return err;
+
+ *value = buf.gunm == 1;
+
+ return 0;
+}
+
+static int usb_charging_acpi_set(struct samsung_galaxybook *galaxybook, const bool value)
+{
+ struct sawb buf = {};
+
+ lockdep_assert_held(&galaxybook->fw_attr_lock);
+
+ buf.safn = GB_SAFN;
+ buf.sasb = GB_SASB_USB_CHARGING_SET;
+ buf.gunm = value ? GB_GUNM_USB_CHARGING_ON : GB_GUNM_USB_CHARGING_OFF;
+
+ return galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
+ &buf, GB_SAWB_LEN_SETTINGS);
+}
+
+/* Block recording (blocks access to camera and microphone) */
+
+static int block_recording_acpi_get(struct samsung_galaxybook *galaxybook, bool *value)
+{
+ struct sawb buf = {};
+ int err;
+
+ buf.safn = GB_SAFN;
+ buf.sasb = GB_SASB_BLOCK_RECORDING;
+ buf.gunm = GB_GUNM_GET;
+
+ err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
+ &buf, GB_SAWB_LEN_SETTINGS);
+ if (err)
+ return err;
+
+ *value = buf.gunm == GB_BLOCK_RECORDING_ON;
+
+ return 0;
+}
+
+static int block_recording_acpi_set(struct samsung_galaxybook *galaxybook, const bool value)
+{
+ struct sawb buf = {};
+ int err;
+
+ lockdep_assert_held(&galaxybook->fw_attr_lock);
+
+ buf.safn = GB_SAFN;
+ buf.sasb = GB_SASB_BLOCK_RECORDING;
+ buf.gunm = GB_GUNM_SET;
+ buf.guds[0] = value ? GB_BLOCK_RECORDING_ON : GB_BLOCK_RECORDING_OFF;
+
+ err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
+ &buf, GB_SAWB_LEN_SETTINGS);
+ if (err)
+ return err;
+
+ input_report_switch(galaxybook->camera_lens_cover_switch,
+ SW_CAMERA_LENS_COVER, value ? 1 : 0);
+ input_sync(galaxybook->camera_lens_cover_switch);
+
+ return 0;
+}
+
+static int galaxybook_block_recording_init(struct samsung_galaxybook *galaxybook)
+{
+ bool value;
+ int err;
+
+ err = galaxybook_enable_acpi_feature(galaxybook, GB_SASB_BLOCK_RECORDING);
+ if (err) {
+ dev_dbg(&galaxybook->platform->dev,
+ "failed to initialize block_recording, error %d\n", err);
+ return GB_NOT_SUPPORTED;
+ }
+
+ guard(mutex)(&galaxybook->fw_attr_lock);
+
+ err = block_recording_acpi_get(galaxybook, &value);
+ if (err) {
+ dev_dbg(&galaxybook->platform->dev,
+ "failed to get initial block_recording state, error %d\n", err);
+ return GB_NOT_SUPPORTED;
+ }
+
+ galaxybook->camera_lens_cover_switch =
+ devm_input_allocate_device(&galaxybook->platform->dev);
+ if (!galaxybook->camera_lens_cover_switch)
+ return -ENOMEM;
+
+ galaxybook->camera_lens_cover_switch->name = "Samsung Galaxy Book Camera Lens Cover";
+ galaxybook->camera_lens_cover_switch->phys = DRIVER_NAME "/input0";
+ galaxybook->camera_lens_cover_switch->id.bustype = BUS_HOST;
+
+ input_set_capability(galaxybook->camera_lens_cover_switch, EV_SW, SW_CAMERA_LENS_COVER);
+
+ err = input_register_device(galaxybook->camera_lens_cover_switch);
+ if (err)
+ return err;
+
+ input_report_switch(galaxybook->camera_lens_cover_switch,
+ SW_CAMERA_LENS_COVER, value ? 1 : 0);
+ input_sync(galaxybook->camera_lens_cover_switch);
+
+ return 0;
+}
+
+/* Firmware Attributes setup */
+
+static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "enumeration\n");
+}
+
+static struct kobj_attribute fw_attr_type = __ATTR_RO(type);
+
+static ssize_t default_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "0\n");
+}
+
+static struct kobj_attribute fw_attr_default_value = __ATTR_RO(default_value);
+
+static ssize_t possible_values_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "0;1\n");
+}
+
+static struct kobj_attribute fw_attr_possible_values = __ATTR_RO(possible_values);
+
+static ssize_t display_name_language_code_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ return sysfs_emit(buf, "%s\n", GB_ATTR_LANGUAGE_CODE);
+}
+
+static struct kobj_attribute fw_attr_display_name_language_code =
+ __ATTR_RO(display_name_language_code);
+
+static ssize_t display_name_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ struct galaxybook_fw_attr *fw_attr =
+ container_of(attr, struct galaxybook_fw_attr, display_name);
+
+ return sysfs_emit(buf, "%s\n", galaxybook_fw_attr_desc[fw_attr->fw_attr_id]);
+}
+
+static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ struct galaxybook_fw_attr *fw_attr =
+ container_of(attr, struct galaxybook_fw_attr, current_value);
+ bool value;
+ int err;
+
+ err = fw_attr->get_value(fw_attr->galaxybook, &value);
+ if (err)
+ return err;
+
+ return sysfs_emit(buf, "%u\n", value);
+}
+
+static ssize_t current_value_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct galaxybook_fw_attr *fw_attr =
+ container_of(attr, struct galaxybook_fw_attr, current_value);
+ struct samsung_galaxybook *galaxybook = fw_attr->galaxybook;
+ bool value;
+ int err;
+
+ if (!count)
+ return -EINVAL;
+
+ err = kstrtobool(buf, &value);
+ if (err)
+ return err;
+
+ guard(mutex)(&galaxybook->fw_attr_lock);
+
+ err = fw_attr->set_value(galaxybook, value);
+ if (err)
+ return err;
+
+ return count;
+}
+
+#define NUM_FW_ATTR_ENUM_ATTRS 6
+
+static int galaxybook_fw_attr_init(struct samsung_galaxybook *galaxybook,
+ const enum galaxybook_fw_attr_id fw_attr_id,
+ int (*get_value)(struct samsung_galaxybook *galaxybook,
+ bool *value),
+ int (*set_value)(struct samsung_galaxybook *galaxybook,
+ const bool value))
+{
+ struct galaxybook_fw_attr *fw_attr;
+ struct attribute **attrs;
+
+ fw_attr = devm_kzalloc(&galaxybook->platform->dev, sizeof(*fw_attr), GFP_KERNEL);
+ if (!fw_attr)
+ return -ENOMEM;
+
+ attrs = devm_kcalloc(&galaxybook->platform->dev, NUM_FW_ATTR_ENUM_ATTRS + 1,
+ sizeof(*attrs), GFP_KERNEL);
+ if (!attrs)
+ return -ENOMEM;
+
+ attrs[0] = &fw_attr_type.attr;
+ attrs[1] = &fw_attr_default_value.attr;
+ attrs[2] = &fw_attr_possible_values.attr;
+ attrs[3] = &fw_attr_display_name_language_code.attr;
+
+ sysfs_attr_init(&fw_attr->display_name.attr);
+ fw_attr->display_name.attr.name = "display_name";
+ fw_attr->display_name.attr.mode = 0444;
+ fw_attr->display_name.show = display_name_show;
+ attrs[4] = &fw_attr->display_name.attr;
+
+ sysfs_attr_init(&fw_attr->current_value.attr);
+ fw_attr->current_value.attr.name = "current_value";
+ fw_attr->current_value.attr.mode = 0644;
+ fw_attr->current_value.show = current_value_show;
+ fw_attr->current_value.store = current_value_store;
+ attrs[5] = &fw_attr->current_value.attr;
+
+ attrs[6] = NULL;
+
+ fw_attr->galaxybook = galaxybook;
+ fw_attr->fw_attr_id = fw_attr_id;
+ fw_attr->attr_group.name = galaxybook_fw_attr_name[fw_attr_id];
+ fw_attr->attr_group.attrs = attrs;
+ fw_attr->get_value = get_value;
+ fw_attr->set_value = set_value;
+
+ return sysfs_create_group(&galaxybook->fw_attrs_kset->kobj, &fw_attr->attr_group);
+}
+
+static void galaxybook_kset_unregister(void *data)
+{
+ struct kset *kset = data;
+
+ kset_unregister(kset);
+}
+
+static void galaxybook_fw_attrs_dev_unregister(void *data)
+{
+ struct device *fw_attrs_dev = data;
+
+ device_unregister(fw_attrs_dev);
+}
+
+static int galaxybook_fw_attrs_init(struct samsung_galaxybook *galaxybook)
+{
+ bool value;
+ int err;
+
+ err = devm_mutex_init(&galaxybook->platform->dev, &galaxybook->fw_attr_lock);
+ if (err)
+ return err;
+
+ galaxybook->fw_attrs_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0),
+ NULL, "%s", DRIVER_NAME);
+ if (IS_ERR(galaxybook->fw_attrs_dev))
+ return PTR_ERR(galaxybook->fw_attrs_dev);
+
+ err = devm_add_action_or_reset(&galaxybook->platform->dev,
+ galaxybook_fw_attrs_dev_unregister,
+ galaxybook->fw_attrs_dev);
+ if (err)
+ return err;
+
+ galaxybook->fw_attrs_kset = kset_create_and_add("attributes", NULL,
+ &galaxybook->fw_attrs_dev->kobj);
+ if (!galaxybook->fw_attrs_kset)
+ return -ENOMEM;
+ err = devm_add_action_or_reset(&galaxybook->platform->dev,
+ galaxybook_kset_unregister, galaxybook->fw_attrs_kset);
+ if (err)
+ return err;
+
+ err = power_on_lid_open_acpi_get(galaxybook, &value);
+ if (!err) {
+ err = galaxybook_fw_attr_init(galaxybook,
+ GB_ATTR_POWER_ON_LID_OPEN,
+ &power_on_lid_open_acpi_get,
+ &power_on_lid_open_acpi_set);
+ if (err)
+ return err;
+ }
+
+ err = usb_charging_acpi_get(galaxybook, &value);
+ if (!err) {
+ err = galaxybook_fw_attr_init(galaxybook,
+ GB_ATTR_USB_CHARGING,
+ &usb_charging_acpi_get,
+ &usb_charging_acpi_set);
+ if (err)
+ return err;
+ }
+
+ err = galaxybook_block_recording_init(galaxybook);
+ if (err == GB_NOT_SUPPORTED)
+ return 0;
+ else if (err)
+ return err;
+
+ galaxybook->has_block_recording = true;
+
+ return galaxybook_fw_attr_init(galaxybook,
+ GB_ATTR_BLOCK_RECORDING,
+ &block_recording_acpi_get,
+ &block_recording_acpi_set);
+}
+
+/*
+ * Hotkeys and notifications
+ */
+
+static void galaxybook_kbd_backlight_hotkey_work(struct work_struct *work)
+{
+ struct samsung_galaxybook *galaxybook =
+ from_work(galaxybook, work, kbd_backlight_hotkey_work);
+ int brightness;
+ int err;
+
+ guard(mutex)(&galaxybook->kbd_backlight_lock);
+
+ brightness = galaxybook->kbd_backlight.brightness;
+ if (brightness < galaxybook->kbd_backlight.max_brightness)
+ brightness++;
+ else
+ brightness = 0;
+
+ err = led_set_brightness_sync(&galaxybook->kbd_backlight, brightness);
+ if (err) {
+ dev_err(&galaxybook->platform->dev,
+ "failed to set kbd_backlight brightness, error %d\n", err);
+ return;
+ }
+
+ led_classdev_notify_brightness_hw_changed(&galaxybook->kbd_backlight, brightness);
+}
+
+static void galaxybook_block_recording_hotkey_work(struct work_struct *work)
+{
+ struct samsung_galaxybook *galaxybook =
+ from_work(galaxybook, work, block_recording_hotkey_work);
+ bool value;
+ int err;
+
+ guard(mutex)(&galaxybook->fw_attr_lock);
+
+ err = block_recording_acpi_get(galaxybook, &value);
+ if (err) {
+ dev_err(&galaxybook->platform->dev,
+ "failed to get block_recording, error %d\n", err);
+ return;
+ }
+
+ err = block_recording_acpi_set(galaxybook, !value);
+ if (err)
+ dev_err(&galaxybook->platform->dev,
+ "failed to set block_recording, error %d\n", err);
+}
+
+static bool galaxybook_i8042_filter(unsigned char data, unsigned char str, struct serio *port,
+ void *context)
+{
+ struct samsung_galaxybook *galaxybook = context;
+ static bool extended;
+
+ if (str & I8042_STR_AUXDATA)
+ return false;
+
+ if (data == 0xe0) {
+ extended = true;
+ return true;
+ } else if (extended) {
+ extended = false;
+ switch (data) {
+ case GB_KEY_KBD_BACKLIGHT_KEYDOWN:
+ return true;
+ case GB_KEY_KBD_BACKLIGHT_KEYUP:
+ if (galaxybook->has_kbd_backlight)
+ schedule_work(&galaxybook->kbd_backlight_hotkey_work);
+ return true;
+
+ case GB_KEY_BLOCK_RECORDING_KEYDOWN:
+ return true;
+ case GB_KEY_BLOCK_RECORDING_KEYUP:
+ if (galaxybook->has_block_recording)
+ schedule_work(&galaxybook->block_recording_hotkey_work);
+ return true;
+
+ /* battery notification already sent to battery + SCAI device */
+ case GB_KEY_BATTERY_NOTIFY_KEYUP:
+ case GB_KEY_BATTERY_NOTIFY_KEYDOWN:
+ return true;
+
+ default:
+ /*
+ * Report the previously filtered e0 before continuing
+ * with the next non-filtered byte.
+ */
+ serio_interrupt(port, 0xe0, 0);
+ return false;
+ }
+ }
+
+ return false;
+}
+
+static void galaxybook_i8042_filter_remove(void *data)
+{
+ struct samsung_galaxybook *galaxybook = data;
+
+ i8042_remove_filter(galaxybook_i8042_filter);
+ cancel_work_sync(&galaxybook->kbd_backlight_hotkey_work);
+ cancel_work_sync(&galaxybook->block_recording_hotkey_work);
+}
+
+static int galaxybook_i8042_filter_install(struct samsung_galaxybook *galaxybook)
+{
+ int err;
+
+ if (!galaxybook->has_kbd_backlight && !galaxybook->has_block_recording)
+ return 0;
+
+ INIT_WORK(&galaxybook->kbd_backlight_hotkey_work,
+ galaxybook_kbd_backlight_hotkey_work);
+ INIT_WORK(&galaxybook->block_recording_hotkey_work,
+ galaxybook_block_recording_hotkey_work);
+
+ err = i8042_install_filter(galaxybook_i8042_filter, galaxybook);
+ if (err)
+ return err;
+
+ return devm_add_action_or_reset(&galaxybook->platform->dev,
+ galaxybook_i8042_filter_remove, galaxybook);
+}
+
+/*
+ * ACPI device setup
+ */
+
+static void galaxybook_acpi_notify(acpi_handle handle, u32 event, void *data)
+{
+ struct samsung_galaxybook *galaxybook = data;
+
+ switch (event) {
+ case GB_ACPI_NOTIFY_BATTERY_STATE_CHANGED:
+ case GB_ACPI_NOTIFY_DEVICE_ON_TABLE:
+ case GB_ACPI_NOTIFY_DEVICE_OFF_TABLE:
+ break;
+ case GB_ACPI_NOTIFY_HOTKEY_PERFORMANCE_MODE:
+ if (galaxybook->has_performance_mode)
+ platform_profile_cycle();
+ break;
+ default:
+ dev_warn(&galaxybook->platform->dev,
+ "unknown ACPI notification event: 0x%x\n", event);
+ }
+
+ acpi_bus_generate_netlink_event(DRIVER_NAME, dev_name(&galaxybook->platform->dev),
+ event, 1);
+}
+
+static int galaxybook_enable_acpi_notify(struct samsung_galaxybook *galaxybook)
+{
+ struct sawb buf = {};
+ int err;
+
+ err = galaxybook_enable_acpi_feature(galaxybook, GB_SASB_NOTIFICATIONS);
+ if (err)
+ return err;
+
+ buf.safn = GB_SAFN;
+ buf.sasb = GB_SASB_NOTIFICATIONS;
+ buf.gunm = GB_GUNM_ACPI_NOTIFY_ENABLE;
+ buf.guds[0] = GB_GUDS_ACPI_NOTIFY_ENABLE;
+
+ return galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
+ &buf, GB_SAWB_LEN_SETTINGS);
+}
+
+static void galaxybook_acpi_remove_notify_handler(void *data)
+{
+ struct samsung_galaxybook *galaxybook = data;
+
+ acpi_remove_notify_handler(galaxybook->acpi->handle, ACPI_ALL_NOTIFY,
+ galaxybook_acpi_notify);
+}
+
+static void galaxybook_acpi_disable(void *data)
+{
+ struct samsung_galaxybook *galaxybook = data;
+
+ acpi_execute_simple_method(galaxybook->acpi->handle,
+ GB_ACPI_METHOD_ENABLE, GB_ACPI_METHOD_ENABLE_OFF);
+}
+
+static int galaxybook_acpi_init(struct samsung_galaxybook *galaxybook)
+{
+ acpi_status status;
+ int err;
+
+ status = acpi_execute_simple_method(galaxybook->acpi->handle, GB_ACPI_METHOD_ENABLE,
+ GB_ACPI_METHOD_ENABLE_ON);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+ err = devm_add_action_or_reset(&galaxybook->platform->dev,
+ galaxybook_acpi_disable, galaxybook);
+ if (err)
+ return err;
+
+ status = acpi_install_notify_handler(galaxybook->acpi->handle, ACPI_ALL_NOTIFY,
+ galaxybook_acpi_notify, galaxybook);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+ err = devm_add_action_or_reset(&galaxybook->platform->dev,
+ galaxybook_acpi_remove_notify_handler, galaxybook);
+ if (err)
+ return err;
+
+ err = galaxybook_enable_acpi_notify(galaxybook);
+ if (err)
+ dev_dbg(&galaxybook->platform->dev, "failed to enable ACPI notifications; "
+ "some hotkeys will not be supported\n");
+
+ err = galaxybook_enable_acpi_feature(galaxybook, GB_SASB_POWER_MANAGEMENT);
+ if (err)
+ dev_dbg(&galaxybook->platform->dev,
+ "failed to initialize ACPI power management features; "
+ "many features of this driver will not be available\n");
+
+ return 0;
+}
+
+/*
+ * Platform driver
+ */
+
+static int galaxybook_probe(struct platform_device *pdev)
+{
+ struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
+ struct samsung_galaxybook *galaxybook;
+ int err;
+
+ if (!adev)
+ return -ENODEV;
+
+ galaxybook = devm_kzalloc(&pdev->dev, sizeof(*galaxybook), GFP_KERNEL);
+ if (!galaxybook)
+ return -ENOMEM;
+
+ galaxybook->platform = pdev;
+ galaxybook->acpi = adev;
+
+ /*
+ * Features must be enabled and initialized in the following order to
+ * avoid failures seen on certain devices:
+ * - GB_SASB_POWER_MANAGEMENT (including performance mode)
+ * - GB_SASB_KBD_BACKLIGHT
+ * - GB_SASB_BLOCK_RECORDING (as part of fw_attrs init)
+ */
+
+ err = galaxybook_acpi_init(galaxybook);
+ if (err)
+ return dev_err_probe(&galaxybook->platform->dev, err,
+ "failed to initialize ACPI device\n");
+
+ err = galaxybook_platform_profile_init(galaxybook);
+ if (!err)
+ galaxybook->has_performance_mode = true;
+ else if (err != GB_NOT_SUPPORTED)
+ return dev_err_probe(&galaxybook->platform->dev, err,
+ "failed to initialize platform profile\n");
+
+ err = galaxybook_battery_threshold_init(galaxybook);
+ if (err)
+ return dev_err_probe(&galaxybook->platform->dev, err,
+ "failed to initialize battery threshold\n");
+
+ err = galaxybook_kbd_backlight_init(galaxybook);
+ if (!err)
+ galaxybook->has_kbd_backlight = true;
+ else if (err != GB_NOT_SUPPORTED)
+ return dev_err_probe(&galaxybook->platform->dev, err,
+ "failed to initialize kbd_backlight\n");
+
+ err = galaxybook_fw_attrs_init(galaxybook);
+ if (err)
+ return dev_err_probe(&galaxybook->platform->dev, err,
+ "failed to initialize firmware-attributes\n");
+
+ err = galaxybook_i8042_filter_install(galaxybook);
+ if (err)
+ return dev_err_probe(&galaxybook->platform->dev, err,
+ "failed to initialize i8042_filter\n");
+
+ return 0;
+}
+
+static const struct acpi_device_id galaxybook_device_ids[] = {
+ { "SAM0427" },
+ { "SAM0428" },
+ { "SAM0429" },
+ { "SAM0430" },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, galaxybook_device_ids);
+
+static struct platform_driver galaxybook_platform_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .acpi_match_table = galaxybook_device_ids,
+ },
+ .probe = galaxybook_probe,
+};
+module_platform_driver(galaxybook_platform_driver);
+
+MODULE_AUTHOR("Joshua Grisham <josh@joshuagrisham.com>");
+MODULE_DESCRIPTION("Samsung Galaxy Book driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/serdev_helpers.h b/drivers/platform/x86/serdev_helpers.h
index 3bc7fd8e1e19..57eac75805e2 100644
--- a/drivers/platform/x86/serdev_helpers.h
+++ b/drivers/platform/x86/serdev_helpers.h
@@ -22,32 +22,14 @@
#include <linux/string.h>
static inline struct device *
-get_serdev_controller(const char *serial_ctrl_hid,
- const char *serial_ctrl_uid,
- int serial_ctrl_port,
- const char *serdev_ctrl_name)
+get_serdev_controller_from_parent(struct device *ctrl_dev,
+ int serial_ctrl_port,
+ const char *serdev_ctrl_name)
{
- struct device *ctrl_dev, *child;
- struct acpi_device *ctrl_adev;
+ struct device *child;
char name[32];
int i;
- ctrl_adev = acpi_dev_get_first_match_dev(serial_ctrl_hid, serial_ctrl_uid, -1);
- if (!ctrl_adev) {
- pr_err("error could not get %s/%s serial-ctrl adev\n",
- serial_ctrl_hid, serial_ctrl_uid ?: "*");
- return ERR_PTR(-ENODEV);
- }
-
- /* get_first_physical_node() returns a weak ref */
- ctrl_dev = get_device(acpi_get_first_physical_node(ctrl_adev));
- if (!ctrl_dev) {
- pr_err("error could not get %s/%s serial-ctrl physical node\n",
- serial_ctrl_hid, serial_ctrl_uid ?: "*");
- ctrl_dev = ERR_PTR(-ENODEV);
- goto put_ctrl_adev;
- }
-
/* Walk host -> uart-ctrl -> port -> serdev-ctrl */
for (i = 0; i < 3; i++) {
switch (i) {
@@ -67,14 +49,40 @@ get_serdev_controller(const char *serial_ctrl_hid,
put_device(ctrl_dev);
if (!child) {
pr_err("error could not find '%s' device\n", name);
- ctrl_dev = ERR_PTR(-ENODEV);
- goto put_ctrl_adev;
+ return ERR_PTR(-ENODEV);
}
ctrl_dev = child;
}
-put_ctrl_adev:
- acpi_dev_put(ctrl_adev);
return ctrl_dev;
}
+
+static inline struct device *
+get_serdev_controller(const char *serial_ctrl_hid,
+ const char *serial_ctrl_uid,
+ int serial_ctrl_port,
+ const char *serdev_ctrl_name)
+{
+ struct acpi_device *adev;
+ struct device *parent;
+
+ adev = acpi_dev_get_first_match_dev(serial_ctrl_hid, serial_ctrl_uid, -1);
+ if (!adev) {
+ pr_err("error could not get %s/%s serial-ctrl adev\n",
+ serial_ctrl_hid, serial_ctrl_uid ?: "*");
+ return ERR_PTR(-ENODEV);
+ }
+
+ /* get_first_physical_node() returns a weak ref */
+ parent = get_device(acpi_get_first_physical_node(adev));
+ acpi_dev_put(adev);
+ if (!parent) {
+ pr_err("error could not get %s/%s serial-ctrl physical node\n",
+ serial_ctrl_hid, serial_ctrl_uid ?: "*");
+ return ERR_PTR(-ENODEV);
+ }
+
+ /* This puts our reference on parent and returns a ref on the ctrl */
+ return get_serdev_controller_from_parent(parent, serial_ctrl_port, serdev_ctrl_name);
+}
diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c
index ed6b28505cd6..db030b0f176a 100644
--- a/drivers/platform/x86/serial-multi-instantiate.c
+++ b/drivers/platform/x86/serial-multi-instantiate.c
@@ -384,6 +384,17 @@ static const struct smi_node cs35l57_hda = {
.bus_type = SMI_AUTO_DETECT,
};
+static const struct smi_node tas2781_hda = {
+ .instances = {
+ { "tas2781-hda", IRQ_RESOURCE_AUTO, 0 },
+ { "tas2781-hda", IRQ_RESOURCE_AUTO, 0 },
+ { "tas2781-hda", IRQ_RESOURCE_AUTO, 0 },
+ { "tas2781-hda", IRQ_RESOURCE_AUTO, 0 },
+ {}
+ },
+ .bus_type = SMI_AUTO_DETECT,
+};
+
/*
* Note new device-ids must also be added to ignore_serial_bus_ids in
* drivers/acpi/scan.c: acpi_device_enumeration_by_parent().
@@ -396,6 +407,7 @@ static const struct acpi_device_id smi_acpi_ids[] = {
{ "CSC3556", (unsigned long)&cs35l56_hda },
{ "CSC3557", (unsigned long)&cs35l57_hda },
{ "INT3515", (unsigned long)&int3515_data },
+ { "TXNW2781", (unsigned long)&tas2781_hda },
/* Non-conforming _HID for Cirrus Logic already released */
{ "CLSA0100", (unsigned long)&cs35l41_hda },
{ "CLSA0101", (unsigned long)&cs35l41_hda },
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
index 3197aaa69da7..b52390fbd743 100644
--- a/drivers/platform/x86/sony-laptop.c
+++ b/drivers/platform/x86/sony-laptop.c
@@ -538,7 +538,7 @@ static void sony_laptop_remove_input(void)
if (!atomic_dec_and_test(&sony_laptop_input.users))
return;
- del_timer_sync(&sony_laptop_input.release_key_timer);
+ timer_delete_sync(&sony_laptop_input.release_key_timer);
/*
* Generate key-up events for remaining keys. Note that we don't
diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c
index 38de0cb20d77..00b1e7c79a3d 100644
--- a/drivers/platform/x86/think-lmi.c
+++ b/drivers/platform/x86/think-lmi.c
@@ -194,7 +194,6 @@ static const char * const level_options[] = {
[TLMI_LEVEL_MASTER] = "master",
};
static struct think_lmi tlmi_priv;
-static const struct class *fw_attr_class;
static DEFINE_MUTEX(tlmi_mutex);
static inline struct tlmi_pwd_setting *to_tlmi_pwd_setting(struct kobject *kobj)
@@ -263,16 +262,11 @@ static int tlmi_simple_call(const char *guid, const char *arg)
return 0;
}
-/* Extract output string from WMI return buffer */
-static int tlmi_extract_output_string(const struct acpi_buffer *output,
- char **string)
+/* Extract output string from WMI return value */
+static int tlmi_extract_output_string(union acpi_object *obj, char **string)
{
- const union acpi_object *obj;
char *s;
- obj = output->pointer;
- if (!obj)
- return -ENOMEM;
if (obj->type != ACPI_TYPE_STRING || !obj->string.pointer)
return -EIO;
@@ -350,20 +344,18 @@ static int tlmi_opcode_setting(char *setting, const char *value)
return ret;
}
-static int tlmi_setting(int item, char **value, const char *guid_string)
+static int tlmi_setting(struct wmi_device *wdev, int item, char **value)
{
- struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
- acpi_status status;
+ union acpi_object *obj;
int ret;
- status = wmi_query_block(guid_string, item, &output);
- if (ACPI_FAILURE(status)) {
- kfree(output.pointer);
+ obj = wmidev_block_query(wdev, item);
+ if (!obj)
return -EIO;
- }
- ret = tlmi_extract_output_string(&output, value);
- kfree(output.pointer);
+ ret = tlmi_extract_output_string(obj, value);
+ kfree(obj);
+
return ret;
}
@@ -371,19 +363,22 @@ static int tlmi_get_bios_selections(const char *item, char **value)
{
const struct acpi_buffer input = { strlen(item), (char *)item };
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *obj;
acpi_status status;
int ret;
status = wmi_evaluate_method(LENOVO_GET_BIOS_SELECTIONS_GUID,
0, 0, &input, &output);
-
- if (ACPI_FAILURE(status)) {
- kfree(output.pointer);
+ if (ACPI_FAILURE(status))
return -EIO;
- }
- ret = tlmi_extract_output_string(&output, value);
- kfree(output.pointer);
+ obj = output.pointer;
+ if (!obj)
+ return -ENODATA;
+
+ ret = tlmi_extract_output_string(obj, value);
+ kfree(obj);
+
return ret;
}
@@ -994,7 +989,7 @@ static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *a
char *item, *value;
int ret;
- ret = tlmi_setting(setting->index, &item, LENOVO_BIOS_SETTING_GUID);
+ ret = tlmi_setting(setting->wdev, setting->index, &item);
if (ret)
return ret;
@@ -1066,8 +1061,8 @@ static ssize_t current_value_store(struct kobject *kobj,
ret = -EINVAL;
goto out;
}
- set_str = kasprintf(GFP_KERNEL, "%s,%s,%s", setting->display_name,
- new_setting, tlmi_priv.pwd_admin->signature);
+ set_str = kasprintf(GFP_KERNEL, "%s,%s,%s", setting->name,
+ new_setting, tlmi_priv.pwd_admin->signature);
if (!set_str) {
ret = -ENOMEM;
goto out;
@@ -1097,7 +1092,7 @@ static ssize_t current_value_store(struct kobject *kobj,
goto out;
}
- set_str = kasprintf(GFP_KERNEL, "%s,%s;", setting->display_name,
+ set_str = kasprintf(GFP_KERNEL, "%s,%s;", setting->name,
new_setting);
if (!set_str) {
ret = -ENOMEM;
@@ -1125,11 +1120,11 @@ static ssize_t current_value_store(struct kobject *kobj,
}
if (auth_str)
- set_str = kasprintf(GFP_KERNEL, "%s,%s,%s", setting->display_name,
- new_setting, auth_str);
+ set_str = kasprintf(GFP_KERNEL, "%s,%s,%s", setting->name,
+ new_setting, auth_str);
else
- set_str = kasprintf(GFP_KERNEL, "%s,%s;", setting->display_name,
- new_setting);
+ set_str = kasprintf(GFP_KERNEL, "%s,%s;", setting->name,
+ new_setting);
if (!set_str) {
ret = -ENOMEM;
goto out;
@@ -1446,11 +1441,7 @@ static int tlmi_sysfs_init(void)
{
int i, ret;
- ret = fw_attributes_class_get(&fw_attr_class);
- if (ret)
- return ret;
-
- tlmi_priv.class_dev = device_create(fw_attr_class, NULL, MKDEV(0, 0),
+ tlmi_priv.class_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0),
NULL, "%s", "thinklmi");
if (IS_ERR(tlmi_priv.class_dev)) {
ret = PTR_ERR(tlmi_priv.class_dev);
@@ -1563,9 +1554,8 @@ static int tlmi_sysfs_init(void)
fail_create_attr:
tlmi_release_attr();
fail_device_created:
- device_destroy(fw_attr_class, MKDEV(0, 0));
+ device_destroy(&firmware_attributes_class, MKDEV(0, 0));
fail_class_created:
- fw_attributes_class_put();
return ret;
}
@@ -1592,7 +1582,7 @@ static struct tlmi_pwd_setting *tlmi_create_auth(const char *pwd_type,
return new_pwd;
}
-static int tlmi_analyze(void)
+static int tlmi_analyze(struct wmi_device *wdev)
{
int i, ret;
@@ -1629,7 +1619,7 @@ static int tlmi_analyze(void)
char *item = NULL;
tlmi_priv.setting[i] = NULL;
- ret = tlmi_setting(i, &item, LENOVO_BIOS_SETTING_GUID);
+ ret = tlmi_setting(wdev, i, &item);
if (ret)
break;
if (!item)
@@ -1639,9 +1629,6 @@ static int tlmi_analyze(void)
continue;
}
- /* It is not allowed to have '/' for file name. Convert it into '\'. */
- strreplace(item, '/', '\\');
-
/* Remove the value part */
strreplace(item, ',', '\0');
@@ -1652,12 +1639,18 @@ static int tlmi_analyze(void)
kfree(item);
goto fail_clear_attr;
}
+ setting->wdev = wdev;
setting->index = i;
+
+ strscpy(setting->name, item);
+ /* It is not allowed to have '/' for file name. Convert it into '\'. */
+ strreplace(item, '/', '\\');
strscpy(setting->display_name, item);
+
/* If BIOS selections supported, load those */
if (tlmi_priv.can_get_bios_selections) {
- ret = tlmi_get_bios_selections(setting->display_name,
- &setting->possible_values);
+ ret = tlmi_get_bios_selections(setting->name,
+ &setting->possible_values);
if (ret || !setting->possible_values)
pr_info("Error retrieving possible values for %d : %s\n",
i, setting->display_name);
@@ -1670,7 +1663,7 @@ static int tlmi_analyze(void)
*/
char *optitem, *optstart, *optend;
- if (!tlmi_setting(setting->index, &optitem, LENOVO_BIOS_SETTING_GUID)) {
+ if (!tlmi_setting(setting->wdev, setting->index, &optitem)) {
optstart = strstr(optitem, "[Optional:");
if (optstart) {
optstart += strlen("[Optional:");
@@ -1788,15 +1781,14 @@ fail_clear_attr:
static void tlmi_remove(struct wmi_device *wdev)
{
tlmi_release_attr();
- device_destroy(fw_attr_class, MKDEV(0, 0));
- fw_attributes_class_put();
+ device_destroy(&firmware_attributes_class, MKDEV(0, 0));
}
static int tlmi_probe(struct wmi_device *wdev, const void *context)
{
int ret;
- ret = tlmi_analyze();
+ ret = tlmi_analyze(wdev);
if (ret)
return ret;
diff --git a/drivers/platform/x86/think-lmi.h b/drivers/platform/x86/think-lmi.h
index f267d8b46957..9b014644d316 100644
--- a/drivers/platform/x86/think-lmi.h
+++ b/drivers/platform/x86/think-lmi.h
@@ -4,6 +4,7 @@
#define _THINK_LMI_H_
#include <linux/types.h>
+#include <linux/wmi.h>
#define TLMI_SETTINGS_COUNT 256
#define TLMI_SETTINGS_MAXLEN 512
@@ -87,7 +88,9 @@ struct tlmi_pwd_setting {
/* Attribute setting details */
struct tlmi_attr_setting {
struct kobject kobj;
+ struct wmi_device *wdev;
int index;
+ char name[TLMI_SETTINGS_MAXLEN];
char display_name[TLMI_SETTINGS_MAXLEN];
char *possible_values;
};
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index 84dcd7da7319..657625dd60a0 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -39,7 +39,6 @@
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/dmi.h>
-#include <linux/fb.h>
#include <linux/freezer.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
@@ -232,6 +231,7 @@ enum tpacpi_hkey_event_t {
/* Thermal events */
TP_HKEY_EV_ALARM_BAT_HOT = 0x6011, /* battery too hot */
TP_HKEY_EV_ALARM_BAT_XHOT = 0x6012, /* battery critically hot */
+ TP_HKEY_EV_ALARM_BAT_LIM_CHANGE = 0x6013, /* battery charge limit changed*/
TP_HKEY_EV_ALARM_SENSOR_HOT = 0x6021, /* sensor too hot */
TP_HKEY_EV_ALARM_SENSOR_XHOT = 0x6022, /* sensor critically hot */
TP_HKEY_EV_THM_TABLE_CHANGED = 0x6030, /* windows; thermal table changed */
@@ -368,9 +368,7 @@ static struct {
u32 beep_needs_two_args:1;
u32 mixer_no_level_control:1;
u32 battery_force_primary:1;
- u32 input_device_registered:1;
u32 platform_drv_registered:1;
- u32 sensors_pdrv_registered:1;
u32 hotkey_poll_active:1;
u32 has_adaptive_kbd:1;
u32 kbd_lang:1;
@@ -963,6 +961,7 @@ static const struct proc_ops dispatch_proc_ops = {
static struct platform_device *tpacpi_pdev;
static struct platform_device *tpacpi_sensors_pdev;
static struct device *tpacpi_hwmon;
+static struct device *tpacpi_pprof;
static struct input_dev *tpacpi_inputdev;
static struct mutex tpacpi_inputdev_send_mutex;
static LIST_HEAD(tpacpi_all_drivers);
@@ -3275,6 +3274,7 @@ static const struct key_entry keymap_lenovo[] __initconst = {
* scancodes to preserve uAPI compatibility, see tpacpi_input_send_key().
*/
{ KE_KEY, 0x131d, { KEY_VENDOR } }, /* System debug info, similar to old ThinkPad key */
+ { KE_KEY, 0x1320, { KEY_LINK_PHONE } },
{ KE_KEY, TP_HKEY_EV_TRACK_DOUBLETAP /* 0x8036 */, { KEY_PROG4 } },
{ KE_END }
};
@@ -3778,6 +3778,10 @@ static bool hotkey_notify_6xxx(const u32 hkey, bool *send_acpi_ev)
pr_alert("THERMAL EMERGENCY: battery is extremely hot!\n");
/* recommended action: immediate sleep/hibernate */
break;
+ case TP_HKEY_EV_ALARM_BAT_LIM_CHANGE:
+ pr_debug("Battery Info: battery charge threshold changed\n");
+ /* User changed charging threshold. No action needed */
+ return true;
case TP_HKEY_EV_ALARM_SENSOR_HOT:
pr_crit("THERMAL ALARM: a sensor reports something is too hot!\n");
/* recommended action: warn user through gui, that */
@@ -7883,6 +7887,7 @@ static struct ibm_struct volume_driver_data = {
#define FAN_NS_CTRL_STATUS BIT(2) /* Bit which determines control is enabled or not */
#define FAN_NS_CTRL BIT(4) /* Bit which determines control is by host or EC */
+#define FAN_CLOCK_TPM (22500*60) /* Ticks per minute for a 22.5 kHz clock */
enum { /* Fan control constants */
fan_status_offset = 0x2f, /* EC register 0x2f */
@@ -7938,6 +7943,7 @@ static int fan_watchdog_maxinterval;
static bool fan_with_ns_addr;
static bool ecfw_with_fan_dec_rpm;
+static bool fan_speed_in_tpr;
static struct mutex fan_mutex;
@@ -8140,8 +8146,11 @@ static int fan_get_speed(unsigned int *speed)
!acpi_ec_read(fan_rpm_offset + 1, &hi)))
return -EIO;
- if (likely(speed))
+ if (likely(speed)) {
*speed = (hi << 8) | lo;
+ if (fan_speed_in_tpr && *speed != 0)
+ *speed = FAN_CLOCK_TPM / *speed;
+ }
break;
case TPACPI_FAN_RD_TPEC_NS:
if (!acpi_ec_read(fan_rpm_status_ns, &lo))
@@ -8174,8 +8183,11 @@ static int fan2_get_speed(unsigned int *speed)
if (rc)
return -EIO;
- if (likely(speed))
+ if (likely(speed)) {
*speed = (hi << 8) | lo;
+ if (fan_speed_in_tpr && *speed != 0)
+ *speed = FAN_CLOCK_TPM / *speed;
+ }
break;
case TPACPI_FAN_RD_TPEC_NS:
@@ -8503,7 +8515,7 @@ static void fan_watchdog_reset(void)
if (fan_watchdog_maxinterval > 0 &&
tpacpi_lifecycle != TPACPI_LIFE_EXITING)
mod_delayed_work(tpacpi_wq, &fan_watchdog_task,
- msecs_to_jiffies(fan_watchdog_maxinterval * 1000));
+ secs_to_jiffies(fan_watchdog_maxinterval));
else
cancel_delayed_work(&fan_watchdog_task);
}
@@ -8786,6 +8798,8 @@ static const struct attribute_group fan_driver_attr_group = {
#define TPACPI_FAN_NOFAN 0x0008 /* no fan available */
#define TPACPI_FAN_NS 0x0010 /* For EC with non-Standard register addresses */
#define TPACPI_FAN_DECRPM 0x0020 /* For ECFW's with RPM in register as decimal */
+#define TPACPI_FAN_TPR 0x0040 /* Fan speed is in Ticks Per Revolution */
+#define TPACPI_FAN_NOACPI 0x0080 /* Don't use ACPI methods even if detected */
static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
TPACPI_QEC_IBM('1', 'Y', TPACPI_FAN_Q1),
@@ -8815,6 +8829,10 @@ static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
TPACPI_Q_LNV3('R', '0', 'V', TPACPI_FAN_NS), /* 11e Gen5 KL-Y */
TPACPI_Q_LNV3('N', '1', 'O', TPACPI_FAN_NOFAN), /* X1 Tablet (2nd gen) */
TPACPI_Q_LNV3('R', '0', 'Q', TPACPI_FAN_DECRPM),/* L480 */
+ TPACPI_Q_LNV('8', 'F', TPACPI_FAN_TPR), /* ThinkPad x120e */
+ TPACPI_Q_LNV3('R', '0', '0', TPACPI_FAN_NOACPI),/* E560 */
+ TPACPI_Q_LNV3('R', '1', '2', TPACPI_FAN_NOACPI),/* T495 */
+ TPACPI_Q_LNV3('R', '1', '3', TPACPI_FAN_NOACPI),/* T495s */
};
static int __init fan_init(struct ibm_init_struct *iibm)
@@ -8866,6 +8884,13 @@ static int __init fan_init(struct ibm_init_struct *iibm)
tp_features.fan_ctrl_status_undef = 1;
}
+ if (quirks & TPACPI_FAN_NOACPI) {
+ /* E560, T495, T495s */
+ pr_info("Ignoring buggy ACPI fan access method\n");
+ fang_handle = NULL;
+ fanw_handle = NULL;
+ }
+
if (gfan_handle) {
/* 570, 600e/x, 770e, 770x */
fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN;
@@ -8885,6 +8910,8 @@ static int __init fan_init(struct ibm_init_struct *iibm)
if (quirks & TPACPI_FAN_Q1)
fan_quirk1_setup();
+ if (quirks & TPACPI_FAN_TPR)
+ fan_speed_in_tpr = true;
/* Try and probe the 2nd fan */
tp_features.second_fan = 1; /* needed for get_speed to work */
res = fan2_get_speed(&speed);
@@ -10318,6 +10345,10 @@ static struct ibm_struct proxsensor_driver_data = {
#define DYTC_MODE_PSC_BALANCE 5 /* Default mode aka balanced */
#define DYTC_MODE_PSC_PERFORM 7 /* High power mode aka performance */
+#define DYTC_MODE_PSCV9_LOWPOWER 1 /* Low power mode */
+#define DYTC_MODE_PSCV9_BALANCE 3 /* Default mode aka balanced */
+#define DYTC_MODE_PSCV9_PERFORM 4 /* High power mode aka performance */
+
#define DYTC_ERR_MASK 0xF /* Bits 0-3 in cmd result are the error result */
#define DYTC_ERR_SUCCESS 1 /* CMD completed successful */
@@ -10338,6 +10369,10 @@ static int dytc_capabilities;
static bool dytc_mmc_get_available;
static int profile_force;
+static int platform_psc_profile_lowpower = DYTC_MODE_PSC_LOWPOWER;
+static int platform_psc_profile_balanced = DYTC_MODE_PSC_BALANCE;
+static int platform_psc_profile_performance = DYTC_MODE_PSC_PERFORM;
+
static int convert_dytc_to_profile(int funcmode, int dytcmode,
enum platform_profile_option *profile)
{
@@ -10359,19 +10394,15 @@ static int convert_dytc_to_profile(int funcmode, int dytcmode,
}
return 0;
case DYTC_FUNCTION_PSC:
- switch (dytcmode) {
- case DYTC_MODE_PSC_LOWPOWER:
+ if (dytcmode == platform_psc_profile_lowpower)
*profile = PLATFORM_PROFILE_LOW_POWER;
- break;
- case DYTC_MODE_PSC_BALANCE:
+ else if (dytcmode == platform_psc_profile_balanced)
*profile = PLATFORM_PROFILE_BALANCED;
- break;
- case DYTC_MODE_PSC_PERFORM:
+ else if (dytcmode == platform_psc_profile_performance)
*profile = PLATFORM_PROFILE_PERFORMANCE;
- break;
- default: /* Unknown mode */
+ else
return -EINVAL;
- }
+
return 0;
case DYTC_FUNCTION_AMT:
/* For now return balanced. It's the closest we have to 'auto' */
@@ -10392,19 +10423,19 @@ static int convert_profile_to_dytc(enum platform_profile_option profile, int *pe
if (dytc_capabilities & BIT(DYTC_FC_MMC))
*perfmode = DYTC_MODE_MMC_LOWPOWER;
else if (dytc_capabilities & BIT(DYTC_FC_PSC))
- *perfmode = DYTC_MODE_PSC_LOWPOWER;
+ *perfmode = platform_psc_profile_lowpower;
break;
case PLATFORM_PROFILE_BALANCED:
if (dytc_capabilities & BIT(DYTC_FC_MMC))
*perfmode = DYTC_MODE_MMC_BALANCE;
else if (dytc_capabilities & BIT(DYTC_FC_PSC))
- *perfmode = DYTC_MODE_PSC_BALANCE;
+ *perfmode = platform_psc_profile_balanced;
break;
case PLATFORM_PROFILE_PERFORMANCE:
if (dytc_capabilities & BIT(DYTC_FC_MMC))
*perfmode = DYTC_MODE_MMC_PERFORM;
else if (dytc_capabilities & BIT(DYTC_FC_PSC))
- *perfmode = DYTC_MODE_PSC_PERFORM;
+ *perfmode = platform_psc_profile_performance;
break;
default: /* Unknown profile */
return -EOPNOTSUPP;
@@ -10416,7 +10447,7 @@ static int convert_profile_to_dytc(enum platform_profile_option profile, int *pe
* dytc_profile_get: Function to register with platform_profile
* handler. Returns current platform profile.
*/
-static int dytc_profile_get(struct platform_profile_handler *pprof,
+static int dytc_profile_get(struct device *dev,
enum platform_profile_option *profile)
{
*profile = dytc_current_profile;
@@ -10491,7 +10522,7 @@ static int dytc_cql_command(int command, int *output)
* dytc_profile_set: Function to register with platform_profile
* handler. Sets current platform profile.
*/
-static int dytc_profile_set(struct platform_profile_handler *pprof,
+static int dytc_profile_set(struct device *dev,
enum platform_profile_option profile)
{
int perfmode;
@@ -10540,6 +10571,21 @@ unlock:
return err;
}
+static int dytc_profile_probe(void *drvdata, unsigned long *choices)
+{
+ set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
+ set_bit(PLATFORM_PROFILE_BALANCED, choices);
+ set_bit(PLATFORM_PROFILE_PERFORMANCE, choices);
+
+ return 0;
+}
+
+static const struct platform_profile_ops dytc_profile_ops = {
+ .probe = dytc_profile_probe,
+ .profile_get = dytc_profile_get,
+ .profile_set = dytc_profile_set,
+};
+
static void dytc_profile_refresh(void)
{
enum platform_profile_option profile;
@@ -10568,24 +10614,14 @@ static void dytc_profile_refresh(void)
err = convert_dytc_to_profile(funcmode, perfmode, &profile);
if (!err && profile != dytc_current_profile) {
dytc_current_profile = profile;
- platform_profile_notify();
+ platform_profile_notify(tpacpi_pprof);
}
}
-static struct platform_profile_handler dytc_profile = {
- .profile_get = dytc_profile_get,
- .profile_set = dytc_profile_set,
-};
-
static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm)
{
int err, output;
- /* Setup supported modes */
- set_bit(PLATFORM_PROFILE_LOW_POWER, dytc_profile.choices);
- set_bit(PLATFORM_PROFILE_BALANCED, dytc_profile.choices);
- set_bit(PLATFORM_PROFILE_PERFORMANCE, dytc_profile.choices);
-
err = dytc_command(DYTC_CMD_QUERY, &output);
if (err)
return err;
@@ -10593,6 +10629,7 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm)
if (output & BIT(DYTC_QUERY_ENABLE_BIT))
dytc_version = (output >> DYTC_QUERY_REV_BIT) & 0xF;
+ dbg_printk(TPACPI_DBG_INIT, "DYTC version %d\n", dytc_version);
/* Check DYTC is enabled and supports mode setting */
if (dytc_version < 5)
return -ENODEV;
@@ -10631,6 +10668,11 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm)
}
} else if (dytc_capabilities & BIT(DYTC_FC_PSC)) { /* PSC MODE */
pr_debug("PSC is supported\n");
+ if (dytc_version >= 9) { /* update profiles for DYTC 9 and up */
+ platform_psc_profile_lowpower = DYTC_MODE_PSCV9_LOWPOWER;
+ platform_psc_profile_balanced = DYTC_MODE_PSCV9_BALANCE;
+ platform_psc_profile_performance = DYTC_MODE_PSCV9_PERFORM;
+ }
} else {
dbg_printk(TPACPI_DBG_INIT, "No DYTC support available\n");
return -ENODEV;
@@ -10640,12 +10682,13 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm)
"DYTC version %d: thermal mode available\n", dytc_version);
/* Create platform_profile structure and register */
- err = platform_profile_register(&dytc_profile);
+ tpacpi_pprof = platform_profile_register(&tpacpi_pdev->dev, "thinkpad-acpi-profile",
+ NULL, &dytc_profile_ops);
/*
* If for some reason platform_profiles aren't enabled
* don't quit terminally.
*/
- if (err)
+ if (IS_ERR(tpacpi_pprof))
return -ENODEV;
/* Ensure initial values are correct */
@@ -10660,7 +10703,8 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm)
static void dytc_profile_exit(void)
{
- platform_profile_remove();
+ if (!IS_ERR_OR_NULL(tpacpi_pprof))
+ platform_profile_remove(tpacpi_pprof);
}
static struct ibm_struct dytc_profile_driver_data = {
@@ -11439,6 +11483,8 @@ static int __must_check __init get_thinkpad_model_data(
tp->vendor = PCI_VENDOR_ID_IBM;
else if (dmi_name_in_vendors("LENOVO"))
tp->vendor = PCI_VENDOR_ID_LENOVO;
+ else if (dmi_name_in_vendors("NEC"))
+ tp->vendor = PCI_VENDOR_ID_LENOVO;
else
return 0;
@@ -11682,7 +11728,7 @@ static int __init set_ibm_param(const char *val, const struct kernel_param *kp)
if (strcmp(ibm->name, kp->name) == 0 && ibm->write) {
if (strlen(val) > sizeof(ibms_init[i].param) - 1)
return -ENOSPC;
- strcpy(ibms_init[i].param, val);
+ strscpy(ibms_init[i].param, val);
return 0;
}
}
@@ -11786,36 +11832,18 @@ MODULE_PARM_DESC(profile_force, "Force profile mode. -1=off, 1=MMC, 2=PSC");
static void thinkpad_acpi_module_exit(void)
{
- struct ibm_struct *ibm, *itmp;
-
tpacpi_lifecycle = TPACPI_LIFE_EXITING;
- if (tpacpi_hwmon)
- hwmon_device_unregister(tpacpi_hwmon);
- if (tp_features.sensors_pdrv_registered)
+ if (tpacpi_sensors_pdev) {
platform_driver_unregister(&tpacpi_hwmon_pdriver);
- if (tp_features.platform_drv_registered)
- platform_driver_unregister(&tpacpi_pdriver);
-
- list_for_each_entry_safe_reverse(ibm, itmp,
- &tpacpi_all_drivers,
- all_drivers) {
- ibm_exit(ibm);
- }
-
- dbg_printk(TPACPI_DBG_INIT, "finished subdriver exit path...\n");
-
- if (tpacpi_inputdev) {
- if (tp_features.input_device_registered)
- input_unregister_device(tpacpi_inputdev);
- else
- input_free_device(tpacpi_inputdev);
+ platform_device_unregister(tpacpi_sensors_pdev);
}
- if (tpacpi_sensors_pdev)
- platform_device_unregister(tpacpi_sensors_pdev);
+ if (tp_features.platform_drv_registered)
+ platform_driver_unregister(&tpacpi_pdriver);
if (tpacpi_pdev)
platform_device_unregister(tpacpi_pdev);
+
if (proc_dir)
remove_proc_entry(TPACPI_PROC_DIR, acpi_root_dir);
if (tpacpi_wq)
@@ -11827,11 +11855,75 @@ static void thinkpad_acpi_module_exit(void)
kfree(thinkpad_id.nummodel_str);
}
+static void tpacpi_subdrivers_release(void *data)
+{
+ struct ibm_struct *ibm, *itmp;
+
+ list_for_each_entry_safe_reverse(ibm, itmp, &tpacpi_all_drivers, all_drivers)
+ ibm_exit(ibm);
+
+ dbg_printk(TPACPI_DBG_INIT, "finished subdriver exit path...\n");
+}
+
+static int __init tpacpi_pdriver_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = devm_mutex_init(&pdev->dev, &tpacpi_inputdev_send_mutex);
+ if (ret)
+ return ret;
+
+ tpacpi_inputdev = devm_input_allocate_device(&pdev->dev);
+ if (!tpacpi_inputdev)
+ return -ENOMEM;
+
+ tpacpi_inputdev->name = "ThinkPad Extra Buttons";
+ tpacpi_inputdev->phys = TPACPI_DRVR_NAME "/input0";
+ tpacpi_inputdev->id.bustype = BUS_HOST;
+ tpacpi_inputdev->id.vendor = thinkpad_id.vendor;
+ tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT;
+ tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION;
+ tpacpi_inputdev->dev.parent = &tpacpi_pdev->dev;
+
+ /* Init subdriver dependencies */
+ tpacpi_detect_brightness_capabilities();
+
+ /* Init subdrivers */
+ for (unsigned int i = 0; i < ARRAY_SIZE(ibms_init); i++) {
+ ret = ibm_init(&ibms_init[i]);
+ if (ret >= 0 && *ibms_init[i].param)
+ ret = ibms_init[i].data->write(ibms_init[i].param);
+ if (ret < 0) {
+ tpacpi_subdrivers_release(NULL);
+ return ret;
+ }
+ }
+
+ ret = devm_add_action_or_reset(&pdev->dev, tpacpi_subdrivers_release, NULL);
+ if (ret)
+ return ret;
+
+ ret = input_register_device(tpacpi_inputdev);
+ if (ret < 0)
+ pr_err("unable to register input device\n");
+
+ return ret;
+}
+
+static int __init tpacpi_hwmon_pdriver_probe(struct platform_device *pdev)
+{
+ tpacpi_hwmon = devm_hwmon_device_register_with_groups(&pdev->dev, TPACPI_NAME,
+ NULL, tpacpi_hwmon_groups);
+ if (IS_ERR(tpacpi_hwmon))
+ pr_err("unable to register hwmon device\n");
+
+ return PTR_ERR_OR_ZERO(tpacpi_hwmon);
+}
static int __init thinkpad_acpi_module_init(void)
{
const struct dmi_system_id *dmi_id;
- int ret, i;
+ int ret;
acpi_object_type obj_type;
tpacpi_lifecycle = TPACPI_LIFE_INIT;
@@ -11892,7 +11984,7 @@ static int __init thinkpad_acpi_module_init(void)
/* Device initialization */
tpacpi_pdev = platform_device_register_simple(TPACPI_DRVR_NAME, PLATFORM_DEVID_NONE,
- NULL, 0);
+ NULL, 0);
if (IS_ERR(tpacpi_pdev)) {
ret = PTR_ERR(tpacpi_pdev);
tpacpi_pdev = NULL;
@@ -11900,50 +11992,8 @@ static int __init thinkpad_acpi_module_init(void)
thinkpad_acpi_module_exit();
return ret;
}
- tpacpi_sensors_pdev = platform_device_register_simple(
- TPACPI_HWMON_DRVR_NAME,
- PLATFORM_DEVID_NONE, NULL, 0);
- if (IS_ERR(tpacpi_sensors_pdev)) {
- ret = PTR_ERR(tpacpi_sensors_pdev);
- tpacpi_sensors_pdev = NULL;
- pr_err("unable to register hwmon platform device\n");
- thinkpad_acpi_module_exit();
- return ret;
- }
- mutex_init(&tpacpi_inputdev_send_mutex);
- tpacpi_inputdev = input_allocate_device();
- if (!tpacpi_inputdev) {
- thinkpad_acpi_module_exit();
- return -ENOMEM;
- } else {
- /* Prepare input device, but don't register */
- tpacpi_inputdev->name = "ThinkPad Extra Buttons";
- tpacpi_inputdev->phys = TPACPI_DRVR_NAME "/input0";
- tpacpi_inputdev->id.bustype = BUS_HOST;
- tpacpi_inputdev->id.vendor = thinkpad_id.vendor;
- tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT;
- tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION;
- tpacpi_inputdev->dev.parent = &tpacpi_pdev->dev;
- }
-
- /* Init subdriver dependencies */
- tpacpi_detect_brightness_capabilities();
-
- /* Init subdrivers */
- for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
- ret = ibm_init(&ibms_init[i]);
- if (ret >= 0 && *ibms_init[i].param)
- ret = ibms_init[i].data->write(ibms_init[i].param);
- if (ret < 0) {
- thinkpad_acpi_module_exit();
- return ret;
- }
- }
-
- tpacpi_lifecycle = TPACPI_LIFE_RUNNING;
-
- ret = platform_driver_register(&tpacpi_pdriver);
+ ret = platform_driver_probe(&tpacpi_pdriver, tpacpi_pdriver_probe);
if (ret) {
pr_err("unable to register main platform driver\n");
thinkpad_acpi_module_exit();
@@ -11951,32 +12001,18 @@ static int __init thinkpad_acpi_module_init(void)
}
tp_features.platform_drv_registered = 1;
- ret = platform_driver_register(&tpacpi_hwmon_pdriver);
- if (ret) {
- pr_err("unable to register hwmon platform driver\n");
- thinkpad_acpi_module_exit();
- return ret;
- }
- tp_features.sensors_pdrv_registered = 1;
-
- tpacpi_hwmon = hwmon_device_register_with_groups(
- &tpacpi_sensors_pdev->dev, TPACPI_NAME, NULL, tpacpi_hwmon_groups);
- if (IS_ERR(tpacpi_hwmon)) {
- ret = PTR_ERR(tpacpi_hwmon);
- tpacpi_hwmon = NULL;
- pr_err("unable to register hwmon device\n");
+ tpacpi_sensors_pdev = platform_create_bundle(&tpacpi_hwmon_pdriver,
+ tpacpi_hwmon_pdriver_probe,
+ NULL, 0, NULL, 0);
+ if (IS_ERR(tpacpi_sensors_pdev)) {
+ ret = PTR_ERR(tpacpi_sensors_pdev);
+ tpacpi_sensors_pdev = NULL;
+ pr_err("unable to register hwmon platform device/driver bundle\n");
thinkpad_acpi_module_exit();
return ret;
}
- ret = input_register_device(tpacpi_inputdev);
- if (ret < 0) {
- pr_err("unable to register input device\n");
- thinkpad_acpi_module_exit();
- return ret;
- } else {
- tp_features.input_device_registered = 1;
- }
+ tpacpi_lifecycle = TPACPI_LIFE_RUNNING;
return 0;
}
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index 78a5aac2dcfd..5ad3a7183d33 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -2755,7 +2755,7 @@ static int toshiba_acpi_enable_hotkeys(struct toshiba_acpi_dev *dev)
}
static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str,
- struct serio *port)
+ struct serio *port, void *context)
{
if (str & I8042_STR_AUXDATA)
return false;
@@ -2915,7 +2915,7 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
if (ec_handle && acpi_has_method(ec_handle, "NTFY")) {
INIT_WORK(&dev->hotkey_work, toshiba_acpi_hotkey_work);
- error = i8042_install_filter(toshiba_acpi_i8042_filter);
+ error = i8042_install_filter(toshiba_acpi_i8042_filter, NULL);
if (error) {
pr_err("Error installing key filter\n");
goto err_free_dev;
diff --git a/drivers/platform/x86/wmi-bmof.c b/drivers/platform/x86/wmi-bmof.c
index df6f0ae6e6c7..3e33da36da8a 100644
--- a/drivers/platform/x86/wmi-bmof.c
+++ b/drivers/platform/x86/wmi-bmof.c
@@ -20,66 +20,66 @@
#define WMI_BMOF_GUID "05901221-D566-11D1-B2F0-00A0C9062910"
-struct bmof_priv {
- union acpi_object *bmofdata;
- struct bin_attribute bmof_bin_attr;
-};
-
-static ssize_t read_bmof(struct file *filp, struct kobject *kobj, struct bin_attribute *attr,
+static ssize_t bmof_read(struct file *filp, struct kobject *kobj, const struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
- struct bmof_priv *priv = container_of(attr, struct bmof_priv, bmof_bin_attr);
+ struct device *dev = kobj_to_dev(kobj);
+ union acpi_object *obj = dev_get_drvdata(dev);
- return memory_read_from_buffer(buf, count, &off, priv->bmofdata->buffer.pointer,
- priv->bmofdata->buffer.length);
+ return memory_read_from_buffer(buf, count, &off, obj->buffer.pointer, obj->buffer.length);
}
-static int wmi_bmof_probe(struct wmi_device *wdev, const void *context)
+static const BIN_ATTR_ADMIN_RO(bmof, 0);
+
+static const struct bin_attribute * const bmof_attrs[] = {
+ &bin_attr_bmof,
+ NULL
+};
+
+static size_t bmof_bin_size(struct kobject *kobj, const struct bin_attribute *attr, int n)
{
- struct bmof_priv *priv;
- int ret;
+ struct device *dev = kobj_to_dev(kobj);
+ union acpi_object *obj = dev_get_drvdata(dev);
+
+ return obj->buffer.length;
+}
- priv = devm_kzalloc(&wdev->dev, sizeof(struct bmof_priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
+static const struct attribute_group bmof_group = {
+ .bin_size = bmof_bin_size,
+ .bin_attrs_new = bmof_attrs,
+};
+
+static const struct attribute_group *bmof_groups[] = {
+ &bmof_group,
+ NULL
+};
- dev_set_drvdata(&wdev->dev, priv);
+static int wmi_bmof_probe(struct wmi_device *wdev, const void *context)
+{
+ union acpi_object *obj;
- priv->bmofdata = wmidev_block_query(wdev, 0);
- if (!priv->bmofdata) {
+ obj = wmidev_block_query(wdev, 0);
+ if (!obj) {
dev_err(&wdev->dev, "failed to read Binary MOF\n");
return -EIO;
}
- if (priv->bmofdata->type != ACPI_TYPE_BUFFER) {
+ if (obj->type != ACPI_TYPE_BUFFER) {
dev_err(&wdev->dev, "Binary MOF is not a buffer\n");
- ret = -EIO;
- goto err_free;
+ kfree(obj);
+ return -EIO;
}
- sysfs_bin_attr_init(&priv->bmof_bin_attr);
- priv->bmof_bin_attr.attr.name = "bmof";
- priv->bmof_bin_attr.attr.mode = 0400;
- priv->bmof_bin_attr.read = read_bmof;
- priv->bmof_bin_attr.size = priv->bmofdata->buffer.length;
-
- ret = device_create_bin_file(&wdev->dev, &priv->bmof_bin_attr);
- if (ret)
- goto err_free;
+ dev_set_drvdata(&wdev->dev, obj);
return 0;
-
- err_free:
- kfree(priv->bmofdata);
- return ret;
}
static void wmi_bmof_remove(struct wmi_device *wdev)
{
- struct bmof_priv *priv = dev_get_drvdata(&wdev->dev);
+ union acpi_object *obj = dev_get_drvdata(&wdev->dev);
- device_remove_bin_file(&wdev->dev, &priv->bmof_bin_attr);
- kfree(priv->bmofdata);
+ kfree(obj);
}
static const struct wmi_device_id wmi_bmof_id_table[] = {
@@ -90,6 +90,7 @@ static const struct wmi_device_id wmi_bmof_id_table[] = {
static struct wmi_driver wmi_bmof_driver = {
.driver = {
.name = "wmi-bmof",
+ .dev_groups = bmof_groups,
},
.probe = wmi_bmof_probe,
.remove = wmi_bmof_remove,
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index 646370bd6b03..e46453750d5f 100644
--- a/drivers/platform/x86/wmi.c
+++ b/drivers/platform/x86/wmi.c
@@ -123,24 +123,6 @@ static const void *find_guid_context(struct wmi_block *wblock,
return NULL;
}
-static acpi_status wmi_method_enable(struct wmi_block *wblock, bool enable)
-{
- struct guid_block *block;
- char method[5];
- acpi_status status;
- acpi_handle handle;
-
- block = &wblock->gblock;
- handle = wblock->acpi_device->handle;
-
- snprintf(method, 5, "WE%02X", block->notify_id);
- status = acpi_execute_simple_method(handle, method, enable);
- if (status == AE_NOT_FOUND)
- return AE_OK;
-
- return status;
-}
-
#define WMI_ACPI_METHOD_NAME_SIZE 5
static inline void get_acpi_method_name(const struct wmi_block *wblock,
@@ -184,6 +166,44 @@ static int wmidev_match_guid(struct device *dev, const void *data)
static const struct bus_type wmi_bus_type;
+static const struct device_type wmi_type_event;
+
+static const struct device_type wmi_type_method;
+
+static int wmi_device_enable(struct wmi_device *wdev, bool enable)
+{
+ struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev);
+ char method[WMI_ACPI_METHOD_NAME_SIZE];
+ acpi_handle handle;
+ acpi_status status;
+
+ if (!(wblock->gblock.flags & ACPI_WMI_EXPENSIVE))
+ return 0;
+
+ if (wblock->dev.dev.type == &wmi_type_method)
+ return 0;
+
+ if (wblock->dev.dev.type == &wmi_type_event)
+ snprintf(method, sizeof(method), "WE%02X", wblock->gblock.notify_id);
+ else
+ get_acpi_method_name(wblock, 'C', method);
+
+ /*
+ * Not all WMI devices marked as expensive actually implement the
+ * necessary ACPI method. Ignore this missing ACPI method to match
+ * the behaviour of the Windows driver.
+ */
+ status = acpi_get_handle(wblock->acpi_device->handle, method, &handle);
+ if (ACPI_FAILURE(status))
+ return 0;
+
+ status = acpi_execute_simple_method(handle, NULL, enable);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ return 0;
+}
+
static struct wmi_device *wmi_find_device_by_guid(const char *guid_string)
{
struct device *dev;
@@ -337,10 +357,8 @@ static acpi_status __query_block(struct wmi_block *wblock, u8 instance,
{
struct guid_block *block;
acpi_handle handle;
- acpi_status status, wc_status = AE_ERROR;
struct acpi_object_list input;
union acpi_object wq_params[1];
- char wc_method[WMI_ACPI_METHOD_NAME_SIZE];
char method[WMI_ACPI_METHOD_NAME_SIZE];
if (!out)
@@ -364,40 +382,9 @@ static acpi_status __query_block(struct wmi_block *wblock, u8 instance,
if (instance == 0 && test_bit(WMI_READ_TAKES_NO_ARGS, &wblock->flags))
input.count = 0;
- /*
- * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
- * enable collection.
- */
- if (block->flags & ACPI_WMI_EXPENSIVE) {
- get_acpi_method_name(wblock, 'C', wc_method);
-
- /*
- * Some GUIDs break the specification by declaring themselves
- * expensive, but have no corresponding WCxx method. So we
- * should not fail if this happens.
- */
- wc_status = acpi_execute_simple_method(handle, wc_method, 1);
- }
-
get_acpi_method_name(wblock, 'Q', method);
- status = acpi_evaluate_object(handle, method, &input, out);
-
- /*
- * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if
- * the WQxx method failed - we should disable collection anyway.
- */
- if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) {
- /*
- * Ignore whether this WCxx call succeeds or not since
- * the previously executed WQxx method call might have
- * succeeded, and returning the failing status code
- * of this call would throw away the result of the WQxx
- * call, potentially leaking memory.
- */
- acpi_execute_simple_method(handle, wc_method, 0);
- }
- return status;
+ return acpi_evaluate_object(handle, method, &input, out);
}
/**
@@ -421,9 +408,15 @@ acpi_status wmi_query_block(const char *guid_string, u8 instance,
if (IS_ERR(wdev))
return AE_ERROR;
+ if (wmi_device_enable(wdev, true) < 0)
+ dev_warn(&wdev->dev, "Failed to enable device\n");
+
wblock = container_of(wdev, struct wmi_block, dev);
status = __query_block(wblock, instance, out);
+ if (wmi_device_enable(wdev, false) < 0)
+ dev_warn(&wdev->dev, "Failed to disable device\n");
+
wmi_device_put(wdev);
return status;
@@ -470,7 +463,14 @@ acpi_status wmi_set_block(const char *guid_string, u8 instance, const struct acp
if (IS_ERR(wdev))
return AE_ERROR;
+ if (wmi_device_enable(wdev, true) < 0)
+ dev_warn(&wdev->dev, "Failed to enable device\n");
+
status = wmidev_block_set(wdev, instance, in);
+
+ if (wmi_device_enable(wdev, false) < 0)
+ dev_warn(&wdev->dev, "Failed to disable device\n");
+
wmi_device_put(wdev);
return status;
@@ -551,7 +551,7 @@ acpi_status wmi_install_notify_handler(const char *guid,
wblock->handler = handler;
wblock->handler_data = data;
- if (ACPI_FAILURE(wmi_method_enable(wblock, true)))
+ if (wmi_device_enable(wdev, true) < 0)
dev_warn(&wblock->dev.dev, "Failed to enable device\n");
status = AE_OK;
@@ -588,7 +588,7 @@ acpi_status wmi_remove_notify_handler(const char *guid)
if (!wblock->handler) {
status = AE_NULL_ENTRY;
} else {
- if (ACPI_FAILURE(wmi_method_enable(wblock, false)))
+ if (wmi_device_enable(wdev, false) < 0)
dev_warn(&wblock->dev.dev, "Failed to disable device\n");
wblock->handler = NULL;
@@ -821,11 +821,19 @@ static int wmi_dev_match(struct device *dev, const struct device_driver *driver)
return 0;
}
+static void wmi_dev_disable(void *data)
+{
+ struct device *dev = data;
+
+ if (wmi_device_enable(to_wmi_device(dev), false) < 0)
+ dev_warn(dev, "Failed to disable device\n");
+}
+
static int wmi_dev_probe(struct device *dev)
{
struct wmi_block *wblock = dev_to_wblock(dev);
struct wmi_driver *wdriver = to_wmi_driver(dev->driver);
- int ret = 0;
+ int ret;
/* Some older WMI drivers will break if instantiated multiple times,
* so they are blocked from probing WMI devices with a duplicated GUID.
@@ -844,18 +852,22 @@ static int wmi_dev_probe(struct device *dev)
return -ENODEV;
}
- if (ACPI_FAILURE(wmi_method_enable(wblock, true)))
+ if (wmi_device_enable(to_wmi_device(dev), true) < 0)
dev_warn(dev, "failed to enable device -- probing anyway\n");
+ /*
+ * We have to make sure that all devres-managed resources are released first because
+ * some might still want to access the underlying WMI device.
+ */
+ ret = devm_add_action_or_reset(dev, wmi_dev_disable, dev);
+ if (ret < 0)
+ return ret;
+
if (wdriver->probe) {
ret = wdriver->probe(to_wmi_device(dev),
find_guid_context(wblock, wdriver));
- if (ret) {
- if (ACPI_FAILURE(wmi_method_enable(wblock, false)))
- dev_warn(dev, "Failed to disable device\n");
-
+ if (ret)
return ret;
- }
}
down_write(&wblock->notify_lock);
@@ -876,9 +888,6 @@ static void wmi_dev_remove(struct device *dev)
if (wdriver->remove)
wdriver->remove(to_wmi_device(dev));
-
- if (ACPI_FAILURE(wmi_method_enable(wblock, false)))
- dev_warn(dev, "failed to disable device\n");
}
static void wmi_dev_shutdown(struct device *dev)
@@ -902,7 +911,11 @@ static void wmi_dev_shutdown(struct device *dev)
if (wdriver->shutdown)
wdriver->shutdown(to_wmi_device(dev));
- if (ACPI_FAILURE(wmi_method_enable(wblock, false)))
+ /*
+ * We still need to disable the WMI device here since devres-managed resources
+ * like wmi_dev_disable() will not be release during shutdown.
+ */
+ if (wmi_device_enable(to_wmi_device(dev), false) < 0)
dev_warn(dev, "Failed to disable device\n");
}
}
diff --git a/drivers/platform/x86/x86-android-tablets/Kconfig b/drivers/platform/x86/x86-android-tablets/Kconfig
index a67bddc43007..193da15ee01c 100644
--- a/drivers/platform/x86/x86-android-tablets/Kconfig
+++ b/drivers/platform/x86/x86-android-tablets/Kconfig
@@ -10,6 +10,7 @@ config X86_ANDROID_TABLETS
depends on ACPI && EFI && PCI
select NEW_LEDS
select LEDS_CLASS
+ select POWER_SUPPLY
help
X86 tablets which ship with Android as (part of) the factory image
typically have various problems with their DSDTs. The factory kernels
diff --git a/drivers/platform/x86/x86-android-tablets/Makefile b/drivers/platform/x86/x86-android-tablets/Makefile
index 41ece5a37137..313be30548bc 100644
--- a/drivers/platform/x86/x86-android-tablets/Makefile
+++ b/drivers/platform/x86/x86-android-tablets/Makefile
@@ -3,7 +3,7 @@
# X86 Android tablet support Makefile
#
+obj-$(CONFIG_X86_ANDROID_TABLETS) += vexia_atla10_ec.o
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
diff --git a/drivers/platform/x86/x86-android-tablets/asus.c b/drivers/platform/x86/x86-android-tablets/asus.c
index 07fbeab2319a..7dde63b9943f 100644
--- a/drivers/platform/x86/x86-android-tablets/asus.c
+++ b/drivers/platform/x86/x86-android-tablets/asus.c
@@ -145,8 +145,8 @@ static const struct x86_i2c_client_info asus_me176c_i2c_clients[] __initconst =
static const struct x86_serdev_info asus_me176c_serdevs[] __initconst = {
{
- .ctrl_hid = "80860F0A",
- .ctrl_uid = "2",
+ .ctrl.acpi.hid = "80860F0A",
+ .ctrl.acpi.uid = "2",
.ctrl_devname = "serial0",
.serdev_hid = "BCM2E3A",
},
diff --git a/drivers/platform/x86/x86-android-tablets/core.c b/drivers/platform/x86/x86-android-tablets/core.c
index 40b5cd9b172f..2a9c47178505 100644
--- a/drivers/platform/x86/x86-android-tablets/core.c
+++ b/drivers/platform/x86/x86-android-tablets/core.c
@@ -212,7 +212,7 @@ static __init int x86_instantiate_i2c_client(const struct x86_dev_info *dev_info
if (board_info.irq < 0)
return board_info.irq;
- if (dev_info->use_pci_devname)
+ if (dev_info->use_pci)
adap = get_i2c_adap_by_pci_parent(client_info);
else
adap = get_i2c_adap_by_handle(client_info);
@@ -271,15 +271,32 @@ static __init int x86_instantiate_spi_dev(const struct x86_dev_info *dev_info, i
return 0;
}
-static __init int x86_instantiate_serdev(const struct x86_serdev_info *info, int idx)
+static __init struct device *
+get_serdev_controller_by_pci_parent(const struct x86_serdev_info *info)
{
+ struct pci_dev *pdev;
+
+ pdev = pci_get_domain_bus_and_slot(0, 0, info->ctrl.pci.devfn);
+ if (!pdev)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ /* This puts our reference on pdev and returns a ref on the ctrl */
+ return get_serdev_controller_from_parent(&pdev->dev, 0, info->ctrl_devname);
+}
+
+static __init int x86_instantiate_serdev(const struct x86_dev_info *dev_info, int idx)
+{
+ const struct x86_serdev_info *info = &dev_info->serdev_info[idx];
struct acpi_device *serdev_adev;
struct serdev_device *serdev;
struct device *ctrl_dev;
int ret = -ENODEV;
- ctrl_dev = get_serdev_controller(info->ctrl_hid, info->ctrl_uid, 0,
- info->ctrl_devname);
+ if (dev_info->use_pci)
+ ctrl_dev = get_serdev_controller_by_pci_parent(info);
+ else
+ ctrl_dev = get_serdev_controller(info->ctrl.acpi.hid, info->ctrl.acpi.uid,
+ 0, info->ctrl_devname);
if (IS_ERR(ctrl_dev))
return PTR_ERR(ctrl_dev);
@@ -446,7 +463,7 @@ static __init int x86_android_tablet_probe(struct platform_device *pdev)
serdev_count = dev_info->serdev_count;
for (i = 0; i < serdev_count; i++) {
- ret = x86_instantiate_serdev(&dev_info->serdev_info[i], i);
+ ret = x86_instantiate_serdev(dev_info, i);
if (ret < 0) {
x86_android_tablet_remove(pdev);
return ret;
diff --git a/drivers/platform/x86/x86-android-tablets/dmi.c b/drivers/platform/x86/x86-android-tablets/dmi.c
index 3e5fa3b6e2fd..278c6d151dc4 100644
--- a/drivers/platform/x86/x86-android-tablets/dmi.c
+++ b/drivers/platform/x86/x86-android-tablets/dmi.c
@@ -180,6 +180,18 @@ const struct dmi_system_id x86_android_tablet_ids[] __initconst = {
.driver_data = (void *)&peaq_c1010_info,
},
{
+ /* Vexia Edu Atla 10 tablet 5V version */
+ .matches = {
+ /* Having all 3 of these not set is somewhat unique */
+ DMI_MATCH(DMI_SYS_VENDOR, "To be filled by O.E.M."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "To be filled by O.E.M."),
+ DMI_MATCH(DMI_BOARD_NAME, "To be filled by O.E.M."),
+ /* Above strings are too generic, also match on BIOS date */
+ DMI_MATCH(DMI_BIOS_DATE, "05/14/2015"),
+ },
+ .driver_data = (void *)&vexia_edu_atla10_5v_info,
+ },
+ {
/* Vexia Edu Atla 10 tablet 9V version */
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
@@ -187,7 +199,7 @@ const struct dmi_system_id x86_android_tablet_ids[] __initconst = {
/* Above strings are too generic, also match on BIOS date */
DMI_MATCH(DMI_BIOS_DATE, "08/25/2014"),
},
- .driver_data = (void *)&vexia_edu_atla10_info,
+ .driver_data = (void *)&vexia_edu_atla10_9v_info,
},
{
/* Whitelabel (sold as various brands) TM800A550L */
diff --git a/drivers/platform/x86/x86-android-tablets/lenovo.c b/drivers/platform/x86/x86-android-tablets/lenovo.c
index a60efbaf4817..1241a97cda39 100644
--- a/drivers/platform/x86/x86-android-tablets/lenovo.c
+++ b/drivers/platform/x86/x86-android-tablets/lenovo.c
@@ -178,8 +178,8 @@ static const struct platform_device_info lenovo_yb1_x90_pdevs[] __initconst = {
*/
static const struct x86_serdev_info lenovo_yb1_x90_serdevs[] __initconst = {
{
- .ctrl_hid = "8086228A",
- .ctrl_uid = "1",
+ .ctrl.acpi.hid = "8086228A",
+ .ctrl.acpi.uid = "1",
.ctrl_devname = "serial0",
.serdev_hid = "BCM2E1A",
},
diff --git a/drivers/platform/x86/x86-android-tablets/other.c b/drivers/platform/x86/x86-android-tablets/other.c
index 3cd0db74c6c9..f7bd9f863c85 100644
--- a/drivers/platform/x86/x86-android-tablets/other.c
+++ b/drivers/platform/x86/x86-android-tablets/other.c
@@ -599,62 +599,122 @@ const struct x86_dev_info whitelabel_tm800a550l_info __initconst = {
};
/*
- * Vexia EDU ATLA 10 tablet, Android 4.2 / 4.4 + Guadalinex Ubuntu tablet
+ * Vexia EDU ATLA 10 tablet 5V, Android 4.4 + Guadalinex Ubuntu tablet
+ * distributed to schools in the Spanish AndalucĂ­a region.
+ */
+static const struct property_entry vexia_edu_atla10_5v_touchscreen_props[] = {
+ PROPERTY_ENTRY_U32("hid-descr-addr", 0x0000),
+ PROPERTY_ENTRY_U32("post-reset-deassert-delay-ms", 120),
+ { }
+};
+
+static const struct software_node vexia_edu_atla10_5v_touchscreen_node = {
+ .properties = vexia_edu_atla10_5v_touchscreen_props,
+};
+
+static const struct x86_i2c_client_info vexia_edu_atla10_5v_i2c_clients[] __initconst = {
+ {
+ /* kxcjk1013 accelerometer */
+ .board_info = {
+ .type = "kxcjk1013",
+ .addr = 0x0f,
+ .dev_name = "kxcjk1013",
+ },
+ .adapter_path = "\\_SB_.I2C3",
+ }, {
+ /* touchscreen controller */
+ .board_info = {
+ .type = "hid-over-i2c",
+ .addr = 0x38,
+ .dev_name = "FTSC1000",
+ .swnode = &vexia_edu_atla10_5v_touchscreen_node,
+ },
+ .adapter_path = "\\_SB_.I2C4",
+ .irq_data = {
+ .type = X86_ACPI_IRQ_TYPE_APIC,
+ .index = 0x44,
+ .trigger = ACPI_LEVEL_SENSITIVE,
+ .polarity = ACPI_ACTIVE_HIGH,
+ },
+ }
+};
+
+static struct gpiod_lookup_table vexia_edu_atla10_5v_ft5416_gpios = {
+ .dev_id = "i2c-FTSC1000",
+ .table = {
+ GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_LOW),
+ { }
+ },
+};
+
+static struct gpiod_lookup_table * const vexia_edu_atla10_5v_gpios[] = {
+ &vexia_edu_atla10_5v_ft5416_gpios,
+ NULL
+};
+
+const struct x86_dev_info vexia_edu_atla10_5v_info __initconst = {
+ .i2c_client_info = vexia_edu_atla10_5v_i2c_clients,
+ .i2c_client_count = ARRAY_SIZE(vexia_edu_atla10_5v_i2c_clients),
+ .gpiod_lookup_tables = vexia_edu_atla10_5v_gpios,
+};
+
+/*
+ * Vexia EDU ATLA 10 tablet 9V, Android 4.2 + Guadalinex Ubuntu tablet
* distributed to schools in the Spanish AndalucĂ­a region.
*/
static const char * const crystal_cove_pwrsrc_psy[] = { "crystal_cove_pwrsrc" };
-static const struct property_entry vexia_edu_atla10_ulpmc_props[] = {
+static const struct property_entry vexia_edu_atla10_9v_ulpmc_props[] = {
PROPERTY_ENTRY_STRING_ARRAY("supplied-from", crystal_cove_pwrsrc_psy),
{ }
};
-static const struct software_node vexia_edu_atla10_ulpmc_node = {
- .properties = vexia_edu_atla10_ulpmc_props,
+static const struct software_node vexia_edu_atla10_9v_ulpmc_node = {
+ .properties = vexia_edu_atla10_9v_ulpmc_props,
};
-static const char * const vexia_edu_atla10_accel_mount_matrix[] = {
+static const char * const vexia_edu_atla10_9v_accel_mount_matrix[] = {
"0", "-1", "0",
"1", "0", "0",
"0", "0", "1"
};
-static const struct property_entry vexia_edu_atla10_accel_props[] = {
- PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", vexia_edu_atla10_accel_mount_matrix),
+static const struct property_entry vexia_edu_atla10_9v_accel_props[] = {
+ PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", vexia_edu_atla10_9v_accel_mount_matrix),
{ }
};
-static const struct software_node vexia_edu_atla10_accel_node = {
- .properties = vexia_edu_atla10_accel_props,
+static const struct software_node vexia_edu_atla10_9v_accel_node = {
+ .properties = vexia_edu_atla10_9v_accel_props,
};
-static const struct property_entry vexia_edu_atla10_touchscreen_props[] = {
+static const struct property_entry vexia_edu_atla10_9v_touchscreen_props[] = {
PROPERTY_ENTRY_U32("hid-descr-addr", 0x0000),
PROPERTY_ENTRY_U32("post-reset-deassert-delay-ms", 120),
{ }
};
-static const struct software_node vexia_edu_atla10_touchscreen_node = {
- .properties = vexia_edu_atla10_touchscreen_props,
+static const struct software_node vexia_edu_atla10_9v_touchscreen_node = {
+ .properties = vexia_edu_atla10_9v_touchscreen_props,
};
-static const struct property_entry vexia_edu_atla10_pmic_props[] = {
+static const struct property_entry vexia_edu_atla10_9v_pmic_props[] = {
PROPERTY_ENTRY_BOOL("linux,register-pwrsrc-power_supply"),
{ }
};
-static const struct software_node vexia_edu_atla10_pmic_node = {
- .properties = vexia_edu_atla10_pmic_props,
+static const struct software_node vexia_edu_atla10_9v_pmic_node = {
+ .properties = vexia_edu_atla10_9v_pmic_props,
};
-static const struct x86_i2c_client_info vexia_edu_atla10_i2c_clients[] __initconst = {
+static const struct x86_i2c_client_info vexia_edu_atla10_9v_i2c_clients[] __initconst = {
{
/* I2C attached embedded controller, used to access fuel-gauge */
.board_info = {
.type = "vexia_atla10_ec",
.addr = 0x76,
.dev_name = "ulpmc",
- .swnode = &vexia_edu_atla10_ulpmc_node,
+ .swnode = &vexia_edu_atla10_9v_ulpmc_node,
},
.adapter_path = "0000:00:18.1",
}, {
@@ -679,7 +739,7 @@ static const struct x86_i2c_client_info vexia_edu_atla10_i2c_clients[] __initcon
.type = "kxtj21009",
.addr = 0x0f,
.dev_name = "kxtj21009",
- .swnode = &vexia_edu_atla10_accel_node,
+ .swnode = &vexia_edu_atla10_9v_accel_node,
},
.adapter_path = "0000:00:18.5",
}, {
@@ -688,7 +748,7 @@ static const struct x86_i2c_client_info vexia_edu_atla10_i2c_clients[] __initcon
.type = "hid-over-i2c",
.addr = 0x38,
.dev_name = "FTSC1000",
- .swnode = &vexia_edu_atla10_touchscreen_node,
+ .swnode = &vexia_edu_atla10_9v_touchscreen_node,
},
.adapter_path = "0000:00:18.6",
.irq_data = {
@@ -703,7 +763,7 @@ static const struct x86_i2c_client_info vexia_edu_atla10_i2c_clients[] __initcon
.type = "intel_soc_pmic_crc",
.addr = 0x6e,
.dev_name = "intel_soc_pmic_crc",
- .swnode = &vexia_edu_atla10_pmic_node,
+ .swnode = &vexia_edu_atla10_9v_pmic_node,
},
.adapter_path = "0000:00:18.7",
.irq_data = {
@@ -715,7 +775,15 @@ static const struct x86_i2c_client_info vexia_edu_atla10_i2c_clients[] __initcon
}
};
-static struct gpiod_lookup_table vexia_edu_atla10_ft5416_gpios = {
+static const struct x86_serdev_info vexia_edu_atla10_9v_serdevs[] __initconst = {
+ {
+ .ctrl.pci.devfn = PCI_DEVFN(0x1e, 3),
+ .ctrl_devname = "serial0",
+ .serdev_hid = "OBDA8723",
+ },
+};
+
+static struct gpiod_lookup_table vexia_edu_atla10_9v_ft5416_gpios = {
.dev_id = "i2c-FTSC1000",
.table = {
GPIO_LOOKUP("INT33FC:00", 60, "reset", GPIO_ACTIVE_LOW),
@@ -723,12 +791,12 @@ static struct gpiod_lookup_table vexia_edu_atla10_ft5416_gpios = {
},
};
-static struct gpiod_lookup_table * const vexia_edu_atla10_gpios[] = {
- &vexia_edu_atla10_ft5416_gpios,
+static struct gpiod_lookup_table * const vexia_edu_atla10_9v_gpios[] = {
+ &vexia_edu_atla10_9v_ft5416_gpios,
NULL
};
-static int __init vexia_edu_atla10_init(struct device *dev)
+static int __init vexia_edu_atla10_9v_init(struct device *dev)
{
struct pci_dev *pdev;
int ret;
@@ -752,12 +820,14 @@ static int __init vexia_edu_atla10_init(struct device *dev)
return 0;
}
-const struct x86_dev_info vexia_edu_atla10_info __initconst = {
- .i2c_client_info = vexia_edu_atla10_i2c_clients,
- .i2c_client_count = ARRAY_SIZE(vexia_edu_atla10_i2c_clients),
- .gpiod_lookup_tables = vexia_edu_atla10_gpios,
- .init = vexia_edu_atla10_init,
- .use_pci_devname = true,
+const struct x86_dev_info vexia_edu_atla10_9v_info __initconst = {
+ .i2c_client_info = vexia_edu_atla10_9v_i2c_clients,
+ .i2c_client_count = ARRAY_SIZE(vexia_edu_atla10_9v_i2c_clients),
+ .serdev_info = vexia_edu_atla10_9v_serdevs,
+ .serdev_count = ARRAY_SIZE(vexia_edu_atla10_9v_serdevs),
+ .gpiod_lookup_tables = vexia_edu_atla10_9v_gpios,
+ .init = vexia_edu_atla10_9v_init,
+ .use_pci = true,
};
/*
diff --git a/drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c b/drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c
new file mode 100644
index 000000000000..5d02af1c5aaa
--- /dev/null
+++ b/drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c
@@ -0,0 +1,261 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * power_supply class (battery) driver for the I2C attached embedded controller
+ * found on Vexia EDU ATLA 10 (9V version) tablets.
+ *
+ * This is based on the ACPI Battery device in the DSDT which should work
+ * expect that it expects the I2C controller to be enumerated as an ACPI
+ * device and the tablet's BIOS enumerates all LPSS devices as PCI devices
+ * (and changing the LPSS BIOS settings from PCI -> ACPI does not work).
+ *
+ * Copyright (c) 2024 Hans de Goede <hansg@kernel.org>
+ */
+
+#include <linux/bits.h>
+#include <linux/devm-helpers.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/power_supply.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include <asm/byteorder.h>
+
+/* State field uses ACPI Battery spec status bits */
+#define ACPI_BATTERY_STATE_DISCHARGING BIT(0)
+#define ACPI_BATTERY_STATE_CHARGING BIT(1)
+
+#define ATLA10_EC_BATTERY_STATE_COMMAND 0x87
+#define ATLA10_EC_BATTERY_INFO_COMMAND 0x88
+
+/* From broken ACPI battery device in DSDT */
+#define ATLA10_EC_VOLTAGE_MIN_DESIGN_uV 3750000
+
+/* Update data every 5 seconds */
+#define UPDATE_INTERVAL_JIFFIES (5 * HZ)
+
+struct atla10_ec_battery_state {
+ u8 status; /* Using ACPI Battery spec status bits */
+ u8 capacity; /* Percent */
+ __le16 charge_now_mAh;
+ __le16 voltage_now_mV;
+ __le16 current_now_mA;
+ __le16 charge_full_mAh;
+ __le16 temp; /* centi degrees Celsius */
+} __packed;
+
+struct atla10_ec_battery_info {
+ __le16 charge_full_design_mAh;
+ __le16 voltage_now_mV; /* Should be design voltage, but is not ? */
+ __le16 charge_full_design2_mAh;
+} __packed;
+
+struct atla10_ec_data {
+ struct i2c_client *client;
+ struct power_supply *psy;
+ struct delayed_work work;
+ struct mutex update_lock;
+ struct atla10_ec_battery_info info;
+ struct atla10_ec_battery_state state;
+ bool valid; /* true if state is valid */
+ unsigned long last_update; /* In jiffies */
+};
+
+static int atla10_ec_cmd(struct atla10_ec_data *data, u8 cmd, u8 len, u8 *values)
+{
+ struct device *dev = &data->client->dev;
+ u8 buf[I2C_SMBUS_BLOCK_MAX];
+ int ret;
+
+ ret = i2c_smbus_read_block_data(data->client, cmd, buf);
+ if (ret != len) {
+ dev_err(dev, "I2C command 0x%02x error: %d\n", cmd, ret);
+ return -EIO;
+ }
+
+ memcpy(values, buf, len);
+ return 0;
+}
+
+static int atla10_ec_update(struct atla10_ec_data *data)
+{
+ int ret;
+
+ if (data->valid && time_before(jiffies, data->last_update + UPDATE_INTERVAL_JIFFIES))
+ return 0;
+
+ ret = atla10_ec_cmd(data, ATLA10_EC_BATTERY_STATE_COMMAND,
+ sizeof(data->state), (u8 *)&data->state);
+ if (ret)
+ return ret;
+
+ data->last_update = jiffies;
+ data->valid = true;
+ return 0;
+}
+
+static int atla10_ec_psy_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct atla10_ec_data *data = power_supply_get_drvdata(psy);
+ int charge_now_mAh, charge_full_mAh, ret;
+
+ guard(mutex)(&data->update_lock);
+
+ ret = atla10_ec_update(data);
+ if (ret)
+ return ret;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ if (data->state.status & ACPI_BATTERY_STATE_DISCHARGING)
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ else if (data->state.status & ACPI_BATTERY_STATE_CHARGING)
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ else if (data->state.capacity == 100)
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ else
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = data->state.capacity;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ /*
+ * The EC has a bug where it reports charge-full-design as
+ * charge-now when the battery is full. Clamp charge-now to
+ * charge-full to workaround this.
+ */
+ charge_now_mAh = le16_to_cpu(data->state.charge_now_mAh);
+ charge_full_mAh = le16_to_cpu(data->state.charge_full_mAh);
+ val->intval = min(charge_now_mAh, charge_full_mAh) * 1000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = le16_to_cpu(data->state.voltage_now_mV) * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = le16_to_cpu(data->state.current_now_mA) * 1000;
+ /*
+ * Documentation/ABI/testing/sysfs-class-power specifies
+ * negative current for discharging.
+ */
+ if (data->state.status & ACPI_BATTERY_STATE_DISCHARGING)
+ val->intval = -val->intval;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ val->intval = le16_to_cpu(data->state.charge_full_mAh) * 1000;
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = le16_to_cpu(data->state.temp) / 10;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ val->intval = le16_to_cpu(data->info.charge_full_design_mAh) * 1000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ val->intval = ATLA10_EC_VOLTAGE_MIN_DESIGN_uV;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = 1;
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void atla10_ec_external_power_changed_work(struct work_struct *work)
+{
+ struct atla10_ec_data *data = container_of(work, struct atla10_ec_data, work.work);
+
+ dev_dbg(&data->client->dev, "External power changed\n");
+ data->valid = false;
+ power_supply_changed(data->psy);
+}
+
+static void atla10_ec_external_power_changed(struct power_supply *psy)
+{
+ struct atla10_ec_data *data = power_supply_get_drvdata(psy);
+
+ /* After charger plug in/out wait 0.5s for things to stabilize */
+ mod_delayed_work(system_wq, &data->work, HZ / 2);
+}
+
+static const enum power_supply_property atla10_ec_psy_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+};
+
+static const struct power_supply_desc atla10_ec_psy_desc = {
+ .name = "atla10_ec_battery",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = atla10_ec_psy_props,
+ .num_properties = ARRAY_SIZE(atla10_ec_psy_props),
+ .get_property = atla10_ec_psy_get_property,
+ .external_power_changed = atla10_ec_external_power_changed,
+};
+
+static int atla10_ec_probe(struct i2c_client *client)
+{
+ struct power_supply_config psy_cfg = { };
+ struct device *dev = &client->dev;
+ struct atla10_ec_data *data;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ psy_cfg.drv_data = data;
+ data->client = client;
+
+ ret = devm_mutex_init(dev, &data->update_lock);
+ if (ret)
+ return ret;
+
+ ret = devm_delayed_work_autocancel(dev, &data->work,
+ atla10_ec_external_power_changed_work);
+ if (ret)
+ return ret;
+
+ ret = atla10_ec_cmd(data, ATLA10_EC_BATTERY_INFO_COMMAND,
+ sizeof(data->info), (u8 *)&data->info);
+ if (ret)
+ return ret;
+
+ data->psy = devm_power_supply_register(dev, &atla10_ec_psy_desc, &psy_cfg);
+ return PTR_ERR_OR_ZERO(data->psy);
+}
+
+static const struct i2c_device_id atla10_ec_id_table[] = {
+ { "vexia_atla10_ec" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, atla10_ec_id_table);
+
+static struct i2c_driver atla10_ec_driver = {
+ .driver = {
+ .name = "vexia_atla10_ec",
+ },
+ .probe = atla10_ec_probe,
+ .id_table = atla10_ec_id_table,
+};
+module_i2c_driver(atla10_ec_driver);
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Battery driver for Vexia EDU ATLA 10 tablet EC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h
index 0fc7e8cff672..dcf8d49e3b5f 100644
--- a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h
+++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h
@@ -57,8 +57,15 @@ struct x86_spi_dev_info {
};
struct x86_serdev_info {
- const char *ctrl_hid;
- const char *ctrl_uid;
+ union {
+ struct {
+ const char *hid;
+ const char *uid;
+ } acpi;
+ struct {
+ unsigned int devfn;
+ } pci;
+ } ctrl;
const char *ctrl_devname;
/*
* ATM the serdev core only supports of or ACPI matching; and so far all
@@ -91,7 +98,7 @@ struct x86_dev_info {
int gpio_button_count;
int (*init)(struct device *dev);
void (*exit)(void);
- bool use_pci_devname;
+ bool use_pci;
};
int x86_android_tablet_get_gpiod(const char *chip, int pin, const char *con_id,
@@ -120,7 +127,8 @@ extern const struct x86_dev_info nextbook_ares8_info;
extern const struct x86_dev_info nextbook_ares8a_info;
extern const struct x86_dev_info peaq_c1010_info;
extern const struct x86_dev_info whitelabel_tm800a550l_info;
-extern const struct x86_dev_info vexia_edu_atla10_info;
+extern const struct x86_dev_info vexia_edu_atla10_5v_info;
+extern const struct x86_dev_info vexia_edu_atla10_9v_info;
extern const struct x86_dev_info xiaomi_mipad2_info;
extern const struct dmi_system_id x86_android_tablet_ids[];