summaryrefslogtreecommitdiffstats
path: root/drivers/platform
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/Kconfig2
-rw-r--r--drivers/platform/Makefile1
-rw-r--r--drivers/platform/arm64/Kconfig35
-rw-r--r--drivers/platform/arm64/Makefile8
-rw-r--r--drivers/platform/arm64/acer-aspire1-ec.c562
-rw-r--r--drivers/platform/chrome/Kconfig1
-rw-r--r--drivers/platform/chrome/cros_ec.c16
-rw-r--r--drivers/platform/chrome/cros_ec_chardev.c9
-rw-r--r--drivers/platform/chrome/cros_ec_debugfs.c10
-rw-r--r--drivers/platform/chrome/cros_ec_lightbar.c9
-rw-r--r--drivers/platform/chrome/cros_ec_lpc.c131
-rw-r--r--drivers/platform/chrome/cros_ec_proto.c2
-rw-r--r--drivers/platform/chrome/cros_ec_proto_test.c72
-rw-r--r--drivers/platform/chrome/cros_ec_sensorhub.c9
-rw-r--r--drivers/platform/chrome/cros_ec_spi.c8
-rw-r--r--drivers/platform/chrome/cros_ec_sysfs.c9
-rw-r--r--drivers/platform/chrome/cros_ec_typec.c19
-rw-r--r--drivers/platform/chrome/cros_ec_uart.c4
-rw-r--r--drivers/platform/chrome/cros_ec_vbc.c9
-rw-r--r--drivers/platform/chrome/cros_hps_i2c.c4
-rw-r--r--drivers/platform/chrome/cros_kbd_led_backlight.c11
-rw-r--r--drivers/platform/chrome/cros_usbpd_logger.c9
-rw-r--r--drivers/platform/chrome/cros_usbpd_notify.c9
-rw-r--r--drivers/platform/chrome/wilco_ec/Kconfig1
-rw-r--r--drivers/platform/chrome/wilco_ec/core.c9
-rw-r--r--drivers/platform/chrome/wilco_ec/debugfs.c9
-rw-r--r--drivers/platform/chrome/wilco_ec/event.c2
-rw-r--r--drivers/platform/chrome/wilco_ec/sysfs.c2
-rw-r--r--drivers/platform/chrome/wilco_ec/telemetry.c9
-rw-r--r--drivers/platform/goldfish/Kconfig1
-rw-r--r--drivers/platform/mellanox/mlxbf-bootctl.c14
-rw-r--r--drivers/platform/mellanox/mlxbf-pmc.c267
-rw-r--r--drivers/platform/mellanox/mlxreg-hotplug.c14
-rw-r--r--drivers/platform/mellanox/nvsw-sn2201.c5
-rw-r--r--drivers/platform/mips/cpu_hwmon.c3
-rw-r--r--drivers/platform/surface/aggregator/core.c46
-rw-r--r--drivers/platform/surface/surface_aggregator_registry.c50
-rw-r--r--drivers/platform/surface/surface_platform_profile.c88
-rw-r--r--drivers/platform/x86/Kconfig68
-rw-r--r--drivers/platform/x86/Makefile9
-rw-r--r--drivers/platform/x86/acerhdf.c2
-rw-r--r--drivers/platform/x86/amd/Kconfig2
-rw-r--r--drivers/platform/x86/amd/hsmp.c632
-rw-r--r--drivers/platform/x86/amd/pmc/Kconfig15
-rw-r--r--drivers/platform/x86/amd/pmc/Makefile1
-rw-r--r--drivers/platform/x86/amd/pmc/mp2_stb.c280
-rw-r--r--drivers/platform/x86/amd/pmc/pmc.c5
-rw-r--r--drivers/platform/x86/amd/pmc/pmc.h15
-rw-r--r--drivers/platform/x86/amd/pmf/Makefile2
-rw-r--r--drivers/platform/x86/amd/pmf/acpi.c141
-rw-r--r--drivers/platform/x86/amd/pmf/core.c17
-rw-r--r--drivers/platform/x86/amd/pmf/pmf-quirks.c51
-rw-r--r--drivers/platform/x86/amd/pmf/pmf.h96
-rw-r--r--drivers/platform/x86/amd/pmf/spc.c32
-rw-r--r--drivers/platform/x86/amd/pmf/sps.c145
-rw-r--r--drivers/platform/x86/amd/pmf/tee-if.c23
-rw-r--r--drivers/platform/x86/amilo-rfkill.c1
-rw-r--r--drivers/platform/x86/asus-laptop.c45
-rw-r--r--drivers/platform/x86/asus-wmi.c567
-rw-r--r--drivers/platform/x86/classmate-laptop.c9
-rw-r--r--drivers/platform/x86/compal-laptop.c2
-rw-r--r--drivers/platform/x86/dell/Kconfig18
-rw-r--r--drivers/platform/x86/dell/Makefile1
-rw-r--r--drivers/platform/x86/dell/dell-laptop.c2
-rw-r--r--drivers/platform/x86/dell/dell-rbtn.c1
-rw-r--r--drivers/platform/x86/dell/dell-smbios-base.c103
-rw-r--r--drivers/platform/x86/dell/dell-uart-backlight.c398
-rw-r--r--drivers/platform/x86/dell/dell-wmi-ddv.c1
-rw-r--r--drivers/platform/x86/dell/dell-wmi-privacy.c5
-rw-r--r--drivers/platform/x86/dell/dell-wmi-sysman/sysman.c2
-rw-r--r--drivers/platform/x86/eeepc-laptop.c1
-rw-r--r--drivers/platform/x86/firmware_attributes_class.c5
-rw-r--r--drivers/platform/x86/firmware_attributes_class.h2
-rw-r--r--drivers/platform/x86/fujitsu-laptop.c135
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/bioscfg.c2
-rw-r--r--drivers/platform/x86/hp/hp-wmi.c83
-rw-r--r--drivers/platform/x86/huawei-wmi.c9
-rw-r--r--drivers/platform/x86/ibm_rtl.c3
-rw-r--r--drivers/platform/x86/ideapad-laptop.c292
-rw-r--r--drivers/platform/x86/ideapad-laptop.h9
-rw-r--r--drivers/platform/x86/inspur_platform_profile.c1
-rw-r--r--drivers/platform/x86/intel/hid.c3
-rw-r--r--drivers/platform/x86/intel/ifs/load.c4
-rw-r--r--drivers/platform/x86/intel/ifs/runtest.c130
-rw-r--r--drivers/platform/x86/intel/oaktrail.c2
-rw-r--r--drivers/platform/x86/intel/pmc/arl.c2
-rw-r--r--drivers/platform/x86/intel/pmc/core.c85
-rw-r--r--drivers/platform/x86/intel/pmc/core.h16
-rw-r--r--drivers/platform/x86/intel/pmc/lnl.c477
-rw-r--r--drivers/platform/x86/intel/pmc/pltdrv.c1
-rw-r--r--drivers/platform/x86/intel/rst.c2
-rw-r--r--drivers/platform/x86/intel/sdsi.c118
-rw-r--r--drivers/platform/x86/intel/smartconnect.c2
-rw-r--r--drivers/platform/x86/intel/speed_select_if/isst_if_common.c1
-rw-r--r--drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c387
-rw-r--r--drivers/platform/x86/intel/tpmi.c39
-rw-r--r--drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c11
-rw-r--r--drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c2
-rw-r--r--drivers/platform/x86/intel/vbtn.c19
-rw-r--r--drivers/platform/x86/intel/vsec.c5
-rw-r--r--drivers/platform/x86/intel/wmi/sbl-fw-update.c1
-rw-r--r--drivers/platform/x86/intel/wmi/thunderbolt.c1
-rw-r--r--drivers/platform/x86/intel_ips.c2
-rw-r--r--drivers/platform/x86/intel_scu_ipcutil.c2
-rw-r--r--drivers/platform/x86/intel_scu_pcidrv.c1
-rw-r--r--drivers/platform/x86/intel_scu_wdt.c1
-rw-r--r--drivers/platform/x86/lenovo-wmi-camera.c127
-rw-r--r--drivers/platform/x86/lenovo-ymc.c60
-rw-r--r--drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c338
-rw-r--r--drivers/platform/x86/lg-laptop.c92
-rw-r--r--drivers/platform/x86/meegopad_anx7428.c150
-rw-r--r--drivers/platform/x86/mlx-platform.c2
-rw-r--r--drivers/platform/x86/msi-laptop.c20
-rw-r--r--drivers/platform/x86/msi-wmi-platform.c428
-rw-r--r--drivers/platform/x86/p2sb.c44
-rw-r--r--drivers/platform/x86/pmc_atom.c79
-rw-r--r--drivers/platform/x86/quickstart.c246
-rw-r--r--drivers/platform/x86/samsung-laptop.c10
-rw-r--r--drivers/platform/x86/serial-multi-instantiate.c28
-rw-r--r--drivers/platform/x86/siemens/simatic-ipc-batt-apollolake.c1
-rw-r--r--drivers/platform/x86/siemens/simatic-ipc-batt-elkhartlake.c1
-rw-r--r--drivers/platform/x86/siemens/simatic-ipc-batt-f7188x.c1
-rw-r--r--drivers/platform/x86/siemens/simatic-ipc-batt.c1
-rw-r--r--drivers/platform/x86/siemens/simatic-ipc.c1
-rw-r--r--drivers/platform/x86/silicom-platform.c7
-rw-r--r--drivers/platform/x86/sony-laptop.c2
-rw-r--r--drivers/platform/x86/think-lmi.c15
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c1100
-rw-r--r--drivers/platform/x86/toshiba_acpi.c60
-rw-r--r--drivers/platform/x86/toshiba_bluetooth.c1
-rw-r--r--drivers/platform/x86/toshiba_haps.c1
-rw-r--r--drivers/platform/x86/touchscreen_dmi.c170
-rw-r--r--drivers/platform/x86/uv_sysfs.c22
-rw-r--r--drivers/platform/x86/wireless-hotkey.c4
-rw-r--r--drivers/platform/x86/wmi-bmof.c1
-rw-r--r--drivers/platform/x86/wmi.c276
-rw-r--r--drivers/platform/x86/x86-android-tablets/Kconfig2
-rw-r--r--drivers/platform/x86/x86-android-tablets/core.c16
-rw-r--r--drivers/platform/x86/x86-android-tablets/dmi.c18
-rw-r--r--drivers/platform/x86/x86-android-tablets/lenovo.c224
-rw-r--r--drivers/platform/x86/x86-android-tablets/other.c133
-rw-r--r--drivers/platform/x86/x86-android-tablets/x86-android-tablets.h3
-rw-r--r--drivers/platform/x86/xiaomi-wmi.c12
-rw-r--r--drivers/platform/x86/xo1-rfkill.c1
144 files changed, 7448 insertions, 2265 deletions
diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig
index 868b203617..81a298517d 100644
--- a/drivers/platform/Kconfig
+++ b/drivers/platform/Kconfig
@@ -14,3 +14,5 @@ source "drivers/platform/olpc/Kconfig"
source "drivers/platform/surface/Kconfig"
source "drivers/platform/x86/Kconfig"
+
+source "drivers/platform/arm64/Kconfig"
diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile
index 4164017297..837202842a 100644
--- a/drivers/platform/Makefile
+++ b/drivers/platform/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_OLPC_EC) += olpc/
obj-$(CONFIG_GOLDFISH) += goldfish/
obj-$(CONFIG_CHROME_PLATFORMS) += chrome/
obj-$(CONFIG_SURFACE_PLATFORMS) += surface/
+obj-$(CONFIG_ARM64_PLATFORM_DEVICES) += arm64/
diff --git a/drivers/platform/arm64/Kconfig b/drivers/platform/arm64/Kconfig
new file mode 100644
index 0000000000..8fdca0f8e9
--- /dev/null
+++ b/drivers/platform/arm64/Kconfig
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# EC-like Drivers for aarch64 based devices.
+#
+
+menuconfig ARM64_PLATFORM_DEVICES
+ bool "ARM64 Platform-Specific Device Drivers"
+ depends on ARM64 || COMPILE_TEST
+ default y
+ help
+ Say Y here to get to see options for platform-specific device drivers
+ for arm64 based devices, primarily EC-like device drivers.
+ This option alone does not add any kernel code.
+
+ If you say N, all options in this submenu will be skipped and disabled.
+
+if ARM64_PLATFORM_DEVICES
+
+config EC_ACER_ASPIRE1
+ tristate "Acer Aspire 1 Embedded Controller driver"
+ depends on I2C
+ depends on DRM
+ depends on POWER_SUPPLY
+ depends on INPUT
+ help
+ Say Y here to enable the EC driver for the (Snapdragon-based)
+ Acer Aspire 1 laptop. The EC handles battery and charging
+ monitoring as well as some misc functions like the lid sensor
+ and USB Type-C DP HPD events.
+
+ This driver provides battery and AC status support for the mentioned
+ laptop where this information is not properly exposed via the
+ standard ACPI devices.
+
+endif # ARM64_PLATFORM_DEVICES
diff --git a/drivers/platform/arm64/Makefile b/drivers/platform/arm64/Makefile
new file mode 100644
index 0000000000..4fcc985557
--- /dev/null
+++ b/drivers/platform/arm64/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for linux/drivers/platform/arm64
+#
+# This dir should only include drivers for EC-like devices.
+#
+
+obj-$(CONFIG_EC_ACER_ASPIRE1) += acer-aspire1-ec.o
diff --git a/drivers/platform/arm64/acer-aspire1-ec.c b/drivers/platform/arm64/acer-aspire1-ec.c
new file mode 100644
index 0000000000..dbb1cce139
--- /dev/null
+++ b/drivers/platform/arm64/acer-aspire1-ec.c
@@ -0,0 +1,562 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2024, Nikita Travkin <nikita@trvn.ru> */
+
+#include <asm-generic/unaligned.h>
+#include <drm/drm_bridge.h>
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/usb/typec_mux.h>
+#include <linux/workqueue_types.h>
+
+#define MILLI_TO_MICRO 1000
+
+#define ASPIRE_EC_EVENT 0x05
+
+#define ASPIRE_EC_EVENT_WATCHDOG 0x20
+#define ASPIRE_EC_EVENT_KBD_BKL_ON 0x57
+#define ASPIRE_EC_EVENT_KBD_BKL_OFF 0x58
+#define ASPIRE_EC_EVENT_LID_CLOSE 0x9b
+#define ASPIRE_EC_EVENT_LID_OPEN 0x9c
+#define ASPIRE_EC_EVENT_BKL_UNBLANKED 0x9d
+#define ASPIRE_EC_EVENT_BKL_BLANKED 0x9e
+#define ASPIRE_EC_EVENT_FG_INF_CHG 0x85
+#define ASPIRE_EC_EVENT_FG_STA_CHG 0xc6
+#define ASPIRE_EC_EVENT_HPD_DIS 0xa3
+#define ASPIRE_EC_EVENT_HPD_CON 0xa4
+
+#define ASPIRE_EC_FG_DYNAMIC 0x07
+#define ASPIRE_EC_FG_STATIC 0x08
+
+#define ASPIRE_EC_FG_FLAG_PRESENT BIT(0)
+#define ASPIRE_EC_FG_FLAG_FULL BIT(1)
+#define ASPIRE_EC_FG_FLAG_DISCHARGING BIT(2)
+#define ASPIRE_EC_FG_FLAG_CHARGING BIT(3)
+
+#define ASPIRE_EC_RAM_READ 0x20
+#define ASPIRE_EC_RAM_WRITE 0x21
+
+#define ASPIRE_EC_RAM_WATCHDOG 0x19
+#define ASPIRE_EC_WATCHDOG_BIT BIT(6)
+
+#define ASPIRE_EC_RAM_KBD_MODE 0x43
+
+#define ASPIRE_EC_RAM_KBD_FN_EN BIT(0)
+#define ASPIRE_EC_RAM_KBD_MEDIA_ON_TOP BIT(5)
+#define ASPIRE_EC_RAM_KBD_ALWAYS_SET BIT(6)
+#define ASPIRE_EC_RAM_KBD_NUM_LAYER_EN BIT(7)
+
+#define ASPIRE_EC_RAM_KBD_MODE_2 0x60
+
+#define ASPIRE_EC_RAM_KBD_MEDIA_NOTIFY BIT(3)
+
+#define ASPIRE_EC_RAM_HPD_STATUS 0xf4
+#define ASPIRE_EC_HPD_CONNECTED 0x03
+
+#define ASPIRE_EC_RAM_LID_STATUS 0x4c
+#define ASPIRE_EC_LID_OPEN BIT(6)
+
+#define ASPIRE_EC_RAM_ADP 0x40
+#define ASPIRE_EC_AC_STATUS BIT(0)
+
+struct aspire_ec {
+ struct i2c_client *client;
+ struct power_supply *bat_psy;
+ struct power_supply *adp_psy;
+ struct input_dev *idev;
+
+ bool bridge_configured;
+ struct drm_bridge bridge;
+ struct work_struct work;
+};
+
+static int aspire_ec_ram_read(struct i2c_client *client, u8 off, u8 *data, u8 data_len)
+{
+ i2c_smbus_write_byte_data(client, ASPIRE_EC_RAM_READ, off);
+ i2c_smbus_read_i2c_block_data(client, ASPIRE_EC_RAM_READ, data_len, data);
+ return 0;
+}
+
+static int aspire_ec_ram_write(struct i2c_client *client, u8 off, u8 data)
+{
+ u8 tmp[2] = {off, data};
+
+ i2c_smbus_write_i2c_block_data(client, ASPIRE_EC_RAM_WRITE, sizeof(tmp), tmp);
+ return 0;
+}
+
+static irqreturn_t aspire_ec_irq_handler(int irq, void *data)
+{
+ struct aspire_ec *ec = data;
+ int id;
+ u8 tmp;
+
+ /*
+ * The original ACPI firmware actually has a small sleep in the handler.
+ *
+ * It seems like in most cases it's not needed but when the device
+ * just exits suspend, our i2c driver has a brief time where data
+ * transfer is not possible yet. So this delay allows us to suppress
+ * quite a bunch of spurious error messages in dmesg. Thus it's kept.
+ */
+ usleep_range(15000, 30000);
+
+ id = i2c_smbus_read_byte_data(ec->client, ASPIRE_EC_EVENT);
+ if (id < 0) {
+ dev_err(&ec->client->dev, "Failed to read event id: %pe\n", ERR_PTR(id));
+ return IRQ_HANDLED;
+ }
+
+ switch (id) {
+ case 0x0: /* No event */
+ break;
+
+ case ASPIRE_EC_EVENT_WATCHDOG:
+ /*
+ * Here acpi responds to the event and clears some bit.
+ * Notify (\_SB.I2C3.BAT1, 0x81) // Information Change
+ * Notify (\_SB.I2C3.ADP1, 0x80) // Status Change
+ */
+ aspire_ec_ram_read(ec->client, ASPIRE_EC_RAM_WATCHDOG, &tmp, sizeof(tmp));
+ tmp &= ~ASPIRE_EC_WATCHDOG_BIT;
+ aspire_ec_ram_write(ec->client, ASPIRE_EC_RAM_WATCHDOG, tmp);
+ break;
+
+ case ASPIRE_EC_EVENT_LID_CLOSE:
+ /* Notify (\_SB.LID0, 0x80) // Status Change */
+ input_report_switch(ec->idev, SW_LID, 1);
+ input_sync(ec->idev);
+ break;
+
+ case ASPIRE_EC_EVENT_LID_OPEN:
+ /* Notify (\_SB.LID0, 0x80) // Status Change */
+ input_report_switch(ec->idev, SW_LID, 0);
+ input_sync(ec->idev);
+ break;
+
+ case ASPIRE_EC_EVENT_FG_INF_CHG:
+ /* Notify (\_SB.I2C3.BAT1, 0x81) // Information Change */
+ fallthrough;
+ case ASPIRE_EC_EVENT_FG_STA_CHG:
+ /* Notify (\_SB.I2C3.BAT1, 0x80) // Status Change */
+ power_supply_changed(ec->bat_psy);
+ power_supply_changed(ec->adp_psy);
+ break;
+
+ case ASPIRE_EC_EVENT_HPD_DIS:
+ if (ec->bridge_configured)
+ drm_bridge_hpd_notify(&ec->bridge, connector_status_disconnected);
+ break;
+
+ case ASPIRE_EC_EVENT_HPD_CON:
+ if (ec->bridge_configured)
+ drm_bridge_hpd_notify(&ec->bridge, connector_status_connected);
+ break;
+
+ case ASPIRE_EC_EVENT_BKL_BLANKED:
+ case ASPIRE_EC_EVENT_BKL_UNBLANKED:
+ /* Display backlight blanked on FN+F6. No action needed. */
+ break;
+
+ case ASPIRE_EC_EVENT_KBD_BKL_ON:
+ case ASPIRE_EC_EVENT_KBD_BKL_OFF:
+ /*
+ * There is a keyboard backlight connector on Aspire 1 that is
+ * controlled by FN+F8. There is no kb backlight on the device though.
+ * Seems like this is used on other devices like Acer Spin 7.
+ * No action needed.
+ */
+ break;
+
+ default:
+ dev_warn(&ec->client->dev, "Unknown event id=0x%x\n", id);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Power supply.
+ */
+
+struct aspire_ec_bat_psy_static_data {
+ u8 unk1;
+ u8 flags;
+ __le16 unk2;
+ __le16 voltage_design;
+ __le16 capacity_full;
+ __le16 unk3;
+ __le16 serial;
+ u8 model_id;
+ u8 vendor_id;
+} __packed;
+
+static const char * const aspire_ec_bat_psy_battery_model[] = {
+ "AP18C4K",
+ "AP18C8K",
+ "AP19B8K",
+ "AP16M4J",
+ "AP16M5J",
+};
+
+static const char * const aspire_ec_bat_psy_battery_vendor[] = {
+ "SANYO",
+ "SONY",
+ "PANASONIC",
+ "SAMSUNG",
+ "SIMPLO",
+ "MOTOROLA",
+ "CELXPERT",
+ "LGC",
+ "GETAC",
+ "MURATA",
+};
+
+struct aspire_ec_bat_psy_dynamic_data {
+ u8 unk1;
+ u8 flags;
+ u8 unk2;
+ __le16 capacity_now;
+ __le16 voltage_now;
+ __le16 current_now;
+ __le16 unk3;
+ __le16 unk4;
+} __packed;
+
+static int aspire_ec_bat_psy_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct aspire_ec *ec = power_supply_get_drvdata(psy);
+ struct aspire_ec_bat_psy_static_data sdat;
+ struct aspire_ec_bat_psy_dynamic_data ddat;
+ int str_index = 0;
+
+ i2c_smbus_read_i2c_block_data(ec->client, ASPIRE_EC_FG_STATIC, sizeof(sdat), (u8 *)&sdat);
+ i2c_smbus_read_i2c_block_data(ec->client, ASPIRE_EC_FG_DYNAMIC, sizeof(ddat), (u8 *)&ddat);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ if (ddat.flags & ASPIRE_EC_FG_FLAG_CHARGING)
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ else if (ddat.flags & ASPIRE_EC_FG_FLAG_DISCHARGING)
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ else if (ddat.flags & ASPIRE_EC_FG_FLAG_FULL)
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = get_unaligned_le16(&ddat.voltage_now) * MILLI_TO_MICRO;
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = le16_to_cpu(sdat.voltage_design) * MILLI_TO_MICRO;
+ break;
+
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ val->intval = get_unaligned_le16(&ddat.capacity_now) * MILLI_TO_MICRO;
+ break;
+
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ val->intval = le16_to_cpu(sdat.capacity_full) * MILLI_TO_MICRO;
+ break;
+
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = get_unaligned_le16(&ddat.capacity_now) * 100;
+ val->intval /= le16_to_cpu(sdat.capacity_full);
+ break;
+
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = (s16)get_unaligned_le16(&ddat.current_now) * MILLI_TO_MICRO;
+ break;
+
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = !!(ddat.flags & ASPIRE_EC_FG_FLAG_PRESENT);
+ break;
+
+ case POWER_SUPPLY_PROP_SCOPE:
+ val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
+ break;
+
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ str_index = sdat.model_id - 1;
+
+ if (str_index >= 0 && str_index < ARRAY_SIZE(aspire_ec_bat_psy_battery_model))
+ val->strval = aspire_ec_bat_psy_battery_model[str_index];
+ else
+ val->strval = "Unknown";
+ break;
+
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ str_index = sdat.vendor_id - 3; /* ACPI uses 3 as an offset here. */
+
+ if (str_index >= 0 && str_index < ARRAY_SIZE(aspire_ec_bat_psy_battery_vendor))
+ val->strval = aspire_ec_bat_psy_battery_vendor[str_index];
+ else
+ val->strval = "Unknown";
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static enum power_supply_property aspire_ec_bat_psy_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_SCOPE,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static const struct power_supply_desc aspire_ec_bat_psy_desc = {
+ .name = "aspire-ec-bat",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .get_property = aspire_ec_bat_psy_get_property,
+ .properties = aspire_ec_bat_psy_props,
+ .num_properties = ARRAY_SIZE(aspire_ec_bat_psy_props),
+};
+
+static int aspire_ec_adp_psy_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct aspire_ec *ec = power_supply_get_drvdata(psy);
+ u8 tmp;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ aspire_ec_ram_read(ec->client, ASPIRE_EC_RAM_ADP, &tmp, sizeof(tmp));
+ val->intval = !!(tmp & ASPIRE_EC_AC_STATUS);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static enum power_supply_property aspire_ec_adp_psy_props[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static const struct power_supply_desc aspire_ec_adp_psy_desc = {
+ .name = "aspire-ec-adp",
+ .type = POWER_SUPPLY_TYPE_MAINS,
+ .get_property = aspire_ec_adp_psy_get_property,
+ .properties = aspire_ec_adp_psy_props,
+ .num_properties = ARRAY_SIZE(aspire_ec_adp_psy_props),
+};
+
+/*
+ * USB-C DP Alt mode HPD.
+ */
+
+static int aspire_ec_bridge_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags)
+{
+ return flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR ? 0 : -EINVAL;
+}
+
+static void aspire_ec_bridge_update_hpd_work(struct work_struct *work)
+{
+ struct aspire_ec *ec = container_of(work, struct aspire_ec, work);
+ u8 tmp;
+
+ aspire_ec_ram_read(ec->client, ASPIRE_EC_RAM_HPD_STATUS, &tmp, sizeof(tmp));
+ if (tmp == ASPIRE_EC_HPD_CONNECTED)
+ drm_bridge_hpd_notify(&ec->bridge, connector_status_connected);
+ else
+ drm_bridge_hpd_notify(&ec->bridge, connector_status_disconnected);
+}
+
+static void aspire_ec_bridge_hpd_enable(struct drm_bridge *bridge)
+{
+ struct aspire_ec *ec = container_of(bridge, struct aspire_ec, bridge);
+
+ schedule_work(&ec->work);
+}
+
+static const struct drm_bridge_funcs aspire_ec_bridge_funcs = {
+ .hpd_enable = aspire_ec_bridge_hpd_enable,
+ .attach = aspire_ec_bridge_attach,
+};
+
+/*
+ * Sysfs attributes.
+ */
+
+static ssize_t fn_lock_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aspire_ec *ec = i2c_get_clientdata(to_i2c_client(dev));
+ u8 tmp;
+
+ aspire_ec_ram_read(ec->client, ASPIRE_EC_RAM_KBD_MODE, &tmp, sizeof(tmp));
+
+ return sysfs_emit(buf, "%u\n", !(tmp & ASPIRE_EC_RAM_KBD_MEDIA_ON_TOP));
+}
+
+static ssize_t fn_lock_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct aspire_ec *ec = i2c_get_clientdata(to_i2c_client(dev));
+ u8 tmp;
+
+ bool state;
+ int ret;
+
+ ret = kstrtobool(buf, &state);
+ if (ret)
+ return ret;
+
+ aspire_ec_ram_read(ec->client, ASPIRE_EC_RAM_KBD_MODE, &tmp, sizeof(tmp));
+
+ if (state)
+ tmp &= ~ASPIRE_EC_RAM_KBD_MEDIA_ON_TOP;
+ else
+ tmp |= ASPIRE_EC_RAM_KBD_MEDIA_ON_TOP;
+
+ aspire_ec_ram_write(ec->client, ASPIRE_EC_RAM_KBD_MODE, tmp);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(fn_lock);
+
+static struct attribute *aspire_ec_attrs[] = {
+ &dev_attr_fn_lock.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(aspire_ec);
+
+static int aspire_ec_probe(struct i2c_client *client)
+{
+ struct power_supply_config psy_cfg = {0};
+ struct device *dev = &client->dev;
+ struct fwnode_handle *fwnode;
+ struct aspire_ec *ec;
+ int ret;
+ u8 tmp;
+
+ ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL);
+ if (!ec)
+ return -ENOMEM;
+
+ ec->client = client;
+ i2c_set_clientdata(client, ec);
+
+ /* Battery status reports */
+ psy_cfg.drv_data = ec;
+ ec->bat_psy = devm_power_supply_register(dev, &aspire_ec_bat_psy_desc, &psy_cfg);
+ if (IS_ERR(ec->bat_psy))
+ return dev_err_probe(dev, PTR_ERR(ec->bat_psy),
+ "Failed to register battery power supply\n");
+
+ ec->adp_psy = devm_power_supply_register(dev, &aspire_ec_adp_psy_desc, &psy_cfg);
+ if (IS_ERR(ec->adp_psy))
+ return dev_err_probe(dev, PTR_ERR(ec->adp_psy),
+ "Failed to register AC power supply\n");
+
+ /* Lid switch */
+ ec->idev = devm_input_allocate_device(dev);
+ if (!ec->idev)
+ return -ENOMEM;
+
+ ec->idev->name = "aspire-ec";
+ ec->idev->phys = "aspire-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, "Input device register failed\n");
+
+ /* Enable the keyboard fn keys */
+ tmp = ASPIRE_EC_RAM_KBD_FN_EN | ASPIRE_EC_RAM_KBD_ALWAYS_SET;
+ tmp |= ASPIRE_EC_RAM_KBD_MEDIA_ON_TOP;
+ aspire_ec_ram_write(client, ASPIRE_EC_RAM_KBD_MODE, tmp);
+
+ aspire_ec_ram_read(client, ASPIRE_EC_RAM_KBD_MODE_2, &tmp, sizeof(tmp));
+ tmp |= ASPIRE_EC_RAM_KBD_MEDIA_NOTIFY;
+ aspire_ec_ram_write(client, ASPIRE_EC_RAM_KBD_MODE_2, tmp);
+
+ /* External Type-C display attach reports */
+ fwnode = device_get_named_child_node(dev, "connector");
+ if (fwnode) {
+ INIT_WORK(&ec->work, aspire_ec_bridge_update_hpd_work);
+ ec->bridge.funcs = &aspire_ec_bridge_funcs;
+ ec->bridge.of_node = to_of_node(fwnode);
+ ec->bridge.ops = DRM_BRIDGE_OP_HPD;
+ ec->bridge.type = DRM_MODE_CONNECTOR_USB;
+
+ ret = devm_drm_bridge_add(dev, &ec->bridge);
+ if (ret) {
+ fwnode_handle_put(fwnode);
+ return dev_err_probe(dev, ret, "Failed to register drm bridge\n");
+ }
+
+ ec->bridge_configured = true;
+ }
+
+ ret = devm_request_threaded_irq(dev, client->irq, NULL,
+ aspire_ec_irq_handler, IRQF_ONESHOT,
+ dev_name(dev), ec);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to request irq\n");
+
+ return 0;
+}
+
+static int aspire_ec_resume(struct device *dev)
+{
+ struct aspire_ec *ec = i2c_get_clientdata(to_i2c_client(dev));
+ u8 tmp;
+
+ aspire_ec_ram_read(ec->client, ASPIRE_EC_RAM_LID_STATUS, &tmp, sizeof(tmp));
+ input_report_switch(ec->idev, SW_LID, !!(tmp & ASPIRE_EC_LID_OPEN));
+ input_sync(ec->idev);
+
+ return 0;
+}
+
+static const struct i2c_device_id aspire_ec_id[] = {
+ { "aspire1-ec", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aspire_ec_id);
+
+static const struct of_device_id aspire_ec_of_match[] = {
+ { .compatible = "acer,aspire1-ec", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, aspire_ec_of_match);
+
+static DEFINE_SIMPLE_DEV_PM_OPS(aspire_ec_pm_ops, NULL, aspire_ec_resume);
+
+static struct i2c_driver aspire_ec_driver = {
+ .driver = {
+ .name = "aspire-ec",
+ .of_match_table = aspire_ec_of_match,
+ .pm = pm_sleep_ptr(&aspire_ec_pm_ops),
+ .dev_groups = aspire_ec_groups,
+ },
+ .probe = aspire_ec_probe,
+ .id_table = aspire_ec_id,
+};
+module_i2c_driver(aspire_ec_driver);
+
+MODULE_DESCRIPTION("Acer Aspire 1 embedded controller");
+MODULE_AUTHOR("Nikita Travkin <nikita@trvn.ru>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig
index 7a83346bfa..073616b5b5 100644
--- a/drivers/platform/chrome/Kconfig
+++ b/drivers/platform/chrome/Kconfig
@@ -132,6 +132,7 @@ config CROS_EC_UART
config CROS_EC_LPC
tristate "ChromeOS Embedded Controller (LPC)"
depends on CROS_EC && ACPI && (X86 || COMPILE_TEST)
+ depends on HAS_IOPORT
help
If you say Y here, you get support for talking to the ChromeOS EC
over an LPC bus, including the LPC Microchip EC (MEC) variant.
diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c
index badc68bbae..47d19f7e29 100644
--- a/drivers/platform/chrome/cros_ec.c
+++ b/drivers/platform/chrome/cros_ec.c
@@ -432,6 +432,12 @@ static void cros_ec_send_resume_event(struct cros_ec_device *ec_dev)
void cros_ec_resume_complete(struct cros_ec_device *ec_dev)
{
cros_ec_send_resume_event(ec_dev);
+
+ /*
+ * Let the mfd devices know about events that occur during
+ * suspend. This way the clients know what to do with them.
+ */
+ cros_ec_report_events_during_suspend(ec_dev);
}
EXPORT_SYMBOL(cros_ec_resume_complete);
@@ -442,12 +448,6 @@ static void cros_ec_enable_irq(struct cros_ec_device *ec_dev)
if (ec_dev->wake_enabled)
disable_irq_wake(ec_dev->irq);
-
- /*
- * Let the mfd devices know about events that occur during
- * suspend. This way the clients know what to do with them.
- */
- cros_ec_report_events_during_suspend(ec_dev);
}
/**
@@ -475,8 +475,8 @@ EXPORT_SYMBOL(cros_ec_resume_early);
*/
int cros_ec_resume(struct cros_ec_device *ec_dev)
{
- cros_ec_enable_irq(ec_dev);
- cros_ec_send_resume_event(ec_dev);
+ cros_ec_resume_early(ec_dev);
+ cros_ec_resume_complete(ec_dev);
return 0;
}
EXPORT_SYMBOL(cros_ec_resume);
diff --git a/drivers/platform/chrome/cros_ec_chardev.c b/drivers/platform/chrome/cros_ec_chardev.c
index 81950bb2c6..7f034ead7a 100644
--- a/drivers/platform/chrome/cros_ec_chardev.c
+++ b/drivers/platform/chrome/cros_ec_chardev.c
@@ -14,6 +14,7 @@
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/platform_data/cros_ec_chardev.h>
@@ -403,17 +404,23 @@ static void cros_ec_chardev_remove(struct platform_device *pdev)
misc_deregister(&data->misc);
}
+static const struct platform_device_id cros_ec_chardev_id[] = {
+ { DRV_NAME, 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, cros_ec_chardev_id);
+
static struct platform_driver cros_ec_chardev_driver = {
.driver = {
.name = DRV_NAME,
},
.probe = cros_ec_chardev_probe,
.remove_new = cros_ec_chardev_remove,
+ .id_table = cros_ec_chardev_id,
};
module_platform_driver(cros_ec_chardev_driver);
-MODULE_ALIAS("platform:" DRV_NAME);
MODULE_AUTHOR("Enric Balletbo i Serra <enric.balletbo@collabora.com>");
MODULE_DESCRIPTION("ChromeOS EC Miscellaneous Character Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/platform/chrome/cros_ec_debugfs.c b/drivers/platform/chrome/cros_ec_debugfs.c
index 6bf6f0e7b5..5996e9d53c 100644
--- a/drivers/platform/chrome/cros_ec_debugfs.c
+++ b/drivers/platform/chrome/cros_ec_debugfs.c
@@ -7,6 +7,7 @@
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/fs.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_data/cros_ec_commands.h>
@@ -329,6 +330,7 @@ static int ec_read_version_supported(struct cros_ec_dev *ec)
if (!msg)
return 0;
+ msg->version = 1;
msg->command = EC_CMD_GET_CMD_VERSIONS + ec->cmd_offset;
msg->outsize = sizeof(*params);
msg->insize = sizeof(*response);
@@ -564,6 +566,12 @@ static int __maybe_unused cros_ec_debugfs_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(cros_ec_debugfs_pm_ops,
cros_ec_debugfs_suspend, cros_ec_debugfs_resume);
+static const struct platform_device_id cros_ec_debugfs_id[] = {
+ { DRV_NAME, 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, cros_ec_debugfs_id);
+
static struct platform_driver cros_ec_debugfs_driver = {
.driver = {
.name = DRV_NAME,
@@ -572,10 +580,10 @@ static struct platform_driver cros_ec_debugfs_driver = {
},
.probe = cros_ec_debugfs_probe,
.remove_new = cros_ec_debugfs_remove,
+ .id_table = cros_ec_debugfs_id,
};
module_platform_driver(cros_ec_debugfs_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Debug logs for ChromeOS EC");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c
index 6677cc6c49..1e69f61115 100644
--- a/drivers/platform/chrome/cros_ec_lightbar.c
+++ b/drivers/platform/chrome/cros_ec_lightbar.c
@@ -9,6 +9,7 @@
#include <linux/fs.h>
#include <linux/kobject.h>
#include <linux/kstrtox.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_ec_proto.h>
@@ -594,6 +595,12 @@ static int __maybe_unused cros_ec_lightbar_suspend(struct device *dev)
static SIMPLE_DEV_PM_OPS(cros_ec_lightbar_pm_ops,
cros_ec_lightbar_suspend, cros_ec_lightbar_resume);
+static const struct platform_device_id cros_ec_lightbar_id[] = {
+ { DRV_NAME, 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, cros_ec_lightbar_id);
+
static struct platform_driver cros_ec_lightbar_driver = {
.driver = {
.name = DRV_NAME,
@@ -602,10 +609,10 @@ static struct platform_driver cros_ec_lightbar_driver = {
},
.probe = cros_ec_lightbar_probe,
.remove_new = cros_ec_lightbar_remove,
+ .id_table = cros_ec_lightbar_id,
};
module_platform_driver(cros_ec_lightbar_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Expose the Chromebook Pixel's lightbar to userspace");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c
index f0f3d3d561..43e0914256 100644
--- a/drivers/platform/chrome/cros_ec_lpc.c
+++ b/drivers/platform/chrome/cros_ec_lpc.c
@@ -34,6 +34,39 @@
/* True if ACPI device is present */
static bool cros_ec_lpc_acpi_device_found;
+/*
+ * Indicates that lpc_driver_data.quirk_mmio_memory_base should
+ * be used as the base port for EC mapped memory.
+ */
+#define CROS_EC_LPC_QUIRK_REMAP_MEMORY BIT(0)
+/*
+ * Indicates that lpc_driver_data.quirk_acpi_id should be used to find
+ * the ACPI device.
+ */
+#define CROS_EC_LPC_QUIRK_ACPI_ID BIT(1)
+
+/**
+ * struct lpc_driver_data - driver data attached to a DMI device ID to indicate
+ * hardware quirks.
+ * @quirks: a bitfield composed of quirks from CROS_EC_LPC_QUIRK_*
+ * @quirk_mmio_memory_base: The first I/O port addressing EC mapped memory (used
+ * when quirk ...REMAP_MEMORY is set.)
+ * @quirk_acpi_id: An ACPI HID to be used to find the ACPI device.
+ */
+struct lpc_driver_data {
+ u32 quirks;
+ u16 quirk_mmio_memory_base;
+ const char *quirk_acpi_id;
+};
+
+/**
+ * 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
* @read: Copy length bytes from EC address offset into buffer dest. Returns
@@ -290,6 +323,7 @@ done:
static int cros_ec_lpc_readmem(struct cros_ec_device *ec, unsigned int offset,
unsigned int bytes, void *dest)
{
+ struct cros_ec_lpc *ec_lpc = ec->priv;
int i = offset;
char *s = dest;
int cnt = 0;
@@ -299,13 +333,13 @@ static int cros_ec_lpc_readmem(struct cros_ec_device *ec, unsigned int offset,
/* fixed length */
if (bytes) {
- cros_ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + offset, bytes, s);
+ cros_ec_lpc_ops.read(ec_lpc->mmio_memory_base + offset, bytes, s);
return bytes;
}
/* string */
for (; i < EC_MEMMAP_SIZE; i++, s++) {
- cros_ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + i, 1, s);
+ cros_ec_lpc_ops.read(ec_lpc->mmio_memory_base + i, 1, s);
cnt++;
if (!*s)
break;
@@ -347,14 +381,64 @@ static void cros_ec_lpc_acpi_notify(acpi_handle device, u32 value, void *data)
pm_system_wakeup();
}
+static acpi_status cros_ec_lpc_parse_device(acpi_handle handle, u32 level,
+ void *context, void **retval)
+{
+ *(struct acpi_device **)context = acpi_fetch_acpi_dev(handle);
+ return AE_CTRL_TERMINATE;
+}
+
+static struct acpi_device *cros_ec_lpc_get_device(const char *id)
+{
+ struct acpi_device *adev = NULL;
+ acpi_status status = acpi_get_devices(id, cros_ec_lpc_parse_device,
+ &adev, NULL);
+ if (ACPI_FAILURE(status)) {
+ pr_warn(DRV_NAME ": Looking for %s failed\n", id);
+ return NULL;
+ }
+
+ return adev;
+}
+
static int cros_ec_lpc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct acpi_device *adev;
acpi_status status;
struct cros_ec_device *ec_dev;
+ struct cros_ec_lpc *ec_lpc;
+ struct lpc_driver_data *driver_data;
u8 buf[2] = {};
int irq, ret;
+ u32 quirks;
+
+ ec_lpc = devm_kzalloc(dev, sizeof(*ec_lpc), GFP_KERNEL);
+ if (!ec_lpc)
+ return -ENOMEM;
+
+ ec_lpc->mmio_memory_base = EC_LPC_ADDR_MEMMAP;
+
+ driver_data = platform_get_drvdata(pdev);
+ if (driver_data) {
+ quirks = driver_data->quirks;
+
+ if (quirks)
+ dev_info(dev, "loaded with quirks %8.08x\n", quirks);
+
+ if (quirks & CROS_EC_LPC_QUIRK_REMAP_MEMORY)
+ ec_lpc->mmio_memory_base = driver_data->quirk_mmio_memory_base;
+
+ if (quirks & CROS_EC_LPC_QUIRK_ACPI_ID) {
+ adev = cros_ec_lpc_get_device(driver_data->quirk_acpi_id);
+ if (!adev) {
+ dev_err(dev, "failed to get ACPI device '%s'",
+ driver_data->quirk_acpi_id);
+ return -ENODEV;
+ }
+ ACPI_COMPANION_SET(dev, adev);
+ }
+ }
/*
* The Framework Laptop (and possibly other non-ChromeOS devices)
@@ -380,7 +464,7 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
cros_ec_lpc_ops.write = cros_ec_lpc_mec_write_bytes;
cros_ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID, 2, buf);
if (buf[0] != 'E' || buf[1] != 'C') {
- if (!devm_request_region(dev, EC_LPC_ADDR_MEMMAP, EC_MEMMAP_SIZE,
+ if (!devm_request_region(dev, ec_lpc->mmio_memory_base, EC_MEMMAP_SIZE,
dev_name(dev))) {
dev_err(dev, "couldn't reserve memmap region\n");
return -EBUSY;
@@ -389,7 +473,7 @@ 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;
- cros_ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID, 2,
+ cros_ec_lpc_ops.read(ec_lpc->mmio_memory_base + EC_MEMMAP_ID, 2,
buf);
if (buf[0] != 'E' || buf[1] != 'C') {
dev_err(dev, "EC ID not detected\n");
@@ -423,6 +507,7 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
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->priv = ec_lpc;
/*
* Some boards do not have an IRQ allotted for cros_ec_lpc,
@@ -479,6 +564,11 @@ static const struct acpi_device_id cros_ec_lpc_acpi_device_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, cros_ec_lpc_acpi_device_ids);
+static const struct lpc_driver_data framework_laptop_amd_lpc_driver_data __initconst = {
+ .quirks = CROS_EC_LPC_QUIRK_REMAP_MEMORY,
+ .quirk_mmio_memory_base = 0xE00,
+};
+
static const struct dmi_system_id cros_ec_lpc_dmi_table[] __initconst = {
{
/*
@@ -533,7 +623,16 @@ static const struct dmi_system_id cros_ec_lpc_dmi_table[] __initconst = {
},
/* A small number of non-Chromebook/box machines also use the ChromeOS EC */
{
- /* the Framework Laptop */
+ /* the Framework Laptop 13 (AMD Ryzen) and 16 (AMD Ryzen) */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Framework"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMD Ryzen"),
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Laptop"),
+ },
+ .driver_data = (void *)&framework_laptop_amd_lpc_driver_data,
+ },
+ {
+ /* the Framework Laptop (Intel 11th, 12th, 13th Generation) */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Framework"),
DMI_MATCH(DMI_PRODUCT_NAME, "Laptop"),
@@ -599,25 +698,16 @@ static struct platform_device cros_ec_lpc_device = {
.name = DRV_NAME
};
-static acpi_status cros_ec_lpc_parse_device(acpi_handle handle, u32 level,
- void *context, void **retval)
-{
- *(bool *)context = true;
- return AE_CTRL_TERMINATE;
-}
-
static int __init cros_ec_lpc_init(void)
{
int ret;
- acpi_status status;
+ const struct dmi_system_id *dmi_match;
+
+ cros_ec_lpc_acpi_device_found = !!cros_ec_lpc_get_device(ACPI_DRV_NAME);
- status = acpi_get_devices(ACPI_DRV_NAME, cros_ec_lpc_parse_device,
- &cros_ec_lpc_acpi_device_found, NULL);
- if (ACPI_FAILURE(status))
- pr_warn(DRV_NAME ": Looking for %s failed\n", ACPI_DRV_NAME);
+ dmi_match = dmi_first_match(cros_ec_lpc_dmi_table);
- if (!cros_ec_lpc_acpi_device_found &&
- !dmi_check_system(cros_ec_lpc_dmi_table)) {
+ if (!cros_ec_lpc_acpi_device_found && !dmi_match) {
pr_err(DRV_NAME ": unsupported system.\n");
return -ENODEV;
}
@@ -630,6 +720,9 @@ static int __init cros_ec_lpc_init(void)
}
if (!cros_ec_lpc_acpi_device_found) {
+ /* Pass the DMI match's driver data down to the platform device */
+ platform_set_drvdata(&cros_ec_lpc_device, dmi_match->driver_data);
+
/* Register the device, and it'll get hooked up automatically */
ret = platform_device_register(&cros_ec_lpc_device);
if (ret) {
diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c
index 945b1b15a0..f8242b8dda 100644
--- a/drivers/platform/chrome/cros_ec_proto.c
+++ b/drivers/platform/chrome/cros_ec_proto.c
@@ -805,9 +805,11 @@ int cros_ec_get_next_event(struct cros_ec_device *ec_dev,
if (ret == -ENOPROTOOPT) {
dev_dbg(ec_dev->dev,
"GET_NEXT_EVENT returned invalid version error.\n");
+ mutex_lock(&ec_dev->lock);
ret = cros_ec_get_host_command_version_mask(ec_dev,
EC_CMD_GET_NEXT_EVENT,
&ver_mask);
+ mutex_unlock(&ec_dev->lock);
if (ret < 0 || ver_mask == 0)
/*
* Do not change the MKBP supported version if we can't
diff --git a/drivers/platform/chrome/cros_ec_proto_test.c b/drivers/platform/chrome/cros_ec_proto_test.c
index b6169d6f24..41378c2ee6 100644
--- a/drivers/platform/chrome/cros_ec_proto_test.c
+++ b/drivers/platform/chrome/cros_ec_proto_test.c
@@ -1543,21 +1543,18 @@ static void cros_ec_proto_test_cmd_xfer_normal(struct kunit *test)
struct cros_ec_device *ec_dev = &priv->ec_dev;
struct ec_xfer_mock *mock;
int ret;
- struct {
- struct cros_ec_command msg;
- u8 data[0x100];
- } __packed buf;
+ DEFINE_RAW_FLEX(struct cros_ec_command, buf, data, 0x100);
ec_dev->max_request = 0xff;
ec_dev->max_response = 0xee;
ec_dev->max_passthru = 0xdd;
- buf.msg.version = 0;
- buf.msg.command = EC_CMD_HELLO;
- buf.msg.insize = 4;
- buf.msg.outsize = 2;
- buf.data[0] = 0x55;
- buf.data[1] = 0xaa;
+ buf->version = 0;
+ buf->command = EC_CMD_HELLO;
+ buf->insize = 4;
+ buf->outsize = 2;
+ buf->data[0] = 0x55;
+ buf->data[1] = 0xaa;
{
u8 *data;
@@ -1572,7 +1569,7 @@ static void cros_ec_proto_test_cmd_xfer_normal(struct kunit *test)
data[3] = 0x33;
}
- ret = cros_ec_cmd_xfer(ec_dev, &buf.msg);
+ ret = cros_ec_cmd_xfer(ec_dev, buf);
KUNIT_EXPECT_EQ(test, ret, 4);
{
@@ -1590,10 +1587,10 @@ static void cros_ec_proto_test_cmd_xfer_normal(struct kunit *test)
KUNIT_EXPECT_EQ(test, data[0], 0x55);
KUNIT_EXPECT_EQ(test, data[1], 0xaa);
- KUNIT_EXPECT_EQ(test, buf.data[0], 0xaa);
- KUNIT_EXPECT_EQ(test, buf.data[1], 0x55);
- KUNIT_EXPECT_EQ(test, buf.data[2], 0xcc);
- KUNIT_EXPECT_EQ(test, buf.data[3], 0x33);
+ KUNIT_EXPECT_EQ(test, buf->data[0], 0xaa);
+ KUNIT_EXPECT_EQ(test, buf->data[1], 0x55);
+ KUNIT_EXPECT_EQ(test, buf->data[2], 0xcc);
+ KUNIT_EXPECT_EQ(test, buf->data[3], 0x33);
}
}
@@ -1603,26 +1600,23 @@ static void cros_ec_proto_test_cmd_xfer_excess_msg_insize(struct kunit *test)
struct cros_ec_device *ec_dev = &priv->ec_dev;
struct ec_xfer_mock *mock;
int ret;
- struct {
- struct cros_ec_command msg;
- u8 data[0x100];
- } __packed buf;
+ DEFINE_RAW_FLEX(struct cros_ec_command, buf, data, 0x100);
ec_dev->max_request = 0xff;
ec_dev->max_response = 0xee;
ec_dev->max_passthru = 0xdd;
- buf.msg.version = 0;
- buf.msg.command = EC_CMD_HELLO;
- buf.msg.insize = 0xee + 1;
- buf.msg.outsize = 2;
+ buf->version = 0;
+ buf->command = EC_CMD_HELLO;
+ buf->insize = 0xee + 1;
+ buf->outsize = 2;
{
mock = cros_kunit_ec_xfer_mock_add(test, 0xcc);
KUNIT_ASSERT_PTR_NE(test, mock, NULL);
}
- ret = cros_ec_cmd_xfer(ec_dev, &buf.msg);
+ ret = cros_ec_cmd_xfer(ec_dev, buf);
KUNIT_EXPECT_EQ(test, ret, 0xcc);
{
@@ -1641,21 +1635,18 @@ static void cros_ec_proto_test_cmd_xfer_excess_msg_outsize_without_passthru(stru
struct cros_ec_proto_test_priv *priv = test->priv;
struct cros_ec_device *ec_dev = &priv->ec_dev;
int ret;
- struct {
- struct cros_ec_command msg;
- u8 data[0x100];
- } __packed buf;
+ DEFINE_RAW_FLEX(struct cros_ec_command, buf, data, 0x100);
ec_dev->max_request = 0xff;
ec_dev->max_response = 0xee;
ec_dev->max_passthru = 0xdd;
- buf.msg.version = 0;
- buf.msg.command = EC_CMD_HELLO;
- buf.msg.insize = 4;
- buf.msg.outsize = 0xff + 1;
+ buf->version = 0;
+ buf->command = EC_CMD_HELLO;
+ buf->insize = 4;
+ buf->outsize = 0xff + 1;
- ret = cros_ec_cmd_xfer(ec_dev, &buf.msg);
+ ret = cros_ec_cmd_xfer(ec_dev, buf);
KUNIT_EXPECT_EQ(test, ret, -EMSGSIZE);
}
@@ -1664,21 +1655,18 @@ static void cros_ec_proto_test_cmd_xfer_excess_msg_outsize_with_passthru(struct
struct cros_ec_proto_test_priv *priv = test->priv;
struct cros_ec_device *ec_dev = &priv->ec_dev;
int ret;
- struct {
- struct cros_ec_command msg;
- u8 data[0x100];
- } __packed buf;
+ DEFINE_RAW_FLEX(struct cros_ec_command, buf, data, 0x100);
ec_dev->max_request = 0xff;
ec_dev->max_response = 0xee;
ec_dev->max_passthru = 0xdd;
- buf.msg.version = 0;
- buf.msg.command = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX) + EC_CMD_HELLO;
- buf.msg.insize = 4;
- buf.msg.outsize = 0xdd + 1;
+ buf->version = 0;
+ buf->command = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX) + EC_CMD_HELLO;
+ buf->insize = 4;
+ buf->outsize = 0xdd + 1;
- ret = cros_ec_cmd_xfer(ec_dev, &buf.msg);
+ ret = cros_ec_cmd_xfer(ec_dev, buf);
KUNIT_EXPECT_EQ(test, ret, -EMSGSIZE);
}
diff --git a/drivers/platform/chrome/cros_ec_sensorhub.c b/drivers/platform/chrome/cros_ec_sensorhub.c
index 31fb8bdaad..50cdae67fa 100644
--- a/drivers/platform/chrome/cros_ec_sensorhub.c
+++ b/drivers/platform/chrome/cros_ec_sensorhub.c
@@ -8,6 +8,7 @@
#include <linux/init.h>
#include <linux/device.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_ec_proto.h>
@@ -247,17 +248,23 @@ static SIMPLE_DEV_PM_OPS(cros_ec_sensorhub_pm_ops,
cros_ec_sensorhub_suspend,
cros_ec_sensorhub_resume);
+static const struct platform_device_id cros_ec_sensorhub_id[] = {
+ { DRV_NAME, 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, cros_ec_sensorhub_id);
+
static struct platform_driver cros_ec_sensorhub_driver = {
.driver = {
.name = DRV_NAME,
.pm = &cros_ec_sensorhub_pm_ops,
},
.probe = cros_ec_sensorhub_probe,
+ .id_table = cros_ec_sensorhub_id,
};
module_platform_driver(cros_ec_sensorhub_driver);
-MODULE_ALIAS("platform:" DRV_NAME);
MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>");
MODULE_DESCRIPTION("ChromeOS EC MEMS Sensor Hub Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/platform/chrome/cros_ec_spi.c b/drivers/platform/chrome/cros_ec_spi.c
index 3e88cc92e8..86a3d32a77 100644
--- a/drivers/platform/chrome/cros_ec_spi.c
+++ b/drivers/platform/chrome/cros_ec_spi.c
@@ -409,7 +409,7 @@ static int do_cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev,
if (!rx_buf)
return -ENOMEM;
- spi_bus_lock(ec_spi->spi->master);
+ spi_bus_lock(ec_spi->spi->controller);
/*
* Leave a gap between CS assertion and clocking of data to allow the
@@ -469,7 +469,7 @@ static int do_cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev,
final_ret = terminate_request(ec_dev);
- spi_bus_unlock(ec_spi->spi->master);
+ spi_bus_unlock(ec_spi->spi->controller);
if (!ret)
ret = final_ret;
@@ -554,7 +554,7 @@ static int do_cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
if (!rx_buf)
return -ENOMEM;
- spi_bus_lock(ec_spi->spi->master);
+ spi_bus_lock(ec_spi->spi->controller);
/* Transmit phase - send our message */
debug_packet(ec_dev->dev, "out", ec_dev->dout, len);
@@ -590,7 +590,7 @@ static int do_cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
final_ret = terminate_request(ec_dev);
- spi_bus_unlock(ec_spi->spi->master);
+ spi_bus_unlock(ec_spi->spi->controller);
if (!ret)
ret = final_ret;
diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c
index 93e67ab4af..9c944146ee 100644
--- a/drivers/platform/chrome/cros_ec_sysfs.c
+++ b/drivers/platform/chrome/cros_ec_sysfs.c
@@ -8,6 +8,7 @@
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/kobject.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_ec_proto.h>
@@ -347,16 +348,22 @@ static void cros_ec_sysfs_remove(struct platform_device *pd)
sysfs_remove_group(&ec_dev->class_dev.kobj, &cros_ec_attr_group);
}
+static const struct platform_device_id cros_ec_sysfs_id[] = {
+ { DRV_NAME, 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, cros_ec_sysfs_id);
+
static struct platform_driver cros_ec_sysfs_driver = {
.driver = {
.name = DRV_NAME,
},
.probe = cros_ec_sysfs_probe,
.remove_new = cros_ec_sysfs_remove,
+ .id_table = cros_ec_sysfs_id,
};
module_platform_driver(cros_ec_sysfs_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Expose the ChromeOS EC through sysfs");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c
index 2b2f14a1b7..4d305876ec 100644
--- a/drivers/platform/chrome/cros_ec_typec.c
+++ b/drivers/platform/chrome/cros_ec_typec.c
@@ -24,6 +24,23 @@
#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)
+static void cros_typec_role_switch_quirk(struct fwnode_handle *fwnode)
+{
+#ifdef CONFIG_ACPI
+ struct fwnode_handle *switch_fwnode;
+
+ /* Supply the USB role switch with the correct pld_crc if it's missing. */
+ switch_fwnode = fwnode_find_reference(fwnode, "usb-role-switch", 0);
+ if (!IS_ERR_OR_NULL(switch_fwnode)) {
+ struct acpi_device *adev = to_acpi_device_node(switch_fwnode);
+
+ if (adev && !adev->pld_crc)
+ adev->pld_crc = to_acpi_device_node(fwnode)->pld_crc;
+ fwnode_handle_put(switch_fwnode);
+ }
+#endif
+}
+
static int cros_typec_parse_port_props(struct typec_capability *cap,
struct fwnode_handle *fwnode,
struct device *dev)
@@ -66,6 +83,8 @@ static int cros_typec_parse_port_props(struct typec_capability *cap,
cap->prefer_role = ret;
}
+ cros_typec_role_switch_quirk(fwnode);
+
cap->fwnode = fwnode;
return 0;
diff --git a/drivers/platform/chrome/cros_ec_uart.c b/drivers/platform/chrome/cros_ec_uart.c
index eb5eddeb73..62bc24f6dc 100644
--- a/drivers/platform/chrome/cros_ec_uart.c
+++ b/drivers/platform/chrome/cros_ec_uart.c
@@ -81,8 +81,8 @@ struct cros_ec_uart {
struct response_info response;
};
-static ssize_t cros_ec_uart_rx_bytes(struct serdev_device *serdev,
- const u8 *data, size_t count)
+static size_t cros_ec_uart_rx_bytes(struct serdev_device *serdev,
+ const u8 *data, size_t count)
{
struct ec_host_response *host_response;
struct cros_ec_device *ec_dev = serdev_device_get_drvdata(serdev);
diff --git a/drivers/platform/chrome/cros_ec_vbc.c b/drivers/platform/chrome/cros_ec_vbc.c
index 274ea0c64b..787a19db49 100644
--- a/drivers/platform/chrome/cros_ec_vbc.c
+++ b/drivers/platform/chrome/cros_ec_vbc.c
@@ -6,6 +6,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_ec_proto.h>
@@ -133,16 +134,22 @@ static void cros_ec_vbc_remove(struct platform_device *pd)
&cros_ec_vbc_attr_group);
}
+static const struct platform_device_id cros_ec_vbc_id[] = {
+ { DRV_NAME, 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, cros_ec_vbc_id);
+
static struct platform_driver cros_ec_vbc_driver = {
.driver = {
.name = DRV_NAME,
},
.probe = cros_ec_vbc_probe,
.remove_new = cros_ec_vbc_remove,
+ .id_table = cros_ec_vbc_id,
};
module_platform_driver(cros_ec_vbc_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Expose the vboot context nvram to userspace");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/platform/chrome/cros_hps_i2c.c b/drivers/platform/chrome/cros_hps_i2c.c
index b313130803..dd14957ec3 100644
--- a/drivers/platform/chrome/cros_hps_i2c.c
+++ b/drivers/platform/chrome/cros_hps_i2c.c
@@ -126,7 +126,7 @@ static int hps_resume(struct device *dev)
hps_set_power(hps, true);
return 0;
}
-static UNIVERSAL_DEV_PM_OPS(hps_pm_ops, hps_suspend, hps_resume, NULL);
+static DEFINE_RUNTIME_DEV_PM_OPS(hps_pm_ops, hps_suspend, hps_resume, NULL);
static const struct i2c_device_id hps_i2c_id[] = {
{ "cros-hps", 0 },
@@ -148,7 +148,7 @@ static struct i2c_driver hps_i2c_driver = {
.id_table = hps_i2c_id,
.driver = {
.name = "cros-hps",
- .pm = &hps_pm_ops,
+ .pm = pm_ptr(&hps_pm_ops),
.acpi_match_table = ACPI_PTR(hps_acpi_id),
},
};
diff --git a/drivers/platform/chrome/cros_kbd_led_backlight.c b/drivers/platform/chrome/cros_kbd_led_backlight.c
index 793fd3f101..b83e4f3286 100644
--- a/drivers/platform/chrome/cros_kbd_led_backlight.c
+++ b/drivers/platform/chrome/cros_kbd_led_backlight.c
@@ -9,6 +9,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/leds.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_data/cros_ec_commands.h>
@@ -247,17 +248,23 @@ static const struct of_device_id keyboard_led_of_match[] = {
MODULE_DEVICE_TABLE(of, keyboard_led_of_match);
#endif
+static const struct platform_device_id keyboard_led_id[] = {
+ { "cros-keyboard-leds", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, keyboard_led_id);
+
static struct platform_driver keyboard_led_driver = {
.driver = {
- .name = "chromeos-keyboard-leds",
+ .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,
};
module_platform_driver(keyboard_led_driver);
MODULE_AUTHOR("Simon Que <sque@chromium.org>");
MODULE_DESCRIPTION("ChromeOS Keyboard backlight LED Driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:chromeos-keyboard-leds");
diff --git a/drivers/platform/chrome/cros_usbpd_logger.c b/drivers/platform/chrome/cros_usbpd_logger.c
index f618757f8b..930c2f4726 100644
--- a/drivers/platform/chrome/cros_usbpd_logger.c
+++ b/drivers/platform/chrome/cros_usbpd_logger.c
@@ -7,6 +7,7 @@
#include <linux/ktime.h>
#include <linux/math64.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_ec_proto.h>
@@ -249,6 +250,12 @@ static int __maybe_unused cros_usbpd_logger_suspend(struct device *dev)
static SIMPLE_DEV_PM_OPS(cros_usbpd_logger_pm_ops, cros_usbpd_logger_suspend,
cros_usbpd_logger_resume);
+static const struct platform_device_id cros_usbpd_logger_id[] = {
+ { DRV_NAME, 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, cros_usbpd_logger_id);
+
static struct platform_driver cros_usbpd_logger_driver = {
.driver = {
.name = DRV_NAME,
@@ -256,10 +263,10 @@ static struct platform_driver cros_usbpd_logger_driver = {
},
.probe = cros_usbpd_logger_probe,
.remove_new = cros_usbpd_logger_remove,
+ .id_table = cros_usbpd_logger_id,
};
module_platform_driver(cros_usbpd_logger_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Logging driver for ChromeOS EC USBPD Charger.");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/platform/chrome/cros_usbpd_notify.c b/drivers/platform/chrome/cros_usbpd_notify.c
index aacad022f2..c83f81d864 100644
--- a/drivers/platform/chrome/cros_usbpd_notify.c
+++ b/drivers/platform/chrome/cros_usbpd_notify.c
@@ -6,6 +6,7 @@
*/
#include <linux/acpi.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_data/cros_usbpd_notify.h>
@@ -218,12 +219,19 @@ static void cros_usbpd_notify_remove_plat(struct platform_device *pdev)
&pdnotify->nb);
}
+static const struct platform_device_id cros_usbpd_notify_id[] = {
+ { DRV_NAME, 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, cros_usbpd_notify_id);
+
static struct platform_driver cros_usbpd_notify_plat_driver = {
.driver = {
.name = DRV_NAME,
},
.probe = cros_usbpd_notify_probe_plat,
.remove_new = cros_usbpd_notify_remove_plat,
+ .id_table = cros_usbpd_notify_id,
};
static int __init cros_usbpd_notify_init(void)
@@ -258,4 +266,3 @@ module_exit(cros_usbpd_notify_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ChromeOS power delivery notifier device");
MODULE_AUTHOR("Jon Flatley <jflat@chromium.org>");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/platform/chrome/wilco_ec/Kconfig b/drivers/platform/chrome/wilco_ec/Kconfig
index 49e8530ca0..d1648fb099 100644
--- a/drivers/platform/chrome/wilco_ec/Kconfig
+++ b/drivers/platform/chrome/wilco_ec/Kconfig
@@ -3,6 +3,7 @@ config WILCO_EC
tristate "ChromeOS Wilco Embedded Controller"
depends on X86 || COMPILE_TEST
depends on ACPI && CROS_EC_LPC && LEDS_CLASS
+ depends on HAS_IOPORT
help
If you say Y here, you get support for talking to the ChromeOS
Wilco EC over an eSPI bus. This uses a simple byte-level protocol
diff --git a/drivers/platform/chrome/wilco_ec/core.c b/drivers/platform/chrome/wilco_ec/core.c
index 9b59a1bed2..3e6b6cd81a 100644
--- a/drivers/platform/chrome/wilco_ec/core.c
+++ b/drivers/platform/chrome/wilco_ec/core.c
@@ -10,6 +10,7 @@
#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/ioport.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_data/wilco-ec.h>
#include <linux/platform_device.h>
@@ -150,6 +151,12 @@ static const struct acpi_device_id wilco_ec_acpi_device_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, wilco_ec_acpi_device_ids);
+static const struct platform_device_id wilco_ec_id[] = {
+ { DRV_NAME, 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, wilco_ec_id);
+
static struct platform_driver wilco_ec_driver = {
.driver = {
.name = DRV_NAME,
@@ -157,6 +164,7 @@ static struct platform_driver wilco_ec_driver = {
},
.probe = wilco_ec_probe,
.remove_new = wilco_ec_remove,
+ .id_table = wilco_ec_id,
};
module_platform_driver(wilco_ec_driver);
@@ -165,4 +173,3 @@ MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>");
MODULE_AUTHOR("Duncan Laurie <dlaurie@chromium.org>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("ChromeOS Wilco Embedded Controller driver");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/platform/chrome/wilco_ec/debugfs.c b/drivers/platform/chrome/wilco_ec/debugfs.c
index 93c11f81ca..983f2fa44b 100644
--- a/drivers/platform/chrome/wilco_ec/debugfs.c
+++ b/drivers/platform/chrome/wilco_ec/debugfs.c
@@ -10,6 +10,7 @@
#include <linux/ctype.h>
#include <linux/debugfs.h>
#include <linux/fs.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_data/wilco-ec.h>
#include <linux/platform_device.h>
@@ -265,17 +266,23 @@ static void wilco_ec_debugfs_remove(struct platform_device *pdev)
debugfs_remove_recursive(debug_info->dir);
}
+static const struct platform_device_id wilco_ec_debugfs_id[] = {
+ { DRV_NAME, 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, wilco_ec_debugfs_id);
+
static struct platform_driver wilco_ec_debugfs_driver = {
.driver = {
.name = DRV_NAME,
},
.probe = wilco_ec_debugfs_probe,
.remove_new = wilco_ec_debugfs_remove,
+ .id_table = wilco_ec_debugfs_id,
};
module_platform_driver(wilco_ec_debugfs_driver);
-MODULE_ALIAS("platform:" DRV_NAME);
MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Wilco EC debugfs driver");
diff --git a/drivers/platform/chrome/wilco_ec/event.c b/drivers/platform/chrome/wilco_ec/event.c
index 13291fb421..bd1fb53ba0 100644
--- a/drivers/platform/chrome/wilco_ec/event.c
+++ b/drivers/platform/chrome/wilco_ec/event.c
@@ -523,7 +523,6 @@ static struct acpi_driver event_driver = {
.notify = event_device_notify,
.remove = event_device_remove,
},
- .owner = THIS_MODULE,
};
static int __init event_module_init(void)
@@ -575,4 +574,3 @@ module_exit(event_module_exit);
MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>");
MODULE_DESCRIPTION("Wilco EC ACPI event driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/platform/chrome/wilco_ec/sysfs.c b/drivers/platform/chrome/wilco_ec/sysfs.c
index 893c59dde3..d44c435596 100644
--- a/drivers/platform/chrome/wilco_ec/sysfs.c
+++ b/drivers/platform/chrome/wilco_ec/sysfs.c
@@ -192,7 +192,7 @@ static ssize_t usb_charge_show(struct device *dev,
if (ret < 0)
return ret;
- return sprintf(buf, "%d\n", rs.val);
+ return sysfs_emit(buf, "%d\n", rs.val);
}
static ssize_t usb_charge_store(struct device *dev,
diff --git a/drivers/platform/chrome/wilco_ec/telemetry.c b/drivers/platform/chrome/wilco_ec/telemetry.c
index b7c616f3d1..21d4cbbb00 100644
--- a/drivers/platform/chrome/wilco_ec/telemetry.c
+++ b/drivers/platform/chrome/wilco_ec/telemetry.c
@@ -30,6 +30,7 @@
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_data/wilco-ec.h>
#include <linux/platform_device.h>
@@ -409,12 +410,19 @@ static void telem_device_remove(struct platform_device *pdev)
put_device(&dev_data->dev);
}
+static const struct platform_device_id telem_id[] = {
+ { DRV_NAME, 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, telem_id);
+
static struct platform_driver telem_driver = {
.probe = telem_device_probe,
.remove_new = telem_device_remove,
.driver = {
.name = DRV_NAME,
},
+ .id_table = telem_id,
};
static int __init telem_module_init(void)
@@ -466,4 +474,3 @@ module_exit(telem_module_exit);
MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>");
MODULE_DESCRIPTION("Wilco EC telemetry driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/platform/goldfish/Kconfig b/drivers/platform/goldfish/Kconfig
index f3d09b1631..03ca5bf19f 100644
--- a/drivers/platform/goldfish/Kconfig
+++ b/drivers/platform/goldfish/Kconfig
@@ -2,6 +2,7 @@
menuconfig GOLDFISH
bool "Platform support for Goldfish virtual devices"
depends on HAS_IOMEM && HAS_DMA
+ default X86_GOLDFISH
help
Say Y here to get to see options for the Goldfish virtual platform.
This option alone does not add any kernel code.
diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c
index c1aef3a8fb..dd5f370c31 100644
--- a/drivers/platform/mellanox/mlxbf-bootctl.c
+++ b/drivers/platform/mellanox/mlxbf-bootctl.c
@@ -463,7 +463,7 @@ static ssize_t large_icm_show(struct device *dev,
if (res.a0)
return -EPERM;
- return snprintf(buf, PAGE_SIZE, "0x%lx", res.a1);
+ return sysfs_emit(buf, "0x%lx", res.a1);
}
static ssize_t large_icm_store(struct device *dev,
@@ -581,7 +581,7 @@ static ssize_t opn_show(struct device *dev,
}
mutex_unlock(&mfg_ops_lock);
- return snprintf(buf, PAGE_SIZE, "%s", (char *)opn_data);
+ return sysfs_emit(buf, "%s", (char *)opn_data);
}
static ssize_t opn_store(struct device *dev,
@@ -632,7 +632,7 @@ static ssize_t sku_show(struct device *dev,
}
mutex_unlock(&mfg_ops_lock);
- return snprintf(buf, PAGE_SIZE, "%s", (char *)sku_data);
+ return sysfs_emit(buf, "%s", (char *)sku_data);
}
static ssize_t sku_store(struct device *dev,
@@ -683,7 +683,7 @@ static ssize_t modl_show(struct device *dev,
}
mutex_unlock(&mfg_ops_lock);
- return snprintf(buf, PAGE_SIZE, "%s", (char *)modl_data);
+ return sysfs_emit(buf, "%s", (char *)modl_data);
}
static ssize_t modl_store(struct device *dev,
@@ -734,7 +734,7 @@ static ssize_t sn_show(struct device *dev,
}
mutex_unlock(&mfg_ops_lock);
- return snprintf(buf, PAGE_SIZE, "%s", (char *)sn_data);
+ return sysfs_emit(buf, "%s", (char *)sn_data);
}
static ssize_t sn_store(struct device *dev,
@@ -785,7 +785,7 @@ static ssize_t uuid_show(struct device *dev,
}
mutex_unlock(&mfg_ops_lock);
- return snprintf(buf, PAGE_SIZE, "%s", (char *)uuid_data);
+ return sysfs_emit(buf, "%s", (char *)uuid_data);
}
static ssize_t uuid_store(struct device *dev,
@@ -836,7 +836,7 @@ static ssize_t rev_show(struct device *dev,
}
mutex_unlock(&mfg_ops_lock);
- return snprintf(buf, PAGE_SIZE, "%s", (char *)rev_data);
+ return sysfs_emit(buf, "%s", (char *)rev_data);
}
static ssize_t rev_store(struct device *dev,
diff --git a/drivers/platform/mellanox/mlxbf-pmc.c b/drivers/platform/mellanox/mlxbf-pmc.c
index b1995ac268..4ed9c7fd2b 100644
--- a/drivers/platform/mellanox/mlxbf-pmc.c
+++ b/drivers/platform/mellanox/mlxbf-pmc.c
@@ -99,8 +99,8 @@
*/
struct mlxbf_pmc_attribute {
struct device_attribute dev_attr;
- int index;
- int nr;
+ unsigned int index;
+ unsigned int nr;
};
/**
@@ -121,7 +121,7 @@ struct mlxbf_pmc_block_info {
void __iomem *mmio_base;
size_t blk_size;
size_t counters;
- int type;
+ unsigned int type;
struct mlxbf_pmc_attribute *attr_counter;
struct mlxbf_pmc_attribute *attr_event;
struct mlxbf_pmc_attribute attr_event_list;
@@ -149,17 +149,17 @@ struct mlxbf_pmc_block_info {
*/
struct mlxbf_pmc_context {
struct platform_device *pdev;
- uint32_t total_blocks;
- uint32_t tile_count;
- uint8_t llt_enable;
- uint8_t mss_enable;
- uint32_t group_num;
+ u32 total_blocks;
+ u32 tile_count;
+ u8 llt_enable;
+ u8 mss_enable;
+ u32 group_num;
struct device *hwmon_dev;
const char *block_name[MLXBF_PMC_MAX_BLOCKS];
struct mlxbf_pmc_block_info block[MLXBF_PMC_MAX_BLOCKS];
const struct attribute_group *groups[MLXBF_PMC_MAX_BLOCKS];
bool svc_sreg_support;
- uint32_t sreg_tbl_perf;
+ u32 sreg_tbl_perf;
unsigned int event_set;
};
@@ -169,7 +169,7 @@ struct mlxbf_pmc_context {
* @evt_name: Name of the event
*/
struct mlxbf_pmc_events {
- int evt_num;
+ u32 evt_num;
char *evt_name;
};
@@ -865,8 +865,7 @@ static struct mlxbf_pmc_context *pmc;
static const char *mlxbf_pmc_svc_uuid_str = "89c036b4-e7d7-11e6-8797-001aca00bfc4";
/* Calls an SMC to access a performance register */
-static int mlxbf_pmc_secure_read(void __iomem *addr, uint32_t command,
- uint64_t *result)
+static int mlxbf_pmc_secure_read(void __iomem *addr, u32 command, u64 *result)
{
struct arm_smccc_res res;
int status, err = 0;
@@ -892,8 +891,7 @@ static int mlxbf_pmc_secure_read(void __iomem *addr, uint32_t command,
}
/* Read from a performance counter */
-static int mlxbf_pmc_read(void __iomem *addr, uint32_t command,
- uint64_t *result)
+static int mlxbf_pmc_read(void __iomem *addr, u32 command, u64 *result)
{
if (pmc->svc_sreg_support)
return mlxbf_pmc_secure_read(addr, command, result);
@@ -907,22 +905,21 @@ static int mlxbf_pmc_read(void __iomem *addr, uint32_t command,
}
/* Convenience function for 32-bit reads */
-static int mlxbf_pmc_readl(void __iomem *addr, uint32_t *result)
+static int mlxbf_pmc_readl(void __iomem *addr, u32 *result)
{
- uint64_t read_out;
+ u64 read_out;
int status;
status = mlxbf_pmc_read(addr, MLXBF_PMC_READ_REG_32, &read_out);
if (status)
return status;
- *result = (uint32_t)read_out;
+ *result = (u32)read_out;
return 0;
}
/* Calls an SMC to access a performance register */
-static int mlxbf_pmc_secure_write(void __iomem *addr, uint32_t command,
- uint64_t value)
+static int mlxbf_pmc_secure_write(void __iomem *addr, u32 command, u64 value)
{
struct arm_smccc_res res;
int status, err = 0;
@@ -945,7 +942,7 @@ static int mlxbf_pmc_secure_write(void __iomem *addr, uint32_t command,
}
/* Write to a performance counter */
-static int mlxbf_pmc_write(void __iomem *addr, int command, uint64_t value)
+static int mlxbf_pmc_write(void __iomem *addr, int command, u64 value)
{
if (pmc->svc_sreg_support)
return mlxbf_pmc_secure_write(addr, command, value);
@@ -959,7 +956,7 @@ static int mlxbf_pmc_write(void __iomem *addr, int command, uint64_t value)
}
/* Check if the register offset is within the mapped region for the block */
-static bool mlxbf_pmc_valid_range(int blk_num, uint32_t offset)
+static bool mlxbf_pmc_valid_range(unsigned int blk_num, u32 offset)
{
if ((offset >= 0) && !(offset % MLXBF_PMC_REG_SIZE) &&
(offset + MLXBF_PMC_REG_SIZE <= pmc->block[blk_num].blk_size))
@@ -969,33 +966,33 @@ static bool mlxbf_pmc_valid_range(int blk_num, uint32_t offset)
}
/* Get the event list corresponding to a certain block */
-static const struct mlxbf_pmc_events *mlxbf_pmc_event_list(const char *blk,
- int *size)
+static const struct mlxbf_pmc_events *mlxbf_pmc_event_list(const char *blk, size_t *psize)
{
const struct mlxbf_pmc_events *events;
+ size_t size;
if (strstr(blk, "tilenet")) {
events = mlxbf_pmc_hnfnet_events;
- *size = ARRAY_SIZE(mlxbf_pmc_hnfnet_events);
+ size = ARRAY_SIZE(mlxbf_pmc_hnfnet_events);
} else if (strstr(blk, "tile")) {
events = mlxbf_pmc_hnf_events;
- *size = ARRAY_SIZE(mlxbf_pmc_hnf_events);
+ size = ARRAY_SIZE(mlxbf_pmc_hnf_events);
} else if (strstr(blk, "triogen")) {
events = mlxbf_pmc_smgen_events;
- *size = ARRAY_SIZE(mlxbf_pmc_smgen_events);
+ size = ARRAY_SIZE(mlxbf_pmc_smgen_events);
} else if (strstr(blk, "trio")) {
switch (pmc->event_set) {
case MLXBF_PMC_EVENT_SET_BF1:
events = mlxbf_pmc_trio_events_1;
- *size = ARRAY_SIZE(mlxbf_pmc_trio_events_1);
+ size = ARRAY_SIZE(mlxbf_pmc_trio_events_1);
break;
case MLXBF_PMC_EVENT_SET_BF2:
events = mlxbf_pmc_trio_events_2;
- *size = ARRAY_SIZE(mlxbf_pmc_trio_events_2);
+ size = ARRAY_SIZE(mlxbf_pmc_trio_events_2);
break;
default:
events = NULL;
- *size = 0;
+ size = 0;
break;
}
} else if (strstr(blk, "mss")) {
@@ -1003,51 +1000,60 @@ static const struct mlxbf_pmc_events *mlxbf_pmc_event_list(const char *blk,
case MLXBF_PMC_EVENT_SET_BF1:
case MLXBF_PMC_EVENT_SET_BF2:
events = mlxbf_pmc_mss_events_1;
- *size = ARRAY_SIZE(mlxbf_pmc_mss_events_1);
+ size = ARRAY_SIZE(mlxbf_pmc_mss_events_1);
break;
case MLXBF_PMC_EVENT_SET_BF3:
events = mlxbf_pmc_mss_events_3;
- *size = ARRAY_SIZE(mlxbf_pmc_mss_events_3);
+ size = ARRAY_SIZE(mlxbf_pmc_mss_events_3);
break;
default:
events = NULL;
- *size = 0;
+ size = 0;
break;
}
} else if (strstr(blk, "ecc")) {
events = mlxbf_pmc_ecc_events;
- *size = ARRAY_SIZE(mlxbf_pmc_ecc_events);
+ size = ARRAY_SIZE(mlxbf_pmc_ecc_events);
} else if (strstr(blk, "pcie")) {
events = mlxbf_pmc_pcie_events;
- *size = ARRAY_SIZE(mlxbf_pmc_pcie_events);
+ size = ARRAY_SIZE(mlxbf_pmc_pcie_events);
} else if (strstr(blk, "l3cache")) {
events = mlxbf_pmc_l3c_events;
- *size = ARRAY_SIZE(mlxbf_pmc_l3c_events);
+ size = ARRAY_SIZE(mlxbf_pmc_l3c_events);
} else if (strstr(blk, "gic")) {
events = mlxbf_pmc_smgen_events;
- *size = ARRAY_SIZE(mlxbf_pmc_smgen_events);
+ size = ARRAY_SIZE(mlxbf_pmc_smgen_events);
} else if (strstr(blk, "smmu")) {
events = mlxbf_pmc_smgen_events;
- *size = ARRAY_SIZE(mlxbf_pmc_smgen_events);
+ size = ARRAY_SIZE(mlxbf_pmc_smgen_events);
} else if (strstr(blk, "llt_miss")) {
events = mlxbf_pmc_llt_miss_events;
- *size = ARRAY_SIZE(mlxbf_pmc_llt_miss_events);
+ size = ARRAY_SIZE(mlxbf_pmc_llt_miss_events);
} else if (strstr(blk, "llt")) {
events = mlxbf_pmc_llt_events;
- *size = ARRAY_SIZE(mlxbf_pmc_llt_events);
+ size = ARRAY_SIZE(mlxbf_pmc_llt_events);
} else {
events = NULL;
- *size = 0;
+ size = 0;
}
+ if (psize)
+ *psize = size;
+
return events;
}
+static bool mlxbf_pmc_event_supported(const char *blk)
+{
+ return !!mlxbf_pmc_event_list(blk, NULL);
+}
+
/* Get the event number given the name */
static int mlxbf_pmc_get_event_num(const char *blk, const char *evt)
{
const struct mlxbf_pmc_events *events;
- int i, size;
+ unsigned int i;
+ size_t size;
events = mlxbf_pmc_event_list(blk, &size);
if (!events)
@@ -1062,10 +1068,11 @@ static int mlxbf_pmc_get_event_num(const char *blk, const char *evt)
}
/* Get the event number given the name */
-static char *mlxbf_pmc_get_event_name(const char *blk, int evt)
+static char *mlxbf_pmc_get_event_name(const char *blk, u32 evt)
{
const struct mlxbf_pmc_events *events;
- int i, size;
+ unsigned int i;
+ size_t size;
events = mlxbf_pmc_event_list(blk, &size);
if (!events)
@@ -1080,9 +1087,9 @@ static char *mlxbf_pmc_get_event_name(const char *blk, int evt)
}
/* Method to enable/disable/reset l3cache counters */
-static int mlxbf_pmc_config_l3_counters(int blk_num, bool enable, bool reset)
+static int mlxbf_pmc_config_l3_counters(unsigned int blk_num, bool enable, bool reset)
{
- uint32_t perfcnt_cfg = 0;
+ u32 perfcnt_cfg = 0;
if (enable)
perfcnt_cfg |= MLXBF_PMC_L3C_PERF_CNT_CFG_EN;
@@ -1095,12 +1102,9 @@ static int mlxbf_pmc_config_l3_counters(int blk_num, bool enable, bool reset)
}
/* Method to handle l3cache counter programming */
-static int mlxbf_pmc_program_l3_counter(int blk_num, uint32_t cnt_num,
- uint32_t evt)
+static int mlxbf_pmc_program_l3_counter(unsigned int blk_num, u32 cnt_num, u32 evt)
{
- uint32_t perfcnt_sel_1 = 0;
- uint32_t perfcnt_sel = 0;
- uint32_t *wordaddr;
+ u32 perfcnt_sel_1 = 0, perfcnt_sel = 0, *wordaddr;
void __iomem *pmcaddr;
int ret;
@@ -1162,11 +1166,10 @@ static int mlxbf_pmc_program_l3_counter(int blk_num, uint32_t cnt_num,
}
/* Method to handle crspace counter programming */
-static int mlxbf_pmc_program_crspace_counter(int blk_num, uint32_t cnt_num,
- uint32_t evt)
+static int mlxbf_pmc_program_crspace_counter(unsigned int blk_num, u32 cnt_num, u32 evt)
{
- uint32_t word;
void *addr;
+ u32 word;
int ret;
addr = pmc->block[blk_num].mmio_base +
@@ -1187,7 +1190,7 @@ static int mlxbf_pmc_program_crspace_counter(int blk_num, uint32_t cnt_num,
}
/* Method to clear crspace counter value */
-static int mlxbf_pmc_clear_crspace_counter(int blk_num, uint32_t cnt_num)
+static int mlxbf_pmc_clear_crspace_counter(unsigned int blk_num, u32 cnt_num)
{
void *addr;
@@ -1199,10 +1202,9 @@ static int mlxbf_pmc_clear_crspace_counter(int blk_num, uint32_t cnt_num)
}
/* Method to program a counter to monitor an event */
-static int mlxbf_pmc_program_counter(int blk_num, uint32_t cnt_num,
- uint32_t evt, bool is_l3)
+static int mlxbf_pmc_program_counter(unsigned int blk_num, u32 cnt_num, u32 evt, bool is_l3)
{
- uint64_t perfctl, perfevt, perfmon_cfg;
+ u64 perfctl, perfevt, perfmon_cfg;
if (cnt_num >= pmc->block[blk_num].counters)
return -ENODEV;
@@ -1263,12 +1265,11 @@ static int mlxbf_pmc_program_counter(int blk_num, uint32_t cnt_num,
}
/* Method to handle l3 counter reads */
-static int mlxbf_pmc_read_l3_counter(int blk_num, uint32_t cnt_num,
- uint64_t *result)
+static int mlxbf_pmc_read_l3_counter(unsigned int blk_num, u32 cnt_num, u64 *result)
{
- uint32_t perfcnt_low = 0, perfcnt_high = 0;
- uint64_t value;
+ u32 perfcnt_low = 0, perfcnt_high = 0;
int status;
+ u64 value;
status = mlxbf_pmc_readl(pmc->block[blk_num].mmio_base +
MLXBF_PMC_L3C_PERF_CNT_LOW +
@@ -1295,11 +1296,10 @@ static int mlxbf_pmc_read_l3_counter(int blk_num, uint32_t cnt_num,
}
/* Method to handle crspace counter reads */
-static int mlxbf_pmc_read_crspace_counter(int blk_num, uint32_t cnt_num,
- uint64_t *result)
+static int mlxbf_pmc_read_crspace_counter(unsigned int blk_num, u32 cnt_num, u64 *result)
{
- uint32_t value;
int status = 0;
+ u32 value;
status = mlxbf_pmc_readl(pmc->block[blk_num].mmio_base +
MLXBF_PMC_CRSPACE_PERFMON_VAL0(pmc->block[blk_num].counters) +
@@ -1313,11 +1313,10 @@ static int mlxbf_pmc_read_crspace_counter(int blk_num, uint32_t cnt_num,
}
/* Method to read the counter value */
-static int mlxbf_pmc_read_counter(int blk_num, uint32_t cnt_num, bool is_l3,
- uint64_t *result)
+static int mlxbf_pmc_read_counter(unsigned int blk_num, u32 cnt_num, bool is_l3, u64 *result)
{
- uint32_t perfcfg_offset, perfval_offset;
- uint64_t perfmon_cfg;
+ u32 perfcfg_offset, perfval_offset;
+ u64 perfmon_cfg;
int status;
if (cnt_num >= pmc->block[blk_num].counters)
@@ -1351,13 +1350,11 @@ static int mlxbf_pmc_read_counter(int blk_num, uint32_t cnt_num, bool is_l3,
}
/* Method to read L3 block event */
-static int mlxbf_pmc_read_l3_event(int blk_num, uint32_t cnt_num,
- uint64_t *result)
+static int mlxbf_pmc_read_l3_event(unsigned int blk_num, u32 cnt_num, u64 *result)
{
- uint32_t perfcnt_sel = 0, perfcnt_sel_1 = 0;
- uint32_t *wordaddr;
+ u32 perfcnt_sel = 0, perfcnt_sel_1 = 0, *wordaddr;
void __iomem *pmcaddr;
- uint64_t evt;
+ u64 evt;
/* Select appropriate register information */
switch (cnt_num) {
@@ -1405,10 +1402,9 @@ static int mlxbf_pmc_read_l3_event(int blk_num, uint32_t cnt_num,
}
/* Method to read crspace block event */
-static int mlxbf_pmc_read_crspace_event(int blk_num, uint32_t cnt_num,
- uint64_t *result)
+static int mlxbf_pmc_read_crspace_event(unsigned int blk_num, u32 cnt_num, u64 *result)
{
- uint32_t word, evt;
+ u32 word, evt;
void *addr;
int ret;
@@ -1429,11 +1425,10 @@ static int mlxbf_pmc_read_crspace_event(int blk_num, uint32_t cnt_num,
}
/* Method to find the event currently being monitored by a counter */
-static int mlxbf_pmc_read_event(int blk_num, uint32_t cnt_num, bool is_l3,
- uint64_t *result)
+static int mlxbf_pmc_read_event(unsigned int blk_num, u32 cnt_num, bool is_l3, u64 *result)
{
- uint32_t perfcfg_offset, perfval_offset;
- uint64_t perfmon_cfg, perfevt;
+ u32 perfcfg_offset, perfval_offset;
+ u64 perfmon_cfg, perfevt;
if (cnt_num >= pmc->block[blk_num].counters)
return -EINVAL;
@@ -1469,9 +1464,9 @@ static int mlxbf_pmc_read_event(int blk_num, uint32_t cnt_num, bool is_l3,
}
/* Method to read a register */
-static int mlxbf_pmc_read_reg(int blk_num, uint32_t offset, uint64_t *result)
+static int mlxbf_pmc_read_reg(unsigned int blk_num, u32 offset, u64 *result)
{
- uint32_t ecc_out;
+ u32 ecc_out;
if (strstr(pmc->block_name[blk_num], "ecc")) {
if (mlxbf_pmc_readl(pmc->block[blk_num].mmio_base + offset,
@@ -1490,7 +1485,7 @@ static int mlxbf_pmc_read_reg(int blk_num, uint32_t offset, uint64_t *result)
}
/* Method to write to a register */
-static int mlxbf_pmc_write_reg(int blk_num, uint32_t offset, uint64_t data)
+static int mlxbf_pmc_write_reg(unsigned int blk_num, u32 offset, u64 data)
{
if (strstr(pmc->block_name[blk_num], "ecc")) {
return mlxbf_pmc_write(pmc->block[blk_num].mmio_base + offset,
@@ -1510,9 +1505,10 @@ static ssize_t mlxbf_pmc_counter_show(struct device *dev,
{
struct mlxbf_pmc_attribute *attr_counter = container_of(
attr, struct mlxbf_pmc_attribute, dev_attr);
- int blk_num, cnt_num, offset;
+ unsigned int blk_num, cnt_num;
bool is_l3 = false;
- uint64_t value;
+ int offset;
+ u64 value;
blk_num = attr_counter->nr;
cnt_num = attr_counter->index;
@@ -1544,14 +1540,16 @@ static ssize_t mlxbf_pmc_counter_store(struct device *dev,
{
struct mlxbf_pmc_attribute *attr_counter = container_of(
attr, struct mlxbf_pmc_attribute, dev_attr);
- int blk_num, cnt_num, offset, err, data;
+ unsigned int blk_num, cnt_num, data;
bool is_l3 = false;
- uint64_t evt_num;
+ u64 evt_num;
+ int offset;
+ int err;
blk_num = attr_counter->nr;
cnt_num = attr_counter->index;
- err = kstrtoint(buf, 0, &data);
+ err = kstrtouint(buf, 0, &data);
if (err < 0)
return err;
@@ -1580,7 +1578,7 @@ static ssize_t mlxbf_pmc_counter_store(struct device *dev,
if (err)
return err;
} else if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_CRSPACE) {
- if (sscanf(attr->attr.name, "counter%d", &cnt_num) != 1)
+ if (sscanf(attr->attr.name, "counter%u", &cnt_num) != 1)
return -EINVAL;
err = mlxbf_pmc_clear_crspace_counter(blk_num, cnt_num);
} else
@@ -1595,10 +1593,11 @@ static ssize_t mlxbf_pmc_event_show(struct device *dev,
{
struct mlxbf_pmc_attribute *attr_event = container_of(
attr, struct mlxbf_pmc_attribute, dev_attr);
- int blk_num, cnt_num, err;
+ unsigned int blk_num, cnt_num;
bool is_l3 = false;
- uint64_t evt_num;
char *evt_name;
+ u64 evt_num;
+ int err;
blk_num = attr_event->nr;
cnt_num = attr_event->index;
@@ -1624,8 +1623,10 @@ static ssize_t mlxbf_pmc_event_store(struct device *dev,
{
struct mlxbf_pmc_attribute *attr_event = container_of(
attr, struct mlxbf_pmc_attribute, dev_attr);
- int blk_num, cnt_num, evt_num, err;
+ unsigned int blk_num, cnt_num;
bool is_l3 = false;
+ int evt_num;
+ int err;
blk_num = attr_event->nr;
cnt_num = attr_event->index;
@@ -1636,7 +1637,7 @@ static ssize_t mlxbf_pmc_event_store(struct device *dev,
if (evt_num < 0)
return -EINVAL;
} else {
- err = kstrtoint(buf, 0, &evt_num);
+ err = kstrtouint(buf, 0, &evt_num);
if (err < 0)
return err;
}
@@ -1658,9 +1659,11 @@ static ssize_t mlxbf_pmc_event_list_show(struct device *dev,
{
struct mlxbf_pmc_attribute *attr_event_list = container_of(
attr, struct mlxbf_pmc_attribute, dev_attr);
- int blk_num, i, size, len = 0, ret = 0;
const struct mlxbf_pmc_events *events;
char e_info[MLXBF_PMC_EVENT_INFO_LEN];
+ unsigned int blk_num, i, len = 0;
+ size_t size;
+ int ret = 0;
blk_num = attr_event_list->nr;
@@ -1686,8 +1689,8 @@ static ssize_t mlxbf_pmc_enable_show(struct device *dev,
{
struct mlxbf_pmc_attribute *attr_enable = container_of(
attr, struct mlxbf_pmc_attribute, dev_attr);
- uint32_t perfcnt_cfg, word;
- int blk_num, value;
+ unsigned int blk_num, value;
+ u32 perfcnt_cfg, word;
blk_num = attr_enable->nr;
@@ -1707,7 +1710,7 @@ static ssize_t mlxbf_pmc_enable_show(struct device *dev,
value = FIELD_GET(MLXBF_PMC_L3C_PERF_CNT_CFG_EN, perfcnt_cfg);
}
- return sysfs_emit(buf, "%d\n", value);
+ return sysfs_emit(buf, "%u\n", value);
}
/* Store function for "enable" sysfs files - only for l3cache & crspace */
@@ -1717,12 +1720,13 @@ static ssize_t mlxbf_pmc_enable_store(struct device *dev,
{
struct mlxbf_pmc_attribute *attr_enable = container_of(
attr, struct mlxbf_pmc_attribute, dev_attr);
- int err, en, blk_num;
- uint32_t word;
+ unsigned int en, blk_num;
+ u32 word;
+ int err;
blk_num = attr_enable->nr;
- err = kstrtoint(buf, 0, &en);
+ err = kstrtouint(buf, 0, &en);
if (err < 0)
return err;
@@ -1760,10 +1764,13 @@ static ssize_t mlxbf_pmc_enable_store(struct device *dev,
}
/* Populate attributes for blocks with counters to monitor performance */
-static int mlxbf_pmc_init_perftype_counter(struct device *dev, int blk_num)
+static int mlxbf_pmc_init_perftype_counter(struct device *dev, unsigned int blk_num)
{
struct mlxbf_pmc_attribute *attr;
- int i = 0, j = 0;
+ unsigned int i = 0, j = 0;
+
+ if (!mlxbf_pmc_event_supported(pmc->block_name[blk_num]))
+ return -ENOENT;
/* "event_list" sysfs to list events supported by the block */
attr = &pmc->block[blk_num].attr_event_list;
@@ -1812,8 +1819,7 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, int blk_num)
attr->dev_attr.store = mlxbf_pmc_counter_store;
attr->index = j;
attr->nr = blk_num;
- attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL,
- "counter%d", j);
+ attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, "counter%u", j);
if (!attr->dev_attr.attr.name)
return -ENOMEM;
pmc->block[blk_num].block_attr[++i] = &attr->dev_attr.attr;
@@ -1825,8 +1831,7 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, int blk_num)
attr->dev_attr.store = mlxbf_pmc_event_store;
attr->index = j;
attr->nr = blk_num;
- attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL,
- "event%d", j);
+ attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, "event%u", j);
if (!attr->dev_attr.attr.name)
return -ENOMEM;
pmc->block[blk_num].block_attr[++i] = &attr->dev_attr.attr;
@@ -1837,30 +1842,31 @@ static int mlxbf_pmc_init_perftype_counter(struct device *dev, int blk_num)
}
/* Populate attributes for blocks with registers to monitor performance */
-static int mlxbf_pmc_init_perftype_reg(struct device *dev, int blk_num)
+static int mlxbf_pmc_init_perftype_reg(struct device *dev, unsigned int blk_num)
{
- struct mlxbf_pmc_attribute *attr;
const struct mlxbf_pmc_events *events;
- int i = 0, j = 0;
+ struct mlxbf_pmc_attribute *attr;
+ unsigned int i = 0;
+ size_t count = 0;
- events = mlxbf_pmc_event_list(pmc->block_name[blk_num], &j);
+ events = mlxbf_pmc_event_list(pmc->block_name[blk_num], &count);
if (!events)
- return -EINVAL;
+ return -ENOENT;
pmc->block[blk_num].attr_event = devm_kcalloc(
- dev, j, sizeof(struct mlxbf_pmc_attribute), GFP_KERNEL);
+ dev, count, sizeof(struct mlxbf_pmc_attribute), GFP_KERNEL);
if (!pmc->block[blk_num].attr_event)
return -ENOMEM;
- while (j > 0) {
- --j;
- attr = &pmc->block[blk_num].attr_event[j];
+ while (count > 0) {
+ --count;
+ attr = &pmc->block[blk_num].attr_event[count];
attr->dev_attr.attr.mode = 0644;
attr->dev_attr.show = mlxbf_pmc_counter_show;
attr->dev_attr.store = mlxbf_pmc_counter_store;
attr->nr = blk_num;
attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL,
- events[j].evt_name);
+ events[count].evt_name);
if (!attr->dev_attr.attr.name)
return -ENOMEM;
pmc->block[blk_num].block_attr[i] = &attr->dev_attr.attr;
@@ -1872,7 +1878,7 @@ static int mlxbf_pmc_init_perftype_reg(struct device *dev, int blk_num)
}
/* Helper to create the bfperf sysfs sub-directories and files */
-static int mlxbf_pmc_create_groups(struct device *dev, int blk_num)
+static int mlxbf_pmc_create_groups(struct device *dev, unsigned int blk_num)
{
int err;
@@ -1883,7 +1889,7 @@ static int mlxbf_pmc_create_groups(struct device *dev, int blk_num)
else if (pmc->block[blk_num].type == MLXBF_PMC_TYPE_REGISTER)
err = mlxbf_pmc_init_perftype_reg(dev, blk_num);
else
- err = -EINVAL;
+ err = -ENOENT;
if (err)
return err;
@@ -1914,19 +1920,20 @@ static bool mlxbf_pmc_guid_match(const guid_t *guid,
/* Helper to map the Performance Counters from the varios blocks */
static int mlxbf_pmc_map_counters(struct device *dev)
{
- uint64_t info[MLXBF_PMC_INFO_SZ];
- int i, tile_num, ret;
+ u64 info[MLXBF_PMC_INFO_SZ];
+ unsigned int tile_num, i;
+ int ret;
for (i = 0; i < pmc->total_blocks; ++i) {
/* Create sysfs for tiles only if block number < tile_count */
if (strstr(pmc->block_name[i], "tilenet")) {
- if (sscanf(pmc->block_name[i], "tilenet%d", &tile_num) != 1)
+ if (sscanf(pmc->block_name[i], "tilenet%u", &tile_num) != 1)
continue;
if (tile_num >= pmc->tile_count)
continue;
} else if (strstr(pmc->block_name[i], "tile")) {
- if (sscanf(pmc->block_name[i], "tile%d", &tile_num) != 1)
+ if (sscanf(pmc->block_name[i], "tile%u", &tile_num) != 1)
continue;
if (tile_num >= pmc->tile_count)
@@ -1936,9 +1943,9 @@ static int mlxbf_pmc_map_counters(struct device *dev)
/* Create sysfs only for enabled MSS blocks */
if (strstr(pmc->block_name[i], "mss") &&
pmc->event_set == MLXBF_PMC_EVENT_SET_BF3) {
- int mss_num;
+ unsigned int mss_num;
- if (sscanf(pmc->block_name[i], "mss%d", &mss_num) != 1)
+ if (sscanf(pmc->block_name[i], "mss%u", &mss_num) != 1)
continue;
if (!((pmc->mss_enable >> mss_num) & 0x1))
@@ -1947,17 +1954,17 @@ static int mlxbf_pmc_map_counters(struct device *dev)
/* Create sysfs only for enabled LLT blocks */
if (strstr(pmc->block_name[i], "llt_miss")) {
- int llt_num;
+ unsigned int llt_num;
- if (sscanf(pmc->block_name[i], "llt_miss%d", &llt_num) != 1)
+ if (sscanf(pmc->block_name[i], "llt_miss%u", &llt_num) != 1)
continue;
if (!((pmc->llt_enable >> llt_num) & 0x1))
continue;
} else if (strstr(pmc->block_name[i], "llt")) {
- int llt_num;
+ unsigned int llt_num;
- if (sscanf(pmc->block_name[i], "llt%d", &llt_num) != 1)
+ if (sscanf(pmc->block_name[i], "llt%u", &llt_num) != 1)
continue;
if (!((pmc->llt_enable >> llt_num) & 0x1))
@@ -1987,6 +1994,10 @@ static int mlxbf_pmc_map_counters(struct device *dev)
return -ENOMEM;
ret = mlxbf_pmc_create_groups(dev, i);
+ if (ret == -ENOENT) {
+ dev_warn(dev, "ignoring unsupported block: '%s'\n", pmc->block_name[i]);
+ continue;
+ }
if (ret)
return ret;
}
diff --git a/drivers/platform/mellanox/mlxreg-hotplug.c b/drivers/platform/mellanox/mlxreg-hotplug.c
index 5c022b258f..0ce9fff1f7 100644
--- a/drivers/platform/mellanox/mlxreg-hotplug.c
+++ b/drivers/platform/mellanox/mlxreg-hotplug.c
@@ -348,20 +348,6 @@ mlxreg_hotplug_work_helper(struct mlxreg_hotplug_priv_data *priv,
u32 regval, bit;
int ret;
- /*
- * Validate if item related to received signal type is valid.
- * It should never happen, excepted the situation when some
- * piece of hardware is broken. In such situation just produce
- * error message and return. Caller must continue to handle the
- * signals from other devices if any.
- */
- if (unlikely(!item)) {
- dev_err(priv->dev, "False signal: at offset:mask 0x%02x:0x%02x.\n",
- item->reg, item->mask);
-
- return;
- }
-
/* Mask event. */
ret = regmap_write(priv->regmap, item->reg + MLXREG_HOTPLUG_MASK_OFF,
0);
diff --git a/drivers/platform/mellanox/nvsw-sn2201.c b/drivers/platform/mellanox/nvsw-sn2201.c
index 3ef6555914..abe7be602f 100644
--- a/drivers/platform/mellanox/nvsw-sn2201.c
+++ b/drivers/platform/mellanox/nvsw-sn2201.c
@@ -1198,6 +1198,7 @@ static int nvsw_sn2201_config_pre_init(struct nvsw_sn2201 *nvsw_sn2201)
static int nvsw_sn2201_probe(struct platform_device *pdev)
{
struct nvsw_sn2201 *nvsw_sn2201;
+ int ret;
nvsw_sn2201 = devm_kzalloc(&pdev->dev, sizeof(*nvsw_sn2201), GFP_KERNEL);
if (!nvsw_sn2201)
@@ -1205,8 +1206,10 @@ static int nvsw_sn2201_probe(struct platform_device *pdev)
nvsw_sn2201->dev = &pdev->dev;
platform_set_drvdata(pdev, nvsw_sn2201);
- platform_device_add_resources(pdev, nvsw_sn2201_lpc_io_resources,
+ ret = platform_device_add_resources(pdev, nvsw_sn2201_lpc_io_resources,
ARRAY_SIZE(nvsw_sn2201_lpc_io_resources));
+ if (ret)
+ return ret;
nvsw_sn2201->main_mux_deferred_nr = NVSW_SN2201_MAIN_MUX_DEFER_NR;
nvsw_sn2201->main_mux_devs = nvsw_sn2201_main_mux_brdinfo;
diff --git a/drivers/platform/mips/cpu_hwmon.c b/drivers/platform/mips/cpu_hwmon.c
index d8c5f9195f..2ac2f31090 100644
--- a/drivers/platform/mips/cpu_hwmon.c
+++ b/drivers/platform/mips/cpu_hwmon.c
@@ -139,6 +139,9 @@ static int __init loongson_hwmon_init(void)
csr_temp_enable = csr_readl(LOONGSON_CSR_FEATURES) &
LOONGSON_CSRF_TEMP;
+ if (!csr_temp_enable && !loongson_chiptemp[0])
+ return -ENODEV;
+
nr_packages = loongson_sysconf.nr_cpus /
loongson_sysconf.cores_per_package;
diff --git a/drivers/platform/surface/aggregator/core.c b/drivers/platform/surface/aggregator/core.c
index 9591a28bc3..797d0645bd 100644
--- a/drivers/platform/surface/aggregator/core.c
+++ b/drivers/platform/surface/aggregator/core.c
@@ -227,8 +227,8 @@ EXPORT_SYMBOL_GPL(ssam_client_bind);
/* -- Glue layer (serdev_device -> ssam_controller). ------------------------ */
-static ssize_t ssam_receive_buf(struct serdev_device *dev, const u8 *buf,
- size_t n)
+static size_t ssam_receive_buf(struct serdev_device *dev, const u8 *buf,
+ size_t n)
{
struct ssam_controller *ctrl;
int ret;
@@ -618,15 +618,17 @@ static const struct acpi_gpio_mapping ssam_acpi_gpios[] = {
static int ssam_serial_hub_probe(struct serdev_device *serdev)
{
- struct acpi_device *ssh = ACPI_COMPANION(&serdev->dev);
+ struct device *dev = &serdev->dev;
+ struct acpi_device *ssh = ACPI_COMPANION(dev);
struct ssam_controller *ctrl;
acpi_status astatus;
int status;
- if (gpiod_count(&serdev->dev, NULL) < 0)
- return -ENODEV;
+ status = gpiod_count(dev, NULL);
+ if (status < 0)
+ return dev_err_probe(dev, status, "no GPIO found\n");
- status = devm_acpi_dev_add_driver_gpios(&serdev->dev, ssam_acpi_gpios);
+ status = devm_acpi_dev_add_driver_gpios(dev, ssam_acpi_gpios);
if (status)
return status;
@@ -637,8 +639,10 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev)
/* Initialize controller. */
status = ssam_controller_init(ctrl, serdev);
- if (status)
+ if (status) {
+ dev_err_probe(dev, status, "failed to initialize ssam controller\n");
goto err_ctrl_init;
+ }
ssam_controller_lock(ctrl);
@@ -646,12 +650,14 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev)
serdev_device_set_drvdata(serdev, ctrl);
serdev_device_set_client_ops(serdev, &ssam_serdev_ops);
status = serdev_device_open(serdev);
- if (status)
+ if (status) {
+ dev_err_probe(dev, status, "failed to open serdev device\n");
goto err_devopen;
+ }
astatus = ssam_serdev_setup_via_acpi(ssh->handle, serdev);
if (ACPI_FAILURE(astatus)) {
- status = -ENXIO;
+ status = dev_err_probe(dev, -ENXIO, "failed to setup serdev\n");
goto err_devinit;
}
@@ -667,25 +673,33 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev)
* states.
*/
status = ssam_log_firmware_version(ctrl);
- if (status)
+ if (status) {
+ dev_err_probe(dev, status, "failed to get firmware version\n");
goto err_initrq;
+ }
status = ssam_ctrl_notif_d0_entry(ctrl);
- if (status)
+ if (status) {
+ dev_err_probe(dev, status, "D0-entry notification failed\n");
goto err_initrq;
+ }
status = ssam_ctrl_notif_display_on(ctrl);
- if (status)
+ if (status) {
+ dev_err_probe(dev, status, "display-on notification failed\n");
goto err_initrq;
+ }
- status = sysfs_create_group(&serdev->dev.kobj, &ssam_sam_group);
+ status = sysfs_create_group(&dev->kobj, &ssam_sam_group);
if (status)
goto err_initrq;
/* Set up IRQ. */
status = ssam_irq_setup(ctrl);
- if (status)
+ if (status) {
+ dev_err_probe(dev, status, "failed to setup IRQ\n");
goto err_irq;
+ }
/* Finally, set main controller reference. */
status = ssam_try_set_controller(ctrl);
@@ -702,7 +716,7 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev)
* resumed. In short, this causes some spurious unwanted wake-ups.
* For now let's thus default power/wakeup to false.
*/
- device_set_wakeup_capable(&serdev->dev, true);
+ device_set_wakeup_capable(dev, true);
acpi_dev_clear_dependencies(ssh);
return 0;
@@ -710,7 +724,7 @@ static int ssam_serial_hub_probe(struct serdev_device *serdev)
err_mainref:
ssam_irq_free(ctrl);
err_irq:
- sysfs_remove_group(&serdev->dev.kobj, &ssam_sam_group);
+ sysfs_remove_group(&dev->kobj, &ssam_sam_group);
err_initrq:
ssam_controller_lock(ctrl);
ssam_controller_shutdown(ctrl);
diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c
index aeb3feae40..1c4d74db08 100644
--- a/drivers/platform/surface/surface_aggregator_registry.c
+++ b/drivers/platform/surface/surface_aggregator_registry.c
@@ -68,12 +68,38 @@ static const struct software_node ssam_node_bat_sb3base = {
.parent = &ssam_node_hub_base,
};
-/* Platform profile / performance-mode device. */
-static const struct software_node ssam_node_tmp_pprof = {
+/* Platform profile / performance-mode device without a fan. */
+static const struct software_node ssam_node_tmp_perf_profile = {
.name = "ssam:01:03:01:00:01",
.parent = &ssam_node_root,
};
+/* Platform profile / performance-mode device with a fan, such that
+ * the fan controller profile can also be switched.
+ */
+static const struct property_entry ssam_node_tmp_perf_profile_has_fan[] = {
+ PROPERTY_ENTRY_BOOL("has_fan"),
+ { }
+};
+
+static const struct software_node ssam_node_tmp_perf_profile_with_fan = {
+ .name = "ssam:01:03:01:00:01",
+ .parent = &ssam_node_root,
+ .properties = ssam_node_tmp_perf_profile_has_fan,
+};
+
+/* Thermal sensors. */
+static const struct software_node ssam_node_tmp_sensors = {
+ .name = "ssam:01:03:01:00:02",
+ .parent = &ssam_node_root,
+};
+
+/* Fan speed function. */
+static const struct software_node ssam_node_fan_speed = {
+ .name = "ssam:01:05:01:01:01",
+ .parent = &ssam_node_root,
+};
+
/* Tablet-mode switch via KIP subsystem. */
static const struct software_node ssam_node_kip_tablet_switch = {
.name = "ssam:01:0e:01:00:01",
@@ -202,7 +228,7 @@ static const struct software_node ssam_node_pos_tablet_switch = {
*/
static const struct software_node *ssam_node_group_gen5[] = {
&ssam_node_root,
- &ssam_node_tmp_pprof,
+ &ssam_node_tmp_perf_profile,
NULL,
};
@@ -213,7 +239,7 @@ static const struct software_node *ssam_node_group_sb3[] = {
&ssam_node_bat_ac,
&ssam_node_bat_main,
&ssam_node_bat_sb3base,
- &ssam_node_tmp_pprof,
+ &ssam_node_tmp_perf_profile,
&ssam_node_bas_dtx,
&ssam_node_hid_base_keyboard,
&ssam_node_hid_base_touchpad,
@@ -227,7 +253,7 @@ static const struct software_node *ssam_node_group_sl3[] = {
&ssam_node_root,
&ssam_node_bat_ac,
&ssam_node_bat_main,
- &ssam_node_tmp_pprof,
+ &ssam_node_tmp_perf_profile,
&ssam_node_hid_main_keyboard,
&ssam_node_hid_main_touchpad,
&ssam_node_hid_main_iid5,
@@ -239,7 +265,7 @@ static const struct software_node *ssam_node_group_sl5[] = {
&ssam_node_root,
&ssam_node_bat_ac,
&ssam_node_bat_main,
- &ssam_node_tmp_pprof,
+ &ssam_node_tmp_perf_profile,
&ssam_node_hid_main_keyboard,
&ssam_node_hid_main_touchpad,
&ssam_node_hid_main_iid5,
@@ -252,7 +278,7 @@ static const struct software_node *ssam_node_group_sls[] = {
&ssam_node_root,
&ssam_node_bat_ac,
&ssam_node_bat_main,
- &ssam_node_tmp_pprof,
+ &ssam_node_tmp_perf_profile,
&ssam_node_pos_tablet_switch,
&ssam_node_hid_sam_keyboard,
&ssam_node_hid_sam_penstash,
@@ -268,7 +294,7 @@ static const struct software_node *ssam_node_group_slg1[] = {
&ssam_node_root,
&ssam_node_bat_ac,
&ssam_node_bat_main,
- &ssam_node_tmp_pprof,
+ &ssam_node_tmp_perf_profile,
NULL,
};
@@ -277,7 +303,7 @@ static const struct software_node *ssam_node_group_sp7[] = {
&ssam_node_root,
&ssam_node_bat_ac,
&ssam_node_bat_main,
- &ssam_node_tmp_pprof,
+ &ssam_node_tmp_perf_profile,
NULL,
};
@@ -287,7 +313,7 @@ static const struct software_node *ssam_node_group_sp8[] = {
&ssam_node_hub_kip,
&ssam_node_bat_ac,
&ssam_node_bat_main,
- &ssam_node_tmp_pprof,
+ &ssam_node_tmp_perf_profile,
&ssam_node_kip_tablet_switch,
&ssam_node_hid_kip_keyboard,
&ssam_node_hid_kip_penstash,
@@ -304,7 +330,9 @@ static const struct software_node *ssam_node_group_sp9[] = {
&ssam_node_hub_kip,
&ssam_node_bat_ac,
&ssam_node_bat_main,
- &ssam_node_tmp_pprof,
+ &ssam_node_tmp_perf_profile_with_fan,
+ &ssam_node_tmp_sensors,
+ &ssam_node_fan_speed,
&ssam_node_pos_tablet_switch,
&ssam_node_hid_kip_keyboard,
&ssam_node_hid_kip_penstash,
diff --git a/drivers/platform/surface/surface_platform_profile.c b/drivers/platform/surface/surface_platform_profile.c
index a5a3941b3f..3de864bc66 100644
--- a/drivers/platform/surface/surface_platform_profile.c
+++ b/drivers/platform/surface/surface_platform_profile.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Surface Platform Profile / Performance Mode driver for Surface System
- * Aggregator Module (thermal subsystem).
+ * Aggregator Module (thermal and fan subsystem).
*
* Copyright (C) 2021-2022 Maximilian Luz <luzmaximilian@gmail.com>
*/
@@ -14,6 +14,7 @@
#include <linux/surface_aggregator/device.h>
+// Enum for the platform performance profile sent to the TMP module.
enum ssam_tmp_profile {
SSAM_TMP_PROFILE_NORMAL = 1,
SSAM_TMP_PROFILE_BATTERY_SAVER = 2,
@@ -21,15 +22,26 @@ enum ssam_tmp_profile {
SSAM_TMP_PROFILE_BEST_PERFORMANCE = 4,
};
+// Enum for the fan profile sent to the FAN module. This fan profile is
+// only sent to the EC if the 'has_fan' property is set. The integers are
+// not a typo, they differ from the performance profile indices.
+enum ssam_fan_profile {
+ SSAM_FAN_PROFILE_NORMAL = 2,
+ SSAM_FAN_PROFILE_BATTERY_SAVER = 1,
+ SSAM_FAN_PROFILE_BETTER_PERFORMANCE = 3,
+ SSAM_FAN_PROFILE_BEST_PERFORMANCE = 4,
+};
+
struct ssam_tmp_profile_info {
__le32 profile;
__le16 unknown1;
__le16 unknown2;
} __packed;
-struct ssam_tmp_profile_device {
+struct ssam_platform_profile_device {
struct ssam_device *sdev;
struct platform_profile_handler handler;
+ bool has_fan;
};
SSAM_DEFINE_SYNC_REQUEST_CL_R(__ssam_tmp_profile_get, struct ssam_tmp_profile_info, {
@@ -42,6 +54,13 @@ SSAM_DEFINE_SYNC_REQUEST_CL_W(__ssam_tmp_profile_set, __le32, {
.command_id = 0x03,
});
+SSAM_DEFINE_SYNC_REQUEST_W(__ssam_fan_profile_set, u8, {
+ .target_category = SSAM_SSH_TC_FAN,
+ .target_id = SSAM_SSH_TID_SAM,
+ .command_id = 0x0e,
+ .instance_id = 0x01,
+});
+
static int ssam_tmp_profile_get(struct ssam_device *sdev, enum ssam_tmp_profile *p)
{
struct ssam_tmp_profile_info info;
@@ -57,12 +76,19 @@ static int ssam_tmp_profile_get(struct ssam_device *sdev, enum ssam_tmp_profile
static int ssam_tmp_profile_set(struct ssam_device *sdev, enum ssam_tmp_profile p)
{
- __le32 profile_le = cpu_to_le32(p);
+ const __le32 profile_le = cpu_to_le32(p);
return ssam_retry(__ssam_tmp_profile_set, sdev, &profile_le);
}
-static int convert_ssam_to_profile(struct ssam_device *sdev, enum ssam_tmp_profile p)
+static int ssam_fan_profile_set(struct ssam_device *sdev, enum ssam_fan_profile p)
+{
+ const u8 profile = p;
+
+ return ssam_retry(__ssam_fan_profile_set, sdev->ctrl, &profile);
+}
+
+static int convert_ssam_tmp_to_profile(struct ssam_device *sdev, enum ssam_tmp_profile p)
{
switch (p) {
case SSAM_TMP_PROFILE_NORMAL:
@@ -83,7 +109,8 @@ static int convert_ssam_to_profile(struct ssam_device *sdev, enum ssam_tmp_profi
}
}
-static int convert_profile_to_ssam(struct ssam_device *sdev, enum platform_profile_option p)
+
+static int convert_profile_to_ssam_tmp(struct ssam_device *sdev, enum platform_profile_option p)
{
switch (p) {
case PLATFORM_PROFILE_LOW_POWER:
@@ -105,20 +132,42 @@ static int convert_profile_to_ssam(struct ssam_device *sdev, enum platform_profi
}
}
+static int convert_profile_to_ssam_fan(struct ssam_device *sdev, enum platform_profile_option p)
+{
+ switch (p) {
+ case PLATFORM_PROFILE_LOW_POWER:
+ return SSAM_FAN_PROFILE_BATTERY_SAVER;
+
+ case PLATFORM_PROFILE_BALANCED:
+ return SSAM_FAN_PROFILE_NORMAL;
+
+ case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
+ return SSAM_FAN_PROFILE_BETTER_PERFORMANCE;
+
+ case PLATFORM_PROFILE_PERFORMANCE:
+ return SSAM_FAN_PROFILE_BEST_PERFORMANCE;
+
+ default:
+ /* This should have already been caught by platform_profile_store(). */
+ WARN(true, "unsupported platform profile");
+ return -EOPNOTSUPP;
+ }
+}
+
static int ssam_platform_profile_get(struct platform_profile_handler *pprof,
enum platform_profile_option *profile)
{
- struct ssam_tmp_profile_device *tpd;
+ struct ssam_platform_profile_device *tpd;
enum ssam_tmp_profile tp;
int status;
- tpd = container_of(pprof, struct ssam_tmp_profile_device, handler);
+ tpd = container_of(pprof, struct ssam_platform_profile_device, handler);
status = ssam_tmp_profile_get(tpd->sdev, &tp);
if (status)
return status;
- status = convert_ssam_to_profile(tpd->sdev, tp);
+ status = convert_ssam_tmp_to_profile(tpd->sdev, tp);
if (status < 0)
return status;
@@ -129,21 +178,32 @@ static int ssam_platform_profile_get(struct platform_profile_handler *pprof,
static int ssam_platform_profile_set(struct platform_profile_handler *pprof,
enum platform_profile_option profile)
{
- struct ssam_tmp_profile_device *tpd;
+ struct ssam_platform_profile_device *tpd;
int tp;
- tpd = container_of(pprof, struct ssam_tmp_profile_device, handler);
+ tpd = container_of(pprof, struct ssam_platform_profile_device, handler);
+
+ tp = convert_profile_to_ssam_tmp(tpd->sdev, profile);
+ if (tp < 0)
+ return tp;
- tp = convert_profile_to_ssam(tpd->sdev, profile);
+ tp = ssam_tmp_profile_set(tpd->sdev, tp);
if (tp < 0)
return tp;
- return ssam_tmp_profile_set(tpd->sdev, tp);
+ if (tpd->has_fan) {
+ tp = convert_profile_to_ssam_fan(tpd->sdev, profile);
+ if (tp < 0)
+ return tp;
+ tp = ssam_fan_profile_set(tpd->sdev, tp);
+ }
+
+ return tp;
}
static int surface_platform_profile_probe(struct ssam_device *sdev)
{
- struct ssam_tmp_profile_device *tpd;
+ struct ssam_platform_profile_device *tpd;
tpd = devm_kzalloc(&sdev->dev, sizeof(*tpd), GFP_KERNEL);
if (!tpd)
@@ -154,6 +214,8 @@ static int surface_platform_profile_probe(struct ssam_device *sdev)
tpd->handler.profile_get = ssam_platform_profile_get;
tpd->handler.profile_set = ssam_platform_profile_set;
+ 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);
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index bdd302274b..ddfccc2267 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -56,8 +56,6 @@ config HUAWEI_WMI
depends on INPUT
select INPUT_SPARSEKMAP
select LEDS_CLASS
- select LEDS_TRIGGERS
- select LEDS_TRIGGER_AUDIO
select NEW_LEDS
help
This driver provides support for Huawei WMI hotkeys, battery charge
@@ -135,6 +133,18 @@ config YOGABOOK
To compile this driver as a module, choose M here: the module will
be called lenovo-yogabook.
+config YT2_1380
+ tristate "Lenovo Yoga Tablet 2 1380 fast charge driver"
+ depends on SERIAL_DEV_BUS
+ depends on EXTCON
+ depends on ACPI
+ help
+ Say Y here to enable support for the custom fast charging protocol
+ found on the Lenovo Yoga Tablet 2 1380F / 1380L models.
+
+ To compile this driver as a module, choose M here: the module will
+ be called lenovo-yogabook.
+
config ACERHDF
tristate "Acer Aspire One temperature and fan driver"
depends on ACPI && THERMAL
@@ -269,8 +279,6 @@ config ASUS_WMI
select INPUT_SPARSEKMAP
select LEDS_CLASS
select NEW_LEDS
- select LEDS_TRIGGERS
- select LEDS_TRIGGER_AUDIO
select ACPI_PLATFORM_PROFILE
help
Say Y here if you have a WMI aware Asus laptop (like Eee PCs or new
@@ -374,6 +382,7 @@ config FUJITSU_LAPTOP
depends on ACPI
depends on INPUT
depends on BACKLIGHT_CLASS_DEVICE
+ depends on ACPI_BATTERY
depends on ACPI_VIDEO || ACPI_VIDEO = n
select INPUT_SPARSEKMAP
select NEW_LEDS
@@ -468,6 +477,7 @@ config LENOVO_YMC
tristate "Lenovo Yoga Tablet Mode Control"
depends on ACPI_WMI
depends on INPUT
+ depends on IDEAPAD_LAPTOP
select INPUT_SPARSEKMAP
help
This driver maps the Tablet Mode Control switch to SW_TABLET_MODE input
@@ -507,8 +517,7 @@ config THINKPAD_ACPI
select NVRAM
select NEW_LEDS
select LEDS_CLASS
- select LEDS_TRIGGERS
- select LEDS_TRIGGER_AUDIO
+ select INPUT_SPARSEKMAP
help
This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
support for Fn-Fx key combinations, Bluetooth control, video
@@ -647,6 +656,30 @@ config THINKPAD_LMI
source "drivers/platform/x86/intel/Kconfig"
+config ACPI_QUICKSTART
+ tristate "ACPI Quickstart button driver"
+ depends on ACPI
+ depends on INPUT
+ select INPUT_SPARSEKMAP
+ help
+ This driver adds support for ACPI quickstart button (PNP0C32) devices.
+ The button emits a manufacturer-specific key value when pressed, so
+ userspace has to map this value to a standard key code.
+
+ To compile this driver as a module, choose M here: the module will be
+ called quickstart.
+
+config MEEGOPAD_ANX7428
+ tristate "MeeGoPad ANX7428 Type-C Switch"
+ depends on ACPI && GPIOLIB && I2C
+ help
+ Some MeeGoPad top-set boxes have an ANX7428 Type-C Switch for
+ USB3.1 Gen 1 and DisplayPort over Type-C alternate mode support.
+
+ This driver takes care of powering on the ANX7428 on supported
+ MeeGoPad top-set boxes. After this the ANX7428 takes care of Type-C
+ connector orientation and PD alternate mode switching autonomously.
+
config MSI_EC
tristate "MSI EC Extras"
depends on ACPI
@@ -690,6 +723,17 @@ config MSI_WMI
To compile this driver as a module, choose M here: the module will
be called msi-wmi.
+config MSI_WMI_PLATFORM
+ tristate "MSI WMI Platform features"
+ depends on ACPI_WMI
+ depends on HWMON
+ help
+ Say Y here if you want to have support for WMI-based platform features
+ like fan sensor access on MSI machines.
+
+ To compile this driver as a module, choose M here: the module will
+ be called msi-wmi-platform.
+
config XO15_EBOOK
tristate "OLPC XO-1.5 ebook switch"
depends on OLPC || COMPILE_TEST
@@ -1001,6 +1045,18 @@ config INSPUR_PLATFORM_PROFILE
To compile this driver as a module, choose M here: the module
will be called inspur-platform-profile.
+config LENOVO_WMI_CAMERA
+ tristate "Lenovo WMI Camera Button driver"
+ depends on ACPI_WMI
+ depends on INPUT
+ help
+ This driver provides support for Lenovo camera button. The Camera
+ button is a GPIO device. This driver receives ACPI notifications when
+ the camera button is switched on/off.
+
+ To compile this driver as a module, choose M here: the module
+ will be called lenovo-wmi-camera.
+
source "drivers/platform/x86/x86-android-tablets/Kconfig"
config FW_ATTR_CLASS
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 1de432e886..e1b1429470 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -66,14 +66,23 @@ obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o
obj-$(CONFIG_YOGABOOK) += lenovo-yogabook.o
+obj-$(CONFIG_YT2_1380) += lenovo-yoga-tab2-pro-1380-fastcharger.o
+obj-$(CONFIG_LENOVO_WMI_CAMERA) += lenovo-wmi-camera.o
# Intel
obj-y += intel/
+# Microsoft
+obj-$(CONFIG_ACPI_QUICKSTART) += quickstart.o
+
+# MeeGoPad
+obj-$(CONFIG_MEEGOPAD_ANX7428) += meegopad_anx7428.o
+
# MSI
obj-$(CONFIG_MSI_EC) += msi-ec.o
obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
obj-$(CONFIG_MSI_WMI) += msi-wmi.o
+obj-$(CONFIG_MSI_WMI_PLATFORM) += msi-wmi-platform.o
# OLPC
obj-$(CONFIG_XO15_EBOOK) += xo15-ebook.o
diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c
index 74bcb3d131..018c484296 100644
--- a/drivers/platform/x86/acerhdf.c
+++ b/drivers/platform/x86/acerhdf.c
@@ -678,7 +678,7 @@ static int __init acerhdf_register_thermal(void)
return -EINVAL;
thz_dev = thermal_zone_device_register_with_trips("acerhdf", trips, ARRAY_SIZE(trips),
- 0, NULL, &acerhdf_dev_ops,
+ NULL, &acerhdf_dev_ops,
&acerhdf_zone_params, 0,
(kernelmode) ? interval*1000 : 0);
if (IS_ERR(thz_dev))
diff --git a/drivers/platform/x86/amd/Kconfig b/drivers/platform/x86/amd/Kconfig
index 54753213cc..f88682d364 100644
--- a/drivers/platform/x86/amd/Kconfig
+++ b/drivers/platform/x86/amd/Kconfig
@@ -8,7 +8,7 @@ source "drivers/platform/x86/amd/pmc/Kconfig"
config AMD_HSMP
tristate "AMD HSMP Driver"
- depends on AMD_NB && X86_64
+ depends on AMD_NB && X86_64 && ACPI
help
The driver provides a way for user space tools to monitor and manage
system management functionality on EPYC server CPUs from AMD.
diff --git a/drivers/platform/x86/amd/hsmp.c b/drivers/platform/x86/amd/hsmp.c
index b55d80e291..8fcf38eed7 100644
--- a/drivers/platform/x86/amd/hsmp.c
+++ b/drivers/platform/x86/amd/hsmp.c
@@ -18,9 +18,11 @@
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/semaphore.h>
+#include <linux/acpi.h>
#define DRIVER_NAME "amd_hsmp"
-#define DRIVER_VERSION "2.0"
+#define DRIVER_VERSION "2.2"
+#define ACPI_HSMP_DEVICE_HID "AMDI0097"
/* HSMP Status / Error codes */
#define HSMP_STATUS_NOT_READY 0x00
@@ -40,9 +42,11 @@
* register into the SMN_INDEX register, and reads/writes the SMN_DATA reg.
* Below are required SMN address for HSMP Mailbox register offsets in SMU address space
*/
-#define SMN_HSMP_MSG_ID 0x3B10534
-#define SMN_HSMP_MSG_RESP 0x3B10980
-#define SMN_HSMP_MSG_DATA 0x3B109E0
+#define SMN_HSMP_BASE 0x3B00000
+#define SMN_HSMP_MSG_ID 0x0010534
+#define SMN_HSMP_MSG_ID_F1A_M0H 0x0010934
+#define SMN_HSMP_MSG_RESP 0x0010980
+#define SMN_HSMP_MSG_DATA 0x00109E0
#define HSMP_INDEX_REG 0xc4
#define HSMP_DATA_REG 0xc8
@@ -53,41 +57,86 @@
#define HSMP_ATTR_GRP_NAME_SIZE 10
+/* These are the strings specified in ACPI table */
+#define MSG_IDOFF_STR "MsgIdOffset"
+#define MSG_ARGOFF_STR "MsgArgOffset"
+#define MSG_RESPOFF_STR "MsgRspOffset"
+
+#define MAX_AMD_SOCKETS 8
+
+struct hsmp_mbaddr_info {
+ u32 base_addr;
+ u32 msg_id_off;
+ u32 msg_resp_off;
+ u32 msg_arg_off;
+ u32 size;
+};
+
struct hsmp_socket {
struct bin_attribute hsmp_attr;
+ struct hsmp_mbaddr_info mbinfo;
void __iomem *metric_tbl_addr;
+ 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;
};
struct hsmp_plat_device {
struct miscdevice hsmp_device;
struct hsmp_socket *sock;
- struct device *dev;
u32 proto_ver;
u16 num_sockets;
+ bool is_acpi_device;
+ bool is_probed;
};
static struct hsmp_plat_device plat_dev;
-static int amd_hsmp_rdwr(struct pci_dev *root, u32 address,
- u32 *value, bool write)
+static int amd_hsmp_pci_rdwr(struct hsmp_socket *sock, u32 offset,
+ u32 *value, bool write)
{
int ret;
- ret = pci_write_config_dword(root, HSMP_INDEX_REG, address);
+ 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(root, HSMP_DATA_REG, *value)
- : pci_read_config_dword(root, HSMP_DATA_REG, value));
+ ret = (write ? pci_write_config_dword(sock->root, HSMP_DATA_REG, *value)
+ : pci_read_config_dword(sock->root, HSMP_DATA_REG, value));
return ret;
}
+static void amd_hsmp_acpi_rdwr(struct hsmp_socket *sock, u32 offset,
+ u32 *value, bool write)
+{
+ if (write)
+ iowrite32(*value, sock->virt_base_addr + offset);
+ else
+ *value = ioread32(sock->virt_base_addr + offset);
+}
+
+static int amd_hsmp_rdwr(struct hsmp_socket *sock, u32 offset,
+ u32 *value, bool write)
+{
+ if (plat_dev.is_acpi_device)
+ amd_hsmp_acpi_rdwr(sock, offset, value, write);
+ else
+ return amd_hsmp_pci_rdwr(sock, offset, value, write);
+
+ return 0;
+}
+
/*
- * Send a message to the HSMP port via PCI-e config space registers.
+ * Send a message to the HSMP port via PCI-e config space registers
+ * or by writing to MMIO space.
*
* The caller is expected to zero out any unused arguments.
* If a response is expected, the number of response words should be greater than 0.
@@ -95,16 +144,19 @@ static int amd_hsmp_rdwr(struct pci_dev *root, u32 address,
* Returns 0 for success and populates the requested number of arguments.
* Returns a negative error code for failure.
*/
-static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg)
+static int __hsmp_send_message(struct hsmp_socket *sock, struct hsmp_message *msg)
{
+ struct hsmp_mbaddr_info *mbinfo;
unsigned long timeout, short_sleep;
u32 mbox_status;
u32 index;
int ret;
+ mbinfo = &sock->mbinfo;
+
/* Clear the status register */
mbox_status = HSMP_STATUS_NOT_READY;
- ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_RESP, &mbox_status, HSMP_WR);
+ ret = amd_hsmp_rdwr(sock, mbinfo->msg_resp_off, &mbox_status, HSMP_WR);
if (ret) {
pr_err("Error %d clearing mailbox status register\n", ret);
return ret;
@@ -113,7 +165,7 @@ static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg)
index = 0;
/* Write any message arguments */
while (index < msg->num_args) {
- ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_DATA + (index << 2),
+ ret = amd_hsmp_rdwr(sock, mbinfo->msg_arg_off + (index << 2),
&msg->args[index], HSMP_WR);
if (ret) {
pr_err("Error %d writing message argument %d\n", ret, index);
@@ -123,7 +175,7 @@ static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg)
}
/* Write the message ID which starts the operation */
- ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_ID, &msg->msg_id, HSMP_WR);
+ ret = amd_hsmp_rdwr(sock, mbinfo->msg_id_off, &msg->msg_id, HSMP_WR);
if (ret) {
pr_err("Error %d writing message ID %u\n", ret, msg->msg_id);
return ret;
@@ -140,7 +192,7 @@ static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg)
timeout = jiffies + msecs_to_jiffies(HSMP_MSG_TIMEOUT);
while (time_before(jiffies, timeout)) {
- ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_RESP, &mbox_status, HSMP_RD);
+ ret = amd_hsmp_rdwr(sock, mbinfo->msg_resp_off, &mbox_status, HSMP_RD);
if (ret) {
pr_err("Error %d reading mailbox status\n", ret);
return ret;
@@ -175,7 +227,7 @@ static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg)
*/
index = 0;
while (index < msg->response_sz) {
- ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_DATA + (index << 2),
+ ret = amd_hsmp_rdwr(sock, mbinfo->msg_arg_off + (index << 2),
&msg->args[index], HSMP_RD);
if (ret) {
pr_err("Error %d reading response %u for message ID:%u\n",
@@ -208,21 +260,19 @@ static int validate_message(struct hsmp_message *msg)
int hsmp_send_message(struct hsmp_message *msg)
{
- struct hsmp_socket *sock = &plat_dev.sock[msg->sock_ind];
- struct amd_northbridge *nb;
+ struct hsmp_socket *sock;
int ret;
if (!msg)
return -EINVAL;
-
- nb = node_to_amd_nb(msg->sock_ind);
- if (!nb || !nb->root)
- return -ENODEV;
-
ret = validate_message(msg);
if (ret)
return ret;
+ if (!plat_dev.sock || msg->sock_ind >= plat_dev.num_sockets)
+ return -ENODEV;
+ sock = &plat_dev.sock[msg->sock_ind];
+
/*
* The time taken by smu operation to complete is between
* 10us to 1ms. Sometime it may take more time.
@@ -233,7 +283,7 @@ int hsmp_send_message(struct hsmp_message *msg)
if (ret < 0)
return ret;
- ret = __hsmp_send_message(nb->root, msg);
+ ret = __hsmp_send_message(sock, msg);
up(&sock->hsmp_sem);
@@ -244,12 +294,7 @@ EXPORT_SYMBOL_GPL(hsmp_send_message);
static int hsmp_test(u16 sock_ind, u32 value)
{
struct hsmp_message msg = { 0 };
- struct amd_northbridge *nb;
- int ret = -ENODEV;
-
- nb = node_to_amd_nb(sock_ind);
- if (!nb || !nb->root)
- return ret;
+ int ret;
/*
* Test the hsmp port by performing TEST command. The test message
@@ -261,14 +306,15 @@ static int hsmp_test(u16 sock_ind, u32 value)
msg.args[0] = value;
msg.sock_ind = sock_ind;
- ret = __hsmp_send_message(nb->root, &msg);
+ ret = hsmp_send_message(&msg);
if (ret)
return ret;
/* Check the response value */
if (msg.args[0] != (value + 1)) {
- pr_err("Socket %d test message failed, Expected 0x%08X, received 0x%08X\n",
- sock_ind, (value + 1), msg.args[0]);
+ dev_err(plat_dev.sock[sock_ind].dev,
+ "Socket %d test message failed, Expected 0x%08X, received 0x%08X\n",
+ sock_ind, (value + 1), msg.args[0]);
return -EBADE;
}
@@ -337,6 +383,181 @@ static const struct file_operations hsmp_fops = {
.compat_ioctl = hsmp_ioctl,
};
+/* This is the UUID used for HSMP */
+static const guid_t acpi_hsmp_uuid = GUID_INIT(0xb74d619d, 0x5707, 0x48bd,
+ 0xa6, 0x9f, 0x4e, 0xa2,
+ 0x87, 0x1f, 0xc2, 0xf6);
+
+static inline bool is_acpi_hsmp_uuid(union acpi_object *obj)
+{
+ if (obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == UUID_SIZE)
+ return guid_equal((guid_t *)obj->buffer.pointer, &acpi_hsmp_uuid);
+
+ return false;
+}
+
+static inline int hsmp_get_uid(struct device *dev, u16 *sock_ind)
+{
+ char *uid;
+
+ /*
+ * UID (ID00, ID01..IDXX) is used for differentiating sockets,
+ * read it and strip the "ID" part of it and convert the remaining
+ * bytes to integer.
+ */
+ uid = acpi_device_uid(ACPI_COMPANION(dev));
+
+ return kstrtou16(uid + 2, 10, sock_ind);
+}
+
+static acpi_status hsmp_resource(struct acpi_resource *res, void *data)
+{
+ struct hsmp_socket *sock = data;
+ struct resource r;
+
+ switch (res->type) {
+ case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
+ if (!acpi_dev_resource_memory(res, &r))
+ return AE_ERROR;
+ if (!r.start || r.end < r.start || !(r.flags & IORESOURCE_MEM_WRITEABLE))
+ return AE_ERROR;
+ sock->mbinfo.base_addr = r.start;
+ sock->mbinfo.size = resource_size(&r);
+ break;
+ case ACPI_RESOURCE_TYPE_END_TAG:
+ break;
+ default:
+ return AE_ERROR;
+ }
+
+ return AE_OK;
+}
+
+static int hsmp_read_acpi_dsd(struct hsmp_socket *sock)
+{
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *guid, *mailbox_package;
+ union acpi_object *dsd;
+ acpi_status status;
+ int ret = 0;
+ int j;
+
+ status = acpi_evaluate_object_typed(ACPI_HANDLE(sock->dev), "_DSD", NULL,
+ &buf, ACPI_TYPE_PACKAGE);
+ if (ACPI_FAILURE(status)) {
+ dev_err(sock->dev, "Failed to read mailbox reg offsets from DSD table, err: %s\n",
+ acpi_format_exception(status));
+ return -ENODEV;
+ }
+
+ dsd = buf.pointer;
+
+ /* HSMP _DSD property should contain 2 objects.
+ * 1. guid which is an acpi object of type ACPI_TYPE_BUFFER
+ * 2. mailbox which is an acpi object of type ACPI_TYPE_PACKAGE
+ * This mailbox object contains 3 more acpi objects of type
+ * ACPI_TYPE_PACKAGE for holding msgid, msgresp, msgarg offsets
+ * these packages inturn contain 2 acpi objects of type
+ * ACPI_TYPE_STRING and ACPI_TYPE_INTEGER
+ */
+ if (!dsd || dsd->type != ACPI_TYPE_PACKAGE || dsd->package.count != 2) {
+ ret = -EINVAL;
+ goto free_buf;
+ }
+
+ guid = &dsd->package.elements[0];
+ mailbox_package = &dsd->package.elements[1];
+ if (!is_acpi_hsmp_uuid(guid) || mailbox_package->type != ACPI_TYPE_PACKAGE) {
+ dev_err(sock->dev, "Invalid hsmp _DSD table data\n");
+ ret = -EINVAL;
+ goto free_buf;
+ }
+
+ for (j = 0; j < mailbox_package->package.count; j++) {
+ union acpi_object *msgobj, *msgstr, *msgint;
+
+ msgobj = &mailbox_package->package.elements[j];
+ msgstr = &msgobj->package.elements[0];
+ msgint = &msgobj->package.elements[1];
+
+ /* package should have 1 string and 1 integer object */
+ if (msgobj->type != ACPI_TYPE_PACKAGE ||
+ msgstr->type != ACPI_TYPE_STRING ||
+ msgint->type != ACPI_TYPE_INTEGER) {
+ ret = -EINVAL;
+ goto free_buf;
+ }
+
+ if (!strncmp(msgstr->string.pointer, MSG_IDOFF_STR,
+ msgstr->string.length)) {
+ sock->mbinfo.msg_id_off = msgint->integer.value;
+ } else if (!strncmp(msgstr->string.pointer, MSG_RESPOFF_STR,
+ msgstr->string.length)) {
+ sock->mbinfo.msg_resp_off = msgint->integer.value;
+ } else if (!strncmp(msgstr->string.pointer, MSG_ARGOFF_STR,
+ msgstr->string.length)) {
+ sock->mbinfo.msg_arg_off = msgint->integer.value;
+ } else {
+ ret = -ENOENT;
+ goto free_buf;
+ }
+ }
+
+ if (!sock->mbinfo.msg_id_off || !sock->mbinfo.msg_resp_off ||
+ !sock->mbinfo.msg_arg_off)
+ ret = -EINVAL;
+
+free_buf:
+ ACPI_FREE(buf.pointer);
+ return ret;
+}
+
+static int hsmp_read_acpi_crs(struct hsmp_socket *sock)
+{
+ acpi_status status;
+
+ status = acpi_walk_resources(ACPI_HANDLE(sock->dev), METHOD_NAME__CRS,
+ hsmp_resource, sock);
+ if (ACPI_FAILURE(status)) {
+ dev_err(sock->dev, "Failed to look up MP1 base address from CRS method, err: %s\n",
+ acpi_format_exception(status));
+ return -EINVAL;
+ }
+ if (!sock->mbinfo.base_addr || !sock->mbinfo.size)
+ return -EINVAL;
+
+ /* The mapped region should be un cached */
+ sock->virt_base_addr = devm_ioremap_uc(sock->dev, sock->mbinfo.base_addr,
+ sock->mbinfo.size);
+ if (!sock->virt_base_addr) {
+ dev_err(sock->dev, "Failed to ioremap MP1 base address\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/* Parse the ACPI table to read the data */
+static int hsmp_parse_acpi_table(struct device *dev, u16 sock_ind)
+{
+ struct hsmp_socket *sock = &plat_dev.sock[sock_ind];
+ int ret;
+
+ sock->sock_ind = sock_ind;
+ sock->dev = dev;
+ plat_dev.is_acpi_device = true;
+
+ sema_init(&sock->hsmp_sem, 1);
+
+ /* Read MP1 base address from CRS method */
+ ret = hsmp_read_acpi_crs(sock);
+ if (ret)
+ return ret;
+
+ /* Read mailbox offsets from DSD table */
+ return hsmp_read_acpi_dsd(sock);
+}
+
static ssize_t hsmp_metric_tbl_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
@@ -345,14 +566,12 @@ static ssize_t hsmp_metric_tbl_read(struct file *filp, struct kobject *kobj,
struct hsmp_message msg = { 0 };
int ret;
- /* Do not support lseek(), reads entire metric table */
- if (count < bin_attr->size) {
- dev_err(plat_dev.dev, "Wrong buffer size\n");
+ if (!sock)
return -EINVAL;
- }
- if (!sock) {
- dev_err(plat_dev.dev, "Failed to read attribute private data\n");
+ /* Do not support lseek(), reads entire metric table */
+ if (count < bin_attr->size) {
+ dev_err(sock->dev, "Wrong buffer size\n");
return -EINVAL;
}
@@ -388,13 +607,13 @@ static int hsmp_get_tbl_dram_base(u16 sock_ind)
*/
dram_addr = msg.args[0] | ((u64)(msg.args[1]) << 32);
if (!dram_addr) {
- dev_err(plat_dev.dev, "Invalid DRAM address for metric table\n");
+ dev_err(sock->dev, "Invalid DRAM address for metric table\n");
return -ENOMEM;
}
- sock->metric_tbl_addr = devm_ioremap(plat_dev.dev, dram_addr,
+ sock->metric_tbl_addr = devm_ioremap(sock->dev, dram_addr,
sizeof(struct hsmp_metric_table));
if (!sock->metric_tbl_addr) {
- dev_err(plat_dev.dev, "Failed to ioremap metric table addr\n");
+ dev_err(sock->dev, "Failed to ioremap metric table addr\n");
return -ENOMEM;
}
return 0;
@@ -422,65 +641,91 @@ static int hsmp_init_metric_tbl_bin_attr(struct bin_attribute **hattrs, u16 sock
hattrs[0] = hattr;
if (plat_dev.proto_ver == HSMP_PROTO_VER6)
- return (hsmp_get_tbl_dram_base(sock_ind));
+ return hsmp_get_tbl_dram_base(sock_ind);
else
return 0;
}
-/* One bin sysfs for metrics table*/
+/* One bin sysfs for metrics table */
#define NUM_HSMP_ATTRS 1
-static int hsmp_create_sysfs_interface(void)
+static int hsmp_create_attr_list(struct attribute_group *attr_grp,
+ struct device *dev, u16 sock_ind)
{
- const struct attribute_group **hsmp_attr_grps;
struct bin_attribute **hsmp_bin_attrs;
+
+ /* Null terminated list of attributes */
+ hsmp_bin_attrs = devm_kcalloc(dev, NUM_HSMP_ATTRS + 1,
+ sizeof(*hsmp_bin_attrs),
+ GFP_KERNEL);
+ if (!hsmp_bin_attrs)
+ return -ENOMEM;
+
+ attr_grp->bin_attrs = hsmp_bin_attrs;
+
+ return hsmp_init_metric_tbl_bin_attr(hsmp_bin_attrs, sock_ind);
+}
+
+static int hsmp_create_non_acpi_sysfs_if(struct device *dev)
+{
+ const struct attribute_group **hsmp_attr_grps;
struct attribute_group *attr_grp;
- int ret;
u16 i;
- /* String formatting is currently limited to u8 sockets */
- if (WARN_ON(plat_dev.num_sockets > U8_MAX))
- return -ERANGE;
-
- hsmp_attr_grps = devm_kzalloc(plat_dev.dev, sizeof(struct attribute_group *) *
- (plat_dev.num_sockets + 1), GFP_KERNEL);
+ hsmp_attr_grps = devm_kcalloc(dev, plat_dev.num_sockets + 1,
+ sizeof(*hsmp_attr_grps),
+ GFP_KERNEL);
if (!hsmp_attr_grps)
return -ENOMEM;
/* Create a sysfs directory for each socket */
for (i = 0; i < plat_dev.num_sockets; i++) {
- attr_grp = devm_kzalloc(plat_dev.dev, sizeof(struct attribute_group), GFP_KERNEL);
+ attr_grp = devm_kzalloc(dev, sizeof(struct attribute_group),
+ GFP_KERNEL);
if (!attr_grp)
return -ENOMEM;
snprintf(plat_dev.sock[i].name, HSMP_ATTR_GRP_NAME_SIZE, "socket%u", (u8)i);
- attr_grp->name = plat_dev.sock[i].name;
-
- /* Null terminated list of attributes */
- hsmp_bin_attrs = devm_kzalloc(plat_dev.dev, sizeof(struct bin_attribute *) *
- (NUM_HSMP_ATTRS + 1), GFP_KERNEL);
- if (!hsmp_bin_attrs)
- return -ENOMEM;
-
- attr_grp->bin_attrs = hsmp_bin_attrs;
+ attr_grp->name = plat_dev.sock[i].name;
attr_grp->is_bin_visible = hsmp_is_sock_attr_visible;
hsmp_attr_grps[i] = attr_grp;
- /* Now create the leaf nodes */
- ret = hsmp_init_metric_tbl_bin_attr(hsmp_bin_attrs, i);
- if (ret)
- return ret;
+ hsmp_create_attr_list(attr_grp, dev, i);
}
- return devm_device_add_groups(plat_dev.dev, hsmp_attr_grps);
+
+ return device_add_groups(dev, hsmp_attr_grps);
}
-static int hsmp_cache_proto_ver(void)
+static int hsmp_create_acpi_sysfs_if(struct device *dev)
+{
+ struct attribute_group *attr_grp;
+ u16 sock_ind;
+ int ret;
+
+ attr_grp = devm_kzalloc(dev, sizeof(struct attribute_group), GFP_KERNEL);
+ if (!attr_grp)
+ return -ENOMEM;
+
+ attr_grp->is_bin_visible = hsmp_is_sock_attr_visible;
+
+ ret = hsmp_get_uid(dev, &sock_ind);
+ if (ret)
+ return ret;
+
+ ret = hsmp_create_attr_list(attr_grp, dev, sock_ind);
+ if (ret)
+ return ret;
+
+ return devm_device_add_group(dev, attr_grp);
+}
+
+static int hsmp_cache_proto_ver(u16 sock_ind)
{
struct hsmp_message msg = { 0 };
int ret;
msg.msg_id = HSMP_GET_PROTO_VER;
- msg.sock_ind = 0;
+ msg.sock_ind = sock_ind;
msg.response_sz = hsmp_msg_desc_table[HSMP_GET_PROTO_VER].response_sz;
ret = hsmp_send_message(&msg);
@@ -490,45 +735,150 @@ static int hsmp_cache_proto_ver(void)
return ret;
}
-static int hsmp_pltdrv_probe(struct platform_device *pdev)
+static inline bool is_f1a_m0h(void)
{
- int ret, i;
+ if (boot_cpu_data.x86 == 0x1A && boot_cpu_data.x86_model <= 0x0F)
+ return true;
- plat_dev.sock = devm_kzalloc(&pdev->dev,
- (plat_dev.num_sockets * sizeof(struct hsmp_socket)),
- GFP_KERNEL);
- if (!plat_dev.sock)
- return -ENOMEM;
- plat_dev.dev = &pdev->dev;
+ return false;
+}
+
+static int init_platform_device(struct device *dev)
+{
+ struct hsmp_socket *sock;
+ int ret, i;
for (i = 0; i < plat_dev.num_sockets; i++) {
- sema_init(&plat_dev.sock[i].hsmp_sem, 1);
- plat_dev.sock[i].sock_ind = i;
+ if (!node_to_amd_nb(i))
+ return -ENODEV;
+ sock = &plat_dev.sock[i];
+ sock->root = node_to_amd_nb(i)->root;
+ sock->sock_ind = i;
+ sock->dev = dev;
+ sock->mbinfo.base_addr = SMN_HSMP_BASE;
+
+ /*
+ * This is a transitional change from non-ACPI to ACPI, only
+ * family 0x1A, model 0x00 platform is supported for both ACPI and non-ACPI.
+ */
+ if (is_f1a_m0h())
+ sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID_F1A_M0H;
+ else
+ sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID;
+
+ sock->mbinfo.msg_resp_off = SMN_HSMP_MSG_RESP;
+ sock->mbinfo.msg_arg_off = SMN_HSMP_MSG_DATA;
+ sema_init(&sock->hsmp_sem, 1);
+
+ /* Test the hsmp interface on each socket */
+ ret = hsmp_test(i, 0xDEADBEEF);
+ if (ret) {
+ dev_err(dev, "HSMP test message failed on Fam:%x model:%x\n",
+ boot_cpu_data.x86, boot_cpu_data.x86_model);
+ dev_err(dev, "Is HSMP disabled in BIOS ?\n");
+ return ret;
+ }
}
- plat_dev.hsmp_device.name = HSMP_CDEV_NAME;
- plat_dev.hsmp_device.minor = MISC_DYNAMIC_MINOR;
- plat_dev.hsmp_device.fops = &hsmp_fops;
- plat_dev.hsmp_device.parent = &pdev->dev;
- plat_dev.hsmp_device.nodename = HSMP_DEVNODE_NAME;
- plat_dev.hsmp_device.mode = 0644;
+ return 0;
+}
- ret = hsmp_cache_proto_ver();
+static const struct acpi_device_id amd_hsmp_acpi_ids[] = {
+ {ACPI_HSMP_DEVICE_HID, 0},
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, amd_hsmp_acpi_ids);
+
+static int hsmp_pltdrv_probe(struct platform_device *pdev)
+{
+ struct acpi_device *adev;
+ u16 sock_ind = 0;
+ int ret;
+
+ /*
+ * On ACPI supported BIOS, there is an ACPI HSMP device added for
+ * each socket, so the per socket probing, but the memory allocated for
+ * sockets should be contiguous to access it as an array,
+ * Hence allocate memory for all the sockets at once instead of allocating
+ * on each probe.
+ */
+ if (!plat_dev.is_probed) {
+ plat_dev.sock = devm_kcalloc(&pdev->dev, plat_dev.num_sockets,
+ sizeof(*plat_dev.sock),
+ GFP_KERNEL);
+ if (!plat_dev.sock)
+ return -ENOMEM;
+ }
+ adev = ACPI_COMPANION(&pdev->dev);
+ if (adev && !acpi_match_device_ids(adev, amd_hsmp_acpi_ids)) {
+ ret = hsmp_get_uid(&pdev->dev, &sock_ind);
+ if (ret)
+ return ret;
+ if (sock_ind >= plat_dev.num_sockets)
+ return -EINVAL;
+ ret = hsmp_parse_acpi_table(&pdev->dev, sock_ind);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to parse ACPI table\n");
+ return ret;
+ }
+ /* Test the hsmp interface */
+ ret = hsmp_test(sock_ind, 0xDEADBEEF);
+ if (ret) {
+ dev_err(&pdev->dev, "HSMP test message failed on Fam:%x model:%x\n",
+ boot_cpu_data.x86, boot_cpu_data.x86_model);
+ dev_err(&pdev->dev, "Is HSMP disabled in BIOS ?\n");
+ return ret;
+ }
+ } else {
+ ret = init_platform_device(&pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to init HSMP mailbox\n");
+ return ret;
+ }
+ }
+
+ ret = hsmp_cache_proto_ver(sock_ind);
if (ret) {
- dev_err(plat_dev.dev, "Failed to read HSMP protocol version\n");
+ dev_err(&pdev->dev, "Failed to read HSMP protocol version\n");
return ret;
}
- ret = hsmp_create_sysfs_interface();
+ if (plat_dev.is_acpi_device)
+ ret = hsmp_create_acpi_sysfs_if(&pdev->dev);
+ else
+ ret = hsmp_create_non_acpi_sysfs_if(&pdev->dev);
if (ret)
- dev_err(plat_dev.dev, "Failed to create HSMP sysfs interface\n");
+ dev_err(&pdev->dev, "Failed to create HSMP sysfs interface\n");
+
+ if (!plat_dev.is_probed) {
+ plat_dev.hsmp_device.name = HSMP_CDEV_NAME;
+ plat_dev.hsmp_device.minor = MISC_DYNAMIC_MINOR;
+ plat_dev.hsmp_device.fops = &hsmp_fops;
+ plat_dev.hsmp_device.parent = &pdev->dev;
+ plat_dev.hsmp_device.nodename = HSMP_DEVNODE_NAME;
+ plat_dev.hsmp_device.mode = 0644;
+
+ ret = misc_register(&plat_dev.hsmp_device);
+ if (ret)
+ return ret;
+
+ plat_dev.is_probed = true;
+ }
+
+ return 0;
- return misc_register(&plat_dev.hsmp_device);
}
static void hsmp_pltdrv_remove(struct platform_device *pdev)
{
- misc_deregister(&plat_dev.hsmp_device);
+ /*
+ * We register only one misc_device even on multi socket system.
+ * So, deregister should happen only once.
+ */
+ if (plat_dev.is_probed) {
+ misc_deregister(&plat_dev.hsmp_device);
+ plat_dev.is_probed = false;
+ }
}
static struct platform_driver amd_hsmp_driver = {
@@ -536,61 +886,91 @@ static struct platform_driver amd_hsmp_driver = {
.remove_new = hsmp_pltdrv_remove,
.driver = {
.name = DRIVER_NAME,
+ .acpi_match_table = amd_hsmp_acpi_ids,
},
};
static struct platform_device *amd_hsmp_platdev;
-static int __init hsmp_plt_init(void)
+static int hsmp_plat_dev_register(void)
{
- int ret = -ENODEV;
- int i;
+ int ret;
- if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD || boot_cpu_data.x86 < 0x19) {
- pr_err("HSMP is not supported on Family:%x model:%x\n",
- boot_cpu_data.x86, boot_cpu_data.x86_model);
- return ret;
+ amd_hsmp_platdev = platform_device_alloc(DRIVER_NAME, PLATFORM_DEVID_NONE);
+ if (!amd_hsmp_platdev)
+ return -ENOMEM;
+
+ ret = platform_device_add(amd_hsmp_platdev);
+ if (ret)
+ platform_device_put(amd_hsmp_platdev);
+
+ return ret;
+}
+
+/*
+ * This check is only needed for backward compatibility of previous platforms.
+ * All new platforms are expected to support ACPI based probing.
+ */
+static bool legacy_hsmp_support(void)
+{
+ if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
+ return false;
+
+ switch (boot_cpu_data.x86) {
+ case 0x19:
+ switch (boot_cpu_data.x86_model) {
+ case 0x00 ... 0x1F:
+ case 0x30 ... 0x3F:
+ case 0x90 ... 0x9F:
+ case 0xA0 ... 0xAF:
+ return true;
+ default:
+ return false;
+ }
+ case 0x1A:
+ switch (boot_cpu_data.x86_model) {
+ case 0x00 ... 0x1F:
+ return true;
+ default:
+ return false;
+ }
+ default:
+ return false;
}
+ return false;
+}
+
+static int __init hsmp_plt_init(void)
+{
+ int ret = -ENODEV;
+
/*
* amd_nb_num() returns number of SMN/DF interfaces present in the system
* if we have N SMN/DF interfaces that ideally means N sockets
*/
plat_dev.num_sockets = amd_nb_num();
- if (plat_dev.num_sockets == 0)
+ if (plat_dev.num_sockets == 0 || plat_dev.num_sockets > MAX_AMD_SOCKETS)
return ret;
- /* Test the hsmp interface on each socket */
- for (i = 0; i < plat_dev.num_sockets; i++) {
- ret = hsmp_test(i, 0xDEADBEEF);
- if (ret) {
- pr_err("HSMP test message failed on Fam:%x model:%x\n",
- boot_cpu_data.x86, boot_cpu_data.x86_model);
- pr_err("Is HSMP disabled in BIOS ?\n");
- return ret;
- }
- }
-
ret = platform_driver_register(&amd_hsmp_driver);
if (ret)
return ret;
- amd_hsmp_platdev = platform_device_alloc(DRIVER_NAME, PLATFORM_DEVID_NONE);
- if (!amd_hsmp_platdev) {
- ret = -ENOMEM;
- goto drv_unregister;
- }
-
- ret = platform_device_add(amd_hsmp_platdev);
- if (ret) {
- platform_device_put(amd_hsmp_platdev);
- goto drv_unregister;
+ if (!plat_dev.is_acpi_device) {
+ if (legacy_hsmp_support()) {
+ /* Not ACPI device, but supports HSMP, register a plat_dev */
+ ret = hsmp_plat_dev_register();
+ } else {
+ /* Not ACPI, Does not support HSMP */
+ pr_info("HSMP is not supported on Family:%x model:%x\n",
+ boot_cpu_data.x86, boot_cpu_data.x86_model);
+ ret = -ENODEV;
+ }
+ if (ret)
+ platform_driver_unregister(&amd_hsmp_driver);
}
- return 0;
-
-drv_unregister:
- platform_driver_unregister(&amd_hsmp_driver);
return ret;
}
diff --git a/drivers/platform/x86/amd/pmc/Kconfig b/drivers/platform/x86/amd/pmc/Kconfig
index 883c0a95ac..94f9563d8b 100644
--- a/drivers/platform/x86/amd/pmc/Kconfig
+++ b/drivers/platform/x86/amd/pmc/Kconfig
@@ -18,3 +18,18 @@ config AMD_PMC
If you choose to compile this driver as a module the module will be
called amd-pmc.
+
+config AMD_MP2_STB
+ bool "AMD SoC MP2 STB function"
+ depends on AMD_PMC
+ default AMD_PMC
+ help
+ AMD MP2 STB function provides a data buffer used to log debug
+ information about the system execution during S2Idle suspend/resume.
+ A data buffer known as the STB (Smart Trace Buffer) is a circular
+ buffer which is a low-level log for the SoC which is used to debug
+ any hangs/stalls during S2Idle suspend/resume.
+
+ Creates debugfs to get STB, a userspace daemon can access STB log of
+ last S2Idle suspend/resume which can help to debug if hangs/stalls
+ during S2Idle suspend/resume.
diff --git a/drivers/platform/x86/amd/pmc/Makefile b/drivers/platform/x86/amd/pmc/Makefile
index 4aaa29d351..f1d9ab19d2 100644
--- a/drivers/platform/x86/amd/pmc/Makefile
+++ b/drivers/platform/x86/amd/pmc/Makefile
@@ -6,3 +6,4 @@
amd-pmc-objs := pmc.o pmc-quirks.o
obj-$(CONFIG_AMD_PMC) += amd-pmc.o
+amd-pmc-$(CONFIG_AMD_MP2_STB) += mp2_stb.o
diff --git a/drivers/platform/x86/amd/pmc/mp2_stb.c b/drivers/platform/x86/amd/pmc/mp2_stb.c
new file mode 100644
index 0000000000..9775ddc1b2
--- /dev/null
+++ b/drivers/platform/x86/amd/pmc/mp2_stb.c
@@ -0,0 +1,280 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD MP2 STB layer
+ *
+ * Copyright (c) 2024, Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Author: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
+ */
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/iopoll.h>
+#include <linux/pci.h>
+#include <linux/sizes.h>
+#include <linux/time.h>
+
+#include "pmc.h"
+
+#define VALID_MSG 0xA
+#define VALID_RESPONSE 2
+
+#define AMD_C2P_MSG0 0x10500
+#define AMD_C2P_MSG1 0x10504
+#define AMD_P2C_MSG0 0x10680
+#define AMD_P2C_MSG1 0x10684
+
+#define MP2_RESP_SLEEP_US 500
+#define MP2_RESP_TIMEOUT_US (1600 * USEC_PER_MSEC)
+
+#define MP2_STB_DATA_LEN_2KB 1
+#define MP2_STB_DATA_LEN_16KB 4
+
+#define MP2_MMIO_BAR 2
+
+struct mp2_cmd_base {
+ union {
+ u32 ul;
+ struct {
+ u32 cmd_id : 4;
+ u32 intr_disable : 1;
+ u32 is_dma_used : 1;
+ u32 rsvd : 26;
+ } field;
+ };
+};
+
+struct mp2_cmd_response {
+ union {
+ u32 resp;
+ struct {
+ u32 cmd_id : 4;
+ u32 status : 4;
+ u32 response : 4;
+ u32 rsvd2 : 20;
+ } field;
+ };
+};
+
+struct mp2_stb_data_valid {
+ union {
+ u32 data_valid;
+ struct {
+ u32 valid : 16;
+ u32 length : 16;
+ } val;
+ };
+};
+
+static int amd_mp2_wait_response(struct amd_mp2_dev *mp2, u8 cmd_id, u32 command_sts)
+{
+ struct mp2_cmd_response cmd_resp;
+
+ if (!readl_poll_timeout(mp2->mmio + AMD_P2C_MSG0, cmd_resp.resp,
+ (cmd_resp.field.response == 0x0 &&
+ cmd_resp.field.status == command_sts &&
+ cmd_resp.field.cmd_id == cmd_id), MP2_RESP_SLEEP_US,
+ MP2_RESP_TIMEOUT_US))
+ return cmd_resp.field.status;
+
+ return -ETIMEDOUT;
+}
+
+static void amd_mp2_stb_send_cmd(struct amd_mp2_dev *mp2, u8 cmd_id, bool is_dma_used)
+{
+ struct mp2_cmd_base cmd_base;
+
+ cmd_base.ul = 0;
+ cmd_base.field.cmd_id = cmd_id;
+ cmd_base.field.intr_disable = 1;
+ cmd_base.field.is_dma_used = is_dma_used;
+
+ writeq(mp2->dma_addr, mp2->mmio + AMD_C2P_MSG1);
+ writel(cmd_base.ul, mp2->mmio + AMD_C2P_MSG0);
+}
+
+static int amd_mp2_stb_region(struct amd_mp2_dev *mp2)
+{
+ struct device *dev = &mp2->pdev->dev;
+ unsigned int len = mp2->stb_len;
+
+ if (!mp2->stbdata) {
+ mp2->vslbase = dmam_alloc_coherent(dev, len, &mp2->dma_addr, GFP_KERNEL);
+ if (!mp2->vslbase)
+ return -ENOMEM;
+
+ mp2->stbdata = devm_kzalloc(dev, len, GFP_KERNEL);
+ if (!mp2->stbdata)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int amd_mp2_process_cmd(struct amd_mp2_dev *mp2, struct file *filp)
+{
+ struct device *dev = &mp2->pdev->dev;
+ struct mp2_stb_data_valid stb_dv;
+ int status;
+
+ stb_dv.data_valid = readl(mp2->mmio + AMD_P2C_MSG1);
+
+ if (stb_dv.val.valid != VALID_MSG) {
+ dev_dbg(dev, "Invalid STB data\n");
+ return -EBADMSG;
+ }
+
+ if (stb_dv.val.length != MP2_STB_DATA_LEN_2KB &&
+ stb_dv.val.length != MP2_STB_DATA_LEN_16KB) {
+ dev_dbg(dev, "Unsupported length\n");
+ return -EMSGSIZE;
+ }
+
+ mp2->stb_len = BIT(stb_dv.val.length) * SZ_1K;
+
+ status = amd_mp2_stb_region(mp2);
+ if (status) {
+ dev_err(dev, "Failed to init STB region, status %d\n", status);
+ return status;
+ }
+
+ amd_mp2_stb_send_cmd(mp2, VALID_MSG, true);
+ status = amd_mp2_wait_response(mp2, VALID_MSG, VALID_RESPONSE);
+ if (status == VALID_RESPONSE) {
+ memcpy_fromio(mp2->stbdata, mp2->vslbase, mp2->stb_len);
+ filp->private_data = mp2->stbdata;
+ mp2->is_stb_data = true;
+ } else {
+ dev_err(dev, "Failed to start STB dump, status %d\n", status);
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int amd_mp2_stb_debugfs_open(struct inode *inode, struct file *filp)
+{
+ struct amd_pmc_dev *dev = filp->f_inode->i_private;
+ struct amd_mp2_dev *mp2 = dev->mp2;
+
+ if (mp2) {
+ if (!mp2->is_stb_data)
+ return amd_mp2_process_cmd(mp2, filp);
+
+ filp->private_data = mp2->stbdata;
+
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static ssize_t amd_mp2_stb_debugfs_read(struct file *filp, char __user *buf, size_t size,
+ loff_t *pos)
+{
+ struct amd_pmc_dev *dev = filp->f_inode->i_private;
+ struct amd_mp2_dev *mp2 = dev->mp2;
+
+ if (!mp2)
+ return -ENODEV;
+
+ if (!filp->private_data)
+ return -EINVAL;
+
+ return simple_read_from_buffer(buf, size, pos, filp->private_data, mp2->stb_len);
+}
+
+static const struct file_operations amd_mp2_stb_debugfs_fops = {
+ .owner = THIS_MODULE,
+ .open = amd_mp2_stb_debugfs_open,
+ .read = amd_mp2_stb_debugfs_read,
+};
+
+static void amd_mp2_dbgfs_register(struct amd_pmc_dev *dev)
+{
+ if (!dev->dbgfs_dir)
+ return;
+
+ debugfs_create_file("stb_read_previous_boot", 0644, dev->dbgfs_dir, dev,
+ &amd_mp2_stb_debugfs_fops);
+}
+
+void amd_mp2_stb_deinit(struct amd_pmc_dev *dev)
+{
+ struct amd_mp2_dev *mp2 = dev->mp2;
+ struct pci_dev *pdev;
+
+ if (mp2 && mp2->pdev) {
+ pdev = mp2->pdev;
+
+ if (mp2->mmio)
+ pci_clear_master(pdev);
+
+ pci_dev_put(pdev);
+
+ if (mp2->devres_gid)
+ devres_release_group(&pdev->dev, mp2->devres_gid);
+
+ dev->mp2 = NULL;
+ }
+}
+
+void amd_mp2_stb_init(struct amd_pmc_dev *dev)
+{
+ struct amd_mp2_dev *mp2 = NULL;
+ struct pci_dev *pdev;
+ int rc;
+
+ mp2 = devm_kzalloc(dev->dev, sizeof(*mp2), GFP_KERNEL);
+ if (!mp2)
+ return;
+
+ pdev = pci_get_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_MP2_STB, NULL);
+ if (!pdev)
+ return;
+
+ dev->mp2 = mp2;
+ mp2->pdev = pdev;
+
+ mp2->devres_gid = devres_open_group(&pdev->dev, NULL, GFP_KERNEL);
+ if (!mp2->devres_gid) {
+ dev_err(&pdev->dev, "devres_open_group failed\n");
+ goto mp2_error;
+ }
+
+ rc = pcim_enable_device(pdev);
+ if (rc) {
+ dev_err(&pdev->dev, "pcim_enable_device failed\n");
+ goto mp2_error;
+ }
+
+ rc = pcim_iomap_regions(pdev, BIT(MP2_MMIO_BAR), "mp2 stb");
+ if (rc) {
+ dev_err(&pdev->dev, "pcim_iomap_regions failed\n");
+ goto mp2_error;
+ }
+
+ mp2->mmio = pcim_iomap_table(pdev)[MP2_MMIO_BAR];
+ if (!mp2->mmio) {
+ dev_err(&pdev->dev, "pcim_iomap_table failed\n");
+ goto mp2_error;
+ }
+
+ pci_set_master(pdev);
+
+ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (rc) {
+ dev_err(&pdev->dev, "failed to set DMA mask\n");
+ goto mp2_error;
+ }
+
+ amd_mp2_dbgfs_register(dev);
+
+ return;
+
+mp2_error:
+ amd_mp2_stb_deinit(dev);
+}
diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c
index 108e12fd58..a3d881f6e5 100644
--- a/drivers/platform/x86/amd/pmc/pmc.c
+++ b/drivers/platform/x86/amd/pmc/pmc.c
@@ -1106,6 +1106,8 @@ static int amd_pmc_probe(struct platform_device *pdev)
}
amd_pmc_dbgfs_register(dev);
+ if (IS_ENABLED(CONFIG_AMD_MP2_STB))
+ amd_mp2_stb_init(dev);
pm_report_max_hw_sleep(U64_MAX);
return 0;
@@ -1122,6 +1124,8 @@ static void amd_pmc_remove(struct platform_device *pdev)
acpi_unregister_lps0_dev(&amd_pmc_s2idle_dev_ops);
amd_pmc_dbgfs_unregister(dev);
pci_dev_put(dev->rdev);
+ if (IS_ENABLED(CONFIG_AMD_MP2_STB))
+ amd_mp2_stb_deinit(dev);
mutex_destroy(&dev->lock);
}
@@ -1132,6 +1136,7 @@ static const struct acpi_device_id amd_pmc_acpi_ids[] = {
{"AMDI0008", 0},
{"AMDI0009", 0},
{"AMDI000A", 0},
+ {"AMDI000B", 0},
{"AMD0004", 0},
{"AMD0005", 0},
{ }
diff --git a/drivers/platform/x86/amd/pmc/pmc.h b/drivers/platform/x86/amd/pmc/pmc.h
index 827eef65e1..9e32d3128c 100644
--- a/drivers/platform/x86/amd/pmc/pmc.h
+++ b/drivers/platform/x86/amd/pmc/pmc.h
@@ -14,6 +14,17 @@
#include <linux/types.h>
#include <linux/mutex.h>
+struct amd_mp2_dev {
+ void __iomem *mmio;
+ void __iomem *vslbase;
+ void *stbdata;
+ void *devres_gid;
+ struct pci_dev *pdev;
+ dma_addr_t dma_addr;
+ int stb_len;
+ bool is_stb_data;
+};
+
struct amd_pmc_dev {
void __iomem *regbase;
void __iomem *smu_virt_addr;
@@ -38,10 +49,13 @@ struct amd_pmc_dev {
struct dentry *dbgfs_dir;
struct quirk_entry *quirks;
bool disable_8042_wakeup;
+ struct amd_mp2_dev *mp2;
};
void amd_pmc_process_restore_quirks(struct amd_pmc_dev *dev);
void amd_pmc_quirks_init(struct amd_pmc_dev *dev);
+void amd_mp2_stb_init(struct amd_pmc_dev *dev);
+void amd_mp2_stb_deinit(struct amd_pmc_dev *dev);
/* List of supported CPU ids */
#define AMD_CPU_ID_RV 0x15D0
@@ -53,5 +67,6 @@ void amd_pmc_quirks_init(struct amd_pmc_dev *dev);
#define AMD_CPU_ID_PS 0x14E8
#define AMD_CPU_ID_SP 0x14A4
#define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507
+#define PCI_DEVICE_ID_AMD_MP2_STB 0x172c
#endif /* PMC_H */
diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile
index 6b26e48ce8..7d6079b025 100644
--- a/drivers/platform/x86/amd/pmf/Makefile
+++ b/drivers/platform/x86/amd/pmf/Makefile
@@ -7,4 +7,4 @@
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
+ tee-if.o spc.o pmf-quirks.o
diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
index 1c0d2bbc1f..1157ec1488 100644
--- a/drivers/platform/x86/amd/pmf/acpi.c
+++ b/drivers/platform/x86/amd/pmf/acpi.c
@@ -90,12 +90,96 @@ out:
return err;
}
+static union acpi_object *apts_if_call(struct amd_pmf_dev *pdev, u32 state_index)
+{
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_handle ahandle = ACPI_HANDLE(pdev->dev);
+ struct acpi_object_list apts_if_arg_list;
+ union acpi_object apts_if_args[3];
+ acpi_status status;
+
+ apts_if_arg_list.count = 3;
+ apts_if_arg_list.pointer = &apts_if_args[0];
+
+ apts_if_args[0].type = ACPI_TYPE_INTEGER;
+ apts_if_args[0].integer.value = 1;
+ apts_if_args[1].type = ACPI_TYPE_INTEGER;
+ apts_if_args[1].integer.value = state_index;
+ apts_if_args[2].type = ACPI_TYPE_INTEGER;
+ apts_if_args[2].integer.value = 0;
+
+ status = acpi_evaluate_object(ahandle, "APTS", &apts_if_arg_list, &buffer);
+ if (ACPI_FAILURE(status)) {
+ dev_err(pdev->dev, "APTS state_idx:%u call failed\n", state_index);
+ kfree(buffer.pointer);
+ return NULL;
+ }
+
+ return buffer.pointer;
+}
+
+static int apts_if_call_store_buffer(struct amd_pmf_dev *pdev,
+ u32 index, void *data, size_t out_sz)
+{
+ union acpi_object *info;
+ size_t size;
+ int err = 0;
+
+ info = apts_if_call(pdev, index);
+ if (!info)
+ return -EIO;
+
+ if (info->type != ACPI_TYPE_BUFFER) {
+ dev_err(pdev->dev, "object is not a buffer\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ size = *(u16 *)info->buffer.pointer;
+ if (info->buffer.length < size) {
+ dev_err(pdev->dev, "buffer smaller than header size %u < %zu\n",
+ info->buffer.length, size);
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (size < out_sz) {
+ dev_err(pdev->dev, "buffer too small %zu\n", size);
+ err = -EINVAL;
+ goto out;
+ }
+
+ memcpy(data, info->buffer.pointer, out_sz);
+out:
+ kfree(info);
+ return err;
+}
+
int is_apmf_func_supported(struct amd_pmf_dev *pdev, unsigned long index)
{
/* If bit-n is set, that indicates function n+1 is supported */
return !!(pdev->supported_func & BIT(index - 1));
}
+int apts_get_static_slider_granular_v2(struct amd_pmf_dev *pdev,
+ struct amd_pmf_apts_granular_output *data, u32 apts_idx)
+{
+ if (!is_apmf_func_supported(pdev, APMF_FUNC_STATIC_SLIDER_GRANULAR))
+ return -EINVAL;
+
+ return apts_if_call_store_buffer(pdev, apts_idx, data, sizeof(*data));
+}
+
+int apmf_get_static_slider_granular_v2(struct amd_pmf_dev *pdev,
+ struct apmf_static_slider_granular_output_v2 *data)
+{
+ if (!is_apmf_func_supported(pdev, APMF_FUNC_STATIC_SLIDER_GRANULAR))
+ return -EINVAL;
+
+ return apmf_if_call_store_buffer(pdev, APMF_FUNC_STATIC_SLIDER_GRANULAR,
+ data, sizeof(*data));
+}
+
int apmf_get_static_slider_granular(struct amd_pmf_dev *pdev,
struct apmf_static_slider_granular_output *data)
{
@@ -140,6 +224,43 @@ static void apmf_sbios_heartbeat_notify(struct work_struct *work)
kfree(info);
}
+int amd_pmf_notify_sbios_heartbeat_event_v2(struct amd_pmf_dev *dev, u8 flag)
+{
+ struct sbios_hb_event_v2 args = { };
+ struct acpi_buffer params;
+ union acpi_object *info;
+
+ args.size = sizeof(args);
+
+ switch (flag) {
+ case ON_LOAD:
+ args.load = 1;
+ break;
+ case ON_UNLOAD:
+ args.unload = 1;
+ break;
+ case ON_SUSPEND:
+ args.suspend = 1;
+ break;
+ case ON_RESUME:
+ args.resume = 1;
+ break;
+ default:
+ dev_dbg(dev->dev, "Failed to send v2 heartbeat event, flag:0x%x\n", flag);
+ return -EINVAL;
+ }
+
+ params.length = sizeof(args);
+ params.pointer = &args;
+
+ info = apmf_if_call(dev, APMF_FUNC_SBIOS_HEARTBEAT_V2, &params);
+ if (!info)
+ return -EIO;
+
+ kfree(info);
+ return 0;
+}
+
int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx)
{
union acpi_object *info;
@@ -166,6 +287,11 @@ int apmf_get_auto_mode_def(struct amd_pmf_dev *pdev, struct apmf_auto_mode *data
return apmf_if_call_store_buffer(pdev, APMF_FUNC_AUTO_MODE, data, sizeof(*data));
}
+int apmf_get_sbios_requests_v2(struct amd_pmf_dev *pdev, struct apmf_sbios_req_v2 *req)
+{
+ return apmf_if_call_store_buffer(pdev, APMF_FUNC_SBIOS_REQUESTS, req, sizeof(*req));
+}
+
int apmf_get_sbios_requests(struct amd_pmf_dev *pdev, struct apmf_sbios_req *req)
{
return apmf_if_call_store_buffer(pdev, APMF_FUNC_SBIOS_REQUESTS,
@@ -217,9 +343,14 @@ static int apmf_if_verify_interface(struct amd_pmf_dev *pdev)
if (err)
return err;
- pdev->supported_func = output.supported_functions;
- dev_dbg(pdev->dev, "supported functions:0x%x notifications:0x%x\n",
- output.supported_functions, output.notification_mask);
+ /* only set if not already set by a quirk */
+ if (!pdev->supported_func)
+ pdev->supported_func = output.supported_functions;
+
+ dev_dbg(pdev->dev, "supported functions:0x%x notifications:0x%x version:%u\n",
+ output.supported_functions, output.notification_mask, output.version);
+
+ pdev->pmf_if_version = output.version;
return 0;
}
@@ -320,7 +451,7 @@ void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev)
{
acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev);
- if (pmf_dev->hb_interval)
+ if (pmf_dev->hb_interval && pmf_dev->pmf_if_version == PMF_IF_V1)
cancel_delayed_work_sync(&pmf_dev->heart_beat);
if (is_apmf_func_supported(pmf_dev, APMF_FUNC_AUTO_MODE) &&
@@ -344,7 +475,7 @@ int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
goto out;
}
- if (pmf_dev->hb_interval) {
+ if (pmf_dev->hb_interval && pmf_dev->pmf_if_version == PMF_IF_V1) {
/* send heartbeats only if the interval is not zero */
INIT_DELAYED_WORK(&pmf_dev->heart_beat, apmf_sbios_heartbeat_notify);
schedule_delayed_work(&pmf_dev->heart_beat, 0);
diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
index 4f734e049f..2d6e255886 100644
--- a/drivers/platform/x86/amd/pmf/core.c
+++ b/drivers/platform/x86/amd/pmf/core.c
@@ -113,8 +113,9 @@ static void amd_pmf_dbgfs_unregister(struct amd_pmf_dev *dev)
static void amd_pmf_dbgfs_register(struct amd_pmf_dev *dev)
{
dev->dbgfs_dir = debugfs_create_dir("amd_pmf", NULL);
- debugfs_create_file("current_power_limits", 0644, dev->dbgfs_dir, dev,
- &current_power_limits_fops);
+ if (dev->pmf_if_version == PMF_IF_V1)
+ debugfs_create_file("current_power_limits", 0644, dev->dbgfs_dir, dev,
+ &current_power_limits_fops);
}
int amd_pmf_get_power_source(void)
@@ -299,6 +300,9 @@ static int amd_pmf_suspend_handler(struct device *dev)
if (pdev->smart_pc_enabled)
cancel_delayed_work_sync(&pdev->pb_work);
+ if (is_apmf_func_supported(pdev, APMF_FUNC_SBIOS_HEARTBEAT_V2))
+ amd_pmf_notify_sbios_heartbeat_event_v2(pdev, ON_SUSPEND);
+
return 0;
}
@@ -313,6 +317,9 @@ static int amd_pmf_resume_handler(struct device *dev)
return ret;
}
+ if (is_apmf_func_supported(pdev, APMF_FUNC_SBIOS_HEARTBEAT_V2))
+ amd_pmf_notify_sbios_heartbeat_event_v2(pdev, ON_RESUME);
+
if (pdev->smart_pc_enabled)
schedule_delayed_work(&pdev->pb_work, msecs_to_jiffies(2000));
@@ -374,6 +381,7 @@ static const struct acpi_device_id amd_pmf_acpi_ids[] = {
{"AMDI0100", 0x100},
{"AMDI0102", 0},
{"AMDI0103", 0},
+ {"AMDI0105", 0},
{ }
};
MODULE_DEVICE_TABLE(acpi, amd_pmf_acpi_ids);
@@ -438,11 +446,14 @@ static int amd_pmf_probe(struct platform_device *pdev)
mutex_init(&dev->lock);
mutex_init(&dev->update_mutex);
+ amd_pmf_quirks_init(dev);
apmf_acpi_init(dev);
platform_set_drvdata(pdev, dev);
amd_pmf_dbgfs_register(dev);
amd_pmf_init_features(dev);
apmf_install_handler(dev);
+ if (is_apmf_func_supported(dev, APMF_FUNC_SBIOS_HEARTBEAT_V2))
+ amd_pmf_notify_sbios_heartbeat_event_v2(dev, ON_LOAD);
dev_info(dev->dev, "registered PMF device successfully\n");
@@ -454,6 +465,8 @@ static void amd_pmf_remove(struct platform_device *pdev)
struct amd_pmf_dev *dev = platform_get_drvdata(pdev);
amd_pmf_deinit_features(dev);
+ if (is_apmf_func_supported(dev, APMF_FUNC_SBIOS_HEARTBEAT_V2))
+ amd_pmf_notify_sbios_heartbeat_event_v2(dev, ON_UNLOAD);
apmf_acpi_deinit(dev);
amd_pmf_dbgfs_unregister(dev);
mutex_destroy(&dev->lock);
diff --git a/drivers/platform/x86/amd/pmf/pmf-quirks.c b/drivers/platform/x86/amd/pmf/pmf-quirks.c
new file mode 100644
index 0000000000..0b2eb0ae85
--- /dev/null
+++ b/drivers/platform/x86/amd/pmf/pmf-quirks.c
@@ -0,0 +1,51 @@
+// 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, "GA403UV"),
+ },
+ .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 66cae1cca7..eeedd0c039 100644
--- a/drivers/platform/x86/amd/pmf/pmf.h
+++ b/drivers/platform/x86/amd/pmf/pmf.h
@@ -17,7 +17,11 @@
#define POLICY_BUF_MAX_SZ 0x4b000
#define POLICY_SIGN_COOKIE 0x31535024
#define POLICY_COOKIE_OFFSET 0x10
-#define POLICY_COOKIE_LEN 0x14
+
+struct cookie_header {
+ u32 sign;
+ u32 length;
+} __packed;
/* APMF Functions */
#define APMF_FUNC_VERIFY_INTERFACE 0
@@ -30,6 +34,7 @@
#define APMF_FUNC_STATIC_SLIDER_GRANULAR 9
#define APMF_FUNC_DYN_SLIDER_AC 11
#define APMF_FUNC_DYN_SLIDER_DC 12
+#define APMF_FUNC_SBIOS_HEARTBEAT_V2 16
/* Message Definitions */
#define SET_SPL 0x03 /* SPL: Sustained Power Limit */
@@ -50,6 +55,8 @@
#define GET_STT_LIMIT_APU 0x20
#define GET_STT_LIMIT_HS2 0x21
#define SET_P3T 0x23 /* P3T: Peak Package Power Limit */
+#define SET_PMF_PPT 0x25
+#define SET_PMF_PPT_APU_ONLY 0x26
/* OS slider update notification */
#define DC_BEST_PERF 0
@@ -83,6 +90,47 @@
#define TA_OUTPUT_RESERVED_MEM 906
#define MAX_OPERATION_PARAMS 4
+#define PMF_IF_V1 1
+#define PMF_IF_V2 2
+
+#define APTS_MAX_STATES 16
+
+/* APTS PMF BIOS Interface */
+struct amd_pmf_apts_output {
+ u16 table_version;
+ u32 fan_table_idx;
+ u32 pmf_ppt;
+ u32 ppt_pmf_apu_only;
+ u32 stt_min_limit;
+ u8 stt_skin_temp_limit_apu;
+ u8 stt_skin_temp_limit_hs2;
+} __packed;
+
+struct amd_pmf_apts_granular_output {
+ u16 size;
+ struct amd_pmf_apts_output val;
+} __packed;
+
+struct amd_pmf_apts_granular {
+ u16 size;
+ struct amd_pmf_apts_output val[APTS_MAX_STATES];
+};
+
+struct sbios_hb_event_v2 {
+ u16 size;
+ u8 load;
+ u8 unload;
+ u8 suspend;
+ u8 resume;
+} __packed;
+
+enum sbios_hb_v2 {
+ ON_LOAD,
+ ON_UNLOAD,
+ ON_SUSPEND,
+ ON_RESUME,
+};
+
/* AMD PMF BIOS interfaces */
struct apmf_verify_interface {
u16 size;
@@ -114,6 +162,18 @@ struct apmf_sbios_req {
u8 skin_temp_hs2;
} __packed;
+struct apmf_sbios_req_v2 {
+ u16 size;
+ u32 pending_req;
+ u8 rsd;
+ u32 ppt_pmf;
+ u32 ppt_pmf_apu_only;
+ u32 stt_min_limit;
+ u8 skin_temp_apu;
+ u8 skin_temp_hs2;
+ u32 custom_policy[10];
+} __packed;
+
struct apmf_fan_idx {
u16 size;
u8 fan_ctl_mode;
@@ -194,6 +254,14 @@ enum power_modes {
POWER_MODE_MAX,
};
+enum power_modes_v2 {
+ POWER_MODE_BEST_PERFORMANCE,
+ POWER_MODE_BALANCED,
+ POWER_MODE_BEST_POWER_EFFICIENCY,
+ POWER_MODE_ENERGY_SAVE,
+ POWER_MODE_V2_MAX,
+};
+
struct amd_pmf_dev {
void __iomem *regbase;
void __iomem *smu_virt_addr;
@@ -229,10 +297,15 @@ struct amd_pmf_dev {
struct delayed_work pb_work;
struct pmf_action_table *prev_data;
u64 policy_addr;
- void *policy_base;
+ void __iomem *policy_base;
bool smart_pc_enabled;
+ u16 pmf_if_version;
};
+struct apmf_sps_prop_granular_v2 {
+ u8 power_states[POWER_SOURCE_MAX][POWER_MODE_V2_MAX];
+} __packed;
+
struct apmf_sps_prop_granular {
u32 fppt;
u32 sppt;
@@ -254,6 +327,16 @@ struct amd_pmf_static_slider_granular {
struct apmf_sps_prop_granular prop[POWER_SOURCE_MAX][POWER_MODE_MAX];
};
+struct apmf_static_slider_granular_output_v2 {
+ u16 size;
+ struct apmf_sps_prop_granular_v2 sps_idx;
+} __packed;
+
+struct amd_pmf_static_slider_granular_v2 {
+ u16 size;
+ struct apmf_sps_prop_granular_v2 sps_idx;
+};
+
struct os_power_slider {
u16 size;
u8 slider_event;
@@ -585,6 +668,7 @@ int amd_pmf_get_power_source(void);
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);
/* SPS Layer */
int amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf);
@@ -602,6 +686,10 @@ const char *amd_pmf_source_as_str(unsigned int state);
int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx);
int amd_pmf_set_sps_power_limits(struct amd_pmf_dev *pmf);
+int apmf_get_static_slider_granular_v2(struct amd_pmf_dev *dev,
+ struct apmf_static_slider_granular_output_v2 *data);
+int apts_get_static_slider_granular_v2(struct amd_pmf_dev *pdev,
+ struct amd_pmf_apts_granular_output *data, u32 apts_idx);
/* Auto Mode Layer */
int apmf_get_auto_mode_def(struct amd_pmf_dev *pdev, struct apmf_auto_mode *data);
@@ -609,6 +697,7 @@ void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev);
void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev);
void amd_pmf_trans_automode(struct amd_pmf_dev *dev, int socket_power, ktime_t time_elapsed_ms);
int apmf_get_sbios_requests(struct amd_pmf_dev *pdev, struct apmf_sbios_req *req);
+int apmf_get_sbios_requests_v2(struct amd_pmf_dev *pdev, struct apmf_sbios_req_v2 *req);
void amd_pmf_update_2_cql(struct amd_pmf_dev *dev, bool is_cql_event);
int amd_pmf_reset_amt(struct amd_pmf_dev *dev);
@@ -631,4 +720,7 @@ int apmf_check_smart_pc(struct amd_pmf_dev *pmf_dev);
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 a3dec14c30..3c153fb142 100644
--- a/drivers/platform/x86/amd/pmf/spc.c
+++ b/drivers/platform/x86/amd/pmf/spc.c
@@ -150,36 +150,26 @@ static int amd_pmf_get_slider_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_
return 0;
}
-static int amd_pmf_get_sensor_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
+static void amd_pmf_get_sensor_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
{
struct amd_sfh_info sfh_info;
- int ret;
+
+ /* Get the latest information from SFH */
+ in->ev_info.user_present = false;
/* Get ALS data */
- ret = amd_get_sfh_info(&sfh_info, MT_ALS);
- if (!ret)
+ if (!amd_get_sfh_info(&sfh_info, MT_ALS))
in->ev_info.ambient_light = sfh_info.ambient_light;
else
- return ret;
+ dev_dbg(dev->dev, "ALS is not enabled/detected\n");
/* get HPD data */
- ret = amd_get_sfh_info(&sfh_info, MT_HPD);
- if (ret)
- return ret;
-
- switch (sfh_info.user_present) {
- case SFH_NOT_DETECTED:
- in->ev_info.user_present = 0xff; /* assume no sensors connected */
- break;
- case SFH_USER_PRESENT:
- in->ev_info.user_present = 1;
- break;
- case SFH_USER_AWAY:
- in->ev_info.user_present = 0;
- break;
+ if (!amd_get_sfh_info(&sfh_info, MT_HPD)) {
+ if (sfh_info.user_present == SFH_USER_PRESENT)
+ in->ev_info.user_present = true;
+ } else {
+ dev_dbg(dev->dev, "HPD is not enabled/detected\n");
}
-
- return 0;
}
void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
diff --git a/drivers/platform/x86/amd/pmf/sps.c b/drivers/platform/x86/amd/pmf/sps.c
index 33e23e25c8..92f7fb2227 100644
--- a/drivers/platform/x86/amd/pmf/sps.c
+++ b/drivers/platform/x86/amd/pmf/sps.c
@@ -10,9 +10,27 @@
#include "pmf.h"
+static struct amd_pmf_static_slider_granular_v2 config_store_v2;
static struct amd_pmf_static_slider_granular config_store;
+static struct amd_pmf_apts_granular apts_config_store;
#ifdef CONFIG_AMD_PMF_DEBUG
+static const char *slider_v2_as_str(unsigned int state)
+{
+ switch (state) {
+ case POWER_MODE_BEST_PERFORMANCE:
+ return "Best Performance";
+ case POWER_MODE_BALANCED:
+ return "Balanced";
+ case POWER_MODE_BEST_POWER_EFFICIENCY:
+ return "Best Power Efficiency";
+ case POWER_MODE_ENERGY_SAVE:
+ return "Energy Save";
+ default:
+ return "Unknown Power Mode";
+ }
+}
+
static const char *slider_as_str(unsigned int state)
{
switch (state) {
@@ -63,10 +81,88 @@ static void amd_pmf_dump_sps_defaults(struct amd_pmf_static_slider_granular *dat
pr_debug("Static Slider Data - END\n");
}
+
+static void amd_pmf_dump_sps_defaults_v2(struct amd_pmf_static_slider_granular_v2 *data)
+{
+ unsigned int i, j;
+
+ pr_debug("Static Slider APTS state index data - BEGIN");
+ pr_debug("size: %u\n", data->size);
+
+ for (i = 0; i < POWER_SOURCE_MAX; i++)
+ for (j = 0; j < POWER_MODE_V2_MAX; j++)
+ pr_debug("%s %s: %u\n", amd_pmf_source_as_str(i), slider_v2_as_str(j),
+ data->sps_idx.power_states[i][j]);
+
+ pr_debug("Static Slider APTS state index data - END\n");
+}
+
+static void amd_pmf_dump_apts_sps_defaults(struct amd_pmf_apts_granular *info)
+{
+ int i;
+
+ pr_debug("Static Slider APTS index default values data - BEGIN");
+
+ for (i = 0; i < APTS_MAX_STATES; i++) {
+ pr_debug("Table Version[%d] = %u\n", i, info->val[i].table_version);
+ pr_debug("Fan Index[%d] = %u\n", i, info->val[i].fan_table_idx);
+ pr_debug("PPT[%d] = %u\n", i, info->val[i].pmf_ppt);
+ pr_debug("PPT APU[%d] = %u\n", i, info->val[i].ppt_pmf_apu_only);
+ pr_debug("STT Min[%d] = %u\n", i, info->val[i].stt_min_limit);
+ pr_debug("STT APU[%d] = %u\n", i, info->val[i].stt_skin_temp_limit_apu);
+ pr_debug("STT HS2[%d] = %u\n", i, info->val[i].stt_skin_temp_limit_hs2);
+ }
+
+ pr_debug("Static Slider APTS index default values data - END");
+}
#else
static void amd_pmf_dump_sps_defaults(struct amd_pmf_static_slider_granular *data) {}
+static void amd_pmf_dump_sps_defaults_v2(struct amd_pmf_static_slider_granular_v2 *data) {}
+static void amd_pmf_dump_apts_sps_defaults(struct amd_pmf_apts_granular *info) {}
#endif
+static void amd_pmf_load_apts_defaults_sps_v2(struct amd_pmf_dev *pdev)
+{
+ struct amd_pmf_apts_granular_output output;
+ struct amd_pmf_apts_output *ps;
+ int i;
+
+ memset(&apts_config_store, 0, sizeof(apts_config_store));
+
+ ps = apts_config_store.val;
+
+ for (i = 0; i < APTS_MAX_STATES; i++) {
+ apts_get_static_slider_granular_v2(pdev, &output, i);
+ ps[i].table_version = output.val.table_version;
+ ps[i].fan_table_idx = output.val.fan_table_idx;
+ ps[i].pmf_ppt = output.val.pmf_ppt;
+ ps[i].ppt_pmf_apu_only = output.val.ppt_pmf_apu_only;
+ ps[i].stt_min_limit = output.val.stt_min_limit;
+ ps[i].stt_skin_temp_limit_apu = output.val.stt_skin_temp_limit_apu;
+ ps[i].stt_skin_temp_limit_hs2 = output.val.stt_skin_temp_limit_hs2;
+ }
+
+ amd_pmf_dump_apts_sps_defaults(&apts_config_store);
+}
+
+static void amd_pmf_load_defaults_sps_v2(struct amd_pmf_dev *dev)
+{
+ struct apmf_static_slider_granular_output_v2 output;
+ unsigned int i, j;
+
+ memset(&config_store_v2, 0, sizeof(config_store_v2));
+ apmf_get_static_slider_granular_v2(dev, &output);
+
+ config_store_v2.size = output.size;
+
+ for (i = 0; i < POWER_SOURCE_MAX; i++)
+ for (j = 0; j < POWER_MODE_V2_MAX; j++)
+ config_store_v2.sps_idx.power_states[i][j] =
+ output.sps_idx.power_states[i][j];
+
+ amd_pmf_dump_sps_defaults_v2(&config_store_v2);
+}
+
static void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev)
{
struct apmf_static_slider_granular_output output;
@@ -94,6 +190,19 @@ static void amd_pmf_load_defaults_sps(struct amd_pmf_dev *dev)
amd_pmf_dump_sps_defaults(&config_store);
}
+static void amd_pmf_update_slider_v2(struct amd_pmf_dev *dev, int idx)
+{
+ amd_pmf_send_cmd(dev, SET_PMF_PPT, false, apts_config_store.val[idx].pmf_ppt, NULL);
+ amd_pmf_send_cmd(dev, SET_PMF_PPT_APU_ONLY, false,
+ apts_config_store.val[idx].ppt_pmf_apu_only, NULL);
+ 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);
+ amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
+ 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,
struct amd_pmf_static_slider_granular *table)
{
@@ -126,6 +235,32 @@ void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
}
}
+static int amd_pmf_update_sps_power_limits_v2(struct amd_pmf_dev *pdev, int pwr_mode)
+{
+ int src, index;
+
+ src = amd_pmf_get_power_source();
+
+ switch (pwr_mode) {
+ case POWER_MODE_PERFORMANCE:
+ index = config_store_v2.sps_idx.power_states[src][POWER_MODE_BEST_PERFORMANCE];
+ amd_pmf_update_slider_v2(pdev, index);
+ break;
+ case POWER_MODE_BALANCED_POWER:
+ index = config_store_v2.sps_idx.power_states[src][POWER_MODE_BALANCED];
+ amd_pmf_update_slider_v2(pdev, index);
+ break;
+ case POWER_MODE_POWER_SAVER:
+ index = config_store_v2.sps_idx.power_states[src][POWER_MODE_BEST_POWER_EFFICIENCY];
+ amd_pmf_update_slider_v2(pdev, index);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
int amd_pmf_set_sps_power_limits(struct amd_pmf_dev *pmf)
{
int mode;
@@ -134,6 +269,9 @@ int amd_pmf_set_sps_power_limits(struct amd_pmf_dev *pmf)
if (mode < 0)
return mode;
+ if (pmf->pmf_if_version == PMF_IF_V2)
+ return amd_pmf_update_sps_power_limits_v2(pmf, mode);
+
amd_pmf_update_slider(pmf, SLIDER_OP_SET, mode, NULL);
return 0;
@@ -256,7 +394,12 @@ 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)) {
- amd_pmf_load_defaults_sps(dev);
+ if (dev->pmf_if_version == PMF_IF_V2) {
+ amd_pmf_load_defaults_sps_v2(dev);
+ amd_pmf_load_apts_defaults_sps_v2(dev);
+ } else {
+ amd_pmf_load_defaults_sps(dev);
+ }
/* update SPS balanced power mode thermals */
amd_pmf_set_sps_power_limits(dev);
diff --git a/drivers/platform/x86/amd/pmf/tee-if.c b/drivers/platform/x86/amd/pmf/tee-if.c
index 4ebfe0f5a7..b438de4d6b 100644
--- a/drivers/platform/x86/amd/pmf/tee-if.c
+++ b/drivers/platform/x86/amd/pmf/tee-if.c
@@ -246,19 +246,24 @@ static void amd_pmf_invoke_cmd(struct work_struct *work)
static int amd_pmf_start_policy_engine(struct amd_pmf_dev *dev)
{
- u32 cookie, length;
+ struct cookie_header *header;
int res;
- cookie = *(u32 *)(dev->policy_buf + POLICY_COOKIE_OFFSET);
- length = *(u32 *)(dev->policy_buf + POLICY_COOKIE_LEN);
+ if (dev->policy_sz < POLICY_COOKIE_OFFSET + sizeof(*header))
+ return -EINVAL;
+
+ header = (struct cookie_header *)(dev->policy_buf + POLICY_COOKIE_OFFSET);
- if (cookie != POLICY_SIGN_COOKIE || !length) {
+ if (header->sign != POLICY_SIGN_COOKIE || !header->length) {
dev_dbg(dev->dev, "cookie doesn't match\n");
return -EINVAL;
}
+ if (dev->policy_sz < header->length + 512)
+ return -EINVAL;
+
/* Update the actual length */
- dev->policy_sz = length + 512;
+ dev->policy_sz = header->length + 512;
res = amd_pmf_invoke_cmd_init(dev);
if (res == TA_PMF_TYPE_SUCCESS) {
/* Now its safe to announce that smart pc is enabled */
@@ -271,7 +276,7 @@ static int amd_pmf_start_policy_engine(struct amd_pmf_dev *dev)
} else {
dev_err(dev->dev, "ta invoke cmd init failed err: %x\n", res);
dev->smart_pc_enabled = false;
- return res;
+ return -EIO;
}
return 0;
@@ -311,8 +316,8 @@ static ssize_t amd_pmf_get_pb_data(struct file *filp, const char __user *buf,
amd_pmf_hex_dump_pb(dev);
ret = amd_pmf_start_policy_engine(dev);
- if (ret)
- return -EINVAL;
+ if (ret < 0)
+ return ret;
return length;
}
@@ -453,7 +458,7 @@ int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev)
goto error;
}
- memcpy(dev->policy_buf, dev->policy_base, dev->policy_sz);
+ memcpy_fromio(dev->policy_buf, dev->policy_base, dev->policy_sz);
amd_pmf_hex_dump_pb(dev);
diff --git a/drivers/platform/x86/amilo-rfkill.c b/drivers/platform/x86/amilo-rfkill.c
index efcf909786..2423dc91de 100644
--- a/drivers/platform/x86/amilo-rfkill.c
+++ b/drivers/platform/x86/amilo-rfkill.c
@@ -171,6 +171,7 @@ static void __exit amilo_rfkill_exit(void)
}
MODULE_AUTHOR("Ben Hutchings <ben@decadent.org.uk>");
+MODULE_DESCRIPTION("Fujitsu-Siemens Amilo rfkill support");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(dmi, amilo_rfkill_id_table);
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c
index bf03ea1b12..ccb33d034e 100644
--- a/drivers/platform/x86/asus-laptop.c
+++ b/drivers/platform/x86/asus-laptop.c
@@ -852,8 +852,8 @@ static ssize_t infos_show(struct device *dev, struct device_attribute *attr,
* so we don't set eof to 1
*/
- len += sprintf(page, ASUS_LAPTOP_NAME " " ASUS_LAPTOP_VERSION "\n");
- len += sprintf(page + len, "Model reference : %s\n", asus->name);
+ len += sysfs_emit_at(page, len, ASUS_LAPTOP_NAME " " ASUS_LAPTOP_VERSION "\n");
+ len += sysfs_emit_at(page, len, "Model reference : %s\n", asus->name);
/*
* The SFUN method probably allows the original driver to get the list
* of features supported by a given model. For now, 0x0100 or 0x0800
@@ -862,7 +862,7 @@ static ssize_t infos_show(struct device *dev, struct device_attribute *attr,
*/
rv = acpi_evaluate_integer(asus->handle, "SFUN", NULL, &temp);
if (ACPI_SUCCESS(rv))
- len += sprintf(page + len, "SFUN value : %#x\n",
+ len += sysfs_emit_at(page, len, "SFUN value : %#x\n",
(uint) temp);
/*
* The HWRS method return informations about the hardware.
@@ -874,7 +874,7 @@ static ssize_t infos_show(struct device *dev, struct device_attribute *attr,
*/
rv = acpi_evaluate_integer(asus->handle, "HWRS", NULL, &temp);
if (ACPI_SUCCESS(rv))
- len += sprintf(page + len, "HWRS value : %#x\n",
+ len += sysfs_emit_at(page, len, "HWRS value : %#x\n",
(uint) temp);
/*
* Another value for userspace: the ASYM method returns 0x02 for
@@ -885,25 +885,25 @@ static ssize_t infos_show(struct device *dev, struct device_attribute *attr,
*/
rv = acpi_evaluate_integer(asus->handle, "ASYM", NULL, &temp);
if (ACPI_SUCCESS(rv))
- len += sprintf(page + len, "ASYM value : %#x\n",
+ len += sysfs_emit_at(page, len, "ASYM value : %#x\n",
(uint) temp);
if (asus->dsdt_info) {
snprintf(buf, 16, "%d", asus->dsdt_info->length);
- len += sprintf(page + len, "DSDT length : %s\n", buf);
+ len += sysfs_emit_at(page, len, "DSDT length : %s\n", buf);
snprintf(buf, 16, "%d", asus->dsdt_info->checksum);
- len += sprintf(page + len, "DSDT checksum : %s\n", buf);
+ len += sysfs_emit_at(page, len, "DSDT checksum : %s\n", buf);
snprintf(buf, 16, "%d", asus->dsdt_info->revision);
- len += sprintf(page + len, "DSDT revision : %s\n", buf);
+ len += sysfs_emit_at(page, len, "DSDT revision : %s\n", buf);
snprintf(buf, 7, "%s", asus->dsdt_info->oem_id);
- len += sprintf(page + len, "OEM id : %s\n", buf);
+ len += sysfs_emit_at(page, len, "OEM id : %s\n", buf);
snprintf(buf, 9, "%s", asus->dsdt_info->oem_table_id);
- len += sprintf(page + len, "OEM table id : %s\n", buf);
+ len += sysfs_emit_at(page, len, "OEM table id : %s\n", buf);
snprintf(buf, 16, "%x", asus->dsdt_info->oem_revision);
- len += sprintf(page + len, "OEM revision : 0x%s\n", buf);
+ len += sysfs_emit_at(page, len, "OEM revision : 0x%s\n", buf);
snprintf(buf, 5, "%s", asus->dsdt_info->asl_compiler_id);
- len += sprintf(page + len, "ASL comp vendor id : %s\n", buf);
+ len += sysfs_emit_at(page, len, "ASL comp vendor id : %s\n", buf);
snprintf(buf, 16, "%x", asus->dsdt_info->asl_compiler_revision);
- len += sprintf(page + len, "ASL comp revision : 0x%s\n", buf);
+ len += sysfs_emit_at(page, len, "ASL comp revision : 0x%s\n", buf);
}
return len;
@@ -933,7 +933,7 @@ static ssize_t ledd_show(struct device *dev, struct device_attribute *attr,
{
struct asus_laptop *asus = dev_get_drvdata(dev);
- return sprintf(buf, "0x%08x\n", asus->ledd_status);
+ return sysfs_emit(buf, "0x%08x\n", asus->ledd_status);
}
static ssize_t ledd_store(struct device *dev, struct device_attribute *attr,
@@ -993,7 +993,7 @@ static ssize_t wlan_show(struct device *dev, struct device_attribute *attr,
{
struct asus_laptop *asus = dev_get_drvdata(dev);
- return sprintf(buf, "%d\n", asus_wireless_status(asus, WL_RSTS));
+ return sysfs_emit(buf, "%d\n", asus_wireless_status(asus, WL_RSTS));
}
static ssize_t wlan_store(struct device *dev, struct device_attribute *attr,
@@ -1022,7 +1022,7 @@ static ssize_t bluetooth_show(struct device *dev, struct device_attribute *attr,
{
struct asus_laptop *asus = dev_get_drvdata(dev);
- return sprintf(buf, "%d\n", asus_wireless_status(asus, BT_RSTS));
+ return sysfs_emit(buf, "%d\n", asus_wireless_status(asus, BT_RSTS));
}
static ssize_t bluetooth_store(struct device *dev,
@@ -1052,7 +1052,7 @@ static ssize_t wimax_show(struct device *dev, struct device_attribute *attr,
{
struct asus_laptop *asus = dev_get_drvdata(dev);
- return sprintf(buf, "%d\n", asus_wireless_status(asus, WM_RSTS));
+ return sysfs_emit(buf, "%d\n", asus_wireless_status(asus, WM_RSTS));
}
static ssize_t wimax_store(struct device *dev, struct device_attribute *attr,
@@ -1081,7 +1081,7 @@ static ssize_t wwan_show(struct device *dev, struct device_attribute *attr,
{
struct asus_laptop *asus = dev_get_drvdata(dev);
- return sprintf(buf, "%d\n", asus_wireless_status(asus, WW_RSTS));
+ return sysfs_emit(buf, "%d\n", asus_wireless_status(asus, WW_RSTS));
}
static ssize_t wwan_store(struct device *dev, struct device_attribute *attr,
@@ -1151,7 +1151,7 @@ static ssize_t ls_switch_show(struct device *dev, struct device_attribute *attr,
{
struct asus_laptop *asus = dev_get_drvdata(dev);
- return sprintf(buf, "%d\n", asus->light_switch);
+ return sysfs_emit(buf, "%d\n", asus->light_switch);
}
static ssize_t ls_switch_store(struct device *dev,
@@ -1182,7 +1182,7 @@ static ssize_t ls_level_show(struct device *dev, struct device_attribute *attr,
{
struct asus_laptop *asus = dev_get_drvdata(dev);
- return sprintf(buf, "%d\n", asus->light_level);
+ return sysfs_emit(buf, "%d\n", asus->light_level);
}
static ssize_t ls_level_store(struct device *dev, struct device_attribute *attr,
@@ -1228,7 +1228,7 @@ static ssize_t ls_value_show(struct device *dev, struct device_attribute *attr,
if (!err)
err = pega_int_read(asus, PEGA_READ_ALS_L, &lo);
if (!err)
- return sprintf(buf, "%d\n", 10 * hi + lo);
+ return sysfs_emit(buf, "%d\n", 10 * hi + lo);
return err;
}
static DEVICE_ATTR_RO(ls_value);
@@ -1264,7 +1264,7 @@ static ssize_t gps_show(struct device *dev, struct device_attribute *attr,
{
struct asus_laptop *asus = dev_get_drvdata(dev);
- return sprintf(buf, "%d\n", asus_gps_status(asus));
+ return sysfs_emit(buf, "%d\n", asus_gps_status(asus));
}
static ssize_t gps_store(struct device *dev, struct device_attribute *attr,
@@ -1925,7 +1925,6 @@ MODULE_DEVICE_TABLE(acpi, asus_device_ids);
static struct acpi_driver asus_acpi_driver = {
.name = ASUS_LAPTOP_NAME,
.class = ASUS_LAPTOP_CLASS,
- .owner = THIS_MODULE,
.ids = asus_device_ids,
.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
.ops = {
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 18be35fdb3..bc9c5db383 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -101,13 +101,6 @@ module_param(fnlock_default, bool, 0444);
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
#define ASUS_ACPI_UID_ASUSWMI "ASUSWMI"
-#define ASUS_ACPI_UID_ATK "ATK"
-
-#define WMI_EVENT_QUEUE_SIZE 0x10
-#define WMI_EVENT_QUEUE_END 0x1
-#define WMI_EVENT_MASK 0xFFFF
-/* The WMI hotkey event value is always the same. */
-#define WMI_EVENT_VALUE_ATK 0xFF
#define WMI_EVENT_MASK 0xFFFF
@@ -133,10 +126,21 @@ module_param(fnlock_default, bool, 0444);
#define ASUS_SCREENPAD_BRIGHT_MAX 255
#define ASUS_SCREENPAD_BRIGHT_DEFAULT 60
+#define ASUS_MINI_LED_MODE_MASK 0x03
+/* Standard modes for devices with only on/off */
+#define ASUS_MINI_LED_OFF 0x00
+#define ASUS_MINI_LED_ON 0x01
+/* New mode on some devices, define here to clarify remapping later */
+#define ASUS_MINI_LED_STRONG_MODE 0x02
+/* New modes for devices with 3 mini-led mode types */
+#define ASUS_MINI_LED_2024_WEAK 0x00
+#define ASUS_MINI_LED_2024_STRONG 0x01
+#define ASUS_MINI_LED_2024_OFF 0x02
+
/* Controls the power state of the USB0 hub on ROG Ally which input is on */
#define ASUS_USB0_PWR_EC0_CSEE "\\_SB.PCI0.SBRG.EC0.CSEE"
/* 300ms so far seems to produce a reliable result on AC and battery */
-#define ASUS_USB0_PWR_EC0_CSEE_WAIT 300
+#define ASUS_USB0_PWR_EC0_CSEE_WAIT 1500
static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL };
@@ -219,7 +223,6 @@ struct asus_wmi {
int dsts_id;
int spec;
int sfun;
- bool wmi_event_queue;
struct input_dev *inputdev;
struct backlight_device *backlight_device;
@@ -251,6 +254,9 @@ struct asus_wmi {
u32 tablet_switch_dev_id;
bool tablet_switch_inverted;
+ /* The ROG Ally device requires the MCU USB device be disconnected before suspend */
+ bool ally_mcu_usb_switch;
+
enum fan_type fan_type;
enum fan_type gpu_fan_type;
enum fan_type mid_fan_type;
@@ -263,22 +269,20 @@ struct asus_wmi {
u8 fan_boost_mode_mask;
u8 fan_boost_mode;
- bool charge_mode_available;
bool egpu_enable_available;
- bool egpu_connect_available;
bool dgpu_disable_available;
- bool gpu_mux_mode_available;
+ u32 gpu_mux_dev;
/* Tunables provided by ASUS for gaming laptops */
- bool ppt_pl2_sppt_available;
- bool ppt_pl1_spl_available;
- bool ppt_apu_sppt_available;
- bool ppt_plat_sppt_available;
- bool ppt_fppt_available;
- bool nv_dyn_boost_available;
- bool nv_temp_tgt_available;
-
- bool kbd_rgb_mode_available;
+ u32 ppt_pl2_sppt;
+ u32 ppt_pl1_spl;
+ u32 ppt_apu_sppt;
+ u32 ppt_platform_sppt;
+ u32 ppt_fppt;
+ u32 nv_dynamic_boost;
+ u32 nv_temp_target;
+
+ u32 kbd_rgb_dev;
bool kbd_rgb_state_available;
bool throttle_thermal_policy_available;
@@ -296,7 +300,7 @@ struct asus_wmi {
bool battery_rsoc_available;
bool panel_overdrive_available;
- bool mini_led_mode_available;
+ u32 mini_led_dev_id;
struct hotplug_slot hotplug_slot;
struct mutex hotplug_lock;
@@ -306,9 +310,6 @@ struct asus_wmi {
bool fnlock_locked;
- /* The ROG Ally device requires the MCU USB device be disconnected before suspend */
- bool ally_mcu_usb_switch;
-
struct asus_wmi_debug debug;
struct asus_wmi_driver *driver;
@@ -489,7 +490,17 @@ static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args)
static int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval)
{
- return asus_wmi_evaluate_method(asus->dsts_id, dev_id, 0, retval);
+ int err;
+
+ err = asus_wmi_evaluate_method(asus->dsts_id, dev_id, 0, retval);
+
+ if (err)
+ return err;
+
+ if (*retval == ~0)
+ return -ENODEV;
+
+ return 0;
}
static int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param,
@@ -680,8 +691,8 @@ static ssize_t dgpu_disable_store(struct device *dev,
if (disable > 1)
return -EINVAL;
- if (asus->gpu_mux_mode_available) {
- result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_GPU_MUX);
+ if (asus->gpu_mux_dev) {
+ result = asus_wmi_get_devstate_simple(asus, asus->gpu_mux_dev);
if (result < 0)
/* An error here may signal greater failure of GPU handling */
return result;
@@ -746,8 +757,8 @@ static ssize_t egpu_enable_store(struct device *dev,
return err;
}
- if (asus->gpu_mux_mode_available) {
- result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_GPU_MUX);
+ if (asus->gpu_mux_dev) {
+ result = asus_wmi_get_devstate_simple(asus, asus->gpu_mux_dev);
if (result < 0) {
/* An error here may signal greater failure of GPU handling */
pr_warn("Failed to get gpu mux status: %d\n", result);
@@ -800,7 +811,7 @@ static ssize_t gpu_mux_mode_show(struct device *dev,
struct asus_wmi *asus = dev_get_drvdata(dev);
int result;
- result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_GPU_MUX);
+ result = asus_wmi_get_devstate_simple(asus, asus->gpu_mux_dev);
if (result < 0)
return result;
@@ -846,7 +857,7 @@ static ssize_t gpu_mux_mode_store(struct device *dev,
}
}
- err = asus_wmi_set_devstate(ASUS_WMI_DEVID_GPU_MUX, optimus, &result);
+ err = asus_wmi_set_devstate(asus->gpu_mux_dev, optimus, &result);
if (err) {
dev_err(dev, "Failed to set GPU MUX mode: %d\n", err);
return err;
@@ -869,8 +880,13 @@ static ssize_t kbd_rgb_mode_store(struct device *dev,
const char *buf, size_t count)
{
u32 cmd, mode, r, g, b, speed;
+ struct led_classdev *led;
+ struct asus_wmi *asus;
int err;
+ led = dev_get_drvdata(dev);
+ asus = container_of(led, struct asus_wmi, kbd_led);
+
if (sscanf(buf, "%d %d %d %d %d %d", &cmd, &mode, &r, &g, &b, &speed) != 6)
return -EINVAL;
@@ -904,7 +920,7 @@ static ssize_t kbd_rgb_mode_store(struct device *dev,
speed = 0xeb;
}
- err = asus_wmi_evaluate_method3(ASUS_WMI_METHODID_DEVS, ASUS_WMI_DEVID_TUF_RGB_MODE,
+ err = asus_wmi_evaluate_method3(ASUS_WMI_METHODID_DEVS, asus->kbd_rgb_dev,
cmd | (mode << 8) | (r << 16) | (g << 24), b | (speed << 8), NULL);
if (err)
return err;
@@ -913,17 +929,12 @@ static ssize_t kbd_rgb_mode_store(struct device *dev,
}
static DEVICE_ATTR_WO(kbd_rgb_mode);
-static ssize_t kbd_rgb_mode_index_show(struct device *device,
- struct device_attribute *attr,
- char *buf)
-{
- return sysfs_emit(buf, "%s\n", "cmd mode red green blue speed");
-}
-static DEVICE_ATTR_RO(kbd_rgb_mode_index);
+static DEVICE_STRING_ATTR_RO(kbd_rgb_mode_index, 0444,
+ "cmd mode red green blue speed");
static struct attribute *kbd_rgb_mode_attrs[] = {
&dev_attr_kbd_rgb_mode.attr,
- &dev_attr_kbd_rgb_mode_index.attr,
+ &dev_attr_kbd_rgb_mode_index.attr.attr,
NULL,
};
@@ -965,17 +976,12 @@ static ssize_t kbd_rgb_state_store(struct device *dev,
}
static DEVICE_ATTR_WO(kbd_rgb_state);
-static ssize_t kbd_rgb_state_index_show(struct device *device,
- struct device_attribute *attr,
- char *buf)
-{
- return sysfs_emit(buf, "%s\n", "cmd boot awake sleep keyboard");
-}
-static DEVICE_ATTR_RO(kbd_rgb_state_index);
+static DEVICE_STRING_ATTR_RO(kbd_rgb_state_index, 0444,
+ "cmd boot awake sleep keyboard");
static struct attribute *kbd_rgb_state_attrs[] = {
&dev_attr_kbd_rgb_state.attr,
- &dev_attr_kbd_rgb_state_index.attr,
+ &dev_attr_kbd_rgb_state_index.attr.attr,
NULL,
};
@@ -994,11 +1000,10 @@ static ssize_t ppt_pl2_sppt_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
int result, err;
u32 value;
- struct asus_wmi *asus = dev_get_drvdata(dev);
-
result = kstrtou32(buf, 10, &value);
if (result)
return result;
@@ -1017,22 +1022,31 @@ static ssize_t ppt_pl2_sppt_store(struct device *dev,
return -EIO;
}
+ asus->ppt_pl2_sppt = value;
sysfs_notify(&asus->platform_device->dev.kobj, NULL, "ppt_pl2_sppt");
return count;
}
-static DEVICE_ATTR_WO(ppt_pl2_sppt);
+
+static ssize_t ppt_pl2_sppt_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%u\n", asus->ppt_pl2_sppt);
+}
+static DEVICE_ATTR_RW(ppt_pl2_sppt);
/* Tunable: PPT, Intel=PL1, AMD=SPL ******************************************/
static ssize_t ppt_pl1_spl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
int result, err;
u32 value;
- struct asus_wmi *asus = dev_get_drvdata(dev);
-
result = kstrtou32(buf, 10, &value);
if (result)
return result;
@@ -1051,22 +1065,30 @@ static ssize_t ppt_pl1_spl_store(struct device *dev,
return -EIO;
}
+ asus->ppt_pl1_spl = value;
sysfs_notify(&asus->platform_device->dev.kobj, NULL, "ppt_pl1_spl");
return count;
}
-static DEVICE_ATTR_WO(ppt_pl1_spl);
+static ssize_t ppt_pl1_spl_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%u\n", asus->ppt_pl1_spl);
+}
+static DEVICE_ATTR_RW(ppt_pl1_spl);
/* Tunable: PPT APU FPPT ******************************************************/
static ssize_t ppt_fppt_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
int result, err;
u32 value;
- struct asus_wmi *asus = dev_get_drvdata(dev);
-
result = kstrtou32(buf, 10, &value);
if (result)
return result;
@@ -1085,22 +1107,31 @@ static ssize_t ppt_fppt_store(struct device *dev,
return -EIO;
}
+ asus->ppt_fppt = value;
sysfs_notify(&asus->platform_device->dev.kobj, NULL, "ppt_fpu_sppt");
return count;
}
-static DEVICE_ATTR_WO(ppt_fppt);
+
+static ssize_t ppt_fppt_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%u\n", asus->ppt_fppt);
+}
+static DEVICE_ATTR_RW(ppt_fppt);
/* Tunable: PPT APU SPPT *****************************************************/
static ssize_t ppt_apu_sppt_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
int result, err;
u32 value;
- struct asus_wmi *asus = dev_get_drvdata(dev);
-
result = kstrtou32(buf, 10, &value);
if (result)
return result;
@@ -1119,22 +1150,31 @@ static ssize_t ppt_apu_sppt_store(struct device *dev,
return -EIO;
}
+ asus->ppt_apu_sppt = value;
sysfs_notify(&asus->platform_device->dev.kobj, NULL, "ppt_apu_sppt");
return count;
}
-static DEVICE_ATTR_WO(ppt_apu_sppt);
+
+static ssize_t ppt_apu_sppt_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%u\n", asus->ppt_apu_sppt);
+}
+static DEVICE_ATTR_RW(ppt_apu_sppt);
/* Tunable: PPT platform SPPT ************************************************/
static ssize_t ppt_platform_sppt_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
int result, err;
u32 value;
- struct asus_wmi *asus = dev_get_drvdata(dev);
-
result = kstrtou32(buf, 10, &value);
if (result)
return result;
@@ -1153,22 +1193,31 @@ static ssize_t ppt_platform_sppt_store(struct device *dev,
return -EIO;
}
+ asus->ppt_platform_sppt = value;
sysfs_notify(&asus->platform_device->dev.kobj, NULL, "ppt_platform_sppt");
return count;
}
-static DEVICE_ATTR_WO(ppt_platform_sppt);
+
+static ssize_t ppt_platform_sppt_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%u\n", asus->ppt_platform_sppt);
+}
+static DEVICE_ATTR_RW(ppt_platform_sppt);
/* Tunable: NVIDIA dynamic boost *********************************************/
static ssize_t nv_dynamic_boost_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
int result, err;
u32 value;
- struct asus_wmi *asus = dev_get_drvdata(dev);
-
result = kstrtou32(buf, 10, &value);
if (result)
return result;
@@ -1187,22 +1236,31 @@ static ssize_t nv_dynamic_boost_store(struct device *dev,
return -EIO;
}
+ asus->nv_dynamic_boost = value;
sysfs_notify(&asus->platform_device->dev.kobj, NULL, "nv_dynamic_boost");
return count;
}
-static DEVICE_ATTR_WO(nv_dynamic_boost);
+
+static ssize_t nv_dynamic_boost_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%u\n", asus->nv_dynamic_boost);
+}
+static DEVICE_ATTR_RW(nv_dynamic_boost);
/* Tunable: NVIDIA temperature target ****************************************/
static ssize_t nv_temp_target_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
int result, err;
u32 value;
- struct asus_wmi *asus = dev_get_drvdata(dev);
-
result = kstrtou32(buf, 10, &value);
if (result)
return result;
@@ -1221,11 +1279,68 @@ static ssize_t nv_temp_target_store(struct device *dev,
return -EIO;
}
+ asus->nv_temp_target = value;
sysfs_notify(&asus->platform_device->dev.kobj, NULL, "nv_temp_target");
return count;
}
-static DEVICE_ATTR_WO(nv_temp_target);
+
+static ssize_t nv_temp_target_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%u\n", asus->nv_temp_target);
+}
+static DEVICE_ATTR_RW(nv_temp_target);
+
+/* Ally MCU Powersave ********************************************************/
+static ssize_t mcu_powersave_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+ int result;
+
+ result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_MCU_POWERSAVE);
+ if (result < 0)
+ return result;
+
+ return sysfs_emit(buf, "%d\n", result);
+}
+
+static ssize_t mcu_powersave_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int result, err;
+ u32 enable;
+
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ result = kstrtou32(buf, 10, &enable);
+ if (result)
+ return result;
+
+ if (enable > 1)
+ return -EINVAL;
+
+ err = asus_wmi_set_devstate(ASUS_WMI_DEVID_MCU_POWERSAVE, enable, &result);
+ if (err) {
+ pr_warn("Failed to set MCU powersave: %d\n", err);
+ return err;
+ }
+
+ if (result > 1) {
+ pr_warn("Failed to set MCU powersave (result): 0x%x\n", result);
+ return -EIO;
+ }
+
+ sysfs_notify(&asus->platform_device->dev.kobj, NULL, "mcu_powersave");
+
+ return count;
+}
+static DEVICE_ATTR_RW(mcu_powersave);
/* Battery ********************************************************************/
@@ -1547,7 +1662,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
{
int rv = 0, num_rgb_groups = 0, led_val;
- if (asus->kbd_rgb_mode_available)
+ if (asus->kbd_rgb_dev)
kbd_rgb_mode_groups[num_rgb_groups++] = &kbd_rgb_mode_group;
if (asus->kbd_rgb_state_available)
kbd_rgb_mode_groups[num_rgb_groups++] = &kbd_rgb_state_group;
@@ -1620,7 +1735,6 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MICMUTE_LED)) {
asus->micmute_led.name = "platform::micmute";
asus->micmute_led.max_brightness = 1;
- asus->micmute_led.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
asus->micmute_led.brightness_set_blocking = micmute_led_set;
asus->micmute_led.default_trigger = "audio-micmute";
@@ -2102,20 +2216,88 @@ static ssize_t panel_od_store(struct device *dev,
}
static DEVICE_ATTR_RW(panel_od);
-/* Mini-LED mode **************************************************************/
-static ssize_t mini_led_mode_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+/* Bootup sound ***************************************************************/
+
+static ssize_t boot_sound_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct asus_wmi *asus = dev_get_drvdata(dev);
int result;
- result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_MINI_LED_MODE);
+ result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_BOOT_SOUND);
if (result < 0)
return result;
return sysfs_emit(buf, "%d\n", result);
}
+static ssize_t boot_sound_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int result, err;
+ u32 snd;
+
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ result = kstrtou32(buf, 10, &snd);
+ if (result)
+ return result;
+
+ if (snd > 1)
+ return -EINVAL;
+
+ err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BOOT_SOUND, snd, &result);
+ if (err) {
+ pr_warn("Failed to set boot sound: %d\n", err);
+ return err;
+ }
+
+ if (result > 1) {
+ pr_warn("Failed to set panel boot sound (result): 0x%x\n", result);
+ return -EIO;
+ }
+
+ sysfs_notify(&asus->platform_device->dev.kobj, NULL, "boot_sound");
+
+ return count;
+}
+static DEVICE_ATTR_RW(boot_sound);
+
+/* Mini-LED mode **************************************************************/
+static ssize_t mini_led_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+ u32 value;
+ int err;
+
+ err = asus_wmi_get_devstate(asus, asus->mini_led_dev_id, &value);
+ if (err < 0)
+ return err;
+ value = value & ASUS_MINI_LED_MODE_MASK;
+
+ /*
+ * Remap the mode values to match previous generation mini-led. The last gen
+ * WMI 0 == off, while on this version WMI 2 ==off (flipped).
+ */
+ if (asus->mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE2) {
+ switch (value) {
+ case ASUS_MINI_LED_2024_WEAK:
+ value = ASUS_MINI_LED_ON;
+ break;
+ case ASUS_MINI_LED_2024_STRONG:
+ value = ASUS_MINI_LED_STRONG_MODE;
+ break;
+ case ASUS_MINI_LED_2024_OFF:
+ value = ASUS_MINI_LED_OFF;
+ break;
+ }
+ }
+
+ return sysfs_emit(buf, "%d\n", value);
+}
+
static ssize_t mini_led_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
@@ -2129,11 +2311,32 @@ static ssize_t mini_led_mode_store(struct device *dev,
if (result)
return result;
- if (mode > 1)
+ if (asus->mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE &&
+ mode > ASUS_MINI_LED_ON)
+ return -EINVAL;
+ if (asus->mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE2 &&
+ mode > ASUS_MINI_LED_STRONG_MODE)
return -EINVAL;
- err = asus_wmi_set_devstate(ASUS_WMI_DEVID_MINI_LED_MODE, mode, &result);
+ /*
+ * Remap the mode values so expected behaviour is the same as the last
+ * generation of mini-LED with 0 == off, 1 == on.
+ */
+ if (asus->mini_led_dev_id == ASUS_WMI_DEVID_MINI_LED_MODE2) {
+ switch (mode) {
+ case ASUS_MINI_LED_OFF:
+ mode = ASUS_MINI_LED_2024_OFF;
+ break;
+ case ASUS_MINI_LED_ON:
+ mode = ASUS_MINI_LED_2024_WEAK;
+ break;
+ case ASUS_MINI_LED_STRONG_MODE:
+ mode = ASUS_MINI_LED_2024_STRONG;
+ break;
+ }
+ }
+ err = asus_wmi_set_devstate(asus->mini_led_dev_id, mode, &result);
if (err) {
pr_warn("Failed to set mini-LED: %d\n", err);
return err;
@@ -2150,6 +2353,23 @@ static ssize_t mini_led_mode_store(struct device *dev,
}
static DEVICE_ATTR_RW(mini_led_mode);
+static ssize_t available_mini_led_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ switch (asus->mini_led_dev_id) {
+ case ASUS_WMI_DEVID_MINI_LED_MODE:
+ return sysfs_emit(buf, "0 1\n");
+ case ASUS_WMI_DEVID_MINI_LED_MODE2:
+ return sysfs_emit(buf, "0 1 2\n");
+ }
+
+ return sysfs_emit(buf, "0\n");
+}
+
+static DEVICE_ATTR_RO(available_mini_led_mode);
+
/* Quirks *********************************************************************/
static void asus_wmi_set_xusb2pr(struct asus_wmi *asus)
@@ -2325,7 +2545,7 @@ static ssize_t pwm1_show(struct device *dev,
/* If we already set a value then just return it */
if (asus->agfn_pwm >= 0)
- return sprintf(buf, "%d\n", asus->agfn_pwm);
+ return sysfs_emit(buf, "%d\n", asus->agfn_pwm);
/*
* If we haven't set already set a value through the AGFN interface,
@@ -2492,13 +2712,6 @@ static ssize_t pwm1_enable_store(struct device *dev,
return count;
}
-static ssize_t fan1_label_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- return sysfs_emit(buf, "%s\n", ASUS_FAN_DESC);
-}
-
static ssize_t asus_hwmon_temp1(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -2511,8 +2724,8 @@ static ssize_t asus_hwmon_temp1(struct device *dev,
if (err < 0)
return err;
- return sprintf(buf, "%ld\n",
- deci_kelvin_to_millicelsius(value & 0xFFFF));
+ return sysfs_emit(buf, "%ld\n",
+ deci_kelvin_to_millicelsius(value & 0xFFFF));
}
/* GPU fan on modern ROG laptops */
@@ -2533,13 +2746,6 @@ static ssize_t fan2_input_show(struct device *dev,
return sysfs_emit(buf, "%d\n", value * 100);
}
-static ssize_t fan2_label_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- return sysfs_emit(buf, "%s\n", ASUS_GPU_FAN_DESC);
-}
-
/* Middle/Center fan on modern ROG laptops */
static ssize_t fan3_input_show(struct device *dev,
struct device_attribute *attr,
@@ -2558,13 +2764,6 @@ static ssize_t fan3_input_show(struct device *dev,
return sysfs_emit(buf, "%d\n", value * 100);
}
-static ssize_t fan3_label_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- return sysfs_emit(buf, "%s\n", ASUS_MID_FAN_DESC);
-}
-
static ssize_t pwm2_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -2661,15 +2860,16 @@ static ssize_t pwm3_enable_store(struct device *dev,
static DEVICE_ATTR_RW(pwm1);
static DEVICE_ATTR_RW(pwm1_enable);
static DEVICE_ATTR_RO(fan1_input);
-static DEVICE_ATTR_RO(fan1_label);
+static DEVICE_STRING_ATTR_RO(fan1_label, 0444, ASUS_FAN_DESC);
+
/* Fan2 - GPU fan */
static DEVICE_ATTR_RW(pwm2_enable);
static DEVICE_ATTR_RO(fan2_input);
-static DEVICE_ATTR_RO(fan2_label);
+static DEVICE_STRING_ATTR_RO(fan2_label, 0444, ASUS_GPU_FAN_DESC);
/* Fan3 - Middle/center fan */
static DEVICE_ATTR_RW(pwm3_enable);
static DEVICE_ATTR_RO(fan3_input);
-static DEVICE_ATTR_RO(fan3_label);
+static DEVICE_STRING_ATTR_RO(fan3_label, 0444, ASUS_MID_FAN_DESC);
/* Temperature */
static DEVICE_ATTR(temp1_input, S_IRUGO, asus_hwmon_temp1, NULL);
@@ -2680,11 +2880,11 @@ static struct attribute *hwmon_attributes[] = {
&dev_attr_pwm2_enable.attr,
&dev_attr_pwm3_enable.attr,
&dev_attr_fan1_input.attr,
- &dev_attr_fan1_label.attr,
+ &dev_attr_fan1_label.attr.attr,
&dev_attr_fan2_input.attr,
- &dev_attr_fan2_label.attr,
+ &dev_attr_fan2_label.attr.attr,
&dev_attr_fan3_input.attr,
- &dev_attr_fan3_label.attr,
+ &dev_attr_fan3_label.attr.attr,
&dev_attr_temp1_input.attr,
NULL
@@ -2701,17 +2901,17 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
if (asus->fan_type != FAN_TYPE_AGFN)
return 0;
} else if (attr == &dev_attr_fan1_input.attr
- || attr == &dev_attr_fan1_label.attr
+ || attr == &dev_attr_fan1_label.attr.attr
|| attr == &dev_attr_pwm1_enable.attr) {
if (asus->fan_type == FAN_TYPE_NONE)
return 0;
} else if (attr == &dev_attr_fan2_input.attr
- || attr == &dev_attr_fan2_label.attr
+ || attr == &dev_attr_fan2_label.attr.attr
|| attr == &dev_attr_pwm2_enable.attr) {
if (asus->gpu_fan_type == FAN_TYPE_NONE)
return 0;
} else if (attr == &dev_attr_fan3_input.attr
- || attr == &dev_attr_fan3_label.attr
+ || attr == &dev_attr_fan3_label.attr.attr
|| attr == &dev_attr_pwm3_enable.attr) {
if (asus->mid_fan_type == FAN_TYPE_NONE)
return 0;
@@ -4020,50 +4220,14 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
static void asus_wmi_notify(u32 value, void *context)
{
struct asus_wmi *asus = context;
- int code;
- int i;
-
- for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) {
- code = asus_wmi_get_event_code(value);
- if (code < 0) {
- pr_warn("Failed to get notify code: %d\n", code);
- return;
- }
-
- if (code == WMI_EVENT_QUEUE_END || code == WMI_EVENT_MASK)
- return;
-
- asus_wmi_handle_event_code(code, asus);
-
- /*
- * Double check that queue is present:
- * ATK (with queue) uses 0xff, ASUSWMI (without) 0xd2.
- */
- if (!asus->wmi_event_queue || value != WMI_EVENT_VALUE_ATK)
- return;
- }
-
- pr_warn("Failed to process event queue, last code: 0x%x\n", code);
-}
-
-static int asus_wmi_notify_queue_flush(struct asus_wmi *asus)
-{
- int code;
- int i;
-
- for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) {
- code = asus_wmi_get_event_code(WMI_EVENT_VALUE_ATK);
- if (code < 0) {
- pr_warn("Failed to get event during flush: %d\n", code);
- return code;
- }
+ int code = asus_wmi_get_event_code(value);
- if (code == WMI_EVENT_QUEUE_END || code == WMI_EVENT_MASK)
- return 0;
+ if (code < 0) {
+ pr_warn("Failed to get notify code: %d\n", code);
+ return;
}
- pr_warn("Failed to flush event queue\n");
- return -EIO;
+ asus_wmi_handle_event_code(code, asus);
}
/* Sysfs **********************************************************************/
@@ -4096,7 +4260,7 @@ static ssize_t show_sys_wmi(struct asus_wmi *asus, int devid, char *buf)
if (value < 0)
return value;
- return sprintf(buf, "%d\n", value);
+ return sysfs_emit(buf, "%d\n", value);
}
#define ASUS_WMI_CREATE_DEVICE_ATTR(_name, _mode, _cm) \
@@ -4172,8 +4336,11 @@ static struct attribute *platform_attributes[] = {
&dev_attr_ppt_platform_sppt.attr,
&dev_attr_nv_dynamic_boost.attr,
&dev_attr_nv_temp_target.attr,
+ &dev_attr_mcu_powersave.attr,
+ &dev_attr_boot_sound.attr,
&dev_attr_panel_od.attr,
&dev_attr_mini_led_mode.attr,
+ &dev_attr_available_mini_led_mode.attr,
NULL
};
@@ -4196,37 +4363,43 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
else if (attr == &dev_attr_als_enable.attr)
devid = ASUS_WMI_DEVID_ALS_ENABLE;
else if (attr == &dev_attr_charge_mode.attr)
- ok = asus->charge_mode_available;
+ devid = ASUS_WMI_DEVID_CHARGE_MODE;
else if (attr == &dev_attr_egpu_enable.attr)
ok = asus->egpu_enable_available;
else if (attr == &dev_attr_egpu_connected.attr)
- ok = asus->egpu_connect_available;
+ devid = ASUS_WMI_DEVID_EGPU_CONNECTED;
else if (attr == &dev_attr_dgpu_disable.attr)
ok = asus->dgpu_disable_available;
else if (attr == &dev_attr_gpu_mux_mode.attr)
- ok = asus->gpu_mux_mode_available;
+ ok = asus->gpu_mux_dev != 0;
else if (attr == &dev_attr_fan_boost_mode.attr)
ok = asus->fan_boost_mode_available;
else if (attr == &dev_attr_throttle_thermal_policy.attr)
ok = asus->throttle_thermal_policy_available;
else if (attr == &dev_attr_ppt_pl2_sppt.attr)
- ok = asus->ppt_pl2_sppt_available;
+ devid = ASUS_WMI_DEVID_PPT_PL2_SPPT;
else if (attr == &dev_attr_ppt_pl1_spl.attr)
- ok = asus->ppt_pl1_spl_available;
+ devid = ASUS_WMI_DEVID_PPT_PL1_SPL;
else if (attr == &dev_attr_ppt_fppt.attr)
- ok = asus->ppt_fppt_available;
+ devid = ASUS_WMI_DEVID_PPT_FPPT;
else if (attr == &dev_attr_ppt_apu_sppt.attr)
- ok = asus->ppt_apu_sppt_available;
+ devid = ASUS_WMI_DEVID_PPT_APU_SPPT;
else if (attr == &dev_attr_ppt_platform_sppt.attr)
- ok = asus->ppt_plat_sppt_available;
+ devid = ASUS_WMI_DEVID_PPT_PLAT_SPPT;
else if (attr == &dev_attr_nv_dynamic_boost.attr)
- ok = asus->nv_dyn_boost_available;
+ devid = ASUS_WMI_DEVID_NV_DYN_BOOST;
else if (attr == &dev_attr_nv_temp_target.attr)
- ok = asus->nv_temp_tgt_available;
+ devid = ASUS_WMI_DEVID_NV_THERM_TARGET;
+ else if (attr == &dev_attr_mcu_powersave.attr)
+ devid = ASUS_WMI_DEVID_MCU_POWERSAVE;
+ else if (attr == &dev_attr_boot_sound.attr)
+ devid = ASUS_WMI_DEVID_BOOT_SOUND;
else if (attr == &dev_attr_panel_od.attr)
- ok = asus->panel_overdrive_available;
+ devid = ASUS_WMI_DEVID_PANEL_OD;
else if (attr == &dev_attr_mini_led_mode.attr)
- ok = asus->mini_led_mode_available;
+ ok = asus->mini_led_dev_id != 0;
+ else if (attr == &dev_attr_available_mini_led_mode.attr)
+ ok = asus->mini_led_dev_id != 0;
if (devid != -1)
ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
@@ -4303,23 +4476,6 @@ static int asus_wmi_platform_init(struct asus_wmi *asus)
asus->dsts_id = ASUS_WMI_METHODID_DSTS;
}
- /*
- * Some devices can have multiple event codes stored in a queue before
- * the module load if it was unloaded intermittently after calling
- * the INIT method (enables event handling). The WMI notify handler is
- * expected to retrieve all event codes until a retrieved code equals
- * queue end marker (One or Ones). Old codes are flushed from the queue
- * upon module load. Not enabling this when it should be has minimal
- * visible impact so fall back if anything goes wrong.
- */
- wmi_uid = wmi_get_acpi_device_uid(asus->driver->event_guid);
- if (wmi_uid && !strcmp(wmi_uid, ASUS_ACPI_UID_ATK)) {
- dev_info(dev, "Detected ATK, enable event queue\n");
-
- if (!asus_wmi_notify_queue_flush(asus))
- asus->wmi_event_queue = true;
- }
-
/* CWAP allow to define the behavior of the Fn+F2 key,
* this method doesn't seems to be present on Eee PCs */
if (asus->driver->quirks->wapf >= 0)
@@ -4481,25 +4637,36 @@ static int asus_wmi_add(struct platform_device *pdev)
if (err)
goto fail_platform;
- asus->charge_mode_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_CHARGE_MODE);
+ /* ensure defaults for tunables */
+ asus->ppt_pl2_sppt = 5;
+ asus->ppt_pl1_spl = 5;
+ asus->ppt_apu_sppt = 5;
+ asus->ppt_platform_sppt = 5;
+ asus->ppt_fppt = 5;
+ asus->nv_dynamic_boost = 5;
+ asus->nv_temp_target = 75;
+
asus->egpu_enable_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_EGPU);
- asus->egpu_connect_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_EGPU_CONNECTED);
asus->dgpu_disable_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_DGPU);
- asus->gpu_mux_mode_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_GPU_MUX);
- asus->kbd_rgb_mode_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_MODE);
asus->kbd_rgb_state_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_STATE);
- asus->ppt_pl2_sppt_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PPT_PL2_SPPT);
- asus->ppt_pl1_spl_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PPT_PL1_SPL);
- asus->ppt_fppt_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PPT_FPPT);
- asus->ppt_apu_sppt_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PPT_APU_SPPT);
- asus->ppt_plat_sppt_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PPT_PLAT_SPPT);
- asus->nv_dyn_boost_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_NV_DYN_BOOST);
- asus->nv_temp_tgt_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_NV_THERM_TARGET);
- asus->panel_overdrive_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PANEL_OD);
- asus->mini_led_mode_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MINI_LED_MODE);
asus->ally_mcu_usb_switch = acpi_has_method(NULL, ASUS_USB0_PWR_EC0_CSEE)
&& dmi_match(DMI_BOARD_NAME, "RC71L");
+ if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MINI_LED_MODE))
+ asus->mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE;
+ else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MINI_LED_MODE2))
+ asus->mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE2;
+
+ if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_GPU_MUX))
+ asus->gpu_mux_dev = ASUS_WMI_DEVID_GPU_MUX;
+ else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_GPU_MUX_VIVO))
+ asus->gpu_mux_dev = ASUS_WMI_DEVID_GPU_MUX_VIVO;
+
+ if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_MODE))
+ asus->kbd_rgb_dev = ASUS_WMI_DEVID_TUF_RGB_MODE;
+ else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_MODE2))
+ asus->kbd_rgb_dev = ASUS_WMI_DEVID_TUF_RGB_MODE2;
+
err = fan_boost_mode_check_present(asus);
if (err)
goto fail_fan_boost_mode;
@@ -4681,6 +4848,7 @@ static int asus_hotk_resume_early(struct device *device)
struct asus_wmi *asus = dev_get_drvdata(device);
if (asus->ally_mcu_usb_switch) {
+ /* sleep required to prevent USB0 being yanked then reappearing rapidly */
if (ACPI_FAILURE(acpi_execute_simple_method(NULL, ASUS_USB0_PWR_EC0_CSEE, 0xB8)))
dev_err(device, "ROG Ally MCU failed to connect USB dev\n");
else
@@ -4692,17 +4860,8 @@ static int asus_hotk_resume_early(struct device *device)
static int asus_hotk_prepare(struct device *device)
{
struct asus_wmi *asus = dev_get_drvdata(device);
- int result, err;
if (asus->ally_mcu_usb_switch) {
- /* When powersave is enabled it causes many issues with resume of USB hub */
- result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_MCU_POWERSAVE);
- if (result == 1) {
- dev_warn(device, "MCU powersave enabled, disabling to prevent resume issues");
- err = asus_wmi_set_devstate(ASUS_WMI_DEVID_MCU_POWERSAVE, 0, &result);
- if (err || result != 1)
- dev_err(device, "Failed to set MCU powersave mode: %d\n", err);
- }
/* sleep required to ensure USB0 is disabled before sleep continues */
if (ACPI_FAILURE(acpi_execute_simple_method(NULL, ASUS_USB0_PWR_EC0_CSEE, 0xB7)))
dev_err(device, "ROG Ally MCU failed to disconnect USB dev\n");
diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c
index 2edaea2492..cb6fce655e 100644
--- a/drivers/platform/x86/classmate-laptop.c
+++ b/drivers/platform/x86/classmate-laptop.c
@@ -13,8 +13,6 @@
#include <linux/input.h>
#include <linux/rfkill.h>
-MODULE_LICENSE("GPL");
-
struct cmpc_accel {
int sensitivity;
int g_select;
@@ -434,7 +432,6 @@ static const struct acpi_device_id cmpc_accel_device_ids_v4[] = {
};
static struct acpi_driver cmpc_accel_acpi_driver_v4 = {
- .owner = THIS_MODULE,
.name = "cmpc_accel_v4",
.class = "cmpc_accel_v4",
.ids = cmpc_accel_device_ids_v4,
@@ -660,7 +657,6 @@ static const struct acpi_device_id cmpc_accel_device_ids[] = {
};
static struct acpi_driver cmpc_accel_acpi_driver = {
- .owner = THIS_MODULE,
.name = "cmpc_accel",
.class = "cmpc_accel",
.ids = cmpc_accel_device_ids,
@@ -754,7 +750,6 @@ static const struct acpi_device_id cmpc_tablet_device_ids[] = {
};
static struct acpi_driver cmpc_tablet_acpi_driver = {
- .owner = THIS_MODULE,
.name = "cmpc_tablet",
.class = "cmpc_tablet",
.ids = cmpc_tablet_device_ids,
@@ -996,7 +991,6 @@ static const struct acpi_device_id cmpc_ipml_device_ids[] = {
};
static struct acpi_driver cmpc_ipml_acpi_driver = {
- .owner = THIS_MODULE,
.name = "cmpc",
.class = "cmpc",
.ids = cmpc_ipml_device_ids,
@@ -1064,7 +1058,6 @@ static const struct acpi_device_id cmpc_keys_device_ids[] = {
};
static struct acpi_driver cmpc_keys_acpi_driver = {
- .owner = THIS_MODULE,
.name = "cmpc_keys",
.class = "cmpc_keys",
.ids = cmpc_keys_device_ids,
@@ -1144,3 +1137,5 @@ static const struct acpi_device_id cmpc_device_ids[] __maybe_unused = {
};
MODULE_DEVICE_TABLE(acpi, cmpc_device_ids);
+MODULE_DESCRIPTION("Support for Intel Classmate PC ACPI devices");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c
index 61c745490d..5546fb1894 100644
--- a/drivers/platform/x86/compal-laptop.c
+++ b/drivers/platform/x86/compal-laptop.c
@@ -1107,7 +1107,7 @@ module_init(compal_init);
module_exit(compal_cleanup);
MODULE_AUTHOR("Cezary Jackiewicz");
-MODULE_AUTHOR("Roald Frederickx (roald.frederickx@gmail.com)");
+MODULE_AUTHOR("Roald Frederickx <roald.frederickx@gmail.com>");
MODULE_DESCRIPTION("Compal Laptop Support");
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig
index e712df67fa..195a8bf532 100644
--- a/drivers/platform/x86/dell/Kconfig
+++ b/drivers/platform/x86/dell/Kconfig
@@ -57,8 +57,6 @@ config DELL_LAPTOP
select POWER_SUPPLY
select LEDS_CLASS
select NEW_LEDS
- select LEDS_TRIGGERS
- select LEDS_TRIGGER_AUDIO
help
This driver adds support for rfkill and backlight control to Dell
laptops (except for some models covered by the Compal driver).
@@ -147,6 +145,21 @@ config DELL_SMO8800
To compile this driver as a module, choose M here: the module will
be called dell-smo8800.
+config DELL_UART_BACKLIGHT
+ tristate "Dell AIO UART Backlight driver"
+ depends on ACPI
+ depends on BACKLIGHT_CLASS_DEVICE
+ depends on SERIAL_DEV_BUS
+ help
+ Say Y here if you want to support Dell AIO UART backlight interface.
+ The Dell AIO machines released after 2017 come with a UART interface
+ to communicate with the backlight scalar board. This driver creates
+ a standard backlight interface and talks to the scalar board through
+ UART to adjust the AIO screen brightness.
+
+ To compile this driver as a module, choose M here: the module will
+ be called dell_uart_backlight.
+
config DELL_WMI
tristate "Dell WMI notifications"
default m
@@ -165,7 +178,6 @@ config DELL_WMI
config DELL_WMI_PRIVACY
bool "Dell WMI Hardware Privacy Support"
- depends on LEDS_TRIGGER_AUDIO = y || DELL_WMI = LEDS_TRIGGER_AUDIO
depends on DELL_WMI
help
This option adds integration with the "Dell Hardware Privacy"
diff --git a/drivers/platform/x86/dell/Makefile b/drivers/platform/x86/dell/Makefile
index 1b89424266..8176a257d9 100644
--- a/drivers/platform/x86/dell/Makefile
+++ b/drivers/platform/x86/dell/Makefile
@@ -14,6 +14,7 @@ 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
diff --git a/drivers/platform/x86/dell/dell-laptop.c b/drivers/platform/x86/dell/dell-laptop.c
index 6586438356..42f7de2b45 100644
--- a/drivers/platform/x86/dell/dell-laptop.c
+++ b/drivers/platform/x86/dell/dell-laptop.c
@@ -2252,7 +2252,6 @@ static int __init dell_init(void)
if (dell_smbios_find_token(GLOBAL_MIC_MUTE_DISABLE) &&
dell_smbios_find_token(GLOBAL_MIC_MUTE_ENABLE) &&
!dell_privacy_has_mic_mute()) {
- micmute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
ret = led_classdev_register(&platform_device->dev, &micmute_led_cdev);
if (ret < 0)
goto fail_led;
@@ -2261,7 +2260,6 @@ static int __init dell_init(void)
if (dell_smbios_find_token(GLOBAL_MUTE_DISABLE) &&
dell_smbios_find_token(GLOBAL_MUTE_ENABLE)) {
- mute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MUTE);
ret = led_classdev_register(&platform_device->dev, &mute_led_cdev);
if (ret < 0)
goto fail_backlight;
diff --git a/drivers/platform/x86/dell/dell-rbtn.c b/drivers/platform/x86/dell/dell-rbtn.c
index c8fcb537fd..a415c432d4 100644
--- a/drivers/platform/x86/dell/dell-rbtn.c
+++ b/drivers/platform/x86/dell/dell-rbtn.c
@@ -295,7 +295,6 @@ static struct acpi_driver rbtn_driver = {
.remove = rbtn_remove,
.notify = rbtn_notify,
},
- .owner = THIS_MODULE,
};
diff --git a/drivers/platform/x86/dell/dell-smbios-base.c b/drivers/platform/x86/dell/dell-smbios-base.c
index e61bfaf8b5..b562ed99ec 100644
--- a/drivers/platform/x86/dell/dell-smbios-base.c
+++ b/drivers/platform/x86/dell/dell-smbios-base.c
@@ -11,6 +11,7 @@
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/container_of.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/capability.h>
@@ -25,11 +26,16 @@ static u32 da_supported_commands;
static int da_num_tokens;
static struct platform_device *platform_device;
static struct calling_interface_token *da_tokens;
-static struct device_attribute *token_location_attrs;
-static struct device_attribute *token_value_attrs;
+static struct token_sysfs_data *token_entries;
static struct attribute **token_attrs;
static DEFINE_MUTEX(smbios_mutex);
+struct token_sysfs_data {
+ struct device_attribute location_attr;
+ struct device_attribute value_attr;
+ struct calling_interface_token *token;
+};
+
struct smbios_device {
struct list_head list;
struct device *device;
@@ -416,47 +422,26 @@ static void __init find_tokens(const struct dmi_header *dm, void *dummy)
}
}
-static int match_attribute(struct device *dev,
- struct device_attribute *attr)
-{
- int i;
-
- for (i = 0; i < da_num_tokens * 2; i++) {
- if (!token_attrs[i])
- continue;
- if (strcmp(token_attrs[i]->name, attr->attr.name) == 0)
- return i/2;
- }
- dev_dbg(dev, "couldn't match: %s\n", attr->attr.name);
- return -EINVAL;
-}
-
static ssize_t location_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- int i;
+ struct token_sysfs_data *data = container_of(attr, struct token_sysfs_data, location_attr);
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- i = match_attribute(dev, attr);
- if (i > 0)
- return sysfs_emit(buf, "%08x", da_tokens[i].location);
- return 0;
+ return sysfs_emit(buf, "%08x", data->token->location);
}
static ssize_t value_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- int i;
+ struct token_sysfs_data *data = container_of(attr, struct token_sysfs_data, value_attr);
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- i = match_attribute(dev, attr);
- if (i > 0)
- return sysfs_emit(buf, "%08x", da_tokens[i].value);
- return 0;
+ return sysfs_emit(buf, "%08x", data->token->value);
}
static struct attribute_group smbios_attribute_group = {
@@ -473,22 +458,15 @@ static int build_tokens_sysfs(struct platform_device *dev)
{
char *location_name;
char *value_name;
- size_t size;
int ret;
int i, j;
- /* (number of tokens + 1 for null terminated */
- size = sizeof(struct device_attribute) * (da_num_tokens + 1);
- token_location_attrs = kzalloc(size, GFP_KERNEL);
- if (!token_location_attrs)
+ token_entries = kcalloc(da_num_tokens, sizeof(*token_entries), GFP_KERNEL);
+ if (!token_entries)
return -ENOMEM;
- token_value_attrs = kzalloc(size, GFP_KERNEL);
- if (!token_value_attrs)
- goto out_allocate_value;
/* need to store both location and value + terminator*/
- size = sizeof(struct attribute *) * ((2 * da_num_tokens) + 1);
- token_attrs = kzalloc(size, GFP_KERNEL);
+ token_attrs = kcalloc((2 * da_num_tokens) + 1, sizeof(*token_attrs), GFP_KERNEL);
if (!token_attrs)
goto out_allocate_attrs;
@@ -496,32 +474,34 @@ static int build_tokens_sysfs(struct platform_device *dev)
/* skip empty */
if (da_tokens[i].tokenID == 0)
continue;
+
+ token_entries[i].token = &da_tokens[i];
+
/* add location */
location_name = kasprintf(GFP_KERNEL, "%04x_location",
da_tokens[i].tokenID);
if (location_name == NULL)
goto out_unwind_strings;
- sysfs_attr_init(&token_location_attrs[i].attr);
- token_location_attrs[i].attr.name = location_name;
- token_location_attrs[i].attr.mode = 0444;
- token_location_attrs[i].show = location_show;
- token_attrs[j++] = &token_location_attrs[i].attr;
+
+ sysfs_attr_init(&token_entries[i].location_attr.attr);
+ token_entries[i].location_attr.attr.name = location_name;
+ token_entries[i].location_attr.attr.mode = 0444;
+ token_entries[i].location_attr.show = location_show;
+ token_attrs[j++] = &token_entries[i].location_attr.attr;
/* add value */
value_name = kasprintf(GFP_KERNEL, "%04x_value",
da_tokens[i].tokenID);
- if (value_name == NULL)
- goto loop_fail_create_value;
- sysfs_attr_init(&token_value_attrs[i].attr);
- token_value_attrs[i].attr.name = value_name;
- token_value_attrs[i].attr.mode = 0444;
- token_value_attrs[i].show = value_show;
- token_attrs[j++] = &token_value_attrs[i].attr;
- continue;
-
-loop_fail_create_value:
- kfree(location_name);
- goto out_unwind_strings;
+ if (!value_name) {
+ kfree(location_name);
+ goto out_unwind_strings;
+ }
+
+ sysfs_attr_init(&token_entries[i].value_attr.attr);
+ token_entries[i].value_attr.attr.name = value_name;
+ token_entries[i].value_attr.attr.mode = 0444;
+ token_entries[i].value_attr.show = value_show;
+ token_attrs[j++] = &token_entries[i].value_attr.attr;
}
smbios_attribute_group.attrs = token_attrs;
@@ -532,14 +512,12 @@ loop_fail_create_value:
out_unwind_strings:
while (i--) {
- kfree(token_location_attrs[i].attr.name);
- kfree(token_value_attrs[i].attr.name);
+ kfree(token_entries[i].location_attr.attr.name);
+ kfree(token_entries[i].value_attr.attr.name);
}
kfree(token_attrs);
out_allocate_attrs:
- kfree(token_value_attrs);
-out_allocate_value:
- kfree(token_location_attrs);
+ kfree(token_entries);
return -ENOMEM;
}
@@ -551,12 +529,11 @@ static void free_group(struct platform_device *pdev)
sysfs_remove_group(&pdev->dev.kobj,
&smbios_attribute_group);
for (i = 0; i < da_num_tokens; i++) {
- kfree(token_location_attrs[i].attr.name);
- kfree(token_value_attrs[i].attr.name);
+ kfree(token_entries[i].location_attr.attr.name);
+ kfree(token_entries[i].value_attr.attr.name);
}
kfree(token_attrs);
- kfree(token_value_attrs);
- kfree(token_location_attrs);
+ kfree(token_entries);
}
static int __init dell_smbios_init(void)
diff --git a/drivers/platform/x86/dell/dell-uart-backlight.c b/drivers/platform/x86/dell/dell-uart-backlight.c
new file mode 100644
index 0000000000..87d2a20b4c
--- /dev/null
+++ b/drivers/platform/x86/dell/dell-uart-backlight.c
@@ -0,0 +1,398 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Dell AIO Serial Backlight Driver
+ *
+ * Copyright (C) 2024 Hans de Goede <hansg@kernel.org>
+ * Copyright (C) 2017 AceLan Kao <acelan.kao@canonical.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/serdev.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include "../serdev_helpers.h"
+
+/* The backlight controller must respond within 1 second */
+#define DELL_BL_TIMEOUT msecs_to_jiffies(1000)
+#define DELL_BL_MAX_BRIGHTNESS 100
+
+/* Defines for the commands send to the controller */
+
+/* 1st byte Start Of Frame 3 MSB bits: cmd-len + 01010 SOF marker */
+#define DELL_SOF(len) (((len) << 5) | 0x0a)
+#define GET_CMD_LEN 3
+#define SET_CMD_LEN 4
+
+/* 2nd byte command */
+#define CMD_GET_VERSION 0x06
+#define CMD_SET_BRIGHTNESS 0x0b
+#define CMD_GET_BRIGHTNESS 0x0c
+#define CMD_SET_BL_POWER 0x0e
+
+/* Indexes and other defines for response received from the controller */
+#define RESP_LEN 0
+#define RESP_CMD 1 /* Echo of CMD byte from command */
+#define RESP_DATA 2 /* Start of received data */
+
+#define SET_RESP_LEN 3
+#define GET_RESP_LEN 4
+#define MIN_RESP_LEN 3
+#define MAX_RESP_LEN 80
+
+struct dell_uart_backlight {
+ struct mutex mutex;
+ wait_queue_head_t wait_queue;
+ struct device *dev;
+ struct backlight_device *bl;
+ u8 *resp;
+ u8 resp_idx;
+ u8 resp_len;
+ u8 resp_max_len;
+ u8 pending_cmd;
+ int status;
+ int power;
+};
+
+/* Checksum: SUM(Length and Cmd and Data) xor 0xFF */
+static u8 dell_uart_checksum(u8 *buf, int len)
+{
+ u8 val = 0;
+
+ while (len-- > 0)
+ val += buf[len];
+
+ return val ^ 0xff;
+}
+
+static int dell_uart_bl_command(struct dell_uart_backlight *dell_bl,
+ const u8 *cmd, int cmd_len,
+ u8 *resp, int resp_max_len)
+{
+ int ret;
+
+ ret = mutex_lock_killable(&dell_bl->mutex);
+ if (ret)
+ return ret;
+
+ dell_bl->status = -EBUSY;
+ dell_bl->resp = resp;
+ dell_bl->resp_idx = 0;
+ dell_bl->resp_len = -1; /* Invalid / unset */
+ dell_bl->resp_max_len = resp_max_len;
+ dell_bl->pending_cmd = cmd[1];
+
+ /* The TTY buffer should be big enough to take the entire cmd in one go */
+ ret = serdev_device_write_buf(to_serdev_device(dell_bl->dev), cmd, cmd_len);
+ if (ret != cmd_len) {
+ dev_err(dell_bl->dev, "Error writing command: %d\n", ret);
+ dell_bl->status = (ret < 0) ? ret : -EIO;
+ goto out;
+ }
+
+ ret = wait_event_timeout(dell_bl->wait_queue, dell_bl->status != -EBUSY,
+ DELL_BL_TIMEOUT);
+ if (ret == 0) {
+ dev_err(dell_bl->dev, "Timed out waiting for response.\n");
+ /* Clear busy status to discard bytes received after this */
+ dell_bl->status = -ETIMEDOUT;
+ }
+
+out:
+ mutex_unlock(&dell_bl->mutex);
+ return dell_bl->status;
+}
+
+static int dell_uart_set_brightness(struct dell_uart_backlight *dell_bl, int brightness)
+{
+ u8 set_brightness[SET_CMD_LEN], resp[SET_RESP_LEN];
+
+ set_brightness[0] = DELL_SOF(SET_CMD_LEN);
+ set_brightness[1] = CMD_SET_BRIGHTNESS;
+ set_brightness[2] = brightness;
+ set_brightness[3] = dell_uart_checksum(set_brightness, 3);
+
+ return dell_uart_bl_command(dell_bl, set_brightness, SET_CMD_LEN, resp, SET_RESP_LEN);
+}
+
+static int dell_uart_get_brightness(struct dell_uart_backlight *dell_bl)
+{
+ struct device *dev = dell_bl->dev;
+ u8 get_brightness[GET_CMD_LEN], resp[GET_RESP_LEN];
+ int ret;
+
+ get_brightness[0] = DELL_SOF(GET_CMD_LEN);
+ get_brightness[1] = CMD_GET_BRIGHTNESS;
+ get_brightness[2] = dell_uart_checksum(get_brightness, 2);
+
+ ret = dell_uart_bl_command(dell_bl, get_brightness, GET_CMD_LEN, resp, GET_RESP_LEN);
+ if (ret)
+ return ret;
+
+ if (resp[RESP_LEN] != GET_RESP_LEN) {
+ dev_err(dev, "Unexpected get brightness response length: %d\n", resp[RESP_LEN]);
+ return -EIO;
+ }
+
+ if (resp[RESP_DATA] > DELL_BL_MAX_BRIGHTNESS) {
+ dev_err(dev, "Unexpected get brightness response: %d\n", resp[RESP_DATA]);
+ return -EIO;
+ }
+
+ return resp[RESP_DATA];
+}
+
+static int dell_uart_set_bl_power(struct dell_uart_backlight *dell_bl, int power)
+{
+ u8 set_power[SET_CMD_LEN], resp[SET_RESP_LEN];
+ int ret;
+
+ 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[3] = dell_uart_checksum(set_power, 3);
+
+ ret = dell_uart_bl_command(dell_bl, set_power, SET_CMD_LEN, resp, SET_RESP_LEN);
+ if (ret)
+ return ret;
+
+ dell_bl->power = power;
+ return 0;
+}
+
+/*
+ * There is no command to get backlight power status,
+ * so we set the backlight power to "on" while initializing,
+ * and then track and report its status by power variable.
+ */
+static int dell_uart_get_bl_power(struct dell_uart_backlight *dell_bl)
+{
+ return dell_bl->power;
+}
+
+static int dell_uart_update_status(struct backlight_device *bd)
+{
+ struct dell_uart_backlight *dell_bl = bl_get_data(bd);
+ int ret;
+
+ ret = dell_uart_set_brightness(dell_bl, bd->props.brightness);
+ if (ret)
+ return ret;
+
+ if (bd->props.power != dell_uart_get_bl_power(dell_bl))
+ return dell_uart_set_bl_power(dell_bl, bd->props.power);
+
+ return 0;
+}
+
+static int dell_uart_get_brightness_op(struct backlight_device *bd)
+{
+ return dell_uart_get_brightness(bl_get_data(bd));
+}
+
+static const struct backlight_ops dell_uart_backlight_ops = {
+ .update_status = dell_uart_update_status,
+ .get_brightness = dell_uart_get_brightness_op,
+};
+
+static size_t dell_uart_bl_receive(struct serdev_device *serdev, const u8 *data, size_t len)
+{
+ struct dell_uart_backlight *dell_bl = serdev_device_get_drvdata(serdev);
+ size_t i;
+ u8 csum;
+
+ dev_dbg(dell_bl->dev, "Recv: %*ph\n", (int)len, data);
+
+ /* Throw away unexpected bytes / remainder of response after an error */
+ if (dell_bl->status != -EBUSY) {
+ dev_warn(dell_bl->dev, "Bytes received out of band, dropping them.\n");
+ return len;
+ }
+
+ i = 0;
+ while (i < len && dell_bl->resp_idx != dell_bl->resp_len) {
+ dell_bl->resp[dell_bl->resp_idx] = data[i++];
+
+ switch (dell_bl->resp_idx) {
+ case RESP_LEN: /* Length byte */
+ dell_bl->resp_len = dell_bl->resp[RESP_LEN];
+ if (dell_bl->resp_len < MIN_RESP_LEN ||
+ dell_bl->resp_len > dell_bl->resp_max_len) {
+ dev_err(dell_bl->dev, "Response length %d out if range %d - %d\n",
+ dell_bl->resp_len, MIN_RESP_LEN, dell_bl->resp_max_len);
+ dell_bl->status = -EIO;
+ goto wakeup;
+ }
+ break;
+ case RESP_CMD: /* CMD byte */
+ if (dell_bl->resp[RESP_CMD] != dell_bl->pending_cmd) {
+ dev_err(dell_bl->dev, "Response cmd 0x%02x != pending 0x%02x\n",
+ dell_bl->resp[RESP_CMD], dell_bl->pending_cmd);
+ dell_bl->status = -EIO;
+ goto wakeup;
+ }
+ break;
+ }
+ dell_bl->resp_idx++;
+ }
+
+ if (dell_bl->resp_idx != dell_bl->resp_len)
+ return len; /* Response not complete yet */
+
+ csum = dell_uart_checksum(dell_bl->resp, dell_bl->resp_len - 1);
+ if (dell_bl->resp[dell_bl->resp_len - 1] == csum) {
+ dell_bl->status = 0; /* Success */
+ } else {
+ dev_err(dell_bl->dev, "Checksum mismatch got 0x%02x expected 0x%02x\n",
+ dell_bl->resp[dell_bl->resp_len - 1], csum);
+ dell_bl->status = -EIO;
+ }
+wakeup:
+ wake_up(&dell_bl->wait_queue);
+ return i;
+}
+
+static const struct serdev_device_ops dell_uart_bl_serdev_ops = {
+ .receive_buf = dell_uart_bl_receive,
+ .write_wakeup = serdev_device_write_wakeup,
+};
+
+static int dell_uart_bl_serdev_probe(struct serdev_device *serdev)
+{
+ u8 get_version[GET_CMD_LEN], resp[MAX_RESP_LEN];
+ struct backlight_properties props = {};
+ struct dell_uart_backlight *dell_bl;
+ struct device *dev = &serdev->dev;
+ int ret;
+
+ dell_bl = devm_kzalloc(dev, sizeof(*dell_bl), GFP_KERNEL);
+ if (!dell_bl)
+ return -ENOMEM;
+
+ mutex_init(&dell_bl->mutex);
+ init_waitqueue_head(&dell_bl->wait_queue);
+ dell_bl->dev = dev;
+
+ ret = devm_serdev_device_open(dev, serdev);
+ if (ret)
+ return dev_err_probe(dev, ret, "opening UART device\n");
+
+ /* 9600 bps, no flow control, these are the default but set them to be sure */
+ serdev_device_set_baudrate(serdev, 9600);
+ serdev_device_set_flow_control(serdev, false);
+ serdev_device_set_drvdata(serdev, dell_bl);
+ serdev_device_set_client_ops(serdev, &dell_uart_bl_serdev_ops);
+
+ get_version[0] = DELL_SOF(GET_CMD_LEN);
+ get_version[1] = CMD_GET_VERSION;
+ get_version[2] = dell_uart_checksum(get_version, 2);
+
+ ret = dell_uart_bl_command(dell_bl, get_version, GET_CMD_LEN, resp, MAX_RESP_LEN);
+ if (ret)
+ return dev_err_probe(dev, ret, "getting firmware version\n");
+
+ dev_dbg(dev, "Firmware version: %.*s\n", resp[RESP_LEN] - 3, resp + RESP_DATA);
+
+ /* Initialize bl_power to a known value */
+ ret = dell_uart_set_bl_power(dell_bl, FB_BLANK_UNBLANK);
+ if (ret)
+ return ret;
+
+ ret = dell_uart_get_brightness(dell_bl);
+ if (ret < 0)
+ return ret;
+
+ props.type = BACKLIGHT_PLATFORM;
+ props.brightness = ret;
+ props.max_brightness = DELL_BL_MAX_BRIGHTNESS;
+ props.power = dell_bl->power;
+
+ dell_bl->bl = devm_backlight_device_register(dev, "dell_uart_backlight",
+ dev, dell_bl,
+ &dell_uart_backlight_ops,
+ &props);
+ return PTR_ERR_OR_ZERO(dell_bl->bl);
+}
+
+struct serdev_device_driver dell_uart_bl_serdev_driver = {
+ .probe = dell_uart_bl_serdev_probe,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ },
+};
+
+static int dell_uart_bl_pdev_probe(struct platform_device *pdev)
+{
+ struct serdev_device *serdev;
+ struct device *ctrl_dev;
+ int ret;
+
+ ctrl_dev = get_serdev_controller("DELL0501", NULL, 0, "serial0");
+ if (IS_ERR(ctrl_dev))
+ return PTR_ERR(ctrl_dev);
+
+ serdev = serdev_device_alloc(to_serdev_controller(ctrl_dev));
+ put_device(ctrl_dev);
+ if (!serdev)
+ return -ENOMEM;
+
+ ret = serdev_device_add(serdev);
+ if (ret) {
+ dev_err(&pdev->dev, "error %d adding serdev\n", ret);
+ serdev_device_put(serdev);
+ return ret;
+ }
+
+ ret = serdev_device_driver_register(&dell_uart_bl_serdev_driver);
+ if (ret)
+ goto err_remove_serdev;
+
+ /*
+ * serdev device <-> driver matching relies on OF or ACPI matches and
+ * neither is available here, manually bind the driver.
+ */
+ ret = device_driver_attach(&dell_uart_bl_serdev_driver.driver, &serdev->dev);
+ if (ret)
+ goto err_unregister_serdev_driver;
+
+ /* So that dell_uart_bl_pdev_remove() can remove the serdev */
+ platform_set_drvdata(pdev, serdev);
+ return 0;
+
+err_unregister_serdev_driver:
+ serdev_device_driver_unregister(&dell_uart_bl_serdev_driver);
+err_remove_serdev:
+ serdev_device_remove(serdev);
+ return ret;
+}
+
+static void dell_uart_bl_pdev_remove(struct platform_device *pdev)
+{
+ struct serdev_device *serdev = platform_get_drvdata(pdev);
+
+ serdev_device_driver_unregister(&dell_uart_bl_serdev_driver);
+ serdev_device_remove(serdev);
+}
+
+static struct platform_driver dell_uart_bl_pdev_driver = {
+ .probe = dell_uart_bl_pdev_probe,
+ .remove_new = dell_uart_bl_pdev_remove,
+ .driver = {
+ .name = "dell-uart-backlight",
+ },
+};
+module_platform_driver(dell_uart_bl_pdev_driver);
+
+MODULE_ALIAS("platform:dell-uart-backlight");
+MODULE_DESCRIPTION("Dell AIO Serial Backlight driver");
+MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/dell/dell-wmi-ddv.c b/drivers/platform/x86/dell/dell-wmi-ddv.c
index db1e9240dd..0b2299f7a2 100644
--- a/drivers/platform/x86/dell/dell-wmi-ddv.c
+++ b/drivers/platform/x86/dell/dell-wmi-ddv.c
@@ -882,6 +882,7 @@ static struct wmi_driver dell_wmi_ddv_driver = {
},
.id_table = dell_wmi_ddv_id_table,
.probe = dell_wmi_ddv_probe,
+ .no_singleton = true,
};
module_wmi_driver(dell_wmi_ddv_driver);
diff --git a/drivers/platform/x86/dell/dell-wmi-privacy.c b/drivers/platform/x86/dell/dell-wmi-privacy.c
index c517bd45dd..4b65e1655d 100644
--- a/drivers/platform/x86/dell/dell-wmi-privacy.c
+++ b/drivers/platform/x86/dell/dell-wmi-privacy.c
@@ -288,7 +288,6 @@ static int dell_privacy_leds_setup(struct device *dev)
priv->cdev.max_brightness = 1;
priv->cdev.brightness_set_blocking = dell_privacy_micmute_led_set;
priv->cdev.default_trigger = "audio-micmute";
- priv->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
return devm_led_classdev_register(dev, &priv->cdev);
}
@@ -298,10 +297,6 @@ static int dell_privacy_wmi_probe(struct wmi_device *wdev, const void *context)
struct key_entry *keymap;
int ret, i, j;
- ret = wmi_has_guid(DELL_PRIVACY_GUID);
- if (!ret)
- pr_debug("Unable to detect available Dell privacy devices!\n");
-
priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c
index b929b4f824..9def7983d7 100644
--- a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c
+++ b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c
@@ -25,7 +25,7 @@ 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 struct class *fw_attr_class;
+static const struct class *fw_attr_class;
/**
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c
index ff1b70269c..447364bed2 100644
--- a/drivers/platform/x86/eeepc-laptop.c
+++ b/drivers/platform/x86/eeepc-laptop.c
@@ -1463,7 +1463,6 @@ MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);
static struct acpi_driver eeepc_acpi_driver = {
.name = EEEPC_LAPTOP_NAME,
.class = EEEPC_ACPI_CLASS,
- .owner = THIS_MODULE,
.ids = eeepc_device_ids,
.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
.ops = {
diff --git a/drivers/platform/x86/firmware_attributes_class.c b/drivers/platform/x86/firmware_attributes_class.c
index fafe8eaf6e..182a07d8ae 100644
--- a/drivers/platform/x86/firmware_attributes_class.c
+++ b/drivers/platform/x86/firmware_attributes_class.c
@@ -10,11 +10,11 @@
static DEFINE_MUTEX(fw_attr_lock);
static int fw_attr_inuse;
-static struct class firmware_attributes_class = {
+static const struct class firmware_attributes_class = {
.name = "firmware-attributes",
};
-int fw_attributes_class_get(struct class **fw_attr_class)
+int fw_attributes_class_get(const struct class **fw_attr_class)
{
int err;
@@ -49,4 +49,5 @@ int fw_attributes_class_put(void)
EXPORT_SYMBOL_GPL(fw_attributes_class_put);
MODULE_AUTHOR("Mark Pearson <markpearson@lenovo.com>");
+MODULE_DESCRIPTION("Firmware attributes class helper module");
MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/firmware_attributes_class.h b/drivers/platform/x86/firmware_attributes_class.h
index 486485cb1f..363c75f1ac 100644
--- a/drivers/platform/x86/firmware_attributes_class.h
+++ b/drivers/platform/x86/firmware_attributes_class.h
@@ -5,7 +5,7 @@
#ifndef FW_ATTR_CLASS_H
#define FW_ATTR_CLASS_H
-int fw_attributes_class_get(struct class **fw_attr_class);
+int fw_attributes_class_get(const struct class **fw_attr_class);
int fw_attributes_class_put(void);
#endif /* FW_ATTR_CLASS_H */
diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c
index 085e044e88..968fc91bd5 100644
--- a/drivers/platform/x86/fujitsu-laptop.c
+++ b/drivers/platform/x86/fujitsu-laptop.c
@@ -49,6 +49,8 @@
#include <linux/kfifo.h>
#include <linux/leds.h>
#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <acpi/battery.h>
#include <acpi/video.h>
#define FUJITSU_DRIVER_VERSION "0.6.0"
@@ -97,6 +99,10 @@
#define BACKLIGHT_OFF (BIT(0) | BIT(1))
#define BACKLIGHT_ON 0
+/* FUNC interface - battery control interface */
+#define FUNC_S006_METHOD 0x1006
+#define CHARGE_CONTROL_RW 0x21
+
/* Scancodes read from the GIRB register */
#define KEY1_CODE 0x410
#define KEY2_CODE 0x411
@@ -132,6 +138,7 @@ struct fujitsu_laptop {
spinlock_t fifo_lock;
int flags_supported;
int flags_state;
+ bool charge_control_supported;
};
static struct acpi_device *fext;
@@ -164,6 +171,110 @@ static int call_fext_func(struct acpi_device *device,
return value;
}
+/* Battery charge control code */
+static ssize_t charge_control_end_threshold_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int cc_end_value, s006_cc_return;
+ int value, ret;
+
+ ret = kstrtouint(buf, 10, &value);
+ if (ret)
+ return ret;
+
+ if (value < 50 || value > 100)
+ return -EINVAL;
+
+ cc_end_value = value * 0x100 + 0x20;
+ s006_cc_return = call_fext_func(fext, FUNC_S006_METHOD,
+ CHARGE_CONTROL_RW, cc_end_value, 0x0);
+ if (s006_cc_return < 0)
+ return s006_cc_return;
+ /*
+ * The S006 0x21 method returns 0x00 in case the provided value
+ * is invalid.
+ */
+ if (s006_cc_return == 0x00)
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t charge_control_end_threshold_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int status;
+
+ status = call_fext_func(fext, FUNC_S006_METHOD,
+ CHARGE_CONTROL_RW, 0x21, 0x0);
+ if (status < 0)
+ return status;
+
+ return sysfs_emit(buf, "%d\n", status);
+}
+
+static DEVICE_ATTR_RW(charge_control_end_threshold);
+
+/* ACPI battery hook */
+static int fujitsu_battery_add_hook(struct power_supply *battery,
+ struct acpi_battery_hook *hook)
+{
+ return device_create_file(&battery->dev,
+ &dev_attr_charge_control_end_threshold);
+}
+
+static int fujitsu_battery_remove_hook(struct power_supply *battery,
+ struct acpi_battery_hook *hook)
+{
+ device_remove_file(&battery->dev,
+ &dev_attr_charge_control_end_threshold);
+
+ return 0;
+}
+
+static struct acpi_battery_hook battery_hook = {
+ .add_battery = fujitsu_battery_add_hook,
+ .remove_battery = fujitsu_battery_remove_hook,
+ .name = "Fujitsu Battery Extension",
+};
+
+/*
+ * These functions are intended to be called from acpi_fujitsu_laptop_add and
+ * acpi_fujitsu_laptop_remove.
+ */
+static int fujitsu_battery_charge_control_add(struct acpi_device *device)
+{
+ struct fujitsu_laptop *priv = acpi_driver_data(device);
+ int s006_cc_return;
+
+ priv->charge_control_supported = false;
+ /*
+ * Check if the S006 0x21 method exists by trying to get the current
+ * battery charge limit.
+ */
+ s006_cc_return = call_fext_func(fext, FUNC_S006_METHOD,
+ CHARGE_CONTROL_RW, 0x21, 0x0);
+ if (s006_cc_return < 0)
+ return s006_cc_return;
+ if (s006_cc_return == UNSUPPORTED_CMD)
+ return -ENODEV;
+
+ priv->charge_control_supported = true;
+ battery_hook_register(&battery_hook);
+
+ return 0;
+}
+
+static void fujitsu_battery_charge_control_remove(struct acpi_device *device)
+{
+ struct fujitsu_laptop *priv = acpi_driver_data(device);
+
+ if (priv->charge_control_supported)
+ battery_hook_unregister(&battery_hook);
+}
+
/* Hardware access for LCD brightness control */
static int set_lcd_level(struct acpi_device *device, int level)
@@ -275,11 +386,11 @@ static ssize_t lid_show(struct device *dev, struct device_attribute *attr,
struct fujitsu_laptop *priv = dev_get_drvdata(dev);
if (!(priv->flags_supported & FLAG_LID))
- return sprintf(buf, "unknown\n");
+ return sysfs_emit(buf, "unknown\n");
if (priv->flags_state & FLAG_LID)
- return sprintf(buf, "open\n");
+ return sysfs_emit(buf, "open\n");
else
- return sprintf(buf, "closed\n");
+ return sysfs_emit(buf, "closed\n");
}
static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
@@ -288,11 +399,11 @@ static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
struct fujitsu_laptop *priv = dev_get_drvdata(dev);
if (!(priv->flags_supported & FLAG_DOCK))
- return sprintf(buf, "unknown\n");
+ return sysfs_emit(buf, "unknown\n");
if (priv->flags_state & FLAG_DOCK)
- return sprintf(buf, "docked\n");
+ return sysfs_emit(buf, "docked\n");
else
- return sprintf(buf, "undocked\n");
+ return sysfs_emit(buf, "undocked\n");
}
static ssize_t radios_show(struct device *dev, struct device_attribute *attr,
@@ -301,11 +412,11 @@ static ssize_t radios_show(struct device *dev, struct device_attribute *attr,
struct fujitsu_laptop *priv = dev_get_drvdata(dev);
if (!(priv->flags_supported & FLAG_RFKILL))
- return sprintf(buf, "unknown\n");
+ return sysfs_emit(buf, "unknown\n");
if (priv->flags_state & FLAG_RFKILL)
- return sprintf(buf, "on\n");
+ return sysfs_emit(buf, "on\n");
else
- return sprintf(buf, "killed\n");
+ return sysfs_emit(buf, "killed\n");
}
static DEVICE_ATTR_RO(lid);
@@ -839,6 +950,10 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device)
if (ret)
goto err_free_fifo;
+ ret = fujitsu_battery_charge_control_add(device);
+ if (ret < 0)
+ pr_warn("Unable to register battery charge control: %d\n", ret);
+
return 0;
err_free_fifo:
@@ -851,6 +966,8 @@ static void acpi_fujitsu_laptop_remove(struct acpi_device *device)
{
struct fujitsu_laptop *priv = acpi_driver_data(device);
+ fujitsu_battery_charge_control_remove(device);
+
fujitsu_laptop_platform_remove(device);
kfifo_free(&priv->fifo);
diff --git a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
index 8c9f4f3227..2dc5015215 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
+++ b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
@@ -24,7 +24,7 @@ struct bioscfg_priv bioscfg_drv = {
.mutex = __MUTEX_INITIALIZER(bioscfg_drv.mutex),
};
-static struct class *fw_attr_class;
+static const struct class *fw_attr_class;
ssize_t display_name_language_code_show(struct kobject *kobj,
struct kobj_attribute *attr,
diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c
index e536604225..5fa5530238 100644
--- a/drivers/platform/x86/hp/hp-wmi.c
+++ b/drivers/platform/x86/hp/hp-wmi.c
@@ -29,15 +29,19 @@
#include <linux/dmi.h>
MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>");
-MODULE_DESCRIPTION("HP laptop WMI hotkeys driver");
+MODULE_DESCRIPTION("HP laptop WMI driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C");
-MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
+MODULE_ALIAS("wmi:5FB7F034-2C63-45E9-BE91-3D44E2C707E4");
#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C"
-#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4"
+#define HPWMI_BIOS_GUID "5FB7F034-2C63-45E9-BE91-3D44E2C707E4"
+
+#define HP_OMEN_EC_THERMAL_PROFILE_FLAGS_OFFSET 0x62
+#define HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET 0x63
#define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95
+
#define zero_if_sup(tmp) (zero_insize_support?0:sizeof(tmp)) // use when zero insize is required
/* DMI board names of devices that should use the omen specific path for
@@ -55,17 +59,25 @@ static const char * const omen_thermal_profile_boards[] = {
"874A", "8603", "8604", "8748", "886B", "886C", "878A", "878B", "878C",
"88C8", "88CB", "8786", "8787", "8788", "88D1", "88D2", "88F4", "88FD",
"88F5", "88F6", "88F7", "88FE", "88FF", "8900", "8901", "8902", "8912",
- "8917", "8918", "8949", "894A", "89EB"
+ "8917", "8918", "8949", "894A", "89EB", "8BAD", "8A42"
};
/* DMI Board names of Omen laptops that are specifically set to be thermal
* profile version 0 by the Omen Command Center app, regardless of what
* the get system design information WMI call returns
*/
-static const char *const omen_thermal_profile_force_v0_boards[] = {
+static const char * const omen_thermal_profile_force_v0_boards[] = {
"8607", "8746", "8747", "8749", "874A", "8748"
};
+/* DMI board names of Omen laptops that have a thermal profile timer which will
+ * cause the embedded controller to set the thermal profile back to
+ * "balanced" when reaching zero.
+ */
+static const char * const omen_timed_thermal_profile_boards[] = {
+ "8BAD", "8A42"
+};
+
/* DMI Board names of Victus laptops */
static const char * const victus_thermal_profile_boards[] = {
"8A25"
@@ -182,6 +194,12 @@ enum hp_thermal_profile_omen_v1 {
HP_OMEN_V1_THERMAL_PROFILE_COOL = 0x50,
};
+enum hp_thermal_profile_omen_flags {
+ HP_OMEN_EC_FLAGS_TURBO = 0x04,
+ HP_OMEN_EC_FLAGS_NOTIMER = 0x02,
+ HP_OMEN_EC_FLAGS_JUSTSET = 0x01,
+};
+
enum hp_thermal_profile_victus {
HP_VICTUS_THERMAL_PROFILE_DEFAULT = 0x00,
HP_VICTUS_THERMAL_PROFILE_PERFORMANCE = 0x01,
@@ -449,7 +467,11 @@ static int hp_wmi_get_tablet_mode(void)
static int omen_thermal_profile_set(int mode)
{
- char buffer[2] = {0, mode};
+ /* The Omen Control Center actively sets the first byte of the buffer to
+ * 255, so let's mimic this behaviour to be as close as possible to
+ * the original software.
+ */
+ char buffer[2] = {-1, mode};
int ret;
ret = hp_wmi_perform_query(HPWMI_SET_PERFORMANCE_MODE, HPWMI_GM,
@@ -659,7 +681,7 @@ static ssize_t display_show(struct device *dev, struct device_attribute *attr,
if (value < 0)
return value;
- return sprintf(buf, "%d\n", value);
+ return sysfs_emit(buf, "%d\n", value);
}
static ssize_t hddtemp_show(struct device *dev, struct device_attribute *attr,
@@ -669,7 +691,7 @@ static ssize_t hddtemp_show(struct device *dev, struct device_attribute *attr,
if (value < 0)
return value;
- return sprintf(buf, "%d\n", value);
+ return sysfs_emit(buf, "%d\n", value);
}
static ssize_t als_show(struct device *dev, struct device_attribute *attr,
@@ -679,7 +701,7 @@ static ssize_t als_show(struct device *dev, struct device_attribute *attr,
if (value < 0)
return value;
- return sprintf(buf, "%d\n", value);
+ return sysfs_emit(buf, "%d\n", value);
}
static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
@@ -689,7 +711,7 @@ static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
if (value < 0)
return value;
- return sprintf(buf, "%d\n", value);
+ return sysfs_emit(buf, "%d\n", value);
}
static ssize_t tablet_show(struct device *dev, struct device_attribute *attr,
@@ -699,7 +721,7 @@ static ssize_t tablet_show(struct device *dev, struct device_attribute *attr,
if (value < 0)
return value;
- return sprintf(buf, "%d\n", value);
+ return sysfs_emit(buf, "%d\n", value);
}
static ssize_t postcode_show(struct device *dev, struct device_attribute *attr,
@@ -710,7 +732,7 @@ static ssize_t postcode_show(struct device *dev, struct device_attribute *attr,
if (value < 0)
return value;
- return sprintf(buf, "0x%x\n", value);
+ return sysfs_emit(buf, "0x%x\n", value);
}
static ssize_t als_store(struct device *dev, struct device_attribute *attr,
@@ -1201,10 +1223,33 @@ static int platform_profile_omen_get(struct platform_profile_handler *pprof,
return 0;
}
+static bool has_omen_thermal_profile_ec_timer(void)
+{
+ const char *board_name = dmi_get_system_info(DMI_BOARD_NAME);
+
+ if (!board_name)
+ return false;
+
+ return match_string(omen_timed_thermal_profile_boards,
+ ARRAY_SIZE(omen_timed_thermal_profile_boards),
+ board_name) >= 0;
+}
+
+inline int omen_thermal_profile_ec_flags_set(enum hp_thermal_profile_omen_flags flags)
+{
+ return ec_write(HP_OMEN_EC_THERMAL_PROFILE_FLAGS_OFFSET, flags);
+}
+
+inline int omen_thermal_profile_ec_timer_set(u8 value)
+{
+ return ec_write(HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET, value);
+}
+
static int platform_profile_omen_set(struct platform_profile_handler *pprof,
enum platform_profile_option profile)
{
int err, tp, tp_version;
+ enum hp_thermal_profile_omen_flags flags = 0;
tp_version = omen_get_thermal_policy_version();
@@ -1238,6 +1283,20 @@ static int platform_profile_omen_set(struct platform_profile_handler *pprof,
if (err < 0)
return err;
+ if (has_omen_thermal_profile_ec_timer()) {
+ err = omen_thermal_profile_ec_timer_set(0);
+ if (err < 0)
+ return err;
+
+ if (profile == PLATFORM_PROFILE_PERFORMANCE)
+ flags = HP_OMEN_EC_FLAGS_NOTIMER |
+ HP_OMEN_EC_FLAGS_TURBO;
+
+ err = omen_thermal_profile_ec_flags_set(flags);
+ if (err < 0)
+ return err;
+ }
+
return 0;
}
diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c
index 0ef1c46b61..09d476dd83 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -310,7 +310,6 @@ static void huawei_wmi_leds_setup(struct device *dev)
huawei->cdev.max_brightness = 1;
huawei->cdev.brightness_set_blocking = &huawei_wmi_micmute_led_set;
huawei->cdev.default_trigger = "audio-micmute";
- huawei->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
huawei->cdev.dev = dev;
huawei->cdev.flags = LED_CORE_SUSPENDRESUME;
@@ -380,7 +379,7 @@ static ssize_t charge_control_start_threshold_show(struct device *dev,
if (err)
return err;
- return sprintf(buf, "%d\n", start);
+ return sysfs_emit(buf, "%d\n", start);
}
static ssize_t charge_control_end_threshold_show(struct device *dev,
@@ -393,7 +392,7 @@ static ssize_t charge_control_end_threshold_show(struct device *dev,
if (err)
return err;
- return sprintf(buf, "%d\n", end);
+ return sysfs_emit(buf, "%d\n", end);
}
static ssize_t charge_control_thresholds_show(struct device *dev,
@@ -406,7 +405,7 @@ static ssize_t charge_control_thresholds_show(struct device *dev,
if (err)
return err;
- return sprintf(buf, "%d %d\n", start, end);
+ return sysfs_emit(buf, "%d %d\n", start, end);
}
static ssize_t charge_control_start_threshold_store(struct device *dev,
@@ -563,7 +562,7 @@ static ssize_t fn_lock_state_show(struct device *dev,
if (err)
return err;
- return sprintf(buf, "%d\n", on);
+ return sysfs_emit(buf, "%d\n", on);
}
static ssize_t fn_lock_state_store(struct device *dev,
diff --git a/drivers/platform/x86/ibm_rtl.c b/drivers/platform/x86/ibm_rtl.c
index 2ab7d9ac54..231b379098 100644
--- a/drivers/platform/x86/ibm_rtl.c
+++ b/drivers/platform/x86/ibm_rtl.c
@@ -29,6 +29,7 @@ static bool debug;
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Show debug output");
+MODULE_DESCRIPTION("IBM Premium Real Time Mode (PRTM) driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Keith Mannthey <kmmanth@us.ibm.com>");
MODULE_AUTHOR("Vernon Mauery <vernux@us.ibm.com>");
@@ -179,7 +180,7 @@ static ssize_t rtl_set_state(struct device *dev,
return ret;
}
-static struct bus_type rtl_subsys = {
+static const struct bus_type rtl_subsys = {
.name = "ibm_rtl",
.dev_name = "ibm_rtl",
};
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
index 88eefccb6e..490815917a 100644
--- a/drivers/platform/x86/ideapad-laptop.c
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -125,6 +125,7 @@ struct ideapad_rfk_priv {
struct ideapad_private {
struct acpi_device *adev;
+ struct mutex vpc_mutex; /* protects the VPC calls */
struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
struct ideapad_rfk_priv rfk_priv[IDEAPAD_RFKILL_DEV_NUM];
struct platform_device *platform_device;
@@ -145,6 +146,7 @@ struct ideapad_private {
bool touchpad_ctrl_via_ec : 1;
bool ctrl_ps2_aux_port : 1;
bool usb_charging : 1;
+ bool ymc_ec_trigger : 1;
} features;
struct {
bool initialized;
@@ -152,6 +154,11 @@ struct ideapad_private {
struct led_classdev led;
unsigned int last_brightness;
} kbd_bl;
+ struct {
+ bool initialized;
+ struct led_classdev led;
+ unsigned int last_brightness;
+ } fn_lock;
};
static bool no_bt_rfkill;
@@ -188,6 +195,12 @@ MODULE_PARM_DESC(touchpad_ctrl_via_ec,
"Enable registering a 'touchpad' sysfs-attribute which can be used to manually "
"tell the EC to enable/disable the touchpad. This may not work on all models.");
+static bool ymc_ec_trigger __read_mostly;
+module_param(ymc_ec_trigger, bool, 0444);
+MODULE_PARM_DESC(ymc_ec_trigger,
+ "Enable EC triggering work-around to force emitting tablet mode events. "
+ "If you need this please report this to: platform-driver-x86@vger.kernel.org");
+
/*
* shared data
*/
@@ -292,6 +305,8 @@ static int debugfs_status_show(struct seq_file *s, void *data)
struct ideapad_private *priv = s->private;
unsigned long value;
+ guard(mutex)(&priv->vpc_mutex);
+
if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &value))
seq_printf(s, "Backlight max: %lu\n", value);
if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL, &value))
@@ -410,7 +425,8 @@ static ssize_t camera_power_show(struct device *dev,
unsigned long result;
int err;
- err = read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &result);
+ scoped_guard(mutex, &priv->vpc_mutex)
+ err = read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &result);
if (err)
return err;
@@ -429,7 +445,8 @@ static ssize_t camera_power_store(struct device *dev,
if (err)
return err;
- err = write_ec_cmd(priv->adev->handle, VPCCMD_W_CAMERA, state);
+ scoped_guard(mutex, &priv->vpc_mutex)
+ err = write_ec_cmd(priv->adev->handle, VPCCMD_W_CAMERA, state);
if (err)
return err;
@@ -482,7 +499,8 @@ static ssize_t fan_mode_show(struct device *dev,
unsigned long result;
int err;
- err = read_ec_data(priv->adev->handle, VPCCMD_R_FAN, &result);
+ scoped_guard(mutex, &priv->vpc_mutex)
+ err = read_ec_data(priv->adev->handle, VPCCMD_R_FAN, &result);
if (err)
return err;
@@ -504,7 +522,8 @@ static ssize_t fan_mode_store(struct device *dev,
if (state > 4 || state == 3)
return -EINVAL;
- err = write_ec_cmd(priv->adev->handle, VPCCMD_W_FAN, state);
+ scoped_guard(mutex, &priv->vpc_mutex)
+ err = write_ec_cmd(priv->adev->handle, VPCCMD_W_FAN, state);
if (err)
return err;
@@ -513,11 +532,8 @@ static ssize_t fan_mode_store(struct device *dev,
static DEVICE_ATTR_RW(fan_mode);
-static ssize_t fn_lock_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static int ideapad_fn_lock_get(struct ideapad_private *priv)
{
- struct ideapad_private *priv = dev_get_drvdata(dev);
unsigned long hals;
int err;
@@ -525,7 +541,40 @@ static ssize_t fn_lock_show(struct device *dev,
if (err)
return err;
- return sysfs_emit(buf, "%d\n", !!test_bit(HALS_FNLOCK_STATE_BIT, &hals));
+ return !!test_bit(HALS_FNLOCK_STATE_BIT, &hals);
+}
+
+static int ideapad_fn_lock_set(struct ideapad_private *priv, bool state)
+{
+ return exec_sals(priv->adev->handle,
+ state ? SALS_FNLOCK_ON : SALS_FNLOCK_OFF);
+}
+
+static void ideapad_fn_lock_led_notify(struct ideapad_private *priv, int brightness)
+{
+ if (!priv->fn_lock.initialized)
+ return;
+
+ if (brightness == priv->fn_lock.last_brightness)
+ return;
+
+ priv->fn_lock.last_brightness = brightness;
+
+ led_classdev_notify_brightness_hw_changed(&priv->fn_lock.led, brightness);
+}
+
+static ssize_t fn_lock_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ideapad_private *priv = dev_get_drvdata(dev);
+ int brightness;
+
+ brightness = ideapad_fn_lock_get(priv);
+ if (brightness < 0)
+ return brightness;
+
+ return sysfs_emit(buf, "%d\n", brightness);
}
static ssize_t fn_lock_store(struct device *dev,
@@ -540,10 +589,12 @@ static ssize_t fn_lock_store(struct device *dev,
if (err)
return err;
- err = exec_sals(priv->adev->handle, state ? SALS_FNLOCK_ON : SALS_FNLOCK_OFF);
+ err = ideapad_fn_lock_set(priv, state);
if (err)
return err;
+ ideapad_fn_lock_led_notify(priv, state);
+
return count;
}
@@ -557,7 +608,8 @@ static ssize_t touchpad_show(struct device *dev,
unsigned long result;
int err;
- err = read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &result);
+ scoped_guard(mutex, &priv->vpc_mutex)
+ err = read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &result);
if (err)
return err;
@@ -578,7 +630,8 @@ static ssize_t touchpad_store(struct device *dev,
if (err)
return err;
- err = write_ec_cmd(priv->adev->handle, VPCCMD_W_TOUCHPAD, state);
+ scoped_guard(mutex, &priv->vpc_mutex)
+ err = write_ec_cmd(priv->adev->handle, VPCCMD_W_TOUCHPAD, state);
if (err)
return err;
@@ -975,6 +1028,8 @@ static int ideapad_rfk_set(void *data, bool blocked)
struct ideapad_rfk_priv *priv = data;
int opcode = ideapad_rfk_data[priv->dev].opcode;
+ guard(mutex)(&priv->priv->vpc_mutex);
+
return write_ec_cmd(priv->priv->adev->handle, opcode, !blocked);
}
@@ -988,6 +1043,8 @@ static void ideapad_sync_rfk_state(struct ideapad_private *priv)
int i;
if (priv->features.hw_rfkill_switch) {
+ guard(mutex)(&priv->vpc_mutex);
+
if (read_ec_data(priv->adev->handle, VPCCMD_R_RF, &hw_blocked))
return;
hw_blocked = !hw_blocked;
@@ -1091,6 +1148,8 @@ static const struct key_entry ideapad_keymap[] = {
{ KE_KEY, 0x07 | IDEAPAD_WMI_KEY, { KEY_HELP } },
{ KE_KEY, 0x0e | IDEAPAD_WMI_KEY, { KEY_PICKUP_PHONE } },
{ KE_KEY, 0x0f | IDEAPAD_WMI_KEY, { KEY_HANGUP_PHONE } },
+ /* Refresh Rate Toggle (Fn+R) */
+ { KE_KEY, 0x10 | IDEAPAD_WMI_KEY, { KEY_REFRESH_RATE_TOGGLE } },
/* Dark mode toggle */
{ KE_KEY, 0x13 | IDEAPAD_WMI_KEY, { KEY_PROG1 } },
/* Sound profile switch */
@@ -1100,7 +1159,7 @@ static const struct key_entry ideapad_keymap[] = {
/* Lenovo Support */
{ KE_KEY, 0x27 | IDEAPAD_WMI_KEY, { KEY_HELP } },
/* Refresh Rate Toggle */
- { KE_KEY, 0x0a | IDEAPAD_WMI_KEY, { KEY_DISPLAYTOGGLE } },
+ { KE_KEY, 0x0a | IDEAPAD_WMI_KEY, { KEY_REFRESH_RATE_TOGGLE } },
{ KE_END },
};
@@ -1159,8 +1218,9 @@ static void ideapad_input_novokey(struct ideapad_private *priv)
{
unsigned long long_pressed;
- if (read_ec_data(priv->adev->handle, VPCCMD_R_NOVO, &long_pressed))
- return;
+ scoped_guard(mutex, &priv->vpc_mutex)
+ if (read_ec_data(priv->adev->handle, VPCCMD_R_NOVO, &long_pressed))
+ return;
if (long_pressed)
ideapad_input_report(priv, 17);
@@ -1172,15 +1232,19 @@ static void ideapad_check_special_buttons(struct ideapad_private *priv)
{
unsigned long bit, value;
- if (read_ec_data(priv->adev->handle, VPCCMD_R_SPECIAL_BUTTONS, &value))
- return;
+ scoped_guard(mutex, &priv->vpc_mutex)
+ if (read_ec_data(priv->adev->handle, VPCCMD_R_SPECIAL_BUTTONS, &value))
+ return;
for_each_set_bit (bit, &value, 16) {
switch (bit) {
case 6: /* Z570 */
case 0: /* Z580 */
- /* Thermal Management button */
- ideapad_input_report(priv, 65);
+ /* Thermal Management / Performance Mode button */
+ if (priv->dytc)
+ platform_profile_cycle();
+ else
+ ideapad_input_report(priv, 65);
break;
case 1:
/* OneKey Theater button */
@@ -1203,6 +1267,8 @@ static int ideapad_backlight_get_brightness(struct backlight_device *blightdev)
unsigned long now;
int err;
+ guard(mutex)(&priv->vpc_mutex);
+
err = read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now);
if (err)
return err;
@@ -1215,6 +1281,8 @@ static int ideapad_backlight_update_status(struct backlight_device *blightdev)
struct ideapad_private *priv = bl_get_data(blightdev);
int err;
+ guard(mutex)(&priv->vpc_mutex);
+
err = write_ec_cmd(priv->adev->handle, VPCCMD_W_BL,
blightdev->props.brightness);
if (err)
@@ -1292,6 +1360,8 @@ static void ideapad_backlight_notify_power(struct ideapad_private *priv)
if (!blightdev)
return;
+ guard(mutex)(&priv->vpc_mutex);
+
if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))
return;
@@ -1304,7 +1374,8 @@ static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
/* if we control brightness via acpi video driver */
if (!priv->blightdev)
- read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now);
+ scoped_guard(mutex, &priv->vpc_mutex)
+ read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now);
else
backlight_force_update(priv->blightdev, BACKLIGHT_UPDATE_HOTKEY);
}
@@ -1461,6 +1532,65 @@ static void ideapad_kbd_bl_exit(struct ideapad_private *priv)
}
/*
+ * FnLock LED
+ */
+static enum led_brightness ideapad_fn_lock_led_cdev_get(struct led_classdev *led_cdev)
+{
+ struct ideapad_private *priv = container_of(led_cdev, struct ideapad_private, fn_lock.led);
+
+ return ideapad_fn_lock_get(priv);
+}
+
+static int ideapad_fn_lock_led_cdev_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct ideapad_private *priv = container_of(led_cdev, struct ideapad_private, fn_lock.led);
+
+ return ideapad_fn_lock_set(priv, brightness);
+}
+
+static int ideapad_fn_lock_led_init(struct ideapad_private *priv)
+{
+ int brightness, err;
+
+ if (!priv->features.fn_lock)
+ return -ENODEV;
+
+ if (WARN_ON(priv->fn_lock.initialized))
+ return -EEXIST;
+
+ priv->fn_lock.led.max_brightness = 1;
+
+ brightness = ideapad_fn_lock_get(priv);
+ if (brightness < 0)
+ return brightness;
+
+ priv->fn_lock.last_brightness = brightness;
+ priv->fn_lock.led.name = "platform::" LED_FUNCTION_FNLOCK;
+ priv->fn_lock.led.brightness_get = ideapad_fn_lock_led_cdev_get;
+ priv->fn_lock.led.brightness_set_blocking = ideapad_fn_lock_led_cdev_set;
+ priv->fn_lock.led.flags = LED_BRIGHT_HW_CHANGED;
+
+ err = led_classdev_register(&priv->platform_device->dev, &priv->fn_lock.led);
+ if (err)
+ return err;
+
+ priv->fn_lock.initialized = true;
+
+ return 0;
+}
+
+static void ideapad_fn_lock_led_exit(struct ideapad_private *priv)
+{
+ if (!priv->fn_lock.initialized)
+ return;
+
+ priv->fn_lock.initialized = false;
+
+ led_classdev_unregister(&priv->fn_lock.led);
+}
+
+/*
* module init/exit
*/
static void ideapad_sync_touchpad_state(struct ideapad_private *priv, bool send_events)
@@ -1470,7 +1600,8 @@ static void ideapad_sync_touchpad_state(struct ideapad_private *priv, bool send_
int ret;
/* Without reading from EC touchpad LED doesn't switch state */
- ret = read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value);
+ scoped_guard(mutex, &priv->vpc_mutex)
+ ret = read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value);
if (ret)
return;
@@ -1498,16 +1629,92 @@ static void ideapad_sync_touchpad_state(struct ideapad_private *priv, bool send_
priv->r_touchpad_val = value;
}
+static const struct dmi_system_id ymc_ec_trigger_quirk_dmi_table[] = {
+ {
+ /* Lenovo Yoga 7 14ARB7 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82QF"),
+ },
+ },
+ {
+ /* Lenovo Yoga 7 14ACN6 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82N7"),
+ },
+ },
+ { }
+};
+
+static void ideapad_laptop_trigger_ec(void)
+{
+ struct ideapad_private *priv;
+ int ret;
+
+ guard(mutex)(&ideapad_shared_mutex);
+
+ priv = ideapad_shared;
+ if (!priv)
+ return;
+
+ if (!priv->features.ymc_ec_trigger)
+ return;
+
+ scoped_guard(mutex, &priv->vpc_mutex)
+ ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_YMC, 1);
+ if (ret)
+ dev_warn(&priv->platform_device->dev, "Could not write YMC: %d\n", ret);
+}
+
+static int ideapad_laptop_nb_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ switch (action) {
+ case IDEAPAD_LAPTOP_YMC_EVENT:
+ ideapad_laptop_trigger_ec();
+ break;
+ }
+
+ return 0;
+}
+
+static struct notifier_block ideapad_laptop_notifier = {
+ .notifier_call = ideapad_laptop_nb_notify,
+};
+
+static BLOCKING_NOTIFIER_HEAD(ideapad_laptop_chain_head);
+
+int ideapad_laptop_register_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&ideapad_laptop_chain_head, nb);
+}
+EXPORT_SYMBOL_NS_GPL(ideapad_laptop_register_notifier, IDEAPAD_LAPTOP);
+
+int ideapad_laptop_unregister_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&ideapad_laptop_chain_head, nb);
+}
+EXPORT_SYMBOL_NS_GPL(ideapad_laptop_unregister_notifier, IDEAPAD_LAPTOP);
+
+void ideapad_laptop_call_notifier(unsigned long action, void *data)
+{
+ blocking_notifier_call_chain(&ideapad_laptop_chain_head, action, data);
+}
+EXPORT_SYMBOL_NS_GPL(ideapad_laptop_call_notifier, IDEAPAD_LAPTOP);
+
static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data)
{
struct ideapad_private *priv = data;
unsigned long vpc1, vpc2, bit;
- if (read_ec_data(handle, VPCCMD_R_VPC1, &vpc1))
- return;
+ scoped_guard(mutex, &priv->vpc_mutex) {
+ if (read_ec_data(handle, VPCCMD_R_VPC1, &vpc1))
+ return;
- if (read_ec_data(handle, VPCCMD_R_VPC2, &vpc2))
- return;
+ if (read_ec_data(handle, VPCCMD_R_VPC2, &vpc2))
+ return;
+ }
vpc1 = (vpc2 << 8) | vpc1;
@@ -1634,6 +1841,8 @@ static void ideapad_check_features(struct ideapad_private *priv)
priv->features.ctrl_ps2_aux_port =
ctrl_ps2_aux_port || dmi_check_system(ctrl_ps2_aux_port_list);
priv->features.touchpad_ctrl_via_ec = touchpad_ctrl_via_ec;
+ priv->features.ymc_ec_trigger =
+ ymc_ec_trigger || dmi_check_system(ymc_ec_trigger_quirk_dmi_table);
if (!read_ec_data(handle, VPCCMD_R_FAN, &val))
priv->features.fan_mode = true;
@@ -1707,7 +1916,6 @@ static void ideapad_wmi_notify(struct wmi_device *wdev, union acpi_object *data)
{
struct ideapad_wmi_private *wpriv = dev_get_drvdata(&wdev->dev);
struct ideapad_private *priv;
- unsigned long result;
mutex_lock(&ideapad_shared_mutex);
@@ -1720,11 +1928,13 @@ static void ideapad_wmi_notify(struct wmi_device *wdev, union acpi_object *data)
ideapad_input_report(priv, 128);
break;
case IDEAPAD_WMI_EVENT_FN_KEYS:
- if (priv->features.set_fn_lock_led &&
- !eval_hals(priv->adev->handle, &result)) {
- bool state = test_bit(HALS_FNLOCK_STATE_BIT, &result);
+ if (priv->features.set_fn_lock_led) {
+ int brightness = ideapad_fn_lock_get(priv);
- exec_sals(priv->adev->handle, state ? SALS_FNLOCK_ON : SALS_FNLOCK_OFF);
+ if (brightness >= 0) {
+ ideapad_fn_lock_set(priv, brightness);
+ ideapad_fn_lock_led_notify(priv, brightness);
+ }
}
if (data->type != ACPI_TYPE_INTEGER) {
@@ -1736,6 +1946,10 @@ 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);
+ /* 0x02 FnLock, 0x03 Esc */
+ if (data->integer.value == 0x02 || data->integer.value == 0x03)
+ ideapad_fn_lock_led_notify(priv, data->integer.value == 0x02);
+
ideapad_input_report(priv,
data->integer.value | IDEAPAD_WMI_KEY);
@@ -1809,6 +2023,10 @@ static int ideapad_acpi_add(struct platform_device *pdev)
priv->adev = adev;
priv->platform_device = pdev;
+ err = devm_mutex_init(&pdev->dev, &priv->vpc_mutex);
+ if (err)
+ return err;
+
ideapad_check_features(priv);
err = ideapad_sysfs_init(priv);
@@ -1829,6 +2047,14 @@ static int ideapad_acpi_add(struct platform_device *pdev)
dev_info(&pdev->dev, "Keyboard backlight control not available\n");
}
+ err = ideapad_fn_lock_led_init(priv);
+ if (err) {
+ if (err != -ENODEV)
+ dev_warn(&pdev->dev, "Could not set up FnLock LED: %d\n", err);
+ else
+ dev_info(&pdev->dev, "FnLock control not available\n");
+ }
+
/*
* On some models without a hw-switch (the yoga 2 13 at least)
* VPCCMD_W_RF must be explicitly set to 1 for the wifi to work.
@@ -1869,6 +2095,8 @@ static int ideapad_acpi_add(struct platform_device *pdev)
if (err)
goto shared_init_failed;
+ ideapad_laptop_register_notifier(&ideapad_laptop_notifier);
+
return 0;
shared_init_failed:
@@ -1885,6 +2113,7 @@ backlight_failed:
for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
ideapad_unregister_rfkill(priv, i);
+ ideapad_fn_lock_led_exit(priv);
ideapad_kbd_bl_exit(priv);
ideapad_input_exit(priv);
@@ -1900,6 +2129,8 @@ static void ideapad_acpi_remove(struct platform_device *pdev)
struct ideapad_private *priv = dev_get_drvdata(&pdev->dev);
int i;
+ ideapad_laptop_unregister_notifier(&ideapad_laptop_notifier);
+
ideapad_shared_exit(priv);
acpi_remove_notify_handler(priv->adev->handle,
@@ -1912,6 +2143,7 @@ static void ideapad_acpi_remove(struct platform_device *pdev)
for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
ideapad_unregister_rfkill(priv, i);
+ ideapad_fn_lock_led_exit(priv);
ideapad_kbd_bl_exit(priv);
ideapad_input_exit(priv);
ideapad_debugfs_exit(priv);
diff --git a/drivers/platform/x86/ideapad-laptop.h b/drivers/platform/x86/ideapad-laptop.h
index 4498a96de5..948cc61800 100644
--- a/drivers/platform/x86/ideapad-laptop.h
+++ b/drivers/platform/x86/ideapad-laptop.h
@@ -12,6 +12,15 @@
#include <linux/acpi.h>
#include <linux/jiffies.h>
#include <linux/errno.h>
+#include <linux/notifier.h>
+
+enum ideapad_laptop_notifier_actions {
+ IDEAPAD_LAPTOP_YMC_EVENT,
+};
+
+int ideapad_laptop_register_notifier(struct notifier_block *nb);
+int ideapad_laptop_unregister_notifier(struct notifier_block *nb);
+void ideapad_laptop_call_notifier(unsigned long action, void *data);
enum {
VPCCMD_R_VPC1 = 0x10,
diff --git a/drivers/platform/x86/inspur_platform_profile.c b/drivers/platform/x86/inspur_platform_profile.c
index 743705bddd..8440defa67 100644
--- a/drivers/platform/x86/inspur_platform_profile.c
+++ b/drivers/platform/x86/inspur_platform_profile.c
@@ -207,6 +207,7 @@ static struct wmi_driver inspur_wmi_driver = {
.id_table = inspur_wmi_id_table,
.probe = inspur_wmi_probe,
.remove = inspur_wmi_remove,
+ .no_singleton = true,
};
module_wmi_driver(inspur_wmi_driver);
diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c
index 9ffbdc988f..10cd65497c 100644
--- a/drivers/platform/x86/intel/hid.c
+++ b/drivers/platform/x86/intel/hid.c
@@ -38,6 +38,7 @@ MODULE_PARM_DESC(enable_sw_tablet_mode,
/* When NOT in tablet mode, VGBS returns with the flag 0x40 */
#define TABLET_MODE_FLAG BIT(6)
+MODULE_DESCRIPTION("Intel HID Event hotkey driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alex Hung");
@@ -49,6 +50,8 @@ static const struct acpi_device_id intel_hid_ids[] = {
{"INTC1076", 0},
{"INTC1077", 0},
{"INTC1078", 0},
+ {"INTC107B", 0},
+ {"INTC10CB", 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, intel_hid_ids);
diff --git a/drivers/platform/x86/intel/ifs/load.c b/drivers/platform/x86/intel/ifs/load.c
index 2cf3b4a881..39f19cb517 100644
--- a/drivers/platform/x86/intel/ifs/load.c
+++ b/drivers/platform/x86/intel/ifs/load.c
@@ -233,7 +233,9 @@ static int copy_hashes_authenticate_chunks_gen2(struct device *dev)
chunk_table[0] = starting_chunk_nr + i;
chunk_table[1] = linear_addr;
do {
+ local_irq_disable();
wrmsrl(MSR_AUTHENTICATE_AND_COPY_CHUNK, (u64)chunk_table);
+ local_irq_enable();
rdmsrl(MSR_CHUNKS_AUTHENTICATION_STATUS, chunk_status.data);
err_code = chunk_status.error_code;
} while (err_code == AUTH_INTERRUPTED_ERROR && --retry_count);
@@ -383,7 +385,7 @@ int ifs_load_firmware(struct device *dev)
unsigned int expected_size;
const struct firmware *fw;
char scan_path[64];
- int ret = -EINVAL;
+ int ret;
snprintf(scan_path, sizeof(scan_path), "intel/ifs_%d/%02x-%02x-%02x-%02x.scan",
test->test_num, boot_cpu_data.x86, boot_cpu_data.x86_model,
diff --git a/drivers/platform/x86/intel/ifs/runtest.c b/drivers/platform/x86/intel/ifs/runtest.c
index 13ecd55c66..be3d51ed0e 100644
--- a/drivers/platform/x86/intel/ifs/runtest.c
+++ b/drivers/platform/x86/intel/ifs/runtest.c
@@ -23,6 +23,12 @@
/* Max retries on the same chunk */
#define MAX_IFS_RETRIES 5
+struct run_params {
+ struct ifs_data *ifsd;
+ union ifs_scan *activate;
+ union ifs_status status;
+};
+
/*
* Number of TSC cycles that a logical CPU will wait for the other
* logical CPU on the core in the WRMSR(ACTIVATE_SCAN).
@@ -63,6 +69,19 @@ static const char * const scan_test_status[] = {
static void message_not_tested(struct device *dev, int cpu, union ifs_status status)
{
+ struct ifs_data *ifsd = ifs_get_data(dev);
+
+ /*
+ * control_error is set when the microcode runs into a problem
+ * loading the image from the reserved BIOS memory, or it has
+ * been corrupted. Reloading the image may fix this issue.
+ */
+ if (status.control_error) {
+ dev_warn(dev, "CPU(s) %*pbl: Scan controller error. Batch: %02x version: 0x%x\n",
+ cpumask_pr_args(cpu_smt_mask(cpu)), ifsd->cur_batch, ifsd->loaded_version);
+ return;
+ }
+
if (status.error_code < ARRAY_SIZE(scan_test_status)) {
dev_info(dev, "CPU(s) %*pbl: SCAN operation did not start. %s\n",
cpumask_pr_args(cpu_smt_mask(cpu)),
@@ -85,16 +104,6 @@ static void message_fail(struct device *dev, int cpu, union ifs_status status)
struct ifs_data *ifsd = ifs_get_data(dev);
/*
- * control_error is set when the microcode runs into a problem
- * loading the image from the reserved BIOS memory, or it has
- * been corrupted. Reloading the image may fix this issue.
- */
- if (status.control_error) {
- dev_err(dev, "CPU(s) %*pbl: could not execute from loaded scan image. Batch: %02x version: 0x%x\n",
- cpumask_pr_args(cpu_smt_mask(cpu)), ifsd->cur_batch, ifsd->loaded_version);
- }
-
- /*
* signature_error is set when the output from the scan chains does not
* match the expected signature. This might be a transient problem (e.g.
* due to a bit flip from an alpha particle or neutron). If the problem
@@ -134,19 +143,56 @@ static bool can_restart(union ifs_status status)
return false;
}
+#define SPINUNIT 100 /* 100 nsec */
+static atomic_t array_cpus_in;
+static atomic_t scan_cpus_in;
+
+/*
+ * Simplified cpu sibling rendezvous loop based on microcode loader __wait_for_cpus()
+ */
+static void wait_for_sibling_cpu(atomic_t *t, long long timeout)
+{
+ int cpu = smp_processor_id();
+ const struct cpumask *smt_mask = cpu_smt_mask(cpu);
+ int all_cpus = cpumask_weight(smt_mask);
+
+ atomic_inc(t);
+ while (atomic_read(t) < all_cpus) {
+ if (timeout < SPINUNIT)
+ return;
+ ndelay(SPINUNIT);
+ timeout -= SPINUNIT;
+ touch_nmi_watchdog();
+ }
+}
+
/*
* Execute the scan. Called "simultaneously" on all threads of a core
* at high priority using the stop_cpus mechanism.
*/
static int doscan(void *data)
{
- int cpu = smp_processor_id();
- u64 *msrs = data;
+ int cpu = smp_processor_id(), start, stop;
+ struct run_params *params = data;
+ union ifs_status status;
+ struct ifs_data *ifsd;
int first;
+ ifsd = params->ifsd;
+
+ if (ifsd->generation) {
+ start = params->activate->gen2.start;
+ stop = params->activate->gen2.stop;
+ } else {
+ start = params->activate->gen0.start;
+ stop = params->activate->gen0.stop;
+ }
+
/* Only the first logical CPU on a core reports result */
first = cpumask_first(cpu_smt_mask(cpu));
+ wait_for_sibling_cpu(&scan_cpus_in, NSEC_PER_SEC);
+
/*
* This WRMSR will wait for other HT threads to also write
* to this MSR (at most for activate.delay cycles). Then it
@@ -155,12 +201,14 @@ static int doscan(void *data)
* take up to 200 milliseconds (in the case where all chunks
* are processed in a single pass) before it retires.
*/
- wrmsrl(MSR_ACTIVATE_SCAN, msrs[0]);
+ wrmsrl(MSR_ACTIVATE_SCAN, params->activate->data);
+ rdmsrl(MSR_SCAN_STATUS, status.data);
- if (cpu == first) {
- /* Pass back the result of the scan */
- rdmsrl(MSR_SCAN_STATUS, msrs[1]);
- }
+ trace_ifs_status(ifsd->cur_batch, start, stop, status.data);
+
+ /* Pass back the result of the scan */
+ if (cpu == first)
+ params->status = status;
return 0;
}
@@ -173,13 +221,13 @@ static int doscan(void *data)
*/
static void ifs_test_core(int cpu, struct device *dev)
{
+ union ifs_status status = {};
union ifs_scan activate;
- union ifs_status status;
unsigned long timeout;
struct ifs_data *ifsd;
int to_start, to_stop;
int status_chunk;
- u64 msrvals[2];
+ struct run_params params;
int retries;
ifsd = ifs_get_data(dev);
@@ -190,6 +238,8 @@ static void ifs_test_core(int cpu, struct device *dev)
to_start = 0;
to_stop = ifsd->valid_chunks - 1;
+ params.ifsd = ifs_get_data(dev);
+
if (ifsd->generation) {
activate.gen2.start = to_start;
activate.gen2.stop = to_stop;
@@ -207,12 +257,11 @@ static void ifs_test_core(int cpu, struct device *dev)
break;
}
- msrvals[0] = activate.data;
- stop_core_cpuslocked(cpu, doscan, msrvals);
+ params.activate = &activate;
+ atomic_set(&scan_cpus_in, 0);
+ stop_core_cpuslocked(cpu, doscan, &params);
- status.data = msrvals[1];
-
- trace_ifs_status(cpu, to_start, to_stop, status.data);
+ status = params.status;
/* Some cases can be retried, give up for others */
if (!can_restart(status))
@@ -239,10 +288,10 @@ static void ifs_test_core(int cpu, struct device *dev)
/* Update status for this core */
ifsd->scan_details = status.data;
- if (status.control_error || status.signature_error) {
+ if (status.signature_error) {
ifsd->status = SCAN_TEST_FAIL;
message_fail(dev, cpu, status);
- } else if (status.error_code) {
+ } else if (status.control_error || status.error_code) {
ifsd->status = SCAN_NOT_TESTED;
message_not_tested(dev, cpu, status);
} else {
@@ -250,34 +299,14 @@ static void ifs_test_core(int cpu, struct device *dev)
}
}
-#define SPINUNIT 100 /* 100 nsec */
-static atomic_t array_cpus_out;
-
-/*
- * Simplified cpu sibling rendezvous loop based on microcode loader __wait_for_cpus()
- */
-static void wait_for_sibling_cpu(atomic_t *t, long long timeout)
-{
- int cpu = smp_processor_id();
- const struct cpumask *smt_mask = cpu_smt_mask(cpu);
- int all_cpus = cpumask_weight(smt_mask);
-
- atomic_inc(t);
- while (atomic_read(t) < all_cpus) {
- if (timeout < SPINUNIT)
- return;
- ndelay(SPINUNIT);
- timeout -= SPINUNIT;
- touch_nmi_watchdog();
- }
-}
-
static int do_array_test(void *data)
{
union ifs_array *command = data;
int cpu = smp_processor_id();
int first;
+ wait_for_sibling_cpu(&array_cpus_in, NSEC_PER_SEC);
+
/*
* Only one logical CPU on a core needs to trigger the Array test via MSR write.
*/
@@ -289,9 +318,6 @@ static int do_array_test(void *data)
rdmsrl(MSR_ARRAY_BIST, command->data);
}
- /* Tests complete faster if the sibling is spinning here */
- wait_for_sibling_cpu(&array_cpus_out, NSEC_PER_SEC);
-
return 0;
}
@@ -312,7 +338,7 @@ static void ifs_array_test_core(int cpu, struct device *dev)
timed_out = true;
break;
}
- atomic_set(&array_cpus_out, 0);
+ atomic_set(&array_cpus_in, 0);
stop_core_cpuslocked(cpu, do_array_test, &command);
if (command.ctrl_result)
diff --git a/drivers/platform/x86/intel/oaktrail.c b/drivers/platform/x86/intel/oaktrail.c
index fa720967e6..217630f40c 100644
--- a/drivers/platform/x86/intel/oaktrail.c
+++ b/drivers/platform/x86/intel/oaktrail.c
@@ -365,7 +365,7 @@ static void __exit oaktrail_cleanup(void)
module_init(oaktrail_init);
module_exit(oaktrail_cleanup);
-MODULE_AUTHOR("Yin Kangkai (kangkai.yin@intel.com)");
+MODULE_AUTHOR("Yin Kangkai <kangkai.yin@intel.com>");
MODULE_DESCRIPTION("Intel Oaktrail Platform ACPI Extras");
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/intel/pmc/arl.c b/drivers/platform/x86/intel/pmc/arl.c
index 34b4cd23bf..e10527c4e3 100644
--- a/drivers/platform/x86/intel/pmc/arl.c
+++ b/drivers/platform/x86/intel/pmc/arl.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/*
* This file contains platform specific structure definitions
- * and init function used by Meteor Lake PCH.
+ * and init function used by Arrow Lake PCH.
*
* Copyright (c) 2022, Intel Corporation.
* All Rights Reserved.
diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c
index 8f9c036809..2ad2f8753e 100644
--- a/drivers/platform/x86/intel/pmc/core.c
+++ b/drivers/platform/x86/intel/pmc/core.c
@@ -678,6 +678,41 @@ static int pmc_core_ltr_show(struct seq_file *s, void *unused)
}
DEFINE_SHOW_ATTRIBUTE(pmc_core_ltr);
+static int pmc_core_s0ix_blocker_show(struct seq_file *s, void *unused)
+{
+ struct pmc_dev *pmcdev = s->private;
+ unsigned int pmcidx;
+
+ for (pmcidx = 0; pmcidx < ARRAY_SIZE(pmcdev->pmcs); pmcidx++) {
+ const struct pmc_bit_map **maps;
+ unsigned int arr_size, r_idx;
+ u32 offset, counter;
+ struct pmc *pmc;
+
+ pmc = pmcdev->pmcs[pmcidx];
+ if (!pmc)
+ continue;
+ maps = pmc->map->s0ix_blocker_maps;
+ offset = pmc->map->s0ix_blocker_offset;
+ arr_size = pmc_core_lpm_get_arr_size(maps);
+
+ for (r_idx = 0; r_idx < arr_size; r_idx++) {
+ const struct pmc_bit_map *map;
+
+ for (map = maps[r_idx]; map->name; map++) {
+ if (!map->blk)
+ continue;
+ counter = pmc_core_reg_read(pmc, offset);
+ seq_printf(s, "PMC%d:%-30s %-30d\n", pmcidx,
+ map->name, counter);
+ offset += map->blk * S0IX_BLK_SIZE;
+ }
+ }
+ }
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(pmc_core_s0ix_blocker);
+
static inline u64 adjust_lpm_residency(struct pmc *pmc, u32 offset,
const int lpm_adj_x2)
{
@@ -1197,6 +1232,9 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
debugfs_create_file("ltr_show", 0444, dir, pmcdev, &pmc_core_ltr_fops);
+ if (primary_pmc->map->s0ix_blocker_maps)
+ debugfs_create_file("s0ix_blocker", 0444, dir, pmcdev, &pmc_core_s0ix_blocker_fops);
+
debugfs_create_file("package_cstate_show", 0444, dir, primary_pmc,
&pmc_core_pkgc_fops);
@@ -1389,6 +1427,15 @@ static int pmc_core_probe(struct platform_device *pdev)
return -ENOMEM;
pmcdev->pmcs[PMC_IDX_MAIN] = primary_pmc;
+ /* The last element in msr_map is empty */
+ pmcdev->num_of_pkgc = ARRAY_SIZE(msr_map) - 1;
+ pmcdev->pkgc_res_cnt = devm_kcalloc(&pdev->dev,
+ pmcdev->num_of_pkgc,
+ sizeof(*pmcdev->pkgc_res_cnt),
+ GFP_KERNEL);
+ 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
@@ -1432,6 +1479,7 @@ static __maybe_unused int pmc_core_suspend(struct device *dev)
{
struct pmc_dev *pmcdev = dev_get_drvdata(dev);
struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN];
+ unsigned int i;
if (pmcdev->suspend)
pmcdev->suspend(pmcdev);
@@ -1440,9 +1488,11 @@ static __maybe_unused int pmc_core_suspend(struct device *dev)
if (pm_suspend_via_firmware())
return 0;
- /* Save PC10 residency for checking later */
- if (rdmsrl_safe(MSR_PKG_C10_RESIDENCY, &pmcdev->pc10_counter))
- return -EIO;
+ /* Save PKGC residency for checking later */
+ for (i = 0; i < pmcdev->num_of_pkgc; i++) {
+ if (rdmsrl_safe(msr_map[i].bit_mask, &pmcdev->pkgc_res_cnt[i]))
+ return -EIO;
+ }
/* Save S0ix residency for checking later */
if (pmc_core_dev_state_get(pmc, &pmcdev->s0ix_counter))
@@ -1451,14 +1501,15 @@ static __maybe_unused int pmc_core_suspend(struct device *dev)
return 0;
}
-static inline bool pmc_core_is_pc10_failed(struct pmc_dev *pmcdev)
+static inline bool pmc_core_is_deepest_pkgc_failed(struct pmc_dev *pmcdev)
{
- u64 pc10_counter;
+ u32 deepest_pkgc_msr = msr_map[pmcdev->num_of_pkgc - 1].bit_mask;
+ u64 deepest_pkgc_residency;
- if (rdmsrl_safe(MSR_PKG_C10_RESIDENCY, &pc10_counter))
+ if (rdmsrl_safe(deepest_pkgc_msr, &deepest_pkgc_residency))
return false;
- if (pc10_counter == pmcdev->pc10_counter)
+ if (deepest_pkgc_residency == pmcdev->pkgc_res_cnt[pmcdev->num_of_pkgc - 1])
return true;
return false;
@@ -1497,10 +1548,22 @@ int pmc_core_resume_common(struct pmc_dev *pmcdev)
if (!warn_on_s0ix_failures)
return 0;
- if (pmc_core_is_pc10_failed(pmcdev)) {
- /* S0ix failed because of PC10 entry failure */
- dev_info(dev, "CPU did not enter PC10!!! (PC10 cnt=0x%llx)\n",
- pmcdev->pc10_counter);
+ if (pmc_core_is_deepest_pkgc_failed(pmcdev)) {
+ /* S0ix failed because of deepest PKGC entry failure */
+ dev_info(dev, "CPU did not enter %s!!! (%s cnt=0x%llx)\n",
+ msr_map[pmcdev->num_of_pkgc - 1].name,
+ msr_map[pmcdev->num_of_pkgc - 1].name,
+ pmcdev->pkgc_res_cnt[pmcdev->num_of_pkgc - 1]);
+
+ for (i = 0; i < pmcdev->num_of_pkgc; i++) {
+ u64 pc_cnt;
+
+ if (!rdmsrl_safe(msr_map[i].bit_mask, &pc_cnt)) {
+ dev_info(dev, "Prev %s cnt = 0x%llx, Current %s cnt = 0x%llx\n",
+ msr_map[i].name, pmcdev->pkgc_res_cnt[i],
+ msr_map[i].name, pc_cnt);
+ }
+ }
return 0;
}
diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h
index 54137faaae..ea04de7eb9 100644
--- a/drivers/platform/x86/intel/pmc/core.h
+++ b/drivers/platform/x86/intel/pmc/core.h
@@ -22,6 +22,7 @@ struct telem_endpoint;
#define PMC_BASE_ADDR_DEFAULT 0xFE000000
#define MAX_NUM_PMC 3
+#define S0IX_BLK_SIZE 4
/* Sunrise Point Power Management Controller PCI Device ID */
#define SPT_PMC_PCI_DEVICE_ID 0x9d21
@@ -282,12 +283,14 @@ enum ppfear_regs {
#define LNL_PMC_LTR_OSSE 0x1B88
#define LNL_NUM_IP_IGN_ALLOWED 27
#define LNL_PPFEAR_NUM_ENTRIES 12
+#define LNL_S0IX_BLOCKER_OFFSET 0x2004
extern const char *pmc_lpm_modes[];
struct pmc_bit_map {
const char *name;
u32 bit_mask;
+ u8 blk;
};
/**
@@ -298,6 +301,7 @@ struct pmc_bit_map {
* @pll_sts: Maps name of PLL to corresponding bit status
* @slps0_dbg_maps: Array of SLP_S0_DBG* registers containing debug info
* @ltr_show_sts: Maps PCH IP Names to their MMIO register offsets
+ * @s0ix_blocker_maps: Maps name of IP block to S0ix blocker counter
* @slp_s0_offset: PWRMBASE offset to read SLP_S0 residency
* @ltr_ignore_offset: PWRMBASE offset to read/write LTR ignore bit
* @regmap_length: Length of memory to map from PWRMBASE address to access
@@ -307,6 +311,7 @@ struct pmc_bit_map {
* @pm_cfg_offset: PWRMBASE offset to PM_CFG register
* @pm_read_disable_bit: Bit index to read PMC_READ_DISABLE
* @slps0_dbg_offset: PWRMBASE offset to SLP_S0_DEBUG_REG*
+ * @s0ix_blocker_offset PWRMBASE offset to S0ix blocker counter
*
* Each PCH has unique set of register offsets and bit indexes. This structure
* captures them to have a common implementation.
@@ -319,6 +324,7 @@ struct pmc_reg_map {
const struct pmc_bit_map *ltr_show_sts;
const struct pmc_bit_map *msr_sts;
const struct pmc_bit_map **lpm_sts;
+ const struct pmc_bit_map **s0ix_blocker_maps;
const u32 slp_s0_offset;
const int slp_s0_res_counter_step;
const u32 ltr_ignore_offset;
@@ -330,6 +336,7 @@ struct pmc_reg_map {
const u32 slps0_dbg_offset;
const u32 ltr_ignore_max;
const u32 pm_vric1_offset;
+ const u32 s0ix_blocker_offset;
/* Low Power Mode registers */
const int lpm_num_maps;
const int lpm_num_modes;
@@ -385,7 +392,8 @@ struct pmc {
* @pmc_xram_read_bit: flag to indicate whether PMC XRAM shadow registers
* used to read MPHY PG and PLL status are available
* @mutex_lock: mutex to complete one transcation
- * @pc10_counter: PC10 residency counter
+ * @pkgc_res_cnt: Array of PKGC residency counters
+ * @num_of_pkgc: Number of PKGC
* @s0ix_counter: S0ix residency (step adjusted)
* @num_lpm_modes: Count of enabled modes
* @lpm_en_modes: Array of enabled modes from lowest to highest priority
@@ -403,13 +411,15 @@ struct pmc_dev {
int pmc_xram_read_bit;
struct mutex lock; /* generic mutex lock for PMC Core */
- u64 pc10_counter;
u64 s0ix_counter;
int num_lpm_modes;
int lpm_en_modes[LPM_MAX_NUM_MODES];
void (*suspend)(struct pmc_dev *pmcdev);
int (*resume)(struct pmc_dev *pmcdev);
+ u64 *pkgc_res_cnt;
+ u8 num_of_pkgc;
+
bool has_die_c6;
u32 die_c6_offset;
struct telem_endpoint *punit_ep;
@@ -532,8 +542,10 @@ 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[];
diff --git a/drivers/platform/x86/intel/pmc/lnl.c b/drivers/platform/x86/intel/pmc/lnl.c
index 068d725046..e7a8077d1a 100644
--- a/drivers/platform/x86/intel/pmc/lnl.c
+++ b/drivers/platform/x86/intel/pmc/lnl.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/*
* This file contains platform specific structure definitions
- * and init function used by Meteor Lake PCH.
+ * and init function used by Lunar Lake PCH.
*
* Copyright (c) 2022, Intel Corporation.
* All Rights Reserved.
@@ -56,264 +56,296 @@ const struct pmc_bit_map lnl_ltr_show_map[] = {
};
const struct pmc_bit_map lnl_power_gating_status_0_map[] = {
- {"PMC_PGD0_PG_STS", BIT(0)},
- {"FUSE_OSSE_PGD0_PG_STS", BIT(1)},
- {"ESPISPI_PGD0_PG_STS", BIT(2)},
- {"XHCI_PGD0_PG_STS", BIT(3)},
- {"SPA_PGD0_PG_STS", BIT(4)},
- {"SPB_PGD0_PG_STS", BIT(5)},
- {"SPR16B0_PGD0_PG_STS", BIT(6)},
- {"GBE_PGD0_PG_STS", BIT(7)},
- {"SBR8B7_PGD0_PG_STS", BIT(8)},
- {"SBR8B6_PGD0_PG_STS", BIT(9)},
- {"SBR16B1_PGD0_PG_STS", BIT(10)},
- {"SBR8B8_PGD0_PG_STS", BIT(11)},
- {"ESE_PGD3_PG_STS", BIT(12)},
- {"D2D_DISP_PGD0_PG_STS", BIT(13)},
- {"LPSS_PGD0_PG_STS", BIT(14)},
- {"LPC_PGD0_PG_STS", BIT(15)},
- {"SMB_PGD0_PG_STS", BIT(16)},
- {"ISH_PGD0_PG_STS", BIT(17)},
- {"SBR8B2_PGD0_PG_STS", BIT(18)},
- {"NPK_PGD0_PG_STS", BIT(19)},
- {"D2D_NOC_PGD0_PG_STS", BIT(20)},
- {"SAFSS_PGD0_PG_STS", BIT(21)},
- {"FUSE_PGD0_PG_STS", BIT(22)},
- {"D2D_DISP_PGD1_PG_STS", BIT(23)},
- {"MPFPW1_PGD0_PG_STS", BIT(24)},
- {"XDCI_PGD0_PG_STS", BIT(25)},
- {"EXI_PGD0_PG_STS", BIT(26)},
- {"CSE_PGD0_PG_STS", BIT(27)},
- {"KVMCC_PGD0_PG_STS", BIT(28)},
- {"PMT_PGD0_PG_STS", BIT(29)},
- {"CLINK_PGD0_PG_STS", BIT(30)},
- {"PTIO_PGD0_PG_STS", BIT(31)},
+ {"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},
+ {"SPR16B0_PGD0_PG_STS", BIT(6), 0},
+ {"GBE_PGD0_PG_STS", BIT(7), 1},
+ {"SBR8B7_PGD0_PG_STS", BIT(8), 0},
+ {"SBR8B6_PGD0_PG_STS", BIT(9), 0},
+ {"SBR16B1_PGD0_PG_STS", BIT(10), 0},
+ {"SBR8B8_PGD0_PG_STS", BIT(11), 0},
+ {"ESE_PGD3_PG_STS", BIT(12), 1},
+ {"D2D_DISP_PGD0_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},
+ {"SBR8B2_PGD0_PG_STS", BIT(18), 0},
+ {"NPK_PGD0_PG_STS", BIT(19), 0},
+ {"D2D_NOC_PGD0_PG_STS", BIT(20), 0},
+ {"SAFSS_PGD0_PG_STS", BIT(21), 0},
+ {"FUSE_PGD0_PG_STS", BIT(22), 0},
+ {"D2D_DISP_PGD1_PG_STS", BIT(23), 1},
+ {"MPFPW1_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},
{}
};
const struct pmc_bit_map lnl_power_gating_status_1_map[] = {
- {"USBR0_PGD0_PG_STS", BIT(0)},
- {"SUSRAM_PGD0_PG_STS", BIT(1)},
- {"SMT1_PGD0_PG_STS", BIT(2)},
- {"U3FPW1_PGD0_PG_STS", BIT(3)},
- {"SMS2_PGD0_PG_STS", BIT(4)},
- {"SMS1_PGD0_PG_STS", BIT(5)},
- {"CSMERTC_PGD0_PG_STS", BIT(6)},
- {"CSMEPSF_PGD0_PG_STS", BIT(7)},
- {"FIA_PG_PGD0_PG_STS", BIT(8)},
- {"SBR16B4_PGD0_PG_STS", BIT(9)},
- {"P2SB8B_PGD0_PG_STS", BIT(10)},
- {"DBG_SBR_PGD0_PG_STS", BIT(11)},
- {"SBR8B9_PGD0_PG_STS", BIT(12)},
- {"OSSE_SMT1_PGD0_PG_STS", BIT(13)},
- {"SBR8B10_PGD0_PG_STS", BIT(14)},
- {"SBR16B3_PGD0_PG_STS", BIT(15)},
- {"G5FPW1_PGD0_PG_STS", BIT(16)},
- {"SBRG_PGD0_PG_STS", BIT(17)},
- {"PSF4_PGD0_PG_STS", BIT(18)},
- {"CNVI_PGD0_PG_STS", BIT(19)},
- {"USFX2_PGD0_PG_STS", BIT(20)},
- {"ENDBG_PGD0_PG_STS", BIT(21)},
- {"FIACPCB_P5X4_PGD0_PG_STS", BIT(22)},
- {"SBR8B3_PGD0_PG_STS", BIT(23)},
- {"SBR8B0_PGD0_PG_STS", BIT(24)},
- {"NPK_PGD1_PG_STS", BIT(25)},
- {"OSSE_HOTHAM_PGD0_PG_STS", BIT(26)},
- {"D2D_NOC_PGD2_PG_STS", BIT(27)},
- {"SBR8B1_PGD0_PG_STS", BIT(28)},
- {"PSF6_PGD0_PG_STS", BIT(29)},
- {"PSF7_PGD0_PG_STS", BIT(30)},
- {"FIA_U_PGD0_PG_STS", BIT(31)},
+ {"USBR0_PGD0_PG_STS", BIT(0), 1},
+ {"SUSRAM_PGD0_PG_STS", BIT(1), 1},
+ {"SMT1_PGD0_PG_STS", BIT(2), 1},
+ {"U3FPW1_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},
+ {"FIA_PG_PGD0_PG_STS", BIT(8), 0},
+ {"SBR16B4_PGD0_PG_STS", BIT(9), 0},
+ {"P2SB8B_PGD0_PG_STS", BIT(10), 1},
+ {"DBG_SBR_PGD0_PG_STS", BIT(11), 0},
+ {"SBR8B9_PGD0_PG_STS", BIT(12), 0},
+ {"OSSE_SMT1_PGD0_PG_STS", BIT(13), 1},
+ {"SBR8B10_PGD0_PG_STS", BIT(14), 0},
+ {"SBR16B3_PGD0_PG_STS", BIT(15), 0},
+ {"G5FPW1_PGD0_PG_STS", BIT(16), 0},
+ {"SBRG_PGD0_PG_STS", BIT(17), 0},
+ {"PSF4_PGD0_PG_STS", BIT(18), 0},
+ {"CNVI_PGD0_PG_STS", BIT(19), 0},
+ {"USFX2_PGD0_PG_STS", BIT(20), 1},
+ {"ENDBG_PGD0_PG_STS", BIT(21), 0},
+ {"FIACPCB_P5X4_PGD0_PG_STS", BIT(22), 0},
+ {"SBR8B3_PGD0_PG_STS", BIT(23), 0},
+ {"SBR8B0_PGD0_PG_STS", BIT(24), 0},
+ {"NPK_PGD1_PG_STS", BIT(25), 0},
+ {"OSSE_HOTHAM_PGD0_PG_STS", BIT(26), 1},
+ {"D2D_NOC_PGD2_PG_STS", BIT(27), 1},
+ {"SBR8B1_PGD0_PG_STS", BIT(28), 0},
+ {"PSF6_PGD0_PG_STS", BIT(29), 0},
+ {"PSF7_PGD0_PG_STS", BIT(30), 0},
+ {"FIA_U_PGD0_PG_STS", BIT(31), 0},
{}
};
const struct pmc_bit_map lnl_power_gating_status_2_map[] = {
- {"PSF8_PGD0_PG_STS", BIT(0)},
- {"SBR16B2_PGD0_PG_STS", BIT(1)},
- {"D2D_IPU_PGD0_PG_STS", BIT(2)},
- {"FIACPCB_U_PGD0_PG_STS", BIT(3)},
- {"TAM_PGD0_PG_STS", BIT(4)},
- {"D2D_NOC_PGD1_PG_STS", BIT(5)},
- {"TBTLSX_PGD0_PG_STS", BIT(6)},
- {"THC0_PGD0_PG_STS", BIT(7)},
- {"THC1_PGD0_PG_STS", BIT(8)},
- {"PMC_PGD0_PG_STS", BIT(9)},
- {"SBR8B5_PGD0_PG_STS", BIT(10)},
- {"UFSPW1_PGD0_PG_STS", BIT(11)},
- {"DBC_PGD0_PG_STS", BIT(12)},
- {"TCSS_PGD0_PG_STS", BIT(13)},
- {"FIA_P5X4_PGD0_PG_STS", BIT(14)},
- {"DISP_PGA_PGD0_PG_STS", BIT(15)},
- {"DISP_PSF_PGD0_PG_STS", BIT(16)},
- {"PSF0_PGD0_PG_STS", BIT(17)},
- {"P2SB16B_PGD0_PG_STS", BIT(18)},
- {"ACE_PGD0_PG_STS", BIT(19)},
- {"ACE_PGD1_PG_STS", BIT(20)},
- {"ACE_PGD2_PG_STS", BIT(21)},
- {"ACE_PGD3_PG_STS", BIT(22)},
- {"ACE_PGD4_PG_STS", BIT(23)},
- {"ACE_PGD5_PG_STS", BIT(24)},
- {"ACE_PGD6_PG_STS", BIT(25)},
- {"ACE_PGD7_PG_STS", BIT(26)},
- {"ACE_PGD8_PG_STS", BIT(27)},
- {"ACE_PGD9_PG_STS", BIT(28)},
- {"ACE_PGD10_PG_STS", BIT(29)},
- {"FIACPCB_PG_PGD0_PG_STS", BIT(30)},
- {"OSSE_PGD0_PG_STS", BIT(31)},
+ {"PSF8_PGD0_PG_STS", BIT(0), 0},
+ {"SBR16B2_PGD0_PG_STS", BIT(1), 0},
+ {"D2D_IPU_PGD0_PG_STS", BIT(2), 1},
+ {"FIACPCB_U_PGD0_PG_STS", BIT(3), 0},
+ {"TAM_PGD0_PG_STS", BIT(4), 1},
+ {"D2D_NOC_PGD1_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_PGD0_PG_STS", BIT(9), 0},
+ {"SBR8B5_PGD0_PG_STS", BIT(10), 0},
+ {"UFSPW1_PGD0_PG_STS", BIT(11), 0},
+ {"DBC_PGD0_PG_STS", BIT(12), 0},
+ {"TCSS_PGD0_PG_STS", BIT(13), 0},
+ {"FIA_P5X4_PGD0_PG_STS", BIT(14), 0},
+ {"DISP_PGA_PGD0_PG_STS", BIT(15), 0},
+ {"DISP_PSF_PGD0_PG_STS", BIT(16), 0},
+ {"PSF0_PGD0_PG_STS", BIT(17), 0},
+ {"P2SB16B_PGD0_PG_STS", BIT(18), 1},
+ {"ACE_PGD0_PG_STS", BIT(19), 0},
+ {"ACE_PGD1_PG_STS", BIT(20), 0},
+ {"ACE_PGD2_PG_STS", BIT(21), 0},
+ {"ACE_PGD3_PG_STS", BIT(22), 0},
+ {"ACE_PGD4_PG_STS", BIT(23), 0},
+ {"ACE_PGD5_PG_STS", BIT(24), 0},
+ {"ACE_PGD6_PG_STS", BIT(25), 0},
+ {"ACE_PGD7_PG_STS", BIT(26), 0},
+ {"ACE_PGD8_PG_STS", BIT(27), 0},
+ {"ACE_PGD9_PG_STS", BIT(28), 0},
+ {"ACE_PGD10_PG_STS", BIT(29), 0},
+ {"FIACPCB_PG_PGD0_PG_STS", BIT(30), 0},
+ {"OSSE_PGD0_PG_STS", BIT(31), 1},
{}
};
const struct pmc_bit_map lnl_d3_status_0_map[] = {
- {"LPSS_D3_STS", BIT(3)},
- {"XDCI_D3_STS", BIT(4)},
- {"XHCI_D3_STS", BIT(5)},
- {"SPA_D3_STS", BIT(12)},
- {"SPB_D3_STS", BIT(13)},
- {"OSSE_D3_STS", BIT(15)},
- {"ESPISPI_D3_STS", BIT(18)},
- {"PSTH_D3_STS", BIT(21)},
+ {"LPSS_D3_STS", BIT(3), 1},
+ {"XDCI_D3_STS", BIT(4), 1},
+ {"XHCI_D3_STS", BIT(5), 1},
+ {"SPA_D3_STS", BIT(12), 0},
+ {"SPB_D3_STS", BIT(13), 0},
+ {"OSSE_D3_STS", BIT(15), 0},
+ {"ESPISPI_D3_STS", BIT(18), 0},
+ {"PSTH_D3_STS", BIT(21), 0},
{}
};
const struct pmc_bit_map lnl_d3_status_1_map[] = {
- {"OSSE_SMT1_D3_STS", BIT(7)},
- {"GBE_D3_STS", BIT(19)},
- {"ITSS_D3_STS", BIT(23)},
- {"CNVI_D3_STS", BIT(27)},
- {"UFSX2_D3_STS", BIT(28)},
- {"OSSE_HOTHAM_D3_STS", BIT(31)},
+ {"OSSE_SMT1_D3_STS", BIT(7), 0},
+ {"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(31), 0},
{}
};
const struct pmc_bit_map lnl_d3_status_2_map[] = {
- {"ESE_D3_STS", BIT(0)},
- {"CSMERTC_D3_STS", BIT(1)},
- {"SUSRAM_D3_STS", BIT(2)},
- {"CSE_D3_STS", BIT(4)},
- {"KVMCC_D3_STS", BIT(5)},
- {"USBR0_D3_STS", BIT(6)},
- {"ISH_D3_STS", BIT(7)},
- {"SMT1_D3_STS", BIT(8)},
- {"SMT2_D3_STS", BIT(9)},
- {"SMT3_D3_STS", BIT(10)},
- {"OSSE_SMT2_D3_STS", BIT(13)},
- {"CLINK_D3_STS", BIT(14)},
- {"PTIO_D3_STS", BIT(16)},
- {"PMT_D3_STS", BIT(17)},
- {"SMS1_D3_STS", BIT(18)},
- {"SMS2_D3_STS", BIT(19)},
+ {"ESE_D3_STS", BIT(0), 0},
+ {"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(13), 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},
{}
};
const struct pmc_bit_map lnl_d3_status_3_map[] = {
- {"THC0_D3_STS", BIT(14)},
- {"THC1_D3_STS", BIT(15)},
- {"OSSE_SMT3_D3_STS", BIT(21)},
- {"ACE_D3_STS", BIT(23)},
+ {"THC0_D3_STS", BIT(14), 1},
+ {"THC1_D3_STS", BIT(15), 1},
+ {"OSSE_SMT3_D3_STS", BIT(21), 0},
+ {"ACE_D3_STS", BIT(23), 0},
{}
};
const struct pmc_bit_map lnl_vnn_req_status_0_map[] = {
- {"LPSS_VNN_REQ_STS", BIT(3)},
- {"OSSE_VNN_REQ_STS", BIT(15)},
- {"ESPISPI_VNN_REQ_STS", BIT(18)},
+ {"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[] = {
- {"NPK_VNN_REQ_STS", BIT(4)},
- {"OSSE_SMT1_VNN_REQ_STS", BIT(7)},
- {"DFXAGG_VNN_REQ_STS", BIT(8)},
- {"EXI_VNN_REQ_STS", BIT(9)},
- {"P2D_VNN_REQ_STS", BIT(18)},
- {"GBE_VNN_REQ_STS", BIT(19)},
- {"SMB_VNN_REQ_STS", BIT(25)},
- {"LPC_VNN_REQ_STS", BIT(26)},
+ {"NPK_VNN_REQ_STS", BIT(4), 1},
+ {"OSSE_SMT1_VNN_REQ_STS", BIT(7), 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},
{}
};
const struct pmc_bit_map lnl_vnn_req_status_2_map[] = {
- {"eSE_VNN_REQ_STS", BIT(0)},
- {"CSMERTC_VNN_REQ_STS", BIT(1)},
- {"CSE_VNN_REQ_STS", BIT(4)},
- {"ISH_VNN_REQ_STS", BIT(7)},
- {"SMT1_VNN_REQ_STS", BIT(8)},
- {"CLINK_VNN_REQ_STS", BIT(14)},
- {"SMS1_VNN_REQ_STS", BIT(18)},
- {"SMS2_VNN_REQ_STS", BIT(19)},
- {"GPIOCOM4_VNN_REQ_STS", BIT(20)},
- {"GPIOCOM3_VNN_REQ_STS", BIT(21)},
- {"GPIOCOM2_VNN_REQ_STS", BIT(22)},
- {"GPIOCOM1_VNN_REQ_STS", BIT(23)},
- {"GPIOCOM0_VNN_REQ_STS", BIT(24)},
+ {"eSE_VNN_REQ_STS", BIT(0), 1},
+ {"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},
+ {"GPIOCOM2_VNN_REQ_STS", BIT(22), 0},
+ {"GPIOCOM1_VNN_REQ_STS", BIT(23), 1},
+ {"GPIOCOM0_VNN_REQ_STS", BIT(24), 1},
{}
};
const struct pmc_bit_map lnl_vnn_req_status_3_map[] = {
- {"DISP_SHIM_VNN_REQ_STS", BIT(2)},
- {"DTS0_VNN_REQ_STS", BIT(7)},
- {"GPIOCOM5_VNN_REQ_STS", BIT(11)},
+ {"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[] = {
- {"CPU_C10_REQ_STS", BIT(0)},
- {"TS_OFF_REQ_STS", BIT(1)},
- {"PNDE_MET_REQ_STS", BIT(2)},
- {"PCIE_DEEP_PM_REQ_STS", BIT(3)},
- {"PMC_CLK_THROTTLE_EN_REQ_STS", BIT(4)},
- {"NPK_VNNAON_REQ_STS", BIT(5)},
- {"VNN_SOC_REQ_STS", BIT(6)},
- {"ISH_VNNAON_REQ_STS", BIT(7)},
- {"D2D_NOC_CFI_QACTIVE_REQ_STS", BIT(8)},
- {"D2D_NOC_GPSB_QACTIVE_REQ_STS", BIT(9)},
- {"D2D_NOC_IPU_QACTIVE_REQ_STS", BIT(10)},
- {"PLT_GREATER_REQ_STS", BIT(11)},
- {"PCIE_CLKREQ_REQ_STS", BIT(12)},
- {"PMC_IDLE_FB_OCP_REQ_STS", BIT(13)},
- {"PM_SYNC_STATES_REQ_STS", BIT(14)},
- {"EA_REQ_STS", BIT(15)},
- {"MPHY_CORE_OFF_REQ_STS", BIT(16)},
- {"BRK_EV_EN_REQ_STS", BIT(17)},
- {"AUTO_DEMO_EN_REQ_STS", BIT(18)},
- {"ITSS_CLK_SRC_REQ_STS", BIT(19)},
- {"LPC_CLK_SRC_REQ_STS", BIT(20)},
- {"ARC_IDLE_REQ_STS", BIT(21)},
- {"MPHY_SUS_REQ_STS", BIT(22)},
- {"FIA_DEEP_PM_REQ_STS", BIT(23)},
- {"UXD_CONNECTED_REQ_STS", BIT(24)},
- {"ARC_INTERRUPT_WAKE_REQ_STS", BIT(25)},
- {"D2D_NOC_DISP_DDI_QACTIVE_REQ_STS", BIT(26)},
- {"PRE_WAKE0_REQ_STS", BIT(27)},
- {"PRE_WAKE1_REQ_STS", BIT(28)},
- {"PRE_WAKE2_EN_REQ_STS", BIT(29)},
- {"WOV_REQ_STS", BIT(30)},
- {"D2D_NOC_DISP_EDP_QACTIVE_REQ_STS_31", BIT(31)},
+ {"CPU_C10_REQ_STS", BIT(0), 0},
+ {"TS_OFF_REQ_STS", BIT(1), 0},
+ {"PNDE_MET_REQ_STS", BIT(2), 1},
+ {"PCIE_DEEP_PM_REQ_STS", BIT(3), 0},
+ {"PMC_CLK_THROTTLE_EN_REQ_STS", BIT(4), 0},
+ {"NPK_VNNAON_REQ_STS", BIT(5), 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_NOC_IPU_QACTIVE_REQ_STS", BIT(10), 1},
+ {"PLT_GREATER_REQ_STS", BIT(11), 1},
+ {"PCIE_CLKREQ_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},
+ {"LPC_CLK_SRC_REQ_STS", BIT(20), 0},
+ {"ARC_IDLE_REQ_STS", BIT(21), 0},
+ {"MPHY_SUS_REQ_STS", BIT(22), 0},
+ {"FIA_DEEP_PM_REQ_STS", BIT(23), 0},
+ {"UXD_CONNECTED_REQ_STS", BIT(24), 1},
+ {"ARC_INTERRUPT_WAKE_REQ_STS", BIT(25), 0},
+ {"D2D_NOC_DISP_DDI_QACTIVE_REQ_STS", BIT(26), 1},
+ {"PRE_WAKE0_REQ_STS", BIT(27), 1},
+ {"PRE_WAKE1_REQ_STS", BIT(28), 1},
+ {"PRE_WAKE2_EN_REQ_STS", BIT(29), 1},
+ {"WOV_REQ_STS", BIT(30), 0},
+ {"D2D_NOC_DISP_EDP_QACTIVE_REQ_STS_31", BIT(31), 1},
{}
};
const struct pmc_bit_map lnl_clocksource_status_map[] = {
- {"AON2_OFF_STS", BIT(0)},
- {"AON3_OFF_STS", BIT(1)},
- {"AON4_OFF_STS", BIT(2)},
- {"AON5_OFF_STS", BIT(3)},
- {"AON1_OFF_STS", BIT(4)},
- {"MPFPW1_0_PLL_OFF_STS", BIT(6)},
- {"USB3_PLL_OFF_STS", BIT(8)},
- {"AON3_SPL_OFF_STS", BIT(9)},
- {"G5FPW1_PLL_OFF_STS", BIT(15)},
- {"XTAL_AGGR_OFF_STS", BIT(17)},
- {"USB2_PLL_OFF_STS", BIT(18)},
- {"SAF_PLL_OFF_STS", BIT(19)},
- {"SE_TCSS_PLL_OFF_STS", BIT(20)},
- {"DDI_PLL_OFF_STS", BIT(21)},
- {"FILTER_PLL_OFF_STS", BIT(22)},
- {"ACE_PLL_OFF_STS", BIT(24)},
- {"FABRIC_PLL_OFF_STS", BIT(25)},
- {"SOC_PLL_OFF_STS", BIT(26)},
- {"REF_OFF_STS", BIT(28)},
- {"IMG_OFF_STS", BIT(29)},
- {"RTC_PLL_OFF_STS", BIT(31)},
+ {"AON2_OFF_STS", BIT(0), 0},
+ {"AON3_OFF_STS", BIT(1), 1},
+ {"AON4_OFF_STS", BIT(2), 1},
+ {"AON5_OFF_STS", BIT(3), 1},
+ {"AON1_OFF_STS", BIT(4), 0},
+ {"MPFPW1_0_PLL_OFF_STS", BIT(6), 1},
+ {"USB3_PLL_OFF_STS", BIT(8), 1},
+ {"AON3_SPL_OFF_STS", BIT(9), 1},
+ {"G5FPW1_PLL_OFF_STS", BIT(15), 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_OFF_STS", BIT(28), 1},
+ {"IMG_OFF_STS", BIT(29), 1},
+ {"RTC_PLL_OFF_STS", BIT(31), 0},
+ {}
+};
+
+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},
+ {"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},
+ {}
+};
+
+const struct pmc_bit_map lnl_rsc_status_map[] = {
+ {"Memory", 0, 1},
+ {"PSF0", 0, 1},
+ {"PSF4", 0, 1},
+ {"PSF6", 0, 1},
+ {"PSF7", 0, 1},
+ {"PSF8", 0, 1},
+ {"SAF_CFI_LINK", 0, 1},
+ {"SBR", 0, 1},
{}
};
@@ -331,7 +363,26 @@ const struct pmc_bit_map *lnl_lpm_maps[] = {
lnl_vnn_req_status_2_map,
lnl_vnn_req_status_3_map,
lnl_vnn_misc_status_map,
- mtl_socm_signal_status_map,
+ lnl_signal_status_map,
+ NULL
+};
+
+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,
+ lnl_rsc_status_map,
+ lnl_vnn_req_status_0_map,
+ lnl_vnn_req_status_1_map,
+ lnl_vnn_req_status_2_map,
+ lnl_vnn_req_status_3_map,
+ lnl_d3_status_0_map,
+ lnl_d3_status_1_map,
+ lnl_d3_status_2_map,
+ lnl_d3_status_3_map,
+ lnl_clocksource_status_map,
+ lnl_vnn_misc_status_map,
+ lnl_signal_status_map,
NULL
};
@@ -475,6 +526,8 @@ const struct pmc_reg_map lnl_socm_reg_map = {
.lpm_sts = lnl_lpm_maps,
.lpm_status_offset = MTL_LPM_STATUS_OFFSET,
.lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET,
+ .s0ix_blocker_maps = lnl_blk_maps,
+ .s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET,
};
#define LNL_NPU_PCI_DEV 0x643e
diff --git a/drivers/platform/x86/intel/pmc/pltdrv.c b/drivers/platform/x86/intel/pmc/pltdrv.c
index ddfba38c21..f2cb87dc2d 100644
--- a/drivers/platform/x86/intel/pmc/pltdrv.c
+++ b/drivers/platform/x86/intel/pmc/pltdrv.c
@@ -86,4 +86,5 @@ static void __exit pmc_core_platform_exit(void)
module_init(pmc_core_platform_init);
module_exit(pmc_core_platform_exit);
+MODULE_DESCRIPTION("Intel PMC Core platform driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/platform/x86/intel/rst.c b/drivers/platform/x86/intel/rst.c
index 35814a7707..f3a60e14d4 100644
--- a/drivers/platform/x86/intel/rst.c
+++ b/drivers/platform/x86/intel/rst.c
@@ -7,6 +7,7 @@
#include <linux/module.h>
#include <linux/slab.h>
+MODULE_DESCRIPTION("Intel Rapid Start Technology Driver");
MODULE_LICENSE("GPL");
static ssize_t irst_show_wakeup_events(struct device *dev,
@@ -125,7 +126,6 @@ static const struct acpi_device_id irst_ids[] = {
};
static struct acpi_driver irst_driver = {
- .owner = THIS_MODULE,
.name = "intel_rapid_start",
.class = "intel_rapid_start",
.ids = irst_ids,
diff --git a/drivers/platform/x86/intel/sdsi.c b/drivers/platform/x86/intel/sdsi.c
index 556e7c6dbb..277e4f4b20 100644
--- a/drivers/platform/x86/intel/sdsi.c
+++ b/drivers/platform/x86/intel/sdsi.c
@@ -15,6 +15,7 @@
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/overflow.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
@@ -66,6 +67,8 @@
#define CTRL_OWNER GENMASK(5, 4)
#define CTRL_COMPLETE BIT(6)
#define CTRL_READY BIT(7)
+#define CTRL_INBAND_LOCK BIT(32)
+#define CTRL_METER_ENABLE_DRAM BIT(33)
#define CTRL_STATUS GENMASK(15, 8)
#define CTRL_PACKET_SIZE GENMASK(31, 16)
#define CTRL_MSG_SIZE GENMASK(63, 48)
@@ -93,6 +96,7 @@ enum sdsi_command {
struct sdsi_mbox_info {
u64 *payload;
void *buffer;
+ u64 control_flags;
int size;
};
@@ -156,8 +160,8 @@ static int sdsi_status_to_errno(u32 status)
}
}
-static int sdsi_mbox_cmd_read(struct sdsi_priv *priv, struct sdsi_mbox_info *info,
- size_t *data_size)
+static int sdsi_mbox_poll(struct sdsi_priv *priv, struct sdsi_mbox_info *info,
+ size_t *data_size)
{
struct device *dev = priv->dev;
u32 total, loop, eom, status, message_size;
@@ -166,18 +170,10 @@ static int sdsi_mbox_cmd_read(struct sdsi_priv *priv, struct sdsi_mbox_info *inf
lockdep_assert_held(&priv->mb_lock);
- /* Format and send the read command */
- control = FIELD_PREP(CTRL_EOM, 1) |
- FIELD_PREP(CTRL_SOM, 1) |
- FIELD_PREP(CTRL_RUN_BUSY, 1) |
- FIELD_PREP(CTRL_PACKET_SIZE, info->size);
- writeq(control, priv->control_addr);
-
/* For reads, data sizes that are larger than the mailbox size are read in packets. */
total = 0;
loop = 0;
do {
- void *buf = info->buffer + (SDSI_SIZE_MAILBOX * loop);
u32 packet_size;
/* Poll on ready bit */
@@ -195,6 +191,11 @@ static int sdsi_mbox_cmd_read(struct sdsi_priv *priv, struct sdsi_mbox_info *inf
if (ret)
break;
+ if (!packet_size) {
+ sdsi_complete_transaction(priv);
+ break;
+ }
+
/* Only the last packet can be less than the mailbox size. */
if (!eom && packet_size != SDSI_SIZE_MAILBOX) {
dev_err(dev, "Invalid packet size\n");
@@ -208,9 +209,13 @@ static int sdsi_mbox_cmd_read(struct sdsi_priv *priv, struct sdsi_mbox_info *inf
break;
}
- sdsi_memcpy64_fromio(buf, priv->mbox_addr, round_up(packet_size, SDSI_SIZE_CMD));
+ if (info->buffer) {
+ void *buf = info->buffer + array_size(SDSI_SIZE_MAILBOX, loop);
- total += packet_size;
+ sdsi_memcpy64_fromio(buf, priv->mbox_addr,
+ round_up(packet_size, SDSI_SIZE_CMD));
+ total += packet_size;
+ }
sdsi_complete_transaction(priv);
} while (!eom && ++loop < MBOX_MAX_PACKETS);
@@ -230,16 +235,34 @@ static int sdsi_mbox_cmd_read(struct sdsi_priv *priv, struct sdsi_mbox_info *inf
dev_warn(dev, "Read count %u differs from expected count %u\n",
total, message_size);
- *data_size = total;
+ if (data_size)
+ *data_size = total;
return 0;
}
-static int sdsi_mbox_cmd_write(struct sdsi_priv *priv, struct sdsi_mbox_info *info)
+static int sdsi_mbox_cmd_read(struct sdsi_priv *priv, struct sdsi_mbox_info *info,
+ size_t *data_size)
+{
+ u64 control;
+
+ lockdep_assert_held(&priv->mb_lock);
+
+ /* Format and send the read command */
+ control = FIELD_PREP(CTRL_EOM, 1) |
+ FIELD_PREP(CTRL_SOM, 1) |
+ FIELD_PREP(CTRL_RUN_BUSY, 1) |
+ FIELD_PREP(CTRL_PACKET_SIZE, info->size) |
+ info->control_flags;
+ writeq(control, priv->control_addr);
+
+ return sdsi_mbox_poll(priv, info, data_size);
+}
+
+static int sdsi_mbox_cmd_write(struct sdsi_priv *priv, struct sdsi_mbox_info *info,
+ size_t *data_size)
{
u64 control;
- u32 status;
- int ret;
lockdep_assert_held(&priv->mb_lock);
@@ -252,23 +275,11 @@ static int sdsi_mbox_cmd_write(struct sdsi_priv *priv, struct sdsi_mbox_info *in
FIELD_PREP(CTRL_SOM, 1) |
FIELD_PREP(CTRL_RUN_BUSY, 1) |
FIELD_PREP(CTRL_READ_WRITE, 1) |
+ FIELD_PREP(CTRL_MSG_SIZE, info->size) |
FIELD_PREP(CTRL_PACKET_SIZE, info->size);
writeq(control, priv->control_addr);
- /* Poll on ready bit */
- ret = readq_poll_timeout(priv->control_addr, control, control & CTRL_READY,
- MBOX_POLLING_PERIOD_US, MBOX_TIMEOUT_US);
-
- if (ret)
- goto release_mbox;
-
- status = FIELD_GET(CTRL_STATUS, control);
- ret = sdsi_status_to_errno(status);
-
-release_mbox:
- sdsi_complete_transaction(priv);
-
- return ret;
+ return sdsi_mbox_poll(priv, info, data_size);
}
static int sdsi_mbox_acquire(struct sdsi_priv *priv, struct sdsi_mbox_info *info)
@@ -312,7 +323,8 @@ static int sdsi_mbox_acquire(struct sdsi_priv *priv, struct sdsi_mbox_info *info
return ret;
}
-static int sdsi_mbox_write(struct sdsi_priv *priv, struct sdsi_mbox_info *info)
+static int sdsi_mbox_write(struct sdsi_priv *priv, struct sdsi_mbox_info *info,
+ size_t *data_size)
{
int ret;
@@ -322,7 +334,7 @@ static int sdsi_mbox_write(struct sdsi_priv *priv, struct sdsi_mbox_info *info)
if (ret)
return ret;
- return sdsi_mbox_cmd_write(priv, info);
+ return sdsi_mbox_cmd_write(priv, info, data_size);
}
static int sdsi_mbox_read(struct sdsi_priv *priv, struct sdsi_mbox_info *info, size_t *data_size)
@@ -338,15 +350,24 @@ static int sdsi_mbox_read(struct sdsi_priv *priv, struct sdsi_mbox_info *info, s
return sdsi_mbox_cmd_read(priv, info, data_size);
}
+static bool sdsi_ib_locked(struct sdsi_priv *priv)
+{
+ return !!FIELD_GET(CTRL_INBAND_LOCK, readq(priv->control_addr));
+}
+
static ssize_t sdsi_provision(struct sdsi_priv *priv, char *buf, size_t count,
enum sdsi_command command)
{
- struct sdsi_mbox_info info;
+ struct sdsi_mbox_info info = {};
int ret;
if (count > (SDSI_SIZE_WRITE_MSG - SDSI_SIZE_CMD))
return -EOVERFLOW;
+ /* Make sure In-band lock is not set */
+ if (sdsi_ib_locked(priv))
+ return -EPERM;
+
/* Qword aligned message + command qword */
info.size = round_up(count, SDSI_SIZE_CMD) + SDSI_SIZE_CMD;
@@ -363,7 +384,9 @@ static ssize_t sdsi_provision(struct sdsi_priv *priv, char *buf, size_t count,
ret = mutex_lock_interruptible(&priv->mb_lock);
if (ret)
goto free_payload;
- ret = sdsi_mbox_write(priv, &info);
+
+ ret = sdsi_mbox_write(priv, &info, NULL);
+
mutex_unlock(&priv->mb_lock);
free_payload:
@@ -404,10 +427,10 @@ static ssize_t provision_cap_write(struct file *filp, struct kobject *kobj,
static BIN_ATTR_WO(provision_cap, SDSI_SIZE_WRITE_MSG);
static ssize_t
-certificate_read(u64 command, struct sdsi_priv *priv, char *buf, loff_t off,
- size_t count)
+certificate_read(u64 command, u64 control_flags, struct sdsi_priv *priv,
+ char *buf, loff_t off, size_t count)
{
- struct sdsi_mbox_info info;
+ struct sdsi_mbox_info info = {};
size_t size;
int ret;
@@ -421,6 +444,7 @@ certificate_read(u64 command, struct sdsi_priv *priv, char *buf, loff_t off,
info.payload = &command;
info.size = sizeof(command);
+ info.control_flags = control_flags;
ret = mutex_lock_interruptible(&priv->mb_lock);
if (ret)
@@ -452,7 +476,7 @@ state_certificate_read(struct file *filp, struct kobject *kobj,
struct device *dev = kobj_to_dev(kobj);
struct sdsi_priv *priv = dev_get_drvdata(dev);
- return certificate_read(SDSI_CMD_READ_STATE, priv, buf, off, count);
+ return certificate_read(SDSI_CMD_READ_STATE, 0, priv, buf, off, count);
}
static BIN_ATTR_ADMIN_RO(state_certificate, SDSI_SIZE_READ_MSG);
@@ -464,10 +488,23 @@ meter_certificate_read(struct file *filp, struct kobject *kobj,
struct device *dev = kobj_to_dev(kobj);
struct sdsi_priv *priv = dev_get_drvdata(dev);
- return certificate_read(SDSI_CMD_READ_METER, priv, buf, off, count);
+ return certificate_read(SDSI_CMD_READ_METER, 0, priv, buf, off, count);
}
static 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,
+ size_t count)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct sdsi_priv *priv = dev_get_drvdata(dev);
+
+ 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 ssize_t registers_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr, char *buf, loff_t off,
size_t count)
@@ -498,6 +535,7 @@ static struct bin_attribute *sdsi_bin_attrs[] = {
&bin_attr_registers,
&bin_attr_state_certificate,
&bin_attr_meter_certificate,
+ &bin_attr_meter_current,
&bin_attr_provision_akc,
&bin_attr_provision_cap,
NULL
@@ -517,7 +555,7 @@ sdsi_battr_is_visible(struct kobject *kobj, struct bin_attribute *attr, int n)
if (!(priv->features & SDSI_FEATURE_SDSI))
return 0;
- if (attr == &bin_attr_meter_certificate)
+ if (attr == &bin_attr_meter_certificate || attr == &bin_attr_meter_current)
return (priv->features & SDSI_FEATURE_METERING) ?
attr->attr.mode : 0;
diff --git a/drivers/platform/x86/intel/smartconnect.c b/drivers/platform/x86/intel/smartconnect.c
index 64c2dc9347..31019a1a6d 100644
--- a/drivers/platform/x86/intel/smartconnect.c
+++ b/drivers/platform/x86/intel/smartconnect.c
@@ -6,6 +6,7 @@
#include <linux/acpi.h>
#include <linux/module.h>
+MODULE_DESCRIPTION("Intel Smart Connect disabling driver");
MODULE_LICENSE("GPL");
static int smartconnect_acpi_init(struct acpi_device *acpi)
@@ -32,7 +33,6 @@ static const struct acpi_device_id smartconnect_ids[] = {
MODULE_DEVICE_TABLE(acpi, smartconnect_ids);
static struct acpi_driver smartconnect_driver = {
- .owner = THIS_MODULE,
.name = "intel_smart_connect",
.class = "intel_smart_connect",
.ids = smartconnect_ids,
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 1accdaaf28..713c0d1fa8 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
@@ -839,4 +839,5 @@ void isst_if_cdev_unregister(int device_type)
}
EXPORT_SYMBOL_GPL(isst_if_cdev_unregister);
+MODULE_DESCRIPTION("ISST common interface module");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c
index 2662fbbddf..7fa360073f 100644
--- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c
+++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c
@@ -17,12 +17,15 @@
* the hardware mapping.
*/
+#define dev_fmt(fmt) "tpmi_sst: " fmt
+
#include <linux/auxiliary_bus.h>
#include <linux/delay.h>
#include <linux/intel_tpmi.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/kernel.h>
+#include <linux/minmax.h>
#include <linux/module.h>
#include <uapi/linux/isst_if.h>
@@ -263,20 +266,33 @@ struct tpmi_per_power_domain_info {
bool write_blocked;
};
+/* Supported maximum partitions */
+#define SST_MAX_PARTITIONS 2
+
/**
* struct tpmi_sst_struct - Store sst info for a package
* @package_id: Package id for this aux device instance
* @number_of_power_domains: Number of power_domains pointed by power_domain_info pointer
* @power_domain_info: Pointer to power domains information
+ * @cdie_mask: Mask of compute dies present in a partition from hardware.
+ * This mask is not present in the version 1 information header.
+ * @io_dies: Number of IO dies in a partition. This will be 0 for TPMI
+ * version 1 information header.
+ * @partition_mask: Mask of all partitions.
+ * @partition_mask_current: Current partition mask as some may have been unbound.
*
* This structure is used store full SST information for a package.
- * Each package has a unique OOB PCI device, which enumerates TPMI.
- * Each Package will have multiple power_domains.
+ * Each package has one or multiple OOB PCI devices. Each package can contain multiple
+ * power domains.
*/
struct tpmi_sst_struct {
int package_id;
- int number_of_power_domains;
- struct tpmi_per_power_domain_info *power_domain_info;
+ struct tpmi_per_power_domain_info *power_domain_info[SST_MAX_PARTITIONS];
+ u16 cdie_mask[SST_MAX_PARTITIONS];
+ u8 number_of_power_domains[SST_MAX_PARTITIONS];
+ u8 io_dies[SST_MAX_PARTITIONS];
+ u8 partition_mask;
+ u8 partition_mask_current;
};
/**
@@ -313,12 +329,11 @@ static int sst_add_perf_profiles(struct auxiliary_device *auxdev,
struct tpmi_per_power_domain_info *pd_info,
int levels)
{
+ struct device *dev = &auxdev->dev;
u64 perf_level_offsets;
int i;
- pd_info->perf_levels = devm_kcalloc(&auxdev->dev, levels,
- sizeof(struct perf_level),
- GFP_KERNEL);
+ pd_info->perf_levels = devm_kcalloc(dev, levels, sizeof(struct perf_level), GFP_KERNEL);
if (!pd_info->perf_levels)
return 0;
@@ -349,6 +364,7 @@ static int sst_add_perf_profiles(struct auxiliary_device *auxdev,
static int sst_main(struct auxiliary_device *auxdev, struct tpmi_per_power_domain_info *pd_info)
{
+ struct device *dev = &auxdev->dev;
int i, mask, levels;
*((u64 *)&pd_info->sst_header) = readq(pd_info->sst_base);
@@ -359,13 +375,13 @@ static int sst_main(struct auxiliary_device *auxdev, struct tpmi_per_power_domai
return -ENODEV;
if (TPMI_MAJOR_VERSION(pd_info->sst_header.interface_version) != ISST_MAJOR_VERSION) {
- dev_err(&auxdev->dev, "SST: Unsupported major version:%lx\n",
+ dev_err(dev, "SST: Unsupported major version:%lx\n",
TPMI_MAJOR_VERSION(pd_info->sst_header.interface_version));
return -ENODEV;
}
if (TPMI_MINOR_VERSION(pd_info->sst_header.interface_version) != ISST_MINOR_VERSION)
- dev_info(&auxdev->dev, "SST: Ignore: Unsupported minor version:%lx\n",
+ dev_info(dev, "SST: Ignore: Unsupported minor version:%lx\n",
TPMI_MINOR_VERSION(pd_info->sst_header.interface_version));
/* Read SST CP Header */
@@ -387,6 +403,126 @@ static int sst_main(struct auxiliary_device *auxdev, struct tpmi_per_power_domai
return 0;
}
+static u8 isst_instance_count(struct tpmi_sst_struct *sst_inst)
+{
+ u8 i, max_part, count = 0;
+
+ /* Partition mask starts from bit 0 and contains 1s only */
+ max_part = hweight8(sst_inst->partition_mask);
+ for (i = 0; i < max_part; i++)
+ count += sst_inst->number_of_power_domains[i];
+
+ return count;
+}
+
+/**
+ * map_cdies() - Map user domain ID to compute domain ID
+ * @sst_inst: TPMI Instance
+ * @id: User domain ID
+ * @partition: Resolved partition
+ *
+ * Helper function to map_partition_power_domain_id() to resolve compute
+ * domain ID and partition. Use hardware provided cdie_mask for a partition
+ * as is to resolve a compute domain ID.
+ *
+ * Return: %-EINVAL on error, otherwise mapped domain ID >= 0.
+ */
+static int map_cdies(struct tpmi_sst_struct *sst_inst, u8 id, u8 *partition)
+{
+ u8 i, max_part;
+
+ max_part = hweight8(sst_inst->partition_mask);
+ for (i = 0; i < max_part; i++) {
+ if (!(sst_inst->cdie_mask[i] & BIT(id)))
+ continue;
+
+ *partition = i;
+ return id - ffs(sst_inst->cdie_mask[i]) + 1;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * map_partition_power_domain_id() - Map user domain ID to partition domain ID
+ * @sst_inst: TPMI Instance
+ * @id: User domain ID
+ * @partition: Resolved partition
+ *
+ * In a partitioned system a CPU package has two separate MMIO ranges (Under
+ * two PCI devices). But the CPU package compute die/power domain IDs are
+ * unique in a package. User space can get compute die/power domain ID from
+ * CPUID and MSR 0x54 for a CPU. So, those IDs need to be preserved even if
+ * they are present in two different partitions with its own order.
+ *
+ * For example for command ISST_IF_COUNT_TPMI_INSTANCES, the valid_mask
+ * is 111111b for a 4 compute and 2 IO dies system. This is presented as
+ * provided by the hardware in a non-partitioned system with the following
+ * order:
+ * I1-I0-C3-C2-C1-C0
+ * Here: "C": for compute and "I" for IO die.
+ * Compute dies are always present first in TPMI instances, as they have
+ * to map to the real power domain/die ID of a system. In a non-partitioned
+ * system there is no way to identify compute and IO die boundaries from
+ * this driver without reading each CPU's mapping.
+ *
+ * The same order needs to be preserved, even if those compute dies are
+ * distributed among multiple partitions. For example:
+ * Partition 1 can contain: I1-C1-C0
+ * Partition 2 can contain: I2-C3-C2
+ *
+ * This will require a conversion of user space IDs to the actual index into
+ * array of stored power domains for each partition. For the above example
+ * this function will return partition and index as follows:
+ *
+ * ============= ========= ===== ========
+ * User space ID Partition Index Die type
+ * ============= ========= ===== ========
+ * 0 0 0 Compute
+ * 1 0 1 Compute
+ * 2 1 0 Compute
+ * 3 1 1 Compute
+ * 4 0 2 IO
+ * 5 1 2 IO
+ * ============= ========= ===== ========
+ *
+ * Return: %-EINVAL on error, otherwise mapped domain ID >= 0.
+ */
+static int map_partition_power_domain_id(struct tpmi_sst_struct *sst_inst, u8 id, u8 *partition)
+{
+ u8 i, io_start_id, max_part;
+
+ *partition = 0;
+
+ /* If any PCI device for partition is unbound, treat this as failure */
+ if (sst_inst->partition_mask != sst_inst->partition_mask_current)
+ return -EINVAL;
+
+ max_part = hweight8(sst_inst->partition_mask);
+
+ /* IO Index begin here */
+ io_start_id = fls(sst_inst->cdie_mask[max_part - 1]);
+
+ if (id < io_start_id)
+ return map_cdies(sst_inst, id, partition);
+
+ for (i = 0; i < max_part; i++) {
+ u8 io_id;
+
+ io_id = id - io_start_id;
+ if (io_id < sst_inst->io_dies[i]) {
+ u8 cdie_range;
+
+ cdie_range = fls(sst_inst->cdie_mask[i]) - ffs(sst_inst->cdie_mask[i]) + 1;
+ *partition = i;
+ return cdie_range + io_id;
+ }
+ io_start_id += sst_inst->io_dies[i];
+ }
+
+ return -EINVAL;
+}
+
/*
* Map a package and power_domain id to SST information structure unique for a power_domain.
* The caller should call under isst_tpmi_dev_lock.
@@ -395,19 +531,20 @@ static struct tpmi_per_power_domain_info *get_instance(int pkg_id, int power_dom
{
struct tpmi_per_power_domain_info *power_domain_info;
struct tpmi_sst_struct *sst_inst;
+ u8 part;
- if (pkg_id < 0 || pkg_id > isst_common.max_index ||
- pkg_id >= topology_max_packages())
+ if (!in_range(pkg_id, 0, topology_max_packages()) || pkg_id > isst_common.max_index)
return NULL;
sst_inst = isst_common.sst_inst[pkg_id];
if (!sst_inst)
return NULL;
- if (power_domain_id < 0 || power_domain_id >= sst_inst->number_of_power_domains)
+ power_domain_id = map_partition_power_domain_id(sst_inst, power_domain_id, &part);
+ if (power_domain_id < 0)
return NULL;
- power_domain_info = &sst_inst->power_domain_info[power_domain_id];
+ power_domain_info = &sst_inst->power_domain_info[part][power_domain_id];
if (power_domain_info && !power_domain_info->sst_base)
return NULL;
@@ -462,10 +599,10 @@ static long isst_if_core_power_state(void __user *argp)
struct tpmi_per_power_domain_info *power_domain_info;
struct isst_core_power core_power;
- if (disable_dynamic_sst_features())
+ if (copy_from_user(&core_power, argp, sizeof(core_power)))
return -EFAULT;
- if (copy_from_user(&core_power, argp, sizeof(core_power)))
+ if (core_power.get_set && disable_dynamic_sst_features())
return -EFAULT;
power_domain_info = get_instance(core_power.socket_id, core_power.power_domain_id);
@@ -579,6 +716,7 @@ static long isst_if_clos_assoc(void __user *argp)
struct tpmi_sst_struct *sst_inst;
int offset, shift, cpu;
u64 val, mask, clos;
+ u8 part;
if (copy_from_user(&clos_assoc, ptr, sizeof(clos_assoc)))
return -EFAULT;
@@ -602,10 +740,11 @@ static long isst_if_clos_assoc(void __user *argp)
sst_inst = isst_common.sst_inst[pkg_id];
- if (clos_assoc.power_domain_id > sst_inst->number_of_power_domains)
+ punit_id = map_partition_power_domain_id(sst_inst, punit_id, &part);
+ if (punit_id < 0)
return -EINVAL;
- power_domain_info = &sst_inst->power_domain_info[punit_id];
+ power_domain_info = &sst_inst->power_domain_info[part][punit_id];
if (assoc_cmds.get_set && power_domain_info->write_blocked)
return -EPERM;
@@ -708,6 +847,8 @@ static int isst_if_get_perf_level(void __user *argp)
{
struct isst_perf_level_info perf_level;
struct tpmi_per_power_domain_info *power_domain_info;
+ unsigned long level_mask;
+ u8 level, support;
if (copy_from_user(&perf_level, argp, sizeof(perf_level)))
return -EFAULT;
@@ -727,12 +868,34 @@ static int isst_if_get_perf_level(void __user *argp)
SST_PP_FEATURE_STATE_START, SST_PP_FEATURE_STATE_WIDTH, SST_MUL_FACTOR_NONE)
perf_level.enabled = !!(power_domain_info->sst_header.cap_mask & BIT(1));
- _read_bf_level_info("bf_support", perf_level.sst_bf_support, 0, 0,
- SST_BF_FEATURE_SUPPORTED_START, SST_BF_FEATURE_SUPPORTED_WIDTH,
- SST_MUL_FACTOR_NONE);
- _read_tf_level_info("tf_support", perf_level.sst_tf_support, 0, 0,
- SST_TF_FEATURE_SUPPORTED_START, SST_TF_FEATURE_SUPPORTED_WIDTH,
- SST_MUL_FACTOR_NONE);
+ level_mask = perf_level.level_mask;
+ perf_level.sst_bf_support = 0;
+ for_each_set_bit(level, &level_mask, BITS_PER_BYTE) {
+ /*
+ * Read BF support for a level. Read output is updated
+ * to "support" variable by the below macro.
+ */
+ _read_bf_level_info("bf_support", support, level, 0, SST_BF_FEATURE_SUPPORTED_START,
+ SST_BF_FEATURE_SUPPORTED_WIDTH, SST_MUL_FACTOR_NONE);
+
+ /* If supported set the bit for the level */
+ if (support)
+ perf_level.sst_bf_support |= BIT(level);
+ }
+
+ perf_level.sst_tf_support = 0;
+ for_each_set_bit(level, &level_mask, BITS_PER_BYTE) {
+ /*
+ * Read TF support for a level. Read output is updated
+ * to "support" variable by the below macro.
+ */
+ _read_tf_level_info("tf_support", support, level, 0, SST_TF_FEATURE_SUPPORTED_START,
+ SST_TF_FEATURE_SUPPORTED_WIDTH, SST_MUL_FACTOR_NONE);
+
+ /* If supported set the bit for the level */
+ if (support)
+ perf_level.sst_tf_support |= BIT(level);
+ }
if (copy_to_user(argp, &perf_level, sizeof(perf_level)))
return -EFAULT;
@@ -1134,18 +1297,28 @@ static int isst_if_get_tpmi_instance_count(void __user *argp)
if (tpmi_inst.socket_id >= topology_max_packages())
return -EINVAL;
- tpmi_inst.count = isst_common.sst_inst[tpmi_inst.socket_id]->number_of_power_domains;
-
sst_inst = isst_common.sst_inst[tpmi_inst.socket_id];
+
+ tpmi_inst.count = isst_instance_count(sst_inst);
+
tpmi_inst.valid_mask = 0;
- for (i = 0; i < sst_inst->number_of_power_domains; ++i) {
+ for (i = 0; i < tpmi_inst.count; i++) {
struct tpmi_per_power_domain_info *pd_info;
+ u8 part;
+ int pd;
- pd_info = &sst_inst->power_domain_info[i];
+ pd = map_partition_power_domain_id(sst_inst, i, &part);
+ if (pd < 0)
+ continue;
+
+ pd_info = &sst_inst->power_domain_info[part][pd];
if (pd_info->sst_base)
tpmi_inst.valid_mask |= BIT(i);
}
+ if (!tpmi_inst.valid_mask)
+ tpmi_inst.count = 0;
+
if (copy_to_user(argp, &tpmi_inst, sizeof(tpmi_inst)))
return -EFAULT;
@@ -1271,102 +1444,175 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd,
int tpmi_sst_dev_add(struct auxiliary_device *auxdev)
{
+ struct tpmi_per_power_domain_info *pd_info;
bool read_blocked = 0, write_blocked = 0;
struct intel_tpmi_plat_info *plat_info;
+ struct device *dev = &auxdev->dev;
struct tpmi_sst_struct *tpmi_sst;
- int i, ret, pkg = 0, inst = 0;
- int num_resources;
+ u8 i, num_resources, io_die_cnt;
+ int ret, pkg = 0, inst = 0;
+ bool first_enum = false;
+ u16 cdie_mask;
+ u8 partition;
ret = tpmi_get_feature_status(auxdev, TPMI_ID_SST, &read_blocked, &write_blocked);
if (ret)
- dev_info(&auxdev->dev, "Can't read feature status: ignoring read/write blocked status\n");
+ dev_info(dev, "Can't read feature status: ignoring read/write blocked status\n");
if (read_blocked) {
- dev_info(&auxdev->dev, "Firmware has blocked reads, exiting\n");
+ dev_info(dev, "Firmware has blocked reads, exiting\n");
return -ENODEV;
}
plat_info = tpmi_get_platform_data(auxdev);
if (!plat_info) {
- dev_err(&auxdev->dev, "No platform info\n");
+ dev_err(dev, "No platform info\n");
return -EINVAL;
}
pkg = plat_info->package_id;
if (pkg >= topology_max_packages()) {
- dev_err(&auxdev->dev, "Invalid package id :%x\n", pkg);
+ dev_err(dev, "Invalid package id :%x\n", pkg);
return -EINVAL;
}
- if (isst_common.sst_inst[pkg])
- return -EEXIST;
+ partition = plat_info->partition;
+ if (partition >= SST_MAX_PARTITIONS) {
+ dev_err(&auxdev->dev, "Invalid partition :%x\n", partition);
+ return -EINVAL;
+ }
num_resources = tpmi_get_resource_count(auxdev);
if (!num_resources)
return -EINVAL;
- tpmi_sst = devm_kzalloc(&auxdev->dev, sizeof(*tpmi_sst), GFP_KERNEL);
- if (!tpmi_sst)
- return -ENOMEM;
+ mutex_lock(&isst_tpmi_dev_lock);
- tpmi_sst->power_domain_info = devm_kcalloc(&auxdev->dev, num_resources,
- sizeof(*tpmi_sst->power_domain_info),
- GFP_KERNEL);
- if (!tpmi_sst->power_domain_info)
- return -ENOMEM;
+ if (isst_common.sst_inst[pkg]) {
+ tpmi_sst = isst_common.sst_inst[pkg];
+ } else {
+ /*
+ * tpmi_sst instance is for a package. So needs to be
+ * allocated only once for both partitions. We can't use
+ * devm_* allocation here as each partition is a
+ * different device, which can be unbound.
+ */
+ tpmi_sst = kzalloc(sizeof(*tpmi_sst), GFP_KERNEL);
+ if (!tpmi_sst) {
+ ret = -ENOMEM;
+ goto unlock_exit;
+ }
+ first_enum = true;
+ }
- tpmi_sst->number_of_power_domains = num_resources;
+ ret = 0;
+
+ pd_info = devm_kcalloc(dev, num_resources, sizeof(*pd_info), GFP_KERNEL);
+ if (!pd_info) {
+ ret = -ENOMEM;
+ goto unlock_free;
+ }
+
+ /* Get the IO die count, if cdie_mask is present */
+ if (plat_info->cdie_mask) {
+ u8 cdie_range;
+
+ cdie_mask = plat_info->cdie_mask;
+ cdie_range = fls(cdie_mask) - ffs(cdie_mask) + 1;
+ io_die_cnt = num_resources - cdie_range;
+ } else {
+ /*
+ * This is a synthetic mask, careful when assuming that
+ * they are compute dies only.
+ */
+ cdie_mask = (1 << num_resources) - 1;
+ io_die_cnt = 0;
+ }
for (i = 0; i < num_resources; ++i) {
struct resource *res;
res = tpmi_get_resource_at_index(auxdev, i);
if (!res) {
- tpmi_sst->power_domain_info[i].sst_base = NULL;
+ pd_info[i].sst_base = NULL;
continue;
}
- tpmi_sst->power_domain_info[i].package_id = pkg;
- tpmi_sst->power_domain_info[i].power_domain_id = i;
- tpmi_sst->power_domain_info[i].auxdev = auxdev;
- tpmi_sst->power_domain_info[i].write_blocked = write_blocked;
- tpmi_sst->power_domain_info[i].sst_base = devm_ioremap_resource(&auxdev->dev, res);
- if (IS_ERR(tpmi_sst->power_domain_info[i].sst_base))
- return PTR_ERR(tpmi_sst->power_domain_info[i].sst_base);
+ pd_info[i].package_id = pkg;
+ pd_info[i].power_domain_id = i;
+ pd_info[i].auxdev = auxdev;
+ pd_info[i].write_blocked = write_blocked;
+ pd_info[i].sst_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(pd_info[i].sst_base)) {
+ ret = PTR_ERR(pd_info[i].sst_base);
+ goto unlock_free;
+ }
- ret = sst_main(auxdev, &tpmi_sst->power_domain_info[i]);
+ ret = sst_main(auxdev, &pd_info[i]);
if (ret) {
- devm_iounmap(&auxdev->dev, tpmi_sst->power_domain_info[i].sst_base);
- tpmi_sst->power_domain_info[i].sst_base = NULL;
+ /*
+ * This entry is not valid, hardware can partially
+ * populate dies. In this case MMIO will have 0xFFs.
+ * Also possible some pre-production hardware has
+ * invalid data. But don't fail and continue to use
+ * other dies with valid data.
+ */
+ devm_iounmap(dev, pd_info[i].sst_base);
+ pd_info[i].sst_base = NULL;
continue;
}
++inst;
}
- if (!inst)
- return -ENODEV;
+ if (!inst) {
+ ret = -ENODEV;
+ goto unlock_free;
+ }
tpmi_sst->package_id = pkg;
+
+ tpmi_sst->power_domain_info[partition] = pd_info;
+ tpmi_sst->number_of_power_domains[partition] = num_resources;
+ tpmi_sst->cdie_mask[partition] = cdie_mask;
+ tpmi_sst->io_dies[partition] = io_die_cnt;
+ tpmi_sst->partition_mask |= BIT(partition);
+ tpmi_sst->partition_mask_current |= BIT(partition);
+
auxiliary_set_drvdata(auxdev, tpmi_sst);
- mutex_lock(&isst_tpmi_dev_lock);
if (isst_common.max_index < pkg)
isst_common.max_index = pkg;
isst_common.sst_inst[pkg] = tpmi_sst;
+
+unlock_free:
+ if (ret && first_enum)
+ kfree(tpmi_sst);
+unlock_exit:
mutex_unlock(&isst_tpmi_dev_lock);
- return 0;
+ return ret;
}
EXPORT_SYMBOL_NS_GPL(tpmi_sst_dev_add, INTEL_TPMI_SST);
void tpmi_sst_dev_remove(struct auxiliary_device *auxdev)
{
struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev);
+ struct intel_tpmi_plat_info *plat_info;
+
+ plat_info = tpmi_get_platform_data(auxdev);
+ if (!plat_info)
+ return;
mutex_lock(&isst_tpmi_dev_lock);
- isst_common.sst_inst[tpmi_sst->package_id] = NULL;
+ tpmi_sst->power_domain_info[plat_info->partition] = NULL;
+ tpmi_sst->partition_mask_current &= ~BIT(plat_info->partition);
+ /* Free the package instance when the all partitions are removed */
+ if (!tpmi_sst->partition_mask_current) {
+ isst_common.sst_inst[tpmi_sst->package_id] = NULL;
+ kfree(tpmi_sst);
+ }
mutex_unlock(&isst_tpmi_dev_lock);
}
EXPORT_SYMBOL_NS_GPL(tpmi_sst_dev_remove, INTEL_TPMI_SST);
@@ -1374,9 +1620,16 @@ EXPORT_SYMBOL_NS_GPL(tpmi_sst_dev_remove, INTEL_TPMI_SST);
void tpmi_sst_dev_suspend(struct auxiliary_device *auxdev)
{
struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev);
- struct tpmi_per_power_domain_info *power_domain_info = tpmi_sst->power_domain_info;
+ struct tpmi_per_power_domain_info *power_domain_info;
+ struct intel_tpmi_plat_info *plat_info;
void __iomem *cp_base;
+ plat_info = tpmi_get_platform_data(auxdev);
+ if (!plat_info)
+ return;
+
+ power_domain_info = tpmi_sst->power_domain_info[plat_info->partition];
+
cp_base = power_domain_info->sst_base + power_domain_info->sst_header.cp_offset;
power_domain_info->saved_sst_cp_control = readq(cp_base + SST_CP_CONTROL_OFFSET);
@@ -1395,9 +1648,16 @@ EXPORT_SYMBOL_NS_GPL(tpmi_sst_dev_suspend, INTEL_TPMI_SST);
void tpmi_sst_dev_resume(struct auxiliary_device *auxdev)
{
struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev);
- struct tpmi_per_power_domain_info *power_domain_info = tpmi_sst->power_domain_info;
+ struct tpmi_per_power_domain_info *power_domain_info;
+ struct intel_tpmi_plat_info *plat_info;
void __iomem *cp_base;
+ plat_info = tpmi_get_platform_data(auxdev);
+ if (!plat_info)
+ return;
+
+ power_domain_info = tpmi_sst->power_domain_info[plat_info->partition];
+
cp_base = power_domain_info->sst_base + power_domain_info->sst_header.cp_offset;
writeq(power_domain_info->saved_sst_cp_control, cp_base + SST_CP_CONTROL_OFFSET);
@@ -1412,7 +1672,7 @@ void tpmi_sst_dev_resume(struct auxiliary_device *auxdev)
}
EXPORT_SYMBOL_NS_GPL(tpmi_sst_dev_resume, INTEL_TPMI_SST);
-#define ISST_TPMI_API_VERSION 0x02
+#define ISST_TPMI_API_VERSION 0x03
int tpmi_sst_init(void)
{
@@ -1469,4 +1729,5 @@ EXPORT_SYMBOL_NS_GPL(tpmi_sst_exit, INTEL_TPMI_SST);
MODULE_IMPORT_NS(INTEL_TPMI);
MODULE_IMPORT_NS(INTEL_TPMI_POWER_DOMAIN);
+MODULE_DESCRIPTION("ISST TPMI interface module");
MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/intel/tpmi.c b/drivers/platform/x86/intel/tpmi.c
index 910df7c654..6c0cbccd80 100644
--- a/drivers/platform/x86/intel/tpmi.c
+++ b/drivers/platform/x86/intel/tpmi.c
@@ -128,6 +128,9 @@ struct intel_tpmi_info {
* @dev: PCI device number
* @bus: PCI bus number
* @pkg: CPU Package id
+ * @segment: PCI segment id
+ * @partition: Package Partition id
+ * @cdie_mask: Bitmap of compute dies in the current partition
* @reserved: Reserved for future use
* @lock: When set to 1 the register is locked and becomes read-only
* until next reset. Not for use by the OS driver.
@@ -139,7 +142,10 @@ struct tpmi_info_header {
u64 dev:5;
u64 bus:8;
u64 pkg:8;
- u64 reserved:39;
+ u64 segment:8;
+ u64 partition:2;
+ u64 cdie_mask:16;
+ u64 reserved:13;
u64 lock:1;
} __packed;
@@ -666,28 +672,44 @@ static int tpmi_create_devices(struct intel_tpmi_info *tpmi_info)
}
#define TPMI_INFO_BUS_INFO_OFFSET 0x08
+#define TPMI_INFO_MAJOR_VERSION 0x00
+#define TPMI_INFO_MINOR_VERSION 0x02
static int tpmi_process_info(struct intel_tpmi_info *tpmi_info,
struct intel_tpmi_pm_feature *pfs)
{
struct tpmi_info_header header;
void __iomem *info_mem;
+ u64 feature_header;
+ int ret = 0;
- info_mem = ioremap(pfs->vsec_offset + TPMI_INFO_BUS_INFO_OFFSET,
- pfs->pfs_header.entry_size * sizeof(u32) - TPMI_INFO_BUS_INFO_OFFSET);
+ info_mem = ioremap(pfs->vsec_offset, pfs->pfs_header.entry_size * sizeof(u32));
if (!info_mem)
return -ENOMEM;
- memcpy_fromio(&header, info_mem, sizeof(header));
+ feature_header = readq(info_mem);
+ if (TPMI_MAJOR_VERSION(feature_header) != TPMI_INFO_MAJOR_VERSION) {
+ ret = -ENODEV;
+ goto error_info_header;
+ }
+
+ memcpy_fromio(&header, info_mem + TPMI_INFO_BUS_INFO_OFFSET, sizeof(header));
tpmi_info->plat_info.package_id = header.pkg;
tpmi_info->plat_info.bus_number = header.bus;
tpmi_info->plat_info.device_number = header.dev;
tpmi_info->plat_info.function_number = header.fn;
+ if (TPMI_MINOR_VERSION(feature_header) >= TPMI_INFO_MINOR_VERSION) {
+ tpmi_info->plat_info.cdie_mask = header.cdie_mask;
+ tpmi_info->plat_info.partition = header.partition;
+ tpmi_info->plat_info.segment = header.segment;
+ }
+
+error_info_header:
iounmap(info_mem);
- return 0;
+ return ret;
}
static int tpmi_fetch_pfs_header(struct intel_tpmi_pm_feature *pfs, u64 start, int size)
@@ -763,8 +785,11 @@ static int intel_vsec_tpmi_init(struct auxiliary_device *auxdev)
* when actual device nodes created outside this
* loop via tpmi_create_devices().
*/
- if (pfs->pfs_header.tpmi_id == TPMI_INFO_ID)
- tpmi_process_info(tpmi_info, pfs);
+ if (pfs->pfs_header.tpmi_id == TPMI_INFO_ID) {
+ ret = tpmi_process_info(tpmi_info, pfs);
+ if (ret)
+ return ret;
+ }
if (pfs->pfs_header.tpmi_id == TPMI_CONTROL_ID)
tpmi_set_control_base(auxdev, tpmi_info, pfs);
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 bd75d61ff8..bb8e72deb3 100644
--- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c
+++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c
@@ -29,7 +29,7 @@
#include "uncore-frequency-common.h"
#define UNCORE_MAJOR_VERSION 0
-#define UNCORE_MINOR_VERSION 1
+#define UNCORE_MINOR_VERSION 2
#define UNCORE_HEADER_INDEX 0
#define UNCORE_FABRIC_CLUSTER_OFFSET 8
@@ -240,6 +240,7 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_
bool read_blocked = 0, write_blocked = 0;
struct intel_tpmi_plat_info *plat_info;
struct tpmi_uncore_struct *tpmi_uncore;
+ bool uncore_sysfs_added = false;
int ret, i, pkg = 0;
int num_resources;
@@ -329,7 +330,7 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_
goto remove_clusters;
}
- if (TPMI_MINOR_VERSION(pd_info->ufs_header_ver) != UNCORE_MINOR_VERSION)
+ if (TPMI_MINOR_VERSION(pd_info->ufs_header_ver) > UNCORE_MINOR_VERSION)
dev_info(&auxdev->dev, "Uncore: Ignore: Unsupported minor version:%lx\n",
TPMI_MINOR_VERSION(pd_info->ufs_header_ver));
@@ -384,9 +385,15 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_
}
/* Point to next cluster offset */
cluster_offset >>= UNCORE_MAX_CLUSTER_PER_DOMAIN;
+ uncore_sysfs_added = true;
}
}
+ if (!uncore_sysfs_added) {
+ ret = -ENODEV;
+ goto remove_clusters;
+ }
+
auxiliary_set_drvdata(auxdev, tpmi_uncore);
tpmi_uncore->root_cluster.root_domain = true;
diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c
index a5e0f5c221..b89c0dda9e 100644
--- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c
+++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c
@@ -242,7 +242,7 @@ static int __init intel_uncore_init(void)
return -ENODEV;
uncore_max_entries = topology_max_packages() *
- topology_max_die_per_package();
+ topology_max_dies_per_package();
uncore_instances = kcalloc(uncore_max_entries,
sizeof(*uncore_instances), GFP_KERNEL);
if (!uncore_instances)
diff --git a/drivers/platform/x86/intel/vbtn.c b/drivers/platform/x86/intel/vbtn.c
index 5d13452bb9..a353e830b6 100644
--- a/drivers/platform/x86/intel/vbtn.c
+++ b/drivers/platform/x86/intel/vbtn.c
@@ -7,11 +7,13 @@
*/
#include <linux/acpi.h>
+#include <linux/cleanup.h>
#include <linux/dmi.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/suspend.h>
#include "../dual_accel_detect.h"
@@ -24,6 +26,7 @@
#define VGBS_TABLET_MODE_FLAGS (VGBS_TABLET_MODE_FLAG | VGBS_TABLET_MODE_FLAG_ALT)
+MODULE_DESCRIPTION("Intel Virtual Button driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AceLan Kao");
@@ -65,6 +68,7 @@ static const struct key_entry intel_vbtn_switchmap[] = {
};
struct intel_vbtn_priv {
+ struct mutex mutex; /* Avoid notify_handler() racing with itself */
struct input_dev *buttons_dev;
struct input_dev *switches_dev;
bool dual_accel;
@@ -154,9 +158,12 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)
bool autorelease;
int ret;
+ guard(mutex)(&priv->mutex);
+
if ((ke = sparse_keymap_entry_from_scancode(priv->buttons_dev, event))) {
if (!priv->has_buttons) {
- dev_warn(&device->dev, "Warning: received a button event on a device without buttons, please report this.\n");
+ dev_warn(&device->dev, "Warning: received 0x%02x button event on a device without buttons, please report this.\n",
+ event);
return;
}
input_dev = priv->buttons_dev;
@@ -256,9 +263,6 @@ static const struct dmi_system_id dmi_switches_allow_list[] = {
static bool intel_vbtn_has_switches(acpi_handle handle, bool dual_accel)
{
- unsigned long long vgbs;
- acpi_status status;
-
/* See dual_accel_detect.h for more info */
if (dual_accel)
return false;
@@ -266,8 +270,7 @@ static bool intel_vbtn_has_switches(acpi_handle handle, bool dual_accel)
if (!dmi_check_system(dmi_switches_allow_list))
return false;
- status = acpi_evaluate_integer(handle, "VGBS", NULL, &vgbs);
- return ACPI_SUCCESS(status);
+ return acpi_has_method(handle, "VGBS");
}
static int intel_vbtn_probe(struct platform_device *device)
@@ -292,6 +295,10 @@ static int intel_vbtn_probe(struct platform_device *device)
return -ENOMEM;
dev_set_drvdata(&device->dev, priv);
+ err = devm_mutex_init(&device->dev, &priv->mutex);
+ if (err)
+ return err;
+
priv->dual_accel = dual_accel;
priv->has_buttons = has_buttons;
priv->has_switches = has_switches;
diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c
index 778eb0aa34..0fdfaf3a4f 100644
--- a/drivers/platform/x86/intel/vsec.c
+++ b/drivers/platform/x86/intel/vsec.c
@@ -236,10 +236,7 @@ static bool intel_vsec_walk_header(struct pci_dev *pdev,
for ( ; *header; header++) {
ret = intel_vsec_add_dev(pdev, *header, info);
- if (ret)
- dev_info(&pdev->dev, "Could not add device for VSEC id %d\n",
- (*header)->id);
- else
+ if (!ret)
have_devices = true;
}
diff --git a/drivers/platform/x86/intel/wmi/sbl-fw-update.c b/drivers/platform/x86/intel/wmi/sbl-fw-update.c
index 040153ad67..75c82c0811 100644
--- a/drivers/platform/x86/intel/wmi/sbl-fw-update.c
+++ b/drivers/platform/x86/intel/wmi/sbl-fw-update.c
@@ -131,6 +131,7 @@ static struct wmi_driver intel_wmi_sbl_fw_update_driver = {
.probe = intel_wmi_sbl_fw_update_probe,
.remove = intel_wmi_sbl_fw_update_remove,
.id_table = intel_wmi_sbl_id_table,
+ .no_singleton = true,
};
module_wmi_driver(intel_wmi_sbl_fw_update_driver);
diff --git a/drivers/platform/x86/intel/wmi/thunderbolt.c b/drivers/platform/x86/intel/wmi/thunderbolt.c
index e2ad3f46f3..08df560a2c 100644
--- a/drivers/platform/x86/intel/wmi/thunderbolt.c
+++ b/drivers/platform/x86/intel/wmi/thunderbolt.c
@@ -63,6 +63,7 @@ static struct wmi_driver intel_wmi_thunderbolt_driver = {
.dev_groups = tbt_groups,
},
.id_table = intel_wmi_thunderbolt_id_table,
+ .no_singleton = true,
};
module_wmi_driver(intel_wmi_thunderbolt_driver);
diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c
index ba38649cc1..73ec4460a1 100644
--- a/drivers/platform/x86/intel_ips.c
+++ b/drivers/platform/x86/intel_ips.c
@@ -1505,7 +1505,7 @@ static int ips_probe(struct pci_dev *dev, const struct pci_device_id *id)
* IRQ handler for ME interaction
* Note: don't use MSI here as the PCH has bugs.
*/
- ret = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_LEGACY);
+ ret = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_INTX);
if (ret < 0)
return ret;
diff --git a/drivers/platform/x86/intel_scu_ipcutil.c b/drivers/platform/x86/intel_scu_ipcutil.c
index b7c10c15a3..7d87cbd4b9 100644
--- a/drivers/platform/x86/intel_scu_ipcutil.c
+++ b/drivers/platform/x86/intel_scu_ipcutil.c
@@ -22,7 +22,7 @@
static int major;
-struct intel_scu_ipc_dev *scu;
+static struct intel_scu_ipc_dev *scu;
static DEFINE_MUTEX(scu_lock);
/* IOCTL commands */
diff --git a/drivers/platform/x86/intel_scu_pcidrv.c b/drivers/platform/x86/intel_scu_pcidrv.c
index d904fad499..dbf0310448 100644
--- a/drivers/platform/x86/intel_scu_pcidrv.c
+++ b/drivers/platform/x86/intel_scu_pcidrv.c
@@ -11,7 +11,6 @@
#include <linux/init.h>
#include <linux/pci.h>
-#include <asm/intel-mid.h>
#include <asm/intel_scu_ipc.h>
static int intel_scu_pci_probe(struct pci_dev *pdev,
diff --git a/drivers/platform/x86/intel_scu_wdt.c b/drivers/platform/x86/intel_scu_wdt.c
index c2479777a1..a5031a2563 100644
--- a/drivers/platform/x86/intel_scu_wdt.c
+++ b/drivers/platform/x86/intel_scu_wdt.c
@@ -13,7 +13,6 @@
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
-#include <asm/intel-mid.h>
#include <asm/io_apic.h>
#include <asm/hw_irq.h>
diff --git a/drivers/platform/x86/lenovo-wmi-camera.c b/drivers/platform/x86/lenovo-wmi-camera.c
new file mode 100644
index 0000000000..0c0bedaf74
--- /dev/null
+++ b/drivers/platform/x86/lenovo-wmi-camera.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Lenovo WMI Camera Button Driver
+ *
+ * Author: Ai Chao <aichao@kylinos.cn>
+ * Copyright (C) 2024 KylinSoft Corporation.
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/wmi.h>
+
+#define WMI_LENOVO_CAMERABUTTON_EVENT_GUID "50C76F1F-D8E4-D895-0A3D-62F4EA400013"
+
+struct lenovo_wmi_priv {
+ struct input_dev *idev;
+ struct mutex notify_lock; /* lenovo WMI camera button notify lock */
+};
+
+enum {
+ SW_CAMERA_OFF = 0,
+ SW_CAMERA_ON = 1,
+};
+
+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) {
+ dev_err(&wdev->dev, "Bad response type %u\n", obj->type);
+ return;
+ }
+
+ if (obj->buffer.length != 1) {
+ dev_err(&wdev->dev, "Invalid buffer length %u\n", obj->buffer.length);
+ return;
+ }
+
+ /*
+ * obj->buffer.pointer[0] is camera mode:
+ * 0 camera close
+ * 1 camera open
+ */
+ camera_mode = obj->buffer.pointer[0];
+ if (camera_mode > SW_CAMERA_ON) {
+ dev_err(&wdev->dev, "Unknown camera mode %u\n", camera_mode);
+ return;
+ }
+
+ mutex_lock(&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);
+
+ mutex_unlock(&priv->notify_lock);
+}
+
+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)
+ return -ENOMEM;
+
+ 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;
+}
+
+static void lenovo_wmi_remove(struct wmi_device *wdev)
+{
+ struct lenovo_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
+
+ mutex_destroy(&priv->notify_lock);
+}
+
+static const struct wmi_device_id lenovo_wmi_id_table[] = {
+ { .guid_string = WMI_LENOVO_CAMERABUTTON_EVENT_GUID },
+ { }
+};
+MODULE_DEVICE_TABLE(wmi, lenovo_wmi_id_table);
+
+static struct wmi_driver lenovo_wmi_driver = {
+ .driver = {
+ .name = "lenovo-wmi-camera",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .id_table = lenovo_wmi_id_table,
+ .no_singleton = true,
+ .probe = lenovo_wmi_probe,
+ .notify = lenovo_wmi_notify,
+ .remove = lenovo_wmi_remove,
+};
+module_wmi_driver(lenovo_wmi_driver);
+
+MODULE_AUTHOR("Ai Chao <aichao@kylinos.cn>");
+MODULE_DESCRIPTION("Lenovo WMI Camera Button Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/lenovo-ymc.c b/drivers/platform/x86/lenovo-ymc.c
index e1fbc35504..e0bbd6a14a 100644
--- a/drivers/platform/x86/lenovo-ymc.c
+++ b/drivers/platform/x86/lenovo-ymc.c
@@ -20,32 +20,10 @@
#define LENOVO_YMC_QUERY_INSTANCE 0
#define LENOVO_YMC_QUERY_METHOD 0x01
-static bool ec_trigger __read_mostly;
-module_param(ec_trigger, bool, 0444);
-MODULE_PARM_DESC(ec_trigger, "Enable EC triggering work-around to force emitting tablet mode events");
-
static bool force;
module_param(force, bool, 0444);
MODULE_PARM_DESC(force, "Force loading on boards without a convertible DMI chassis-type");
-static const struct dmi_system_id ec_trigger_quirk_dmi_table[] = {
- {
- /* Lenovo Yoga 7 14ARB7 */
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_NAME, "82QF"),
- },
- },
- {
- /* Lenovo Yoga 7 14ACN6 */
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_NAME, "82N7"),
- },
- },
- { }
-};
-
static const struct dmi_system_id allowed_chasis_types_dmi_table[] = {
{
.matches = {
@@ -62,21 +40,8 @@ static const struct dmi_system_id allowed_chasis_types_dmi_table[] = {
struct lenovo_ymc_private {
struct input_dev *input_dev;
- struct acpi_device *ec_acpi_dev;
};
-static void lenovo_ymc_trigger_ec(struct wmi_device *wdev, struct lenovo_ymc_private *priv)
-{
- int err;
-
- if (!priv->ec_acpi_dev)
- return;
-
- err = write_ec_cmd(priv->ec_acpi_dev->handle, VPCCMD_W_YMC, 1);
- if (err)
- dev_warn(&wdev->dev, "Could not write YMC: %d\n", err);
-}
-
static const struct key_entry lenovo_ymc_keymap[] = {
/* Laptop */
{ KE_SW, 0x01, { .sw = { SW_TABLET_MODE, 0 } } },
@@ -125,11 +90,9 @@ static void lenovo_ymc_notify(struct wmi_device *wdev, union acpi_object *data)
free_obj:
kfree(obj);
- lenovo_ymc_trigger_ec(wdev, priv);
+ ideapad_laptop_call_notifier(IDEAPAD_LAPTOP_YMC_EVENT, &code);
}
-static void acpi_dev_put_helper(void *p) { acpi_dev_put(p); }
-
static int lenovo_ymc_probe(struct wmi_device *wdev, const void *ctx)
{
struct lenovo_ymc_private *priv;
@@ -143,29 +106,10 @@ static int lenovo_ymc_probe(struct wmi_device *wdev, const void *ctx)
return -ENODEV;
}
- ec_trigger |= dmi_check_system(ec_trigger_quirk_dmi_table);
-
priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
- if (ec_trigger) {
- pr_debug("Lenovo YMC enable EC triggering.\n");
- priv->ec_acpi_dev = acpi_dev_get_first_match_dev("VPC2004", NULL, -1);
-
- if (!priv->ec_acpi_dev) {
- dev_err(&wdev->dev, "Could not find EC ACPI device.\n");
- return -ENODEV;
- }
- err = devm_add_action_or_reset(&wdev->dev,
- acpi_dev_put_helper, priv->ec_acpi_dev);
- if (err) {
- dev_err(&wdev->dev,
- "Could not clean up EC ACPI device: %d\n", err);
- return err;
- }
- }
-
input_dev = devm_input_allocate_device(&wdev->dev);
if (!input_dev)
return -ENOMEM;
@@ -192,7 +136,6 @@ static int lenovo_ymc_probe(struct wmi_device *wdev, const void *ctx)
dev_set_drvdata(&wdev->dev, priv);
/* Report the state for the first time on probe */
- lenovo_ymc_trigger_ec(wdev, priv);
lenovo_ymc_notify(wdev, NULL);
return 0;
}
@@ -217,3 +160,4 @@ module_wmi_driver(lenovo_ymc_driver);
MODULE_AUTHOR("Gergo Koteles <soyer@irl.hu>");
MODULE_DESCRIPTION("Lenovo Yoga Mode Control driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IDEAPAD_LAPTOP);
diff --git a/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c b/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c
new file mode 100644
index 0000000000..d525bdc8ca
--- /dev/null
+++ b/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Support for the custom fast charging protocol found on the Lenovo Yoga
+ * Tablet 2 1380F / 1380L models.
+ *
+ * Copyright (C) 2024 Hans de Goede <hansg@kernel.org>
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/extcon.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/platform_device.h>
+#include <linux/serdev.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include "serdev_helpers.h"
+
+#define YT2_1380_FC_PDEV_NAME "lenovo-yoga-tab2-pro-1380-fastcharger"
+#define YT2_1380_FC_SERDEV_CTRL "serial0"
+#define YT2_1380_FC_SERDEV_NAME "serial0-0"
+#define YT2_1380_FC_EXTCON_NAME "i2c-lc824206xa"
+
+#define YT2_1380_FC_MAX_TRIES 5
+#define YT2_1380_FC_PIN_SW_DELAY_US (10 * USEC_PER_MSEC)
+#define YT2_1380_FC_UART_DRAIN_DELAY_US (50 * USEC_PER_MSEC)
+#define YT2_1380_FC_VOLT_SW_DELAY_US (1000 * USEC_PER_MSEC)
+
+struct yt2_1380_fc {
+ struct device *dev;
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *gpio_state;
+ struct pinctrl_state *uart_state;
+ struct gpio_desc *uart3_txd;
+ struct gpio_desc *uart3_rxd;
+ struct extcon_dev *extcon;
+ struct notifier_block nb;
+ struct work_struct work;
+ bool fast_charging;
+};
+
+static int yt2_1380_fc_set_gpio_mode(struct yt2_1380_fc *fc, bool enable)
+{
+ struct pinctrl_state *state = enable ? fc->gpio_state : fc->uart_state;
+ int ret;
+
+ ret = pinctrl_select_state(fc->pinctrl, state);
+ if (ret) {
+ dev_err(fc->dev, "Error %d setting pinctrl state\n", ret);
+ return ret;
+ }
+
+ fsleep(YT2_1380_FC_PIN_SW_DELAY_US);
+ return 0;
+}
+
+static bool yt2_1380_fc_dedicated_charger_connected(struct yt2_1380_fc *fc)
+{
+ return extcon_get_state(fc->extcon, EXTCON_CHG_USB_DCP) > 0;
+}
+
+static bool yt2_1380_fc_fast_charger_connected(struct yt2_1380_fc *fc)
+{
+ return extcon_get_state(fc->extcon, EXTCON_CHG_USB_FAST) > 0;
+}
+
+static void yt2_1380_fc_worker(struct work_struct *work)
+{
+ struct yt2_1380_fc *fc = container_of(work, struct yt2_1380_fc, work);
+ int i, ret;
+
+ /* Do nothing if already fast charging */
+ if (yt2_1380_fc_fast_charger_connected(fc))
+ return;
+
+ for (i = 0; i < YT2_1380_FC_MAX_TRIES; i++) {
+ /* Set pins to UART mode (for charger disconnect and retries) */
+ ret = yt2_1380_fc_set_gpio_mode(fc, false);
+ if (ret)
+ return;
+
+ /* Only try 12V charging if a dedicated charger is detected */
+ if (!yt2_1380_fc_dedicated_charger_connected(fc))
+ return;
+
+ /* Send the command to switch to 12V charging */
+ ret = serdev_device_write_buf(to_serdev_device(fc->dev), "SC", strlen("SC"));
+ if (ret != strlen("SC")) {
+ dev_err(fc->dev, "Error %d writing to uart\n", ret);
+ return;
+ }
+
+ fsleep(YT2_1380_FC_UART_DRAIN_DELAY_US);
+
+ /* Re-check a charger is still connected */
+ if (!yt2_1380_fc_dedicated_charger_connected(fc))
+ return;
+
+ /*
+ * Now switch the lines to GPIO (output, high). The charger
+ * expects the lines being driven high after the command.
+ * Presumably this is used to detect the tablet getting
+ * unplugged (to switch back to 5V output on unplug).
+ */
+ ret = yt2_1380_fc_set_gpio_mode(fc, true);
+ if (ret)
+ return;
+
+ fsleep(YT2_1380_FC_VOLT_SW_DELAY_US);
+
+ if (yt2_1380_fc_fast_charger_connected(fc))
+ return; /* Success */
+ }
+
+ dev_dbg(fc->dev, "Failed to switch to 12V charging (not the original charger?)\n");
+ /* Failed to enable 12V fast charging, reset pins to default UART mode */
+ yt2_1380_fc_set_gpio_mode(fc, false);
+}
+
+static int yt2_1380_fc_extcon_evt(struct notifier_block *nb,
+ unsigned long event, void *param)
+{
+ struct yt2_1380_fc *fc = container_of(nb, struct yt2_1380_fc, nb);
+
+ schedule_work(&fc->work);
+ return NOTIFY_OK;
+}
+
+static size_t yt2_1380_fc_receive(struct serdev_device *serdev, const u8 *data, size_t len)
+{
+ /*
+ * Since the USB data lines are shorted for DCP detection, echos of
+ * the "SC" command send in yt2_1380_fc_worker() will be received.
+ */
+ dev_dbg(&serdev->dev, "recv: %*ph\n", (int)len, data);
+ return len;
+}
+
+static const struct serdev_device_ops yt2_1380_fc_serdev_ops = {
+ .receive_buf = yt2_1380_fc_receive,
+ .write_wakeup = serdev_device_write_wakeup,
+};
+
+static int yt2_1380_fc_serdev_probe(struct serdev_device *serdev)
+{
+ struct device *dev = &serdev->dev;
+ struct yt2_1380_fc *fc;
+ int ret;
+
+ fc = devm_kzalloc(dev, sizeof(*fc), GFP_KERNEL);
+ if (!fc)
+ return -ENOMEM;
+
+ fc->dev = dev;
+ fc->nb.notifier_call = yt2_1380_fc_extcon_evt;
+ INIT_WORK(&fc->work, yt2_1380_fc_worker);
+
+ /*
+ * Do this first since it may return -EPROBE_DEFER.
+ * There is no extcon_put(), so there is no need to free this.
+ */
+ fc->extcon = extcon_get_extcon_dev(YT2_1380_FC_EXTCON_NAME);
+ if (IS_ERR(fc->extcon))
+ return dev_err_probe(dev, PTR_ERR(fc->extcon), "getting extcon\n");
+
+ fc->pinctrl = devm_pinctrl_get(dev);
+ if (IS_ERR(fc->pinctrl))
+ return dev_err_probe(dev, PTR_ERR(fc->pinctrl), "getting pinctrl\n");
+
+ /*
+ * To switch the UART3 pins connected to the USB data lines between
+ * UART and GPIO modes.
+ */
+ fc->gpio_state = pinctrl_lookup_state(fc->pinctrl, "uart3_gpio");
+ fc->uart_state = pinctrl_lookup_state(fc->pinctrl, "uart3_uart");
+ if (IS_ERR(fc->gpio_state) || IS_ERR(fc->uart_state))
+ return dev_err_probe(dev, -EINVAL, "getting pinctrl states\n");
+
+ ret = yt2_1380_fc_set_gpio_mode(fc, true);
+ if (ret)
+ return ret;
+
+ fc->uart3_txd = devm_gpiod_get(dev, "uart3_txd", GPIOD_OUT_HIGH);
+ if (IS_ERR(fc->uart3_txd))
+ return dev_err_probe(dev, PTR_ERR(fc->uart3_txd), "getting uart3_txd gpio\n");
+
+ fc->uart3_rxd = devm_gpiod_get(dev, "uart3_rxd", GPIOD_OUT_HIGH);
+ if (IS_ERR(fc->uart3_rxd))
+ return dev_err_probe(dev, PTR_ERR(fc->uart3_rxd), "getting uart3_rxd gpio\n");
+
+ ret = yt2_1380_fc_set_gpio_mode(fc, false);
+ if (ret)
+ return ret;
+
+ ret = devm_serdev_device_open(dev, serdev);
+ if (ret)
+ return dev_err_probe(dev, ret, "opening UART device\n");
+
+ serdev_device_set_baudrate(serdev, 600);
+ serdev_device_set_flow_control(serdev, false);
+ serdev_device_set_drvdata(serdev, fc);
+ serdev_device_set_client_ops(serdev, &yt2_1380_fc_serdev_ops);
+
+ ret = devm_extcon_register_notifier_all(dev, fc->extcon, &fc->nb);
+ if (ret)
+ return dev_err_probe(dev, ret, "registering extcon notifier\n");
+
+ /* In case the extcon already has detected a DCP charger */
+ schedule_work(&fc->work);
+
+ return 0;
+}
+
+struct serdev_device_driver yt2_1380_fc_serdev_driver = {
+ .probe = yt2_1380_fc_serdev_probe,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ },
+};
+
+static const struct pinctrl_map yt2_1380_fc_pinctrl_map[] = {
+ PIN_MAP_MUX_GROUP(YT2_1380_FC_SERDEV_NAME, "uart3_uart",
+ "INT33FC:00", "uart3_grp", "uart"),
+ PIN_MAP_MUX_GROUP(YT2_1380_FC_SERDEV_NAME, "uart3_gpio",
+ "INT33FC:00", "uart3_grp_gpio", "gpio"),
+};
+
+static int yt2_1380_fc_pdev_probe(struct platform_device *pdev)
+{
+ struct serdev_device *serdev;
+ struct device *ctrl_dev;
+ int ret;
+
+ /* Register pinctrl mappings for setting the UART3 pins mode */
+ ret = pinctrl_register_mappings(yt2_1380_fc_pinctrl_map,
+ ARRAY_SIZE(yt2_1380_fc_pinctrl_map));
+ if (ret)
+ return ret;
+
+ /* And create the serdev to talk to the charger over the UART3 pins */
+ ctrl_dev = get_serdev_controller("PNP0501", "1", 0, YT2_1380_FC_SERDEV_CTRL);
+ if (IS_ERR(ctrl_dev)) {
+ ret = PTR_ERR(ctrl_dev);
+ goto out_pinctrl_unregister_mappings;
+ }
+
+ serdev = serdev_device_alloc(to_serdev_controller(ctrl_dev));
+ put_device(ctrl_dev);
+ if (!serdev) {
+ ret = -ENOMEM;
+ goto out_pinctrl_unregister_mappings;
+ }
+
+ ret = serdev_device_add(serdev);
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret, "adding serdev\n");
+ serdev_device_put(serdev);
+ goto out_pinctrl_unregister_mappings;
+ }
+
+ /*
+ * serdev device <-> driver matching relies on OF or ACPI matches and
+ * neither is available here, manually bind the driver.
+ */
+ ret = device_driver_attach(&yt2_1380_fc_serdev_driver.driver, &serdev->dev);
+ if (ret) {
+ /* device_driver_attach() maps EPROBE_DEFER to EAGAIN, map it back */
+ ret = (ret == -EAGAIN) ? -EPROBE_DEFER : ret;
+ dev_err_probe(&pdev->dev, ret, "attaching serdev driver\n");
+ goto out_serdev_device_remove;
+ }
+
+ /* So that yt2_1380_fc_pdev_remove() can remove the serdev */
+ platform_set_drvdata(pdev, serdev);
+ return 0;
+
+out_serdev_device_remove:
+ serdev_device_remove(serdev);
+out_pinctrl_unregister_mappings:
+ pinctrl_unregister_mappings(yt2_1380_fc_pinctrl_map);
+ return ret;
+}
+
+static void yt2_1380_fc_pdev_remove(struct platform_device *pdev)
+{
+ struct serdev_device *serdev = platform_get_drvdata(pdev);
+
+ serdev_device_remove(serdev);
+ pinctrl_unregister_mappings(yt2_1380_fc_pinctrl_map);
+}
+
+static struct platform_driver yt2_1380_fc_pdev_driver = {
+ .probe = yt2_1380_fc_pdev_probe,
+ .remove_new = yt2_1380_fc_pdev_remove,
+ .driver = {
+ .name = YT2_1380_FC_PDEV_NAME,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+};
+
+static int __init yt2_1380_fc_module_init(void)
+{
+ int ret;
+
+ /*
+ * serdev driver MUST be registered first because pdev driver calls
+ * device_driver_attach() on the serdev, serdev-driver pair.
+ */
+ ret = serdev_device_driver_register(&yt2_1380_fc_serdev_driver);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&yt2_1380_fc_pdev_driver);
+ if (ret)
+ serdev_device_driver_unregister(&yt2_1380_fc_serdev_driver);
+
+ return ret;
+}
+module_init(yt2_1380_fc_module_init);
+
+static void __exit yt2_1380_fc_module_exit(void)
+{
+ platform_driver_unregister(&yt2_1380_fc_pdev_driver);
+ serdev_device_driver_unregister(&yt2_1380_fc_serdev_driver);
+}
+module_exit(yt2_1380_fc_module_exit);
+
+MODULE_ALIAS("platform:" YT2_1380_FC_PDEV_NAME);
+MODULE_DESCRIPTION("Lenovo Yoga Tablet 2 1380 fast charge driver");
+MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c
index ad3c39e9e9..9c7857842c 100644
--- a/drivers/platform/x86/lg-laptop.c
+++ b/drivers/platform/x86/lg-laptop.c
@@ -39,8 +39,6 @@ MODULE_LICENSE("GPL");
#define WMI_METHOD_WMBB "2B4F501A-BD3C-4394-8DCF-00A7D2BC8210"
#define WMI_EVENT_GUID WMI_EVENT_GUID0
-#define WMAB_METHOD "\\XINI.WMAB"
-#define WMBB_METHOD "\\XINI.WMBB"
#define SB_GGOV_METHOD "\\_SB.GGOV"
#define GOV_TLED 0x2020008
#define WM_GET 1
@@ -74,7 +72,7 @@ static u32 inited;
static int battery_limit_use_wmbb;
static struct led_classdev kbd_backlight;
-static enum led_brightness get_kbd_backlight_level(void);
+static enum led_brightness get_kbd_backlight_level(struct device *dev);
static const struct key_entry wmi_keymap[] = {
{KE_KEY, 0x70, {KEY_F15} }, /* LG control panel (F1) */
@@ -84,7 +82,6 @@ static const struct key_entry wmi_keymap[] = {
* this key both sends an event and
* changes backlight level.
*/
- {KE_KEY, 0x80, {KEY_RFKILL} },
{KE_END, 0}
};
@@ -128,11 +125,10 @@ static int ggov(u32 arg0)
return res;
}
-static union acpi_object *lg_wmab(u32 method, u32 arg1, u32 arg2)
+static union acpi_object *lg_wmab(struct device *dev, u32 method, u32 arg1, u32 arg2)
{
union acpi_object args[3];
acpi_status status;
- acpi_handle handle;
struct acpi_object_list arg;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -143,29 +139,22 @@ static union acpi_object *lg_wmab(u32 method, u32 arg1, u32 arg2)
args[2].type = ACPI_TYPE_INTEGER;
args[2].integer.value = arg2;
- status = acpi_get_handle(NULL, (acpi_string) WMAB_METHOD, &handle);
- if (ACPI_FAILURE(status)) {
- pr_err("Cannot get handle");
- return NULL;
- }
-
arg.count = 3;
arg.pointer = args;
- status = acpi_evaluate_object(handle, NULL, &arg, &buffer);
+ status = acpi_evaluate_object(ACPI_HANDLE(dev), "WMAB", &arg, &buffer);
if (ACPI_FAILURE(status)) {
- acpi_handle_err(handle, "WMAB: call failed.\n");
+ dev_err(dev, "WMAB: call failed.\n");
return NULL;
}
return buffer.pointer;
}
-static union acpi_object *lg_wmbb(u32 method_id, u32 arg1, u32 arg2)
+static union acpi_object *lg_wmbb(struct device *dev, u32 method_id, u32 arg1, u32 arg2)
{
union acpi_object args[3];
acpi_status status;
- acpi_handle handle;
struct acpi_object_list arg;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
u8 buf[32];
@@ -181,18 +170,12 @@ static union acpi_object *lg_wmbb(u32 method_id, u32 arg1, u32 arg2)
args[2].buffer.length = 32;
args[2].buffer.pointer = buf;
- status = acpi_get_handle(NULL, (acpi_string)WMBB_METHOD, &handle);
- if (ACPI_FAILURE(status)) {
- pr_err("Cannot get handle");
- return NULL;
- }
-
arg.count = 3;
arg.pointer = args;
- status = acpi_evaluate_object(handle, NULL, &arg, &buffer);
+ status = acpi_evaluate_object(ACPI_HANDLE(dev), "WMBB", &arg, &buffer);
if (ACPI_FAILURE(status)) {
- acpi_handle_err(handle, "WMAB: call failed.\n");
+ dev_err(dev, "WMBB: call failed.\n");
return NULL;
}
@@ -223,7 +206,7 @@ static void wmi_notify(u32 value, void *context)
if (eventcode == 0x10000000) {
led_classdev_notify_brightness_hw_changed(
- &kbd_backlight, get_kbd_backlight_level());
+ &kbd_backlight, get_kbd_backlight_level(kbd_backlight.dev->parent));
} else {
key = sparse_keymap_entry_from_scancode(
wmi_input_dev, eventcode);
@@ -272,14 +255,7 @@ static void wmi_input_setup(void)
static void acpi_notify(struct acpi_device *device, u32 event)
{
- struct key_entry *key;
-
acpi_handle_debug(device->handle, "notify: %d\n", event);
- if (inited & INIT_SPARSE_KEYMAP) {
- key = sparse_keymap_entry_from_scancode(wmi_input_dev, 0x80);
- if (key && key->type == KE_KEY)
- sparse_keymap_report_entry(wmi_input_dev, key, 1, true);
- }
}
static ssize_t fan_mode_store(struct device *dev,
@@ -295,7 +271,7 @@ static ssize_t fan_mode_store(struct device *dev,
if (ret)
return ret;
- r = lg_wmab(WM_FAN_MODE, WM_GET, 0);
+ r = lg_wmab(dev, WM_FAN_MODE, WM_GET, 0);
if (!r)
return -EIO;
@@ -306,9 +282,9 @@ static ssize_t fan_mode_store(struct device *dev,
m = r->integer.value;
kfree(r);
- r = lg_wmab(WM_FAN_MODE, WM_SET, (m & 0xffffff0f) | (value << 4));
+ r = lg_wmab(dev, WM_FAN_MODE, WM_SET, (m & 0xffffff0f) | (value << 4));
kfree(r);
- r = lg_wmab(WM_FAN_MODE, WM_SET, (m & 0xfffffff0) | value);
+ r = lg_wmab(dev, WM_FAN_MODE, WM_SET, (m & 0xfffffff0) | value);
kfree(r);
return count;
@@ -320,7 +296,7 @@ static ssize_t fan_mode_show(struct device *dev,
unsigned int status;
union acpi_object *r;
- r = lg_wmab(WM_FAN_MODE, WM_GET, 0);
+ r = lg_wmab(dev, WM_FAN_MODE, WM_GET, 0);
if (!r)
return -EIO;
@@ -347,7 +323,7 @@ static ssize_t usb_charge_store(struct device *dev,
if (ret)
return ret;
- r = lg_wmbb(WMBB_USB_CHARGE, WM_SET, value);
+ r = lg_wmbb(dev, WMBB_USB_CHARGE, WM_SET, value);
if (!r)
return -EIO;
@@ -361,7 +337,7 @@ static ssize_t usb_charge_show(struct device *dev,
unsigned int status;
union acpi_object *r;
- r = lg_wmbb(WMBB_USB_CHARGE, WM_GET, 0);
+ r = lg_wmbb(dev, WMBB_USB_CHARGE, WM_GET, 0);
if (!r)
return -EIO;
@@ -389,7 +365,7 @@ static ssize_t reader_mode_store(struct device *dev,
if (ret)
return ret;
- r = lg_wmab(WM_READER_MODE, WM_SET, value);
+ r = lg_wmab(dev, WM_READER_MODE, WM_SET, value);
if (!r)
return -EIO;
@@ -403,7 +379,7 @@ static ssize_t reader_mode_show(struct device *dev,
unsigned int status;
union acpi_object *r;
- r = lg_wmab(WM_READER_MODE, WM_GET, 0);
+ r = lg_wmab(dev, WM_READER_MODE, WM_GET, 0);
if (!r)
return -EIO;
@@ -431,7 +407,7 @@ static ssize_t fn_lock_store(struct device *dev,
if (ret)
return ret;
- r = lg_wmab(WM_FN_LOCK, WM_SET, value);
+ r = lg_wmab(dev, WM_FN_LOCK, WM_SET, value);
if (!r)
return -EIO;
@@ -445,7 +421,7 @@ static ssize_t fn_lock_show(struct device *dev,
unsigned int status;
union acpi_object *r;
- r = lg_wmab(WM_FN_LOCK, WM_GET, 0);
+ r = lg_wmab(dev, WM_FN_LOCK, WM_GET, 0);
if (!r)
return -EIO;
@@ -475,9 +451,9 @@ static ssize_t charge_control_end_threshold_store(struct device *dev,
union acpi_object *r;
if (battery_limit_use_wmbb)
- r = lg_wmbb(WMBB_BATT_LIMIT, WM_SET, value);
+ r = lg_wmbb(&pf_device->dev, WMBB_BATT_LIMIT, WM_SET, value);
else
- r = lg_wmab(WM_BATT_LIMIT, WM_SET, value);
+ r = lg_wmab(&pf_device->dev, WM_BATT_LIMIT, WM_SET, value);
if (!r)
return -EIO;
@@ -496,7 +472,7 @@ static ssize_t charge_control_end_threshold_show(struct device *device,
union acpi_object *r;
if (battery_limit_use_wmbb) {
- r = lg_wmbb(WMBB_BATT_LIMIT, WM_GET, 0);
+ r = lg_wmbb(&pf_device->dev, WMBB_BATT_LIMIT, WM_GET, 0);
if (!r)
return -EIO;
@@ -507,7 +483,7 @@ static ssize_t charge_control_end_threshold_show(struct device *device,
status = r->buffer.pointer[0x10];
} else {
- r = lg_wmab(WM_BATT_LIMIT, WM_GET, 0);
+ r = lg_wmab(&pf_device->dev, WM_BATT_LIMIT, WM_GET, 0);
if (!r)
return -EIO;
@@ -586,7 +562,7 @@ static void tpad_led_set(struct led_classdev *cdev,
{
union acpi_object *r;
- r = lg_wmab(WM_TLED, WM_SET, brightness > LED_OFF);
+ r = lg_wmab(cdev->dev->parent, WM_TLED, WM_SET, brightness > LED_OFF);
kfree(r);
}
@@ -608,16 +584,16 @@ static void kbd_backlight_set(struct led_classdev *cdev,
val = 0;
if (brightness >= LED_FULL)
val = 0x24;
- r = lg_wmab(WM_KEY_LIGHT, WM_SET, val);
+ r = lg_wmab(cdev->dev->parent, WM_KEY_LIGHT, WM_SET, val);
kfree(r);
}
-static enum led_brightness get_kbd_backlight_level(void)
+static enum led_brightness get_kbd_backlight_level(struct device *dev)
{
union acpi_object *r;
int val;
- r = lg_wmab(WM_KEY_LIGHT, WM_GET, 0);
+ r = lg_wmab(dev, WM_KEY_LIGHT, WM_GET, 0);
if (!r)
return LED_OFF;
@@ -645,7 +621,7 @@ static enum led_brightness get_kbd_backlight_level(void)
static enum led_brightness kbd_backlight_get(struct led_classdev *cdev)
{
- return get_kbd_backlight_level();
+ return get_kbd_backlight_level(cdev->dev->parent);
}
static LED_DEVICE(kbd_backlight, 255, LED_BRIGHT_HW_CHANGED);
@@ -672,6 +648,11 @@ static struct platform_driver pf_driver = {
static int acpi_add(struct acpi_device *device)
{
+ struct platform_device_info pdev_info = {
+ .fwnode = acpi_fwnode_handle(device),
+ .name = PLATFORM_NAME,
+ .id = PLATFORM_DEVID_NONE,
+ };
int ret;
const char *product;
int year = 2017;
@@ -683,9 +664,7 @@ static int acpi_add(struct acpi_device *device)
if (ret)
return ret;
- pf_device = platform_device_register_simple(PLATFORM_NAME,
- PLATFORM_DEVID_NONE,
- NULL, 0);
+ pf_device = platform_device_register_full(&pdev_info);
if (IS_ERR(pf_device)) {
ret = PTR_ERR(pf_device);
pf_device = NULL;
@@ -736,7 +715,7 @@ static int acpi_add(struct acpi_device *device)
default:
year = 2019;
}
- pr_info("product: %s year: %d\n", product, year);
+ pr_info("product: %s year: %d\n", product ?: "unknown", year);
if (year >= 2019)
battery_limit_use_wmbb = 1;
@@ -776,7 +755,7 @@ static void acpi_remove(struct acpi_device *device)
}
static const struct acpi_device_id device_ids[] = {
- {"LGEX0815", 0},
+ {"LGEX0820", 0},
{"", 0}
};
MODULE_DEVICE_TABLE(acpi, device_ids);
@@ -790,7 +769,6 @@ static struct acpi_driver acpi_driver = {
.remove = acpi_remove,
.notify = acpi_notify,
},
- .owner = THIS_MODULE,
};
static int __init acpi_init(void)
diff --git a/drivers/platform/x86/meegopad_anx7428.c b/drivers/platform/x86/meegopad_anx7428.c
new file mode 100644
index 0000000000..b2c4d4f526
--- /dev/null
+++ b/drivers/platform/x86/meegopad_anx7428.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver to power on the Analogix ANX7428 USB Type-C crosspoint switch
+ * on MeeGoPad top-set boxes.
+ *
+ * The MeeGoPad T8 and T9 are Cherry Trail top-set boxes which
+ * use an ANX7428 to provide a Type-C port with USB3.1 Gen 1 and
+ * DisplayPort over Type-C alternate mode support.
+ *
+ * The ANX7428 has a microcontroller which takes care of the PD
+ * negotiation and automatically sets the builtin Crosspoint Switch
+ * to send the right signal to the 4 highspeed pairs of the Type-C
+ * connector. It also takes care of HPD and AUX channel routing for
+ * DP alternate mode.
+ *
+ * IOW the ANX7428 operates fully autonomous and to the x5-Z8350 SoC
+ * things look like there simply is a USB-3 Type-A connector and a
+ * separate DisplayPort connector. Except that the BIOS does not
+ * power on the ANX7428 at boot. This driver takes care of powering
+ * on the ANX7428.
+ *
+ * It should be possible to tell the micro-controller which data- and/or
+ * power-role to negotiate and to swap the role(s) after negotiation
+ * but the MeeGoPad top-set boxes always draw their power from a separate
+ * power-connector and they only support USB host-mode. So this functionality
+ * is unnecessary and due to lack of documentation this is tricky to support.
+ *
+ * For a more complete ANX7428 driver see drivers/usb/misc/anx7418/ of
+ * the LineageOS kernel for the LG G5 (International) aka the LG H850:
+ * https://github.com/LineageOS/android_kernel_lge_msm8996/
+ *
+ * (C) Copyright 2024 Hans de Goede <hansg@kernel.org>
+ */
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/dmi.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+/* Register addresses and fields */
+#define VENDOR_ID 0x00
+#define DEVICE_ID 0x02
+
+#define TX_STATUS 0x16
+#define STATUS_SUCCESS BIT(0)
+#define STATUS_ERROR BIT(1)
+#define OCM_STARTUP BIT(7)
+
+static bool force;
+module_param(force, bool, 0444);
+MODULE_PARM_DESC(force, "Force the driver to probe on unknown boards");
+
+static const struct acpi_gpio_params enable_gpio = { 0, 0, false };
+static const struct acpi_gpio_params reset_gpio = { 1, 0, true };
+
+static const struct acpi_gpio_mapping meegopad_anx7428_gpios[] = {
+ { "enable-gpios", &enable_gpio, 1 },
+ { "reset-gpios", &reset_gpio, 1 },
+ { }
+};
+
+static const struct dmi_system_id meegopad_anx7428_ids[] = {
+ {
+ /* Meegopad T08 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Default string"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Default string"),
+ DMI_MATCH(DMI_BOARD_NAME, "T3 MRD"),
+ DMI_MATCH(DMI_BOARD_VERSION, "V1.1"),
+ },
+ },
+ { }
+};
+
+static int anx7428_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct gpio_desc *gpio;
+ int ret, val;
+
+ if (!dmi_check_system(meegopad_anx7428_ids) && !force) {
+ dev_warn(dev, "Not probing unknown board, pass meegopad_anx7428.force=1 to probe");
+ return -ENODEV;
+ }
+
+ ret = devm_acpi_dev_add_driver_gpios(dev, meegopad_anx7428_gpios);
+ if (ret)
+ return ret;
+
+ /*
+ * Set GPIOs to desired values while getting them, they are not needed
+ * afterwards. Ordering and delays come from android_kernel_lge_msm8996.
+ */
+ gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH);
+ if (IS_ERR(gpio))
+ return dev_err_probe(dev, PTR_ERR(gpio), "getting enable GPIO\n");
+
+ fsleep(10000);
+
+ gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(gpio))
+ return dev_err_probe(dev, PTR_ERR(gpio), "getting reset GPIO\n");
+
+ /* Wait for the OCM (On Chip Microcontroller) to start */
+ ret = read_poll_timeout(i2c_smbus_read_byte_data, val,
+ val >= 0 && (val & OCM_STARTUP),
+ 5000, 50000, true, client, TX_STATUS);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "On Chip Microcontroller did not start, status: 0x%02x\n",
+ val);
+
+ ret = i2c_smbus_read_word_data(client, VENDOR_ID);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "reading vendor-id register\n");
+ val = ret;
+
+ ret = i2c_smbus_read_word_data(client, DEVICE_ID);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "reading device-id register\n");
+
+ dev_dbg(dev, "Powered on ANX7428 id %04x:%04x\n", val, ret);
+ return 0;
+}
+
+static const struct acpi_device_id anx7428_acpi_match[] = {
+ { "ANXO7418" }, /* ACPI says 7418 (max 2 DP lanes version) but HW is 7428 */
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, anx7428_acpi_match);
+
+static struct i2c_driver anx7428_driver = {
+ .driver = {
+ .name = "meegopad_anx7428",
+ .acpi_match_table = anx7428_acpi_match,
+ },
+ .probe = anx7428_probe,
+};
+module_i2c_driver(anx7428_driver);
+
+MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
+MODULE_DESCRIPTION("MeeGoPad ANX7428 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c
index 32981e2ad3..9d70146fd7 100644
--- a/drivers/platform/x86/mlx-platform.c
+++ b/drivers/platform/x86/mlx-platform.c
@@ -6659,6 +6659,6 @@ static void __exit mlxplat_exit(void)
}
module_exit(mlxplat_exit);
-MODULE_AUTHOR("Vadim Pasternak (vadimp@mellanox.com)");
+MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
MODULE_DESCRIPTION("Mellanox platform driver");
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c
index f4c6c36e05..e5391a3701 100644
--- a/drivers/platform/x86/msi-laptop.c
+++ b/drivers/platform/x86/msi-laptop.c
@@ -317,7 +317,7 @@ static ssize_t show_wlan(struct device *dev,
if (ret < 0)
return ret;
- return sprintf(buf, "%i\n", enabled);
+ return sysfs_emit(buf, "%i\n", enabled);
}
static ssize_t store_wlan(struct device *dev,
@@ -341,7 +341,7 @@ static ssize_t show_bluetooth(struct device *dev,
if (ret < 0)
return ret;
- return sprintf(buf, "%i\n", enabled);
+ return sysfs_emit(buf, "%i\n", enabled);
}
static ssize_t store_bluetooth(struct device *dev,
@@ -364,7 +364,7 @@ static ssize_t show_threeg(struct device *dev,
if (ret < 0)
return ret;
- return sprintf(buf, "%i\n", threeg_s);
+ return sysfs_emit(buf, "%i\n", threeg_s);
}
static ssize_t store_threeg(struct device *dev,
@@ -383,7 +383,7 @@ static ssize_t show_lcd_level(struct device *dev,
if (ret < 0)
return ret;
- return sprintf(buf, "%i\n", ret);
+ return sysfs_emit(buf, "%i\n", ret);
}
static ssize_t store_lcd_level(struct device *dev,
@@ -413,7 +413,7 @@ static ssize_t show_auto_brightness(struct device *dev,
if (ret < 0)
return ret;
- return sprintf(buf, "%i\n", ret);
+ return sysfs_emit(buf, "%i\n", ret);
}
static ssize_t store_auto_brightness(struct device *dev,
@@ -443,7 +443,7 @@ static ssize_t show_touchpad(struct device *dev,
if (result < 0)
return result;
- return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_TOUCHPAD_MASK));
+ return sysfs_emit(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_TOUCHPAD_MASK));
}
static ssize_t show_turbo(struct device *dev,
@@ -457,7 +457,7 @@ static ssize_t show_turbo(struct device *dev,
if (result < 0)
return result;
- return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_TURBO_MASK));
+ return sysfs_emit(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_TURBO_MASK));
}
static ssize_t show_eco(struct device *dev,
@@ -471,7 +471,7 @@ static ssize_t show_eco(struct device *dev,
if (result < 0)
return result;
- return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_ECO_MASK));
+ return sysfs_emit(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_ECO_MASK));
}
static ssize_t show_turbo_cooldown(struct device *dev,
@@ -485,7 +485,7 @@ static ssize_t show_turbo_cooldown(struct device *dev,
if (result < 0)
return result;
- return sprintf(buf, "%i\n", (!!(rdata & MSI_STANDARD_EC_TURBO_MASK)) |
+ return sysfs_emit(buf, "%i\n", (!!(rdata & MSI_STANDARD_EC_TURBO_MASK)) |
(!!(rdata & MSI_STANDARD_EC_TURBO_COOLDOWN_MASK) << 1));
}
@@ -500,7 +500,7 @@ static ssize_t show_auto_fan(struct device *dev,
if (result < 0)
return result;
- return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_AUTOFAN_MASK));
+ return sysfs_emit(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_AUTOFAN_MASK));
}
static ssize_t store_auto_fan(struct device *dev,
diff --git a/drivers/platform/x86/msi-wmi-platform.c b/drivers/platform/x86/msi-wmi-platform.c
new file mode 100644
index 0000000000..436fb91a47
--- /dev/null
+++ b/drivers/platform/x86/msi-wmi-platform.c
@@ -0,0 +1,428 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Linux driver for WMI platform features on MSI notebooks.
+ *
+ * Copyright (C) 2024 Armin Wolf <W_Armin@gmx.de>
+ */
+
+#define pr_format(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/device/driver.h>
+#include <linux/errno.h>
+#include <linux/hwmon.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+#include <linux/rwsem.h>
+#include <linux/types.h>
+#include <linux/wmi.h>
+
+#include <asm/unaligned.h>
+
+#define DRIVER_NAME "msi-wmi-platform"
+
+#define MSI_PLATFORM_GUID "ABBC0F6E-8EA1-11d1-00A0-C90629100000"
+
+#define MSI_WMI_PLATFORM_INTERFACE_VERSION 2
+
+#define MSI_PLATFORM_WMI_MAJOR_OFFSET 1
+#define MSI_PLATFORM_WMI_MINOR_OFFSET 2
+
+#define MSI_PLATFORM_EC_FLAGS_OFFSET 1
+#define MSI_PLATFORM_EC_MINOR_MASK GENMASK(3, 0)
+#define MSI_PLATFORM_EC_MAJOR_MASK GENMASK(5, 4)
+#define MSI_PLATFORM_EC_CHANGED_PAGE BIT(6)
+#define MSI_PLATFORM_EC_IS_TIGERLAKE BIT(7)
+#define MSI_PLATFORM_EC_VERSION_OFFSET 2
+
+static bool force;
+module_param_unsafe(force, bool, 0);
+MODULE_PARM_DESC(force, "Force loading without checking for supported WMI interface versions");
+
+enum msi_wmi_platform_method {
+ MSI_PLATFORM_GET_PACKAGE = 0x01,
+ MSI_PLATFORM_SET_PACKAGE = 0x02,
+ MSI_PLATFORM_GET_EC = 0x03,
+ MSI_PLATFORM_SET_EC = 0x04,
+ MSI_PLATFORM_GET_BIOS = 0x05,
+ MSI_PLATFORM_SET_BIOS = 0x06,
+ MSI_PLATFORM_GET_SMBUS = 0x07,
+ MSI_PLATFORM_SET_SMBUS = 0x08,
+ MSI_PLATFORM_GET_MASTER_BATTERY = 0x09,
+ MSI_PLATFORM_SET_MASTER_BATTERY = 0x0a,
+ MSI_PLATFORM_GET_SLAVE_BATTERY = 0x0b,
+ MSI_PLATFORM_SET_SLAVE_BATTERY = 0x0c,
+ MSI_PLATFORM_GET_TEMPERATURE = 0x0d,
+ MSI_PLATFORM_SET_TEMPERATURE = 0x0e,
+ MSI_PLATFORM_GET_THERMAL = 0x0f,
+ MSI_PLATFORM_SET_THERMAL = 0x10,
+ MSI_PLATFORM_GET_FAN = 0x11,
+ MSI_PLATFORM_SET_FAN = 0x12,
+ MSI_PLATFORM_GET_DEVICE = 0x13,
+ MSI_PLATFORM_SET_DEVICE = 0x14,
+ MSI_PLATFORM_GET_POWER = 0x15,
+ MSI_PLATFORM_SET_POWER = 0x16,
+ MSI_PLATFORM_GET_DEBUG = 0x17,
+ MSI_PLATFORM_SET_DEBUG = 0x18,
+ MSI_PLATFORM_GET_AP = 0x19,
+ MSI_PLATFORM_SET_AP = 0x1a,
+ MSI_PLATFORM_GET_DATA = 0x1b,
+ MSI_PLATFORM_SET_DATA = 0x1c,
+ MSI_PLATFORM_GET_WMI = 0x1d,
+};
+
+struct msi_wmi_platform_debugfs_data {
+ struct wmi_device *wdev;
+ enum msi_wmi_platform_method method;
+ struct rw_semaphore buffer_lock; /* Protects debugfs buffer */
+ size_t length;
+ u8 buffer[32];
+};
+
+static const char * const msi_wmi_platform_debugfs_names[] = {
+ "get_package",
+ "set_package",
+ "get_ec",
+ "set_ec",
+ "get_bios",
+ "set_bios",
+ "get_smbus",
+ "set_smbus",
+ "get_master_battery",
+ "set_master_battery",
+ "get_slave_battery",
+ "set_slave_battery",
+ "get_temperature",
+ "set_temperature",
+ "get_thermal",
+ "set_thermal",
+ "get_fan",
+ "set_fan",
+ "get_device",
+ "set_device",
+ "get_power",
+ "set_power",
+ "get_debug",
+ "set_debug",
+ "get_ap",
+ "set_ap",
+ "get_data",
+ "set_data",
+ "get_wmi"
+};
+
+static int msi_wmi_platform_parse_buffer(union acpi_object *obj, u8 *output, size_t length)
+{
+ if (obj->type != ACPI_TYPE_BUFFER)
+ return -ENOMSG;
+
+ if (obj->buffer.length != length)
+ return -EPROTO;
+
+ if (!obj->buffer.pointer[0])
+ return -EIO;
+
+ memcpy(output, obj->buffer.pointer, obj->buffer.length);
+
+ 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)
+{
+ struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_buffer in = {
+ .length = input_length,
+ .pointer = input
+ };
+ union acpi_object *obj;
+ acpi_status status;
+ int ret;
+
+ if (!input_length || !output_length)
+ return -EINVAL;
+
+ status = wmidev_evaluate_method(wdev, 0x0, method, &in, &out);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ obj = out.pointer;
+ if (!obj)
+ return -ENODATA;
+
+ ret = msi_wmi_platform_parse_buffer(obj, output, output_length);
+ kfree(obj);
+
+ return ret;
+}
+
+static umode_t msi_wmi_platform_is_visible(const void *drvdata, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ return 0444;
+}
+
+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);
+ u8 input[32] = { 0 };
+ u8 output[32];
+ u16 data;
+ int ret;
+
+ ret = msi_wmi_platform_query(wdev, 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)
+ *val = 0;
+ else
+ *val = 480000 / data;
+
+ return 0;
+}
+
+static const struct hwmon_ops msi_wmi_platform_ops = {
+ .is_visible = msi_wmi_platform_is_visible,
+ .read = msi_wmi_platform_read,
+};
+
+static const struct hwmon_channel_info * const msi_wmi_platform_info[] = {
+ HWMON_CHANNEL_INFO(fan,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT
+ ),
+ NULL
+};
+
+static const struct hwmon_chip_info msi_wmi_platform_chip_info = {
+ .ops = &msi_wmi_platform_ops,
+ .info = msi_wmi_platform_info,
+};
+
+static ssize_t msi_wmi_platform_write(struct file *fp, const char __user *input, size_t length,
+ loff_t *offset)
+{
+ struct seq_file *seq = fp->private_data;
+ struct msi_wmi_platform_debugfs_data *data = seq->private;
+ u8 payload[32] = { };
+ ssize_t ret;
+
+ /* Do not allow partial writes */
+ if (*offset != 0)
+ return -EINVAL;
+
+ /* Do not allow incomplete command buffers */
+ if (length != data->length)
+ return -EINVAL;
+
+ ret = simple_write_to_buffer(payload, sizeof(payload), offset, input, length);
+ if (ret < 0)
+ return ret;
+
+ down_write(&data->buffer_lock);
+ ret = msi_wmi_platform_query(data->wdev, data->method, payload, data->length, data->buffer,
+ data->length);
+ up_write(&data->buffer_lock);
+
+ if (ret < 0)
+ return ret;
+
+ return length;
+}
+
+static int msi_wmi_platform_show(struct seq_file *seq, void *p)
+{
+ struct msi_wmi_platform_debugfs_data *data = seq->private;
+ int ret;
+
+ down_read(&data->buffer_lock);
+ ret = seq_write(seq, data->buffer, data->length);
+ up_read(&data->buffer_lock);
+
+ return ret;
+}
+
+static int msi_wmi_platform_open(struct inode *inode, struct file *fp)
+{
+ struct msi_wmi_platform_debugfs_data *data = inode->i_private;
+
+ /* The seq_file uses the last byte of the buffer for detecting buffer overflows */
+ return single_open_size(fp, msi_wmi_platform_show, data, data->length + 1);
+}
+
+static const struct file_operations msi_wmi_platform_debugfs_fops = {
+ .owner = THIS_MODULE,
+ .open = msi_wmi_platform_open,
+ .read = seq_read,
+ .write = msi_wmi_platform_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void msi_wmi_platform_debugfs_remove(void *data)
+{
+ struct dentry *dir = data;
+
+ debugfs_remove_recursive(dir);
+}
+
+static void msi_wmi_platform_debugfs_add(struct wmi_device *wdev, 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);
+ if (!data)
+ return;
+
+ data->wdev = wdev;
+ data->method = method;
+ init_rwsem(&data->buffer_lock);
+
+ /* The ACPI firmware for now always requires a 32 byte input buffer due to
+ * a peculiarity in how Windows handles the CreateByteField() ACPI operator.
+ */
+ data->length = 32;
+
+ entry = debugfs_create_file(name, 0600, dir, data, &msi_wmi_platform_debugfs_fops);
+ if (IS_ERR(entry))
+ devm_kfree(&wdev->dev, data);
+}
+
+static void msi_wmi_platform_debugfs_init(struct wmi_device *wdev)
+{
+ 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));
+
+ 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);
+ 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],
+ method);
+}
+
+static int msi_wmi_platform_hwmon_init(struct wmi_device *wdev)
+{
+ struct device *hdev;
+
+ hdev = devm_hwmon_device_register_with_info(&wdev->dev, "msi_wmi_platform", wdev,
+ &msi_wmi_platform_chip_info, NULL);
+
+ return PTR_ERR_OR_ZERO(hdev);
+}
+
+static int msi_wmi_platform_ec_init(struct wmi_device *wdev)
+{
+ 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,
+ sizeof(output));
+ if (ret < 0)
+ return ret;
+
+ flags = output[MSI_PLATFORM_EC_FLAGS_OFFSET];
+
+ dev_dbg(&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",
+ &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");
+ }
+
+ return 0;
+}
+
+static int msi_wmi_platform_init(struct wmi_device *wdev)
+{
+ u8 input[32] = { 0 };
+ u8 output[32];
+ int ret;
+
+ ret = msi_wmi_platform_query(wdev, 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",
+ output[MSI_PLATFORM_WMI_MAJOR_OFFSET],
+ output[MSI_PLATFORM_WMI_MINOR_OFFSET]);
+
+ if (output[MSI_PLATFORM_WMI_MAJOR_OFFSET] != MSI_WMI_PLATFORM_INTERFACE_VERSION) {
+ if (!force)
+ return -ENODEV;
+
+ dev_warn(&wdev->dev, "Loading despite unsupported WMI interface version (%u.%u)\n",
+ output[MSI_PLATFORM_WMI_MAJOR_OFFSET],
+ output[MSI_PLATFORM_WMI_MINOR_OFFSET]);
+ }
+
+ return 0;
+}
+
+static int msi_wmi_platform_probe(struct wmi_device *wdev, const void *context)
+{
+ int ret;
+
+ ret = msi_wmi_platform_init(wdev);
+ if (ret < 0)
+ return ret;
+
+ ret = msi_wmi_platform_ec_init(wdev);
+ if (ret < 0)
+ return ret;
+
+ msi_wmi_platform_debugfs_init(wdev);
+
+ return msi_wmi_platform_hwmon_init(wdev);
+}
+
+static const struct wmi_device_id msi_wmi_platform_id_table[] = {
+ { MSI_PLATFORM_GUID, NULL },
+ { }
+};
+MODULE_DEVICE_TABLE(wmi, msi_wmi_platform_id_table);
+
+static struct wmi_driver msi_wmi_platform_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .id_table = msi_wmi_platform_id_table,
+ .probe = msi_wmi_platform_probe,
+ .no_singleton = true,
+};
+module_wmi_driver(msi_wmi_platform_driver);
+
+MODULE_AUTHOR("Armin Wolf <W_Armin@gmx.de>");
+MODULE_DESCRIPTION("MSI WMI platform features");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/p2sb.c b/drivers/platform/x86/p2sb.c
index 3d66e1d4eb..3bf5d22434 100644
--- a/drivers/platform/x86/p2sb.c
+++ b/drivers/platform/x86/p2sb.c
@@ -43,7 +43,7 @@ struct p2sb_res_cache {
static struct p2sb_res_cache p2sb_resources[NR_P2SB_RES_CACHE];
-static int p2sb_get_devfn(unsigned int *devfn)
+static void p2sb_get_devfn(unsigned int *devfn)
{
unsigned int fn = P2SB_DEVFN_DEFAULT;
const struct x86_cpu_id *id;
@@ -53,15 +53,11 @@ static int p2sb_get_devfn(unsigned int *devfn)
fn = (unsigned int)id->driver_data;
*devfn = fn;
- return 0;
}
-static bool p2sb_valid_resource(struct resource *res)
+static bool p2sb_valid_resource(const struct resource *res)
{
- if (res->flags)
- return true;
-
- return false;
+ return res->flags & ~IORESOURCE_UNSET;
}
/* Copy resource from the first BAR of the device in question */
@@ -135,9 +131,7 @@ static int p2sb_cache_resources(void)
int ret;
/* Get devfn for P2SB device itself */
- ret = p2sb_get_devfn(&devfn_p2sb);
- if (ret)
- return ret;
+ p2sb_get_devfn(&devfn_p2sb);
bus = p2sb_get_bus(NULL);
if (!bus)
@@ -194,17 +188,13 @@ static int p2sb_cache_resources(void)
int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem)
{
struct p2sb_res_cache *cache;
- int ret;
bus = p2sb_get_bus(bus);
if (!bus)
return -ENODEV;
- if (!devfn) {
- ret = p2sb_get_devfn(&devfn);
- if (ret)
- return ret;
- }
+ if (!devfn)
+ p2sb_get_devfn(&devfn);
cache = &p2sb_resources[PCI_FUNC(devfn)];
if (cache->bus_dev_id != bus->dev.id)
@@ -220,16 +210,20 @@ EXPORT_SYMBOL_GPL(p2sb_bar);
static int __init p2sb_fs_init(void)
{
- p2sb_cache_resources();
- return 0;
+ return p2sb_cache_resources();
}
/*
- * pci_rescan_remove_lock to avoid access to unhidden P2SB devices can
- * not be locked in sysfs pci bus rescan path because of deadlock. To
- * avoid the deadlock, access to P2SB devices with the lock at an early
- * step in kernel initialization and cache required resources. This
- * should happen after subsys_initcall which initializes PCI subsystem
- * and before device_initcall which requires P2SB resources.
+ * pci_rescan_remove_lock() can not be locked in sysfs PCI bus rescan path
+ * because of deadlock. To avoid the deadlock, access P2SB devices with the lock
+ * at an early step in kernel initialization and cache required resources.
+ *
+ * We want to run as early as possible. If the P2SB was assigned a bad BAR,
+ * we'll need to wait on pcibios_assign_resources() to fix it. So, our list of
+ * initcall dependencies looks something like this:
+ *
+ * ...
+ * subsys_initcall (pci_subsys_init)
+ * fs_initcall (pcibios_assign_resources)
*/
-fs_initcall(p2sb_fs_init);
+fs_initcall_sync(p2sb_fs_init);
diff --git a/drivers/platform/x86/pmc_atom.c b/drivers/platform/x86/pmc_atom.c
index 93a6414c66..0aa7076bc9 100644
--- a/drivers/platform/x86/pmc_atom.c
+++ b/drivers/platform/x86/pmc_atom.c
@@ -6,6 +6,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/acpi.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/dmi.h>
@@ -17,6 +18,7 @@
#include <linux/platform_device.h>
#include <linux/pci.h>
#include <linux/seq_file.h>
+#include <linux/suspend.h>
struct pmc_bit_map {
const char *name;
@@ -448,6 +450,82 @@ static int pmc_setup_clks(struct pci_dev *pdev, void __iomem *pmc_regmap,
return 0;
}
+#ifdef CONFIG_SUSPEND
+static void pmc_dev_state_check(u32 sts, const struct pmc_bit_map *sts_map,
+ u32 fd, const struct pmc_bit_map *fd_map,
+ u32 sts_possible_false_pos)
+{
+ int index;
+
+ for (index = 0; sts_map[index].name; index++) {
+ if (!(fd_map[index].bit_mask & fd) &&
+ !(sts_map[index].bit_mask & sts)) {
+ if (sts_map[index].bit_mask & sts_possible_false_pos)
+ pm_pr_dbg("%s is in D0 prior to s2idle\n",
+ sts_map[index].name);
+ else
+ pr_err("%s is in D0 prior to s2idle\n",
+ sts_map[index].name);
+ }
+ }
+}
+
+static void pmc_s2idle_check(void)
+{
+ struct pmc_dev *pmc = &pmc_device;
+ const struct pmc_reg_map *m = pmc->map;
+ u32 func_dis, func_dis_2;
+ u32 d3_sts_0, d3_sts_1;
+ u32 false_pos_sts_0, false_pos_sts_1;
+ int i;
+
+ func_dis = pmc_reg_read(pmc, PMC_FUNC_DIS);
+ func_dis_2 = pmc_reg_read(pmc, PMC_FUNC_DIS_2);
+ d3_sts_0 = pmc_reg_read(pmc, PMC_D3_STS_0);
+ d3_sts_1 = pmc_reg_read(pmc, PMC_D3_STS_1);
+
+ /*
+ * Some blocks are not used on lower-featured versions of the SoC and
+ * always report D0, add these to false_pos mask to log at debug level.
+ */
+ if (m->d3_sts_1 == byt_d3_sts_1_map) {
+ /* Bay Trail */
+ false_pos_sts_0 = BIT_GBE | BIT_SATA | BIT_PCIE_PORT0 |
+ BIT_PCIE_PORT1 | BIT_PCIE_PORT2 | BIT_PCIE_PORT3 |
+ BIT_LPSS2_F5_I2C5;
+ false_pos_sts_1 = BIT_SMB | BIT_USH_SS_PHY | BIT_DFX;
+ } else {
+ /* Cherry Trail */
+ false_pos_sts_0 = BIT_GBE | BIT_SATA | BIT_LPSS2_F7_I2C7;
+ false_pos_sts_1 = BIT_SMB | BIT_STS_ISH;
+ }
+
+ pmc_dev_state_check(d3_sts_0, m->d3_sts_0, func_dis, m->func_dis, false_pos_sts_0);
+ pmc_dev_state_check(d3_sts_1, m->d3_sts_1, func_dis_2, m->func_dis_2, false_pos_sts_1);
+
+ /* Forced-on PMC clocks prevent S0i3 */
+ for (i = 0; i < PMC_CLK_NUM; i++) {
+ u32 ctl = pmc_reg_read(pmc, PMC_CLK_CTL_OFFSET + 4 * i);
+
+ if ((ctl & PMC_MASK_CLK_CTL) != PMC_CLK_CTL_FORCE_ON)
+ continue;
+
+ pr_err("clock %d is ON prior to freeze (ctl 0x%08x)\n", i, ctl);
+ }
+}
+
+static struct acpi_s2idle_dev_ops pmc_s2idle_ops = {
+ .check = pmc_s2idle_check,
+};
+
+static void pmc_s2idle_check_register(void)
+{
+ acpi_register_lps0_dev(&pmc_s2idle_ops);
+}
+#else
+static void pmc_s2idle_check_register(void) {}
+#endif
+
static int pmc_setup_dev(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct pmc_dev *pmc = &pmc_device;
@@ -485,6 +563,7 @@ static int pmc_setup_dev(struct pci_dev *pdev, const struct pci_device_id *ent)
dev_warn(&pdev->dev, "platform clocks register failed: %d\n",
ret);
+ pmc_s2idle_check_register();
pmc->init = true;
return ret;
}
diff --git a/drivers/platform/x86/quickstart.c b/drivers/platform/x86/quickstart.c
new file mode 100644
index 0000000000..df496c7e71
--- /dev/null
+++ b/drivers/platform/x86/quickstart.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ACPI Direct App Launch driver
+ *
+ * Copyright (C) 2024 Armin Wolf <W_Armin@gmx.de>
+ * Copyright (C) 2022 Arvid Norlander <lkml@vorapal.se>
+ * Copyright (C) 2007-2010 Angelo Arrifano <miknix@gmail.com>
+ *
+ * Information gathered from disassembled dsdt and from here:
+ * <https://archive.org/details/microsoft-acpi-dirapplaunch>
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
+#include <linux/mod_devicetable.h>
+#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>
+#include <linux/types.h>
+
+#include <asm/unaligned.h>
+
+#define DRIVER_NAME "quickstart"
+
+/*
+ * There will be two events:
+ * 0x02 - Button was pressed while device was off/sleeping.
+ * 0x80 - Button was pressed while device was up.
+ */
+#define QUICKSTART_EVENT_RUNTIME 0x80
+
+struct quickstart_data {
+ struct device *dev;
+ struct mutex input_lock; /* Protects input sequence during notify */
+ struct input_dev *input_device;
+ char input_name[32];
+ char phys[32];
+ u32 id;
+};
+
+/*
+ * Knowing what these buttons do require system specific knowledge.
+ * This could be done by matching on DMI data in a long quirk table.
+ * However, it is easier to leave it up to user space to figure this out.
+ *
+ * Using for example udev hwdb the scancode 0x1 can be remapped suitably.
+ */
+static const struct key_entry quickstart_keymap[] = {
+ { KE_KEY, 0x1, { KEY_UNKNOWN } },
+ { KE_END, 0 },
+};
+
+static ssize_t button_id_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct quickstart_data *data = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%u\n", data->id);
+}
+static DEVICE_ATTR_RO(button_id);
+
+static struct attribute *quickstart_attrs[] = {
+ &dev_attr_button_id.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(quickstart);
+
+static void quickstart_notify(acpi_handle handle, u32 event, void *context)
+{
+ struct quickstart_data *data = context;
+
+ switch (event) {
+ case QUICKSTART_EVENT_RUNTIME:
+ mutex_lock(&data->input_lock);
+ sparse_keymap_report_event(data->input_device, 0x1, 1, true);
+ mutex_unlock(&data->input_lock);
+
+ acpi_bus_generate_netlink_event(DRIVER_NAME, dev_name(data->dev), event, 0);
+ break;
+ default:
+ dev_err(data->dev, FW_INFO "Unexpected ACPI notify event (%u)\n", event);
+ break;
+ }
+}
+
+/*
+ * The GHID ACPI method is used to indicate the "role" of the button.
+ * However, all the meanings of these values are vendor defined.
+ *
+ * We do however expose this value to user space.
+ */
+static int quickstart_get_ghid(struct quickstart_data *data)
+{
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_handle handle = ACPI_HANDLE(data->dev);
+ union acpi_object *obj;
+ acpi_status status;
+ int ret = 0;
+
+ /*
+ * This returns a buffer telling the button usage ID,
+ * and triggers pending notify events (The ones before booting).
+ */
+ status = acpi_evaluate_object_typed(handle, "GHID", NULL, &buffer, ACPI_TYPE_BUFFER);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ obj = buffer.pointer;
+ if (!obj)
+ return -ENODATA;
+
+ /*
+ * Quoting the specification:
+ * "The GHID method can return a BYTE, WORD, or DWORD.
+ * The value must be encoded in little-endian byte
+ * order (least significant byte first)."
+ */
+ switch (obj->buffer.length) {
+ case 1:
+ data->id = obj->buffer.pointer[0];
+ break;
+ case 2:
+ data->id = get_unaligned_le16(obj->buffer.pointer);
+ break;
+ case 4:
+ data->id = get_unaligned_le32(obj->buffer.pointer);
+ break;
+ default:
+ dev_err(data->dev,
+ FW_BUG "GHID method returned buffer of unexpected length %u\n",
+ obj->buffer.length);
+ ret = -EIO;
+ break;
+ }
+
+ kfree(obj);
+
+ return ret;
+}
+
+static void quickstart_notify_remove(void *context)
+{
+ struct quickstart_data *data = context;
+ acpi_handle handle;
+
+ handle = ACPI_HANDLE(data->dev);
+
+ acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, quickstart_notify);
+}
+
+static void quickstart_mutex_destroy(void *data)
+{
+ struct mutex *lock = data;
+
+ mutex_destroy(lock);
+}
+
+static int quickstart_probe(struct platform_device *pdev)
+{
+ struct quickstart_data *data;
+ acpi_handle handle;
+ acpi_status status;
+ int ret;
+
+ handle = ACPI_HANDLE(&pdev->dev);
+ if (!handle)
+ return -ENODEV;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, data);
+
+ mutex_init(&data->input_lock);
+ ret = devm_add_action_or_reset(&pdev->dev, quickstart_mutex_destroy, &data->input_lock);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * We have to initialize the device wakeup before evaluating GHID because
+ * doing so will notify the device if the button was used to wake the machine
+ * from S5.
+ */
+ device_init_wakeup(&pdev->dev, true);
+
+ ret = quickstart_get_ghid(data);
+ if (ret < 0)
+ return ret;
+
+ data->input_device = devm_input_allocate_device(&pdev->dev);
+ if (!data->input_device)
+ return -ENOMEM;
+
+ ret = sparse_keymap_setup(data->input_device, quickstart_keymap, NULL);
+ if (ret < 0)
+ return ret;
+
+ snprintf(data->input_name, sizeof(data->input_name), "Quickstart Button %u", data->id);
+ snprintf(data->phys, sizeof(data->phys), DRIVER_NAME "/input%u", data->id);
+
+ data->input_device->name = data->input_name;
+ data->input_device->phys = data->phys;
+ data->input_device->id.bustype = BUS_HOST;
+
+ ret = input_register_device(data->input_device);
+ if (ret < 0)
+ return ret;
+
+ status = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY, quickstart_notify, data);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ return devm_add_action_or_reset(&pdev->dev, quickstart_notify_remove, data);
+}
+
+static const struct acpi_device_id quickstart_device_ids[] = {
+ { "PNP0C32" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, quickstart_device_ids);
+
+static struct platform_driver quickstart_platform_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .dev_groups = quickstart_groups,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ .acpi_match_table = quickstart_device_ids,
+ },
+ .probe = quickstart_probe,
+};
+module_platform_driver(quickstart_platform_driver);
+
+MODULE_AUTHOR("Armin Wolf <W_Armin@gmx.de>");
+MODULE_AUTHOR("Arvid Norlander <lkml@vorpal.se>");
+MODULE_AUTHOR("Angelo Arrifano");
+MODULE_DESCRIPTION("ACPI Direct App Launch driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c
index b4aa8ba35d..3d2f8e7583 100644
--- a/drivers/platform/x86/samsung-laptop.c
+++ b/drivers/platform/x86/samsung-laptop.c
@@ -661,9 +661,9 @@ static ssize_t get_performance_level(struct device *dev,
/* The logic is backwards, yeah, lots of fun... */
for (i = 0; config->performance_levels[i].name; ++i) {
if (sretval.data[0] == config->performance_levels[i].value)
- return sprintf(buf, "%s\n", config->performance_levels[i].name);
+ return sysfs_emit(buf, "%s\n", config->performance_levels[i].name);
}
- return sprintf(buf, "%s\n", "unknown");
+ return sysfs_emit(buf, "%s\n", "unknown");
}
static ssize_t set_performance_level(struct device *dev,
@@ -744,7 +744,7 @@ static ssize_t get_battery_life_extender(struct device *dev,
if (ret < 0)
return ret;
- return sprintf(buf, "%d\n", ret);
+ return sysfs_emit(buf, "%d\n", ret);
}
static ssize_t set_battery_life_extender(struct device *dev,
@@ -813,7 +813,7 @@ static ssize_t get_usb_charge(struct device *dev,
if (ret < 0)
return ret;
- return sprintf(buf, "%d\n", ret);
+ return sysfs_emit(buf, "%d\n", ret);
}
static ssize_t set_usb_charge(struct device *dev,
@@ -878,7 +878,7 @@ static ssize_t get_lid_handling(struct device *dev,
if (ret < 0)
return ret;
- return sprintf(buf, "%d\n", ret);
+ return sysfs_emit(buf, "%d\n", ret);
}
static ssize_t set_lid_handling(struct device *dev,
diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c
index 8158e3cf5d..97b9c63922 100644
--- a/drivers/platform/x86/serial-multi-instantiate.c
+++ b/drivers/platform/x86/serial-multi-instantiate.c
@@ -329,6 +329,19 @@ static const struct smi_node cs35l41_hda = {
.bus_type = SMI_AUTO_DETECT,
};
+static const struct smi_node cs35l54_hda = {
+ .instances = {
+ { "cs35l54-hda", IRQ_RESOURCE_AUTO, 0 },
+ { "cs35l54-hda", IRQ_RESOURCE_AUTO, 0 },
+ { "cs35l54-hda", IRQ_RESOURCE_AUTO, 0 },
+ { "cs35l54-hda", IRQ_RESOURCE_AUTO, 0 },
+ /* a 5th entry is an alias address, not a real device */
+ { "cs35l54-hda_dummy_dev" },
+ {}
+ },
+ .bus_type = SMI_AUTO_DETECT,
+};
+
static const struct smi_node cs35l56_hda = {
.instances = {
{ "cs35l56-hda", IRQ_RESOURCE_AUTO, 0 },
@@ -342,6 +355,19 @@ static const struct smi_node cs35l56_hda = {
.bus_type = SMI_AUTO_DETECT,
};
+static const struct smi_node cs35l57_hda = {
+ .instances = {
+ { "cs35l57-hda", IRQ_RESOURCE_AUTO, 0 },
+ { "cs35l57-hda", IRQ_RESOURCE_AUTO, 0 },
+ { "cs35l57-hda", IRQ_RESOURCE_AUTO, 0 },
+ { "cs35l57-hda", IRQ_RESOURCE_AUTO, 0 },
+ /* a 5th entry is an alias address, not a real device */
+ { "cs35l57-hda_dummy_dev" },
+ {}
+ },
+ .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().
@@ -350,7 +376,9 @@ static const struct acpi_device_id smi_acpi_ids[] = {
{ "BSG1160", (unsigned long)&bsg1160_data },
{ "BSG2150", (unsigned long)&bsg2150_data },
{ "CSC3551", (unsigned long)&cs35l41_hda },
+ { "CSC3554", (unsigned long)&cs35l54_hda },
{ "CSC3556", (unsigned long)&cs35l56_hda },
+ { "CSC3557", (unsigned long)&cs35l57_hda },
{ "INT3515", (unsigned long)&int3515_data },
/* Non-conforming _HID for Cirrus Logic already released */
{ "CLSA0100", (unsigned long)&cs35l41_hda },
diff --git a/drivers/platform/x86/siemens/simatic-ipc-batt-apollolake.c b/drivers/platform/x86/siemens/simatic-ipc-batt-apollolake.c
index 31a139d87d..5edc294de6 100644
--- a/drivers/platform/x86/siemens/simatic-ipc-batt-apollolake.c
+++ b/drivers/platform/x86/siemens/simatic-ipc-batt-apollolake.c
@@ -45,6 +45,7 @@ static struct platform_driver simatic_ipc_batt_driver = {
module_platform_driver(simatic_ipc_batt_driver);
+MODULE_DESCRIPTION("CMOS Battery monitoring for Simatic IPCs based on Apollo Lake GPIO");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" KBUILD_MODNAME);
MODULE_SOFTDEP("pre: simatic-ipc-batt platform:apollolake-pinctrl");
diff --git a/drivers/platform/x86/siemens/simatic-ipc-batt-elkhartlake.c b/drivers/platform/x86/siemens/simatic-ipc-batt-elkhartlake.c
index a7676f2240..e6a56d14b5 100644
--- a/drivers/platform/x86/siemens/simatic-ipc-batt-elkhartlake.c
+++ b/drivers/platform/x86/siemens/simatic-ipc-batt-elkhartlake.c
@@ -45,6 +45,7 @@ static struct platform_driver simatic_ipc_batt_driver = {
module_platform_driver(simatic_ipc_batt_driver);
+MODULE_DESCRIPTION("CMOS Battery monitoring for Simatic IPCs based on Elkhart Lake GPIO");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" KBUILD_MODNAME);
MODULE_SOFTDEP("pre: simatic-ipc-batt platform:elkhartlake-pinctrl");
diff --git a/drivers/platform/x86/siemens/simatic-ipc-batt-f7188x.c b/drivers/platform/x86/siemens/simatic-ipc-batt-f7188x.c
index 5e77e05fdb..f8849d0e48 100644
--- a/drivers/platform/x86/siemens/simatic-ipc-batt-f7188x.c
+++ b/drivers/platform/x86/siemens/simatic-ipc-batt-f7188x.c
@@ -81,6 +81,7 @@ static struct platform_driver simatic_ipc_batt_driver = {
module_platform_driver(simatic_ipc_batt_driver);
+MODULE_DESCRIPTION("CMOS Battery monitoring for Simatic IPCs based on Nuvoton GPIO");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" KBUILD_MODNAME);
MODULE_SOFTDEP("pre: simatic-ipc-batt gpio_f7188x platform:elkhartlake-pinctrl platform:alderlake-pinctrl");
diff --git a/drivers/platform/x86/siemens/simatic-ipc-batt.c b/drivers/platform/x86/siemens/simatic-ipc-batt.c
index c6dd263b4e..d9aff10608 100644
--- a/drivers/platform/x86/siemens/simatic-ipc-batt.c
+++ b/drivers/platform/x86/siemens/simatic-ipc-batt.c
@@ -247,6 +247,7 @@ static struct platform_driver simatic_ipc_batt_driver = {
module_platform_driver(simatic_ipc_batt_driver);
+MODULE_DESCRIPTION("CMOS core battery driver for Siemens Simatic IPCs");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" KBUILD_MODNAME);
MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
diff --git a/drivers/platform/x86/siemens/simatic-ipc.c b/drivers/platform/x86/siemens/simatic-ipc.c
index 8ca6e277fa..7039874d8f 100644
--- a/drivers/platform/x86/siemens/simatic-ipc.c
+++ b/drivers/platform/x86/siemens/simatic-ipc.c
@@ -231,6 +231,7 @@ static void __exit simatic_ipc_exit_module(void)
module_init(simatic_ipc_init_module);
module_exit(simatic_ipc_exit_module);
+MODULE_DESCRIPTION("Siemens SIMATIC IPC platform driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Gerd Haeussler <gerd.haeussler.ext@siemens.com>");
MODULE_ALIAS("dmi:*:svnSIEMENSAG:*");
diff --git a/drivers/platform/x86/silicom-platform.c b/drivers/platform/x86/silicom-platform.c
index 6ce43ccb31..c0910af16a 100644
--- a/drivers/platform/x86/silicom-platform.c
+++ b/drivers/platform/x86/silicom-platform.c
@@ -256,12 +256,7 @@ static void silicom_gpio_set(struct gpio_chip *gc,
if (direction == GPIO_LINE_DIRECTION_IN)
return;
- if (value)
- silicom_mec_port_set(channel, 0);
- else if (value == 0)
- silicom_mec_port_set(channel, 1);
- else
- pr_err("Wrong argument value: %d\n", value);
+ silicom_mec_port_set(channel, !value);
}
static int silicom_gpio_direction_output(struct gpio_chip *gc,
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
index 40878e327a..3e94fdd1ea 100644
--- a/drivers/platform/x86/sony-laptop.c
+++ b/drivers/platform/x86/sony-laptop.c
@@ -3303,7 +3303,6 @@ static struct acpi_driver sony_nc_driver = {
.name = SONY_NC_DRIVER_NAME,
.class = SONY_NC_CLASS,
.ids = sony_nc_device_ids,
- .owner = THIS_MODULE,
.ops = {
.add = sony_nc_add,
.remove = sony_nc_remove,
@@ -4844,7 +4843,6 @@ static struct acpi_driver sony_pic_driver = {
.name = SONY_PIC_DRIVER_NAME,
.class = SONY_PIC_CLASS,
.ids = sony_pic_device_ids,
- .owner = THIS_MODULE,
.ops = {
.add = sony_pic_add,
.remove = sony_pic_remove,
diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c
index ce3e08815a..0f2264bb75 100644
--- a/drivers/platform/x86/think-lmi.c
+++ b/drivers/platform/x86/think-lmi.c
@@ -175,9 +175,6 @@ MODULE_PARM_DESC(debug_support, "Enable debug command support");
#define TLMI_SMP_PWD BIT(6) /* System Management */
#define TLMI_CERT BIT(7) /* Certificate Based */
-#define to_tlmi_pwd_setting(kobj) container_of(kobj, struct tlmi_pwd_setting, kobj)
-#define to_tlmi_attr_setting(kobj) container_of(kobj, struct tlmi_attr_setting, kobj)
-
static const struct tlmi_err_codes tlmi_errs[] = {
{"Success", 0},
{"Not Supported", -EOPNOTSUPP},
@@ -195,9 +192,19 @@ static const char * const level_options[] = {
[TLMI_LEVEL_MASTER] = "master",
};
static struct think_lmi tlmi_priv;
-static struct class *fw_attr_class;
+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)
+{
+ return container_of(kobj, struct tlmi_pwd_setting, kobj);
+}
+
+static inline struct tlmi_attr_setting *to_tlmi_attr_setting(struct kobject *kobj)
+{
+ return container_of(kobj, struct tlmi_attr_setting, kobj);
+}
+
/* Convert BIOS WMI error string to suitable error code */
static int tlmi_errstr_to_err(const char *errstr)
{
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index 5ecd9d3325..397b409064 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -45,6 +45,7 @@
#include <linux/hwmon-sysfs.h>
#include <linux/init.h>
#include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
@@ -69,6 +70,7 @@
#include <linux/sysfs.h>
#include <linux/types.h>
#include <linux/uaccess.h>
+#include <linux/units.h>
#include <linux/workqueue.h>
#include <acpi/battery.h>
@@ -156,16 +158,33 @@ enum {
/* HKEY events */
enum tpacpi_hkey_event_t {
- /* Hotkey-related */
- TP_HKEY_EV_HOTKEY_BASE = 0x1001, /* first hotkey (FN+F1) */
+ /* Original hotkeys */
+ TP_HKEY_EV_ORIG_KEY_START = 0x1001, /* First hotkey (FN+F1) */
TP_HKEY_EV_BRGHT_UP = 0x1010, /* Brightness up */
TP_HKEY_EV_BRGHT_DOWN = 0x1011, /* Brightness down */
TP_HKEY_EV_KBD_LIGHT = 0x1012, /* Thinklight/kbd backlight */
TP_HKEY_EV_VOL_UP = 0x1015, /* Volume up or unmute */
TP_HKEY_EV_VOL_DOWN = 0x1016, /* Volume down or unmute */
TP_HKEY_EV_VOL_MUTE = 0x1017, /* Mixer output mute */
+ TP_HKEY_EV_ORIG_KEY_END = 0x1020, /* Last original hotkey code */
+
+ /* Adaptive keyboard (2014 X1 Carbon) */
+ TP_HKEY_EV_DFR_CHANGE_ROW = 0x1101, /* Change adaptive kbd Fn row mode */
+ TP_HKEY_EV_DFR_S_QUICKVIEW_ROW = 0x1102, /* Set adap. kbd Fn row to function mode */
+ TP_HKEY_EV_ADAPTIVE_KEY_START = 0x1103, /* First hotkey code on adaptive kbd */
+ TP_HKEY_EV_ADAPTIVE_KEY_END = 0x1116, /* Last hotkey code on adaptive kbd */
+
+ /* Extended hotkey events in 2017+ models */
+ TP_HKEY_EV_EXTENDED_KEY_START = 0x1300, /* First extended hotkey code */
TP_HKEY_EV_PRIVACYGUARD_TOGGLE = 0x130f, /* Toggle priv.guard on/off */
+ TP_HKEY_EV_EXTENDED_KEY_END = 0x1319, /* Last extended hotkey code using
+ * hkey -> scancode translation for
+ * compat. Later codes are entered
+ * directly in the sparse-keymap.
+ */
TP_HKEY_EV_AMT_TOGGLE = 0x131a, /* Toggle AMT on/off */
+ TP_HKEY_EV_DOUBLETAP_TOGGLE = 0x131c, /* Toggle trackpoint doubletap on/off */
+ TP_HKEY_EV_PROFILE_TOGGLE = 0x131f, /* Toggle platform profile */
/* Reasons for waking up from S3/S4 */
TP_HKEY_EV_WKUP_S3_UNDOCK = 0x2304, /* undock requested, S3 */
@@ -230,6 +249,9 @@ enum tpacpi_hkey_event_t {
/* Misc */
TP_HKEY_EV_RFKILL_CHANGED = 0x7000, /* rfkill switch changed */
+
+ /* Misc2 */
+ TP_HKEY_EV_TRACK_DOUBLETAP = 0x8036, /* trackpoint doubletap */
};
/****************************************************************************
@@ -351,6 +373,7 @@ static struct {
u32 hotkey_poll_active:1;
u32 has_adaptive_kbd:1;
u32 kbd_lang:1;
+ u32 trackpoint_doubletap:1;
struct quirk_entry *quirks;
} tp_features;
@@ -1742,15 +1765,15 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */
TP_ACPI_HOTKEYSCAN_THINKPAD,
TP_ACPI_HOTKEYSCAN_UNK1,
TP_ACPI_HOTKEYSCAN_UNK2,
- TP_ACPI_HOTKEYSCAN_UNK3,
+ TP_ACPI_HOTKEYSCAN_MICMUTE,
TP_ACPI_HOTKEYSCAN_UNK4,
- TP_ACPI_HOTKEYSCAN_UNK5,
- TP_ACPI_HOTKEYSCAN_UNK6,
- TP_ACPI_HOTKEYSCAN_UNK7,
- TP_ACPI_HOTKEYSCAN_UNK8,
+ TP_ACPI_HOTKEYSCAN_CONFIG,
+ TP_ACPI_HOTKEYSCAN_SEARCH,
+ TP_ACPI_HOTKEYSCAN_SCALE,
+ TP_ACPI_HOTKEYSCAN_FILE,
/* Adaptive keyboard keycodes */
- TP_ACPI_HOTKEYSCAN_ADAPTIVE_START,
+ TP_ACPI_HOTKEYSCAN_ADAPTIVE_START, /* 32 / 0x20 */
TP_ACPI_HOTKEYSCAN_MUTE2 = TP_ACPI_HOTKEYSCAN_ADAPTIVE_START,
TP_ACPI_HOTKEYSCAN_BRIGHTNESS_ZERO,
TP_ACPI_HOTKEYSCAN_CLIPPING_TOOL,
@@ -1762,7 +1785,7 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */
TP_ACPI_HOTKEYSCAN_UNK11,
TP_ACPI_HOTKEYSCAN_UNK12,
TP_ACPI_HOTKEYSCAN_UNK13,
- TP_ACPI_HOTKEYSCAN_CONFIG,
+ TP_ACPI_HOTKEYSCAN_CONFIG2,
TP_ACPI_HOTKEYSCAN_NEW_TAB,
TP_ACPI_HOTKEYSCAN_RELOAD,
TP_ACPI_HOTKEYSCAN_BACK,
@@ -1773,7 +1796,7 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */
TP_ACPI_HOTKEYSCAN_ROTATE_DISPLAY,
/* Lenovo extended keymap, starting at 0x1300 */
- TP_ACPI_HOTKEYSCAN_EXTENDED_START,
+ TP_ACPI_HOTKEYSCAN_EXTENDED_START, /* 52 / 0x34 */
/* first new observed key (star, favorites) is 0x1311 */
TP_ACPI_HOTKEYSCAN_STAR = 69,
TP_ACPI_HOTKEYSCAN_CLIPPING_TOOL2,
@@ -1784,9 +1807,6 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */
TP_ACPI_HOTKEYSCAN_NOTIFICATION_CENTER,
TP_ACPI_HOTKEYSCAN_PICKUP_PHONE,
TP_ACPI_HOTKEYSCAN_HANGUP_PHONE,
-
- /* Hotkey keymap size */
- TPACPI_HOTKEY_MAP_LEN
};
enum { /* Keys/events available through NVRAM polling */
@@ -1899,10 +1919,7 @@ static u32 hotkey_driver_mask; /* events needed by the driver */
static u32 hotkey_user_mask; /* events visible to userspace */
static u32 hotkey_acpi_mask; /* events enabled in firmware */
-static u16 *hotkey_keycode_map;
-
-static void tpacpi_driver_event(const unsigned int hkey_event);
-static void hotkey_driver_event(const unsigned int scancode);
+static bool tpacpi_driver_event(const unsigned int hkey_event);
static void hotkey_poll_setup(const bool may_warn);
/* HKEY.MHKG() return bits */
@@ -2234,32 +2251,56 @@ static void tpacpi_input_send_tabletsw(void)
}
}
-/* Do NOT call without validating scancode first */
-static void tpacpi_input_send_key(const unsigned int scancode)
+static bool tpacpi_input_send_key(const u32 hkey, bool *send_acpi_ev)
{
- const unsigned int keycode = hotkey_keycode_map[scancode];
-
- if (keycode != KEY_RESERVED) {
- mutex_lock(&tpacpi_inputdev_send_mutex);
+ bool known_ev;
+ u32 scancode;
- input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, scancode);
- input_report_key(tpacpi_inputdev, keycode, 1);
- input_sync(tpacpi_inputdev);
+ if (tpacpi_driver_event(hkey))
+ return true;
- input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, scancode);
- input_report_key(tpacpi_inputdev, keycode, 0);
- input_sync(tpacpi_inputdev);
+ /*
+ * Before the conversion to using the sparse-keymap helpers the driver used to
+ * map the hkey event codes to 0x00 - 0x4d scancodes so that a straight scancode
+ * indexed array could be used to map scancodes to keycodes:
+ *
+ * 0x1001 - 0x1020 -> 0x00 - 0x1f (Original ThinkPad events)
+ * 0x1103 - 0x1116 -> 0x20 - 0x33 (Adaptive keyboard, 2014 X1 Carbon)
+ * 0x1300 - 0x1319 -> 0x34 - 0x4d (Additional keys send in 2017+ models)
+ *
+ * The sparse-keymap tables still use these scancodes for these ranges to
+ * preserve userspace API compatibility (e.g. hwdb keymappings).
+ */
+ if (hkey >= TP_HKEY_EV_ORIG_KEY_START &&
+ hkey <= TP_HKEY_EV_ORIG_KEY_END) {
+ scancode = hkey - TP_HKEY_EV_ORIG_KEY_START;
+ if (!(hotkey_user_mask & (1 << scancode)))
+ return true; /* Not reported but still a known code */
+ } else if (hkey >= TP_HKEY_EV_ADAPTIVE_KEY_START &&
+ hkey <= TP_HKEY_EV_ADAPTIVE_KEY_END) {
+ scancode = hkey - TP_HKEY_EV_ADAPTIVE_KEY_START +
+ TP_ACPI_HOTKEYSCAN_ADAPTIVE_START;
+ } else if (hkey >= TP_HKEY_EV_EXTENDED_KEY_START &&
+ hkey <= TP_HKEY_EV_EXTENDED_KEY_END) {
+ scancode = hkey - TP_HKEY_EV_EXTENDED_KEY_START +
+ TP_ACPI_HOTKEYSCAN_EXTENDED_START;
+ } else {
+ /*
+ * Do not send ACPI netlink events for unknown hotkeys, to
+ * avoid userspace starting to rely on them. Instead these
+ * should be added to the keymap to send evdev events.
+ */
+ if (send_acpi_ev)
+ *send_acpi_ev = false;
- mutex_unlock(&tpacpi_inputdev_send_mutex);
+ scancode = hkey;
}
-}
-/* Do NOT call without validating scancode first */
-static void tpacpi_input_send_key_masked(const unsigned int scancode)
-{
- hotkey_driver_event(scancode);
- if (hotkey_user_mask & (1 << scancode))
- tpacpi_input_send_key(scancode);
+ mutex_lock(&tpacpi_inputdev_send_mutex);
+ known_ev = sparse_keymap_report_event(tpacpi_inputdev, scancode, 1, true);
+ mutex_unlock(&tpacpi_inputdev_send_mutex);
+
+ return known_ev;
}
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
@@ -2268,7 +2309,7 @@ static struct tp_acpi_drv_struct ibm_hotkey_acpidriver;
/* Do NOT call without validating scancode first */
static void tpacpi_hotkey_send_key(unsigned int scancode)
{
- tpacpi_input_send_key_masked(scancode);
+ tpacpi_input_send_key(TP_HKEY_EV_ORIG_KEY_START + scancode, NULL);
}
static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m)
@@ -2573,6 +2614,9 @@ static void hotkey_poll_setup_safe(const bool __unused)
{
}
+static void hotkey_poll_stop_sync(void)
+{
+}
#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
static int hotkey_inputdev_open(struct input_dev *dev)
@@ -2677,7 +2721,7 @@ static ssize_t hotkey_bios_enabled_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- return sprintf(buf, "0\n");
+ return sysfs_emit(buf, "0\n");
}
static DEVICE_ATTR_RO(hotkey_bios_enabled);
@@ -3042,11 +3086,8 @@ static void tpacpi_send_radiosw_update(void)
static void hotkey_exit(void)
{
-#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
mutex_lock(&hotkey_mutex);
hotkey_poll_stop_sync();
- mutex_unlock(&hotkey_mutex);
-#endif
dbg_printk(TPACPI_DBG_EXIT | TPACPI_DBG_HKEY,
"restoring original HKEY status and mask\n");
/* yes, there is a bitwise or below, we want the
@@ -3055,15 +3096,8 @@ static void hotkey_exit(void)
hotkey_mask_set(hotkey_orig_mask)) |
hotkey_status_set(false)) != 0)
pr_err("failed to restore hot key mask to BIOS defaults\n");
-}
-static void __init hotkey_unmap(const unsigned int scancode)
-{
- if (hotkey_keycode_map[scancode] != KEY_RESERVED) {
- clear_bit(hotkey_keycode_map[scancode],
- tpacpi_inputdev->keybit);
- hotkey_keycode_map[scancode] = KEY_RESERVED;
- }
+ mutex_unlock(&hotkey_mutex);
}
/*
@@ -3095,9 +3129,6 @@ static const struct tpacpi_quirk tpacpi_hotkey_qtable[] __initconst = {
TPACPI_Q_IBM('1', 'D', TPACPI_HK_Q_INIMASK), /* X22, X23, X24 */
};
-typedef u16 tpacpi_keymap_entry_t;
-typedef tpacpi_keymap_entry_t tpacpi_keymap_t[TPACPI_HOTKEY_MAP_LEN];
-
static int hotkey_init_tablet_mode(void)
{
int in_tablet_mode = 0, res;
@@ -3134,209 +3165,126 @@ static int hotkey_init_tablet_mode(void)
return in_tablet_mode;
}
-static int __init hotkey_init(struct ibm_init_struct *iibm)
-{
- /* Requirements for changing the default keymaps:
- *
- * 1. Many of the keys are mapped to KEY_RESERVED for very
- * good reasons. Do not change them unless you have deep
- * knowledge on the IBM and Lenovo ThinkPad firmware for
- * the various ThinkPad models. The driver behaves
- * differently for KEY_RESERVED: such keys have their
- * hot key mask *unset* in mask_recommended, and also
- * in the initial hot key mask programmed into the
- * firmware at driver load time, which means the firm-
- * ware may react very differently if you change them to
- * something else;
- *
- * 2. You must be subscribed to the linux-thinkpad and
- * ibm-acpi-devel mailing lists, and you should read the
- * list archives since 2007 if you want to change the
- * keymaps. This requirement exists so that you will
- * know the past history of problems with the thinkpad-
- * acpi driver keymaps, and also that you will be
- * listening to any bug reports;
- *
- * 3. Do not send thinkpad-acpi specific patches directly to
- * for merging, *ever*. Send them to the linux-acpi
- * mailinglist for comments. Merging is to be done only
- * through acpi-test and the ACPI maintainer.
- *
- * If the above is too much to ask, don't change the keymap.
- * Ask the thinkpad-acpi maintainer to do it, instead.
+static const struct key_entry keymap_ibm[] __initconst = {
+ /* Original hotkey mappings translated scancodes 0x00 - 0x1f */
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF1, { KEY_FN_F1 } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF2, { KEY_BATTERY } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF3, { KEY_COFFEE } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF4, { KEY_SLEEP } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF5, { KEY_WLAN } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF6, { KEY_FN_F6 } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF7, { KEY_SWITCHVIDEOMODE } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF8, { KEY_FN_F8 } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF9, { KEY_FN_F9 } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF10, { KEY_FN_F10 } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF11, { KEY_FN_F11 } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF12, { KEY_SUSPEND } },
+ /* Brightness: firmware always reacts, suppressed through hotkey_reserved_mask. */
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNHOME, { KEY_BRIGHTNESSUP } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNEND, { KEY_BRIGHTNESSDOWN } },
+ /* Thinklight: firmware always reacts, suppressed through hotkey_reserved_mask. */
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNPAGEUP, { KEY_KBDILLUMTOGGLE } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNSPACE, { KEY_ZOOM } },
+ /*
+ * Volume: firmware always reacts and reprograms the built-in *extra* mixer.
+ * Suppressed by default through hotkey_reserved_mask.
*/
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_VOLUMEUP, { KEY_VOLUMEUP } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_VOLUMEDOWN, { KEY_VOLUMEDOWN } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_MUTE, { KEY_MUTE } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_THINKPAD, { KEY_VENDOR } },
+ { KE_END }
+};
+
+static const struct key_entry keymap_lenovo[] __initconst = {
+ /* Original hotkey mappings translated scancodes 0x00 - 0x1f */
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF1, { KEY_FN_F1 } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF2, { KEY_COFFEE } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF3, { KEY_BATTERY } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF4, { KEY_SLEEP } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF5, { KEY_WLAN } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF6, { KEY_CAMERA, } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF7, { KEY_SWITCHVIDEOMODE } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF8, { KEY_FN_F8 } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF9, { KEY_FN_F9 } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF10, { KEY_FN_F10 } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF11, { KEY_FN_F11 } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNF12, { KEY_SUSPEND } },
+ /*
+ * These should be enabled --only-- when ACPI video is disabled and
+ * are handled in a special way by the init code.
+ */
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNHOME, { KEY_BRIGHTNESSUP } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNEND, { KEY_BRIGHTNESSDOWN } },
+ /* Suppressed by default through hotkey_reserved_mask. */
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNPAGEUP, { KEY_KBDILLUMTOGGLE } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FNSPACE, { KEY_ZOOM } },
+ /*
+ * Volume: z60/z61, T60 (BIOS version?): firmware always reacts and
+ * reprograms the built-in *extra* mixer.
+ * T60?, T61, R60?, R61: firmware and EC tries to send these over
+ * the regular keyboard (not through tpacpi). There are still weird bugs
+ * re. MUTE. May cause the BIOS to interfere with the HDA mixer.
+ * Suppressed by default through hotkey_reserved_mask.
+ */
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_VOLUMEUP, { KEY_VOLUMEUP } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_VOLUMEDOWN, { KEY_VOLUMEDOWN } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_MUTE, { KEY_MUTE } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_THINKPAD, { KEY_VENDOR } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_MICMUTE, { KEY_MICMUTE } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_CONFIG, { KEY_CONFIG } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_SEARCH, { KEY_SEARCH } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_SCALE, { KEY_SCALE } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FILE, { KEY_FILE } },
+ /* Adaptive keyboard mappings for Carbon X1 2014 translated scancodes 0x20 - 0x33 */
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_MUTE2, { KEY_RESERVED } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_BRIGHTNESS_ZERO, { KEY_BRIGHTNESS_MIN } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_CLIPPING_TOOL, { KEY_SELECTIVE_SCREENSHOT } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_CLOUD, { KEY_XFER } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_UNK9, { KEY_RESERVED } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_VOICE, { KEY_VOICECOMMAND } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_UNK10, { KEY_RESERVED } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_GESTURES, { KEY_RESERVED } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_UNK11, { KEY_RESERVED } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_UNK12, { KEY_RESERVED } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_UNK13, { KEY_RESERVED } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_CONFIG2, { KEY_CONFIG } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_NEW_TAB, { KEY_RESERVED } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_RELOAD, { KEY_REFRESH } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_BACK, { KEY_BACK } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_MIC_DOWN, { KEY_RESERVED } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_MIC_UP, { KEY_RESERVED } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_MIC_CANCELLATION, { KEY_RESERVED } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_CAMERA_MODE, { KEY_RESERVED } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_ROTATE_DISPLAY, { KEY_RESERVED } },
+ /* Extended hotkeys mappings translated scancodes 0x34 - 0x4d */
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_STAR, { KEY_BOOKMARKS } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_CLIPPING_TOOL2, { KEY_SELECTIVE_SCREENSHOT } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_CALCULATOR, { KEY_CALC } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_BLUETOOTH, { KEY_BLUETOOTH } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_KEYBOARD, { KEY_KEYBOARD } },
+ /* Used by "Lenovo Quick Clean" */
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_FN_RIGHT_SHIFT, { KEY_FN_RIGHT_SHIFT } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_NOTIFICATION_CENTER, { KEY_NOTIFICATION_CENTER } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_PICKUP_PHONE, { KEY_PICKUP_PHONE } },
+ { KE_KEY, TP_ACPI_HOTKEYSCAN_HANGUP_PHONE, { KEY_HANGUP_PHONE } },
+ /*
+ * All mapping below are for raw untranslated hkey event codes mapped directly
+ * after switching to sparse keymap support. The mappings above use translated
+ * 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, TP_HKEY_EV_TRACK_DOUBLETAP /* 0x8036 */, { KEY_PROG4 } },
+ { KE_END }
+};
+static int __init hotkey_init(struct ibm_init_struct *iibm)
+{
enum keymap_index {
TPACPI_KEYMAP_IBM_GENERIC = 0,
TPACPI_KEYMAP_LENOVO_GENERIC,
};
- static const tpacpi_keymap_t tpacpi_keymaps[] __initconst = {
- /* Generic keymap for IBM ThinkPads */
- [TPACPI_KEYMAP_IBM_GENERIC] = {
- /* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */
- KEY_FN_F1, KEY_BATTERY, KEY_COFFEE, KEY_SLEEP,
- KEY_WLAN, KEY_FN_F6, KEY_SWITCHVIDEOMODE, KEY_FN_F8,
- KEY_FN_F9, KEY_FN_F10, KEY_FN_F11, KEY_SUSPEND,
-
- /* Scan codes 0x0C to 0x1F: Other ACPI HKEY hot keys */
- KEY_UNKNOWN, /* 0x0C: FN+BACKSPACE */
- KEY_UNKNOWN, /* 0x0D: FN+INSERT */
- KEY_UNKNOWN, /* 0x0E: FN+DELETE */
-
- /* brightness: firmware always reacts to them */
- KEY_RESERVED, /* 0x0F: FN+HOME (brightness up) */
- KEY_RESERVED, /* 0x10: FN+END (brightness down) */
-
- /* Thinklight: firmware always react to it */
- KEY_RESERVED, /* 0x11: FN+PGUP (thinklight toggle) */
-
- KEY_UNKNOWN, /* 0x12: FN+PGDOWN */
- KEY_ZOOM, /* 0x13: FN+SPACE (zoom) */
-
- /* Volume: firmware always react to it and reprograms
- * the built-in *extra* mixer. Never map it to control
- * another mixer by default. */
- KEY_RESERVED, /* 0x14: VOLUME UP */
- KEY_RESERVED, /* 0x15: VOLUME DOWN */
- KEY_RESERVED, /* 0x16: MUTE */
-
- KEY_VENDOR, /* 0x17: Thinkpad/AccessIBM/Lenovo */
-
- /* (assignments unknown, please report if found) */
- KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
- KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
-
- /* No assignments, only used for Adaptive keyboards. */
- KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
- KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
- KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
- KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
- KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
-
- /* No assignment, used for newer Lenovo models */
- KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
- KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
- KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
- KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
- KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
- KEY_UNKNOWN, KEY_UNKNOWN
-
- },
-
- /* Generic keymap for Lenovo ThinkPads */
- [TPACPI_KEYMAP_LENOVO_GENERIC] = {
- /* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */
- KEY_FN_F1, KEY_COFFEE, KEY_BATTERY, KEY_SLEEP,
- KEY_WLAN, KEY_CAMERA, KEY_SWITCHVIDEOMODE, KEY_FN_F8,
- KEY_FN_F9, KEY_FN_F10, KEY_FN_F11, KEY_SUSPEND,
-
- /* Scan codes 0x0C to 0x1F: Other ACPI HKEY hot keys */
- KEY_UNKNOWN, /* 0x0C: FN+BACKSPACE */
- KEY_UNKNOWN, /* 0x0D: FN+INSERT */
- KEY_UNKNOWN, /* 0x0E: FN+DELETE */
-
- /* These should be enabled --only-- when ACPI video
- * is disabled (i.e. in "vendor" mode), and are handled
- * in a special way by the init code */
- KEY_BRIGHTNESSUP, /* 0x0F: FN+HOME (brightness up) */
- KEY_BRIGHTNESSDOWN, /* 0x10: FN+END (brightness down) */
-
- KEY_RESERVED, /* 0x11: FN+PGUP (thinklight toggle) */
-
- KEY_UNKNOWN, /* 0x12: FN+PGDOWN */
- KEY_ZOOM, /* 0x13: FN+SPACE (zoom) */
-
- /* Volume: z60/z61, T60 (BIOS version?): firmware always
- * react to it and reprograms the built-in *extra* mixer.
- * Never map it to control another mixer by default.
- *
- * T60?, T61, R60?, R61: firmware and EC tries to send
- * these over the regular keyboard, so these are no-ops,
- * but there are still weird bugs re. MUTE, so do not
- * change unless you get test reports from all Lenovo
- * models. May cause the BIOS to interfere with the
- * HDA mixer.
- */
- KEY_RESERVED, /* 0x14: VOLUME UP */
- KEY_RESERVED, /* 0x15: VOLUME DOWN */
- KEY_RESERVED, /* 0x16: MUTE */
-
- KEY_VENDOR, /* 0x17: Thinkpad/AccessIBM/Lenovo */
-
- /* (assignments unknown, please report if found) */
- KEY_UNKNOWN, KEY_UNKNOWN,
-
- /*
- * The mic mute button only sends 0x1a. It does not
- * automatically mute the mic or change the mute light.
- */
- KEY_MICMUTE, /* 0x1a: Mic mute (since ?400 or so) */
-
- /* (assignments unknown, please report if found) */
- KEY_UNKNOWN,
-
- /* Extra keys in use since the X240 / T440 / T540 */
- KEY_CONFIG, KEY_SEARCH, KEY_SCALE, KEY_FILE,
-
- /*
- * These are the adaptive keyboard keycodes for Carbon X1 2014.
- * The first item in this list is the Mute button which is
- * emitted with 0x103 through
- * adaptive_keyboard_hotkey_notify_hotkey() when the sound
- * symbol is held.
- * We'll need to offset those by 0x20.
- */
- KEY_RESERVED, /* Mute held, 0x103 */
- KEY_BRIGHTNESS_MIN, /* Backlight off */
- KEY_RESERVED, /* Clipping tool */
- KEY_RESERVED, /* Cloud */
- KEY_RESERVED,
- KEY_VOICECOMMAND, /* Voice */
- KEY_RESERVED,
- KEY_RESERVED, /* Gestures */
- KEY_RESERVED,
- KEY_RESERVED,
- KEY_RESERVED,
- KEY_CONFIG, /* Settings */
- KEY_RESERVED, /* New tab */
- KEY_REFRESH, /* Reload */
- KEY_BACK, /* Back */
- KEY_RESERVED, /* Microphone down */
- KEY_RESERVED, /* Microphone up */
- KEY_RESERVED, /* Microphone cancellation */
- KEY_RESERVED, /* Camera mode */
- KEY_RESERVED, /* Rotate display, 0x116 */
-
- /*
- * These are found in 2017 models (e.g. T470s, X270).
- * The lowest known value is 0x311, which according to
- * the manual should launch a user defined favorite
- * application.
- *
- * The offset for these is TP_ACPI_HOTKEYSCAN_EXTENDED_START,
- * corresponding to 0x34.
- */
-
- /* (assignments unknown, please report if found) */
- KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
- KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
- KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
- KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
- KEY_UNKNOWN,
-
- KEY_BOOKMARKS, /* Favorite app, 0x311 */
- KEY_SELECTIVE_SCREENSHOT, /* Clipping tool */
- KEY_CALC, /* Calculator (above numpad, P52) */
- KEY_BLUETOOTH, /* Bluetooth */
- KEY_KEYBOARD, /* Keyboard, 0x315 */
- KEY_FN_RIGHT_SHIFT, /* Fn + right Shift */
- KEY_NOTIFICATION_CENTER, /* Notification Center */
- KEY_PICKUP_PHONE, /* Answer incoming call */
- KEY_HANGUP_PHONE, /* Decline incoming call */
- },
- };
-
static const struct tpacpi_quirk tpacpi_keymap_qtable[] __initconst = {
/* Generic maps (fallback) */
{
@@ -3351,17 +3299,11 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
},
};
-#define TPACPI_HOTKEY_MAP_SIZE sizeof(tpacpi_keymap_t)
-#define TPACPI_HOTKEY_MAP_TYPESIZE sizeof(tpacpi_keymap_entry_t)
-
- int res, i;
- int status;
- int hkeyv;
+ unsigned long keymap_id, quirks;
+ const struct key_entry *keymap;
bool radiosw_state = false;
bool tabletsw_state = false;
-
- unsigned long quirks;
- unsigned long keymap_id;
+ int hkeyv, res, status;
vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY,
"initializing hotkey subdriver\n");
@@ -3501,30 +3443,29 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
/* Set up key map */
keymap_id = tpacpi_check_quirks(tpacpi_keymap_qtable,
ARRAY_SIZE(tpacpi_keymap_qtable));
- BUG_ON(keymap_id >= ARRAY_SIZE(tpacpi_keymaps));
dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY,
"using keymap number %lu\n", keymap_id);
- hotkey_keycode_map = kmemdup(&tpacpi_keymaps[keymap_id],
- TPACPI_HOTKEY_MAP_SIZE, GFP_KERNEL);
- if (!hotkey_keycode_map) {
- pr_err("failed to allocate memory for key map\n");
- return -ENOMEM;
+ /* Keys which should be reserved on both IBM and Lenovo models */
+ hotkey_reserved_mask = TP_ACPI_HKEY_KBD_LIGHT_MASK |
+ TP_ACPI_HKEY_VOLUP_MASK |
+ TP_ACPI_HKEY_VOLDWN_MASK |
+ TP_ACPI_HKEY_MUTE_MASK;
+ /*
+ * Reserve brightness up/down unconditionally on IBM models, on Lenovo
+ * models these are disabled based on acpi_video_get_backlight_type().
+ */
+ if (keymap_id == TPACPI_KEYMAP_IBM_GENERIC) {
+ hotkey_reserved_mask |= TP_ACPI_HKEY_BRGHTUP_MASK |
+ TP_ACPI_HKEY_BRGHTDWN_MASK;
+ keymap = keymap_ibm;
+ } else {
+ keymap = keymap_lenovo;
}
- input_set_capability(tpacpi_inputdev, EV_MSC, MSC_SCAN);
- tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE;
- tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN;
- tpacpi_inputdev->keycode = hotkey_keycode_map;
- for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) {
- if (hotkey_keycode_map[i] != KEY_RESERVED) {
- input_set_capability(tpacpi_inputdev, EV_KEY,
- hotkey_keycode_map[i]);
- } else {
- if (i < sizeof(hotkey_reserved_mask)*8)
- hotkey_reserved_mask |= 1 << i;
- }
- }
+ res = sparse_keymap_setup(tpacpi_inputdev, keymap, NULL);
+ if (res)
+ return res;
if (tp_features.hotkey_wlsw) {
input_set_capability(tpacpi_inputdev, EV_SW, SW_RFKILL_ALL);
@@ -3547,11 +3488,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
/* Disable brightness up/down on Lenovo thinkpads when
* ACPI is handling them, otherwise it is plain impossible
* for userspace to do something even remotely sane */
- hotkey_reserved_mask |=
- (1 << TP_ACPI_HOTKEYSCAN_FNHOME)
- | (1 << TP_ACPI_HOTKEYSCAN_FNEND);
- hotkey_unmap(TP_ACPI_HOTKEYSCAN_FNHOME);
- hotkey_unmap(TP_ACPI_HOTKEYSCAN_FNEND);
+ hotkey_reserved_mask |= TP_ACPI_HKEY_BRGHTUP_MASK |
+ TP_ACPI_HKEY_BRGHTDWN_MASK;
}
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
@@ -3591,6 +3529,9 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
hotkey_poll_setup_safe(true);
+ /* Enable doubletap by default */
+ tp_features.trackpoint_doubletap = 1;
+
return 0;
}
@@ -3608,10 +3549,6 @@ static const int adaptive_keyboard_modes[] = {
FUNCTION_MODE
};
-#define DFR_CHANGE_ROW 0x101
-#define DFR_SHOW_QUICKVIEW_ROW 0x102
-#define FIRST_ADAPTIVE_KEY 0x103
-
/* press Fn key a while second, it will switch to Function Mode. Then
* release Fn key, previous mode be restored.
*/
@@ -3662,152 +3599,67 @@ static int adaptive_keyboard_get_next_mode(int mode)
return adaptive_keyboard_modes[i];
}
-static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode)
+static void adaptive_keyboard_change_row(void)
{
- int current_mode = 0;
- int new_mode = 0;
- int keycode;
-
- switch (scancode) {
- case DFR_CHANGE_ROW:
- if (adaptive_keyboard_mode_is_saved) {
- new_mode = adaptive_keyboard_prev_mode;
- adaptive_keyboard_mode_is_saved = false;
- } else {
- current_mode = adaptive_keyboard_get_mode();
- if (current_mode < 0)
- return false;
- new_mode = adaptive_keyboard_get_next_mode(
- current_mode);
- }
-
- if (adaptive_keyboard_set_mode(new_mode) < 0)
- return false;
-
- return true;
-
- case DFR_SHOW_QUICKVIEW_ROW:
- current_mode = adaptive_keyboard_get_mode();
- if (current_mode < 0)
- return false;
-
- adaptive_keyboard_prev_mode = current_mode;
- adaptive_keyboard_mode_is_saved = true;
-
- if (adaptive_keyboard_set_mode (FUNCTION_MODE) < 0)
- return false;
- return true;
-
- default:
- if (scancode < FIRST_ADAPTIVE_KEY ||
- scancode >= FIRST_ADAPTIVE_KEY +
- TP_ACPI_HOTKEYSCAN_EXTENDED_START -
- TP_ACPI_HOTKEYSCAN_ADAPTIVE_START) {
- pr_info("Unhandled adaptive keyboard key: 0x%x\n",
- scancode);
- return false;
- }
- keycode = hotkey_keycode_map[scancode - FIRST_ADAPTIVE_KEY +
- TP_ACPI_HOTKEYSCAN_ADAPTIVE_START];
- if (keycode != KEY_RESERVED) {
- mutex_lock(&tpacpi_inputdev_send_mutex);
-
- input_report_key(tpacpi_inputdev, keycode, 1);
- input_sync(tpacpi_inputdev);
-
- input_report_key(tpacpi_inputdev, keycode, 0);
- input_sync(tpacpi_inputdev);
+ int mode;
- mutex_unlock(&tpacpi_inputdev_send_mutex);
- }
- return true;
+ if (adaptive_keyboard_mode_is_saved) {
+ mode = adaptive_keyboard_prev_mode;
+ adaptive_keyboard_mode_is_saved = false;
+ } else {
+ mode = adaptive_keyboard_get_mode();
+ if (mode < 0)
+ return;
+ mode = adaptive_keyboard_get_next_mode(mode);
}
+
+ adaptive_keyboard_set_mode(mode);
}
-static bool hotkey_notify_extended_hotkey(const u32 hkey)
+static void adaptive_keyboard_s_quickview_row(void)
{
- unsigned int scancode;
+ int mode;
- switch (hkey) {
- case TP_HKEY_EV_PRIVACYGUARD_TOGGLE:
- case TP_HKEY_EV_AMT_TOGGLE:
- tpacpi_driver_event(hkey);
- return true;
- }
+ mode = adaptive_keyboard_get_mode();
+ if (mode < 0)
+ return;
- /* Extended keycodes start at 0x300 and our offset into the map
- * TP_ACPI_HOTKEYSCAN_EXTENDED_START. The calculated scancode
- * will be positive, but might not be in the correct range.
- */
- scancode = (hkey & 0xfff) - (0x300 - TP_ACPI_HOTKEYSCAN_EXTENDED_START);
- if (scancode >= TP_ACPI_HOTKEYSCAN_EXTENDED_START &&
- scancode < TPACPI_HOTKEY_MAP_LEN) {
- tpacpi_input_send_key(scancode);
- return true;
- }
+ adaptive_keyboard_prev_mode = mode;
+ adaptive_keyboard_mode_is_saved = true;
- return false;
+ adaptive_keyboard_set_mode(FUNCTION_MODE);
}
-static bool hotkey_notify_hotkey(const u32 hkey,
- bool *send_acpi_ev,
- bool *ignore_acpi_ev)
+/* 0x1000-0x1FFF: key presses */
+static bool hotkey_notify_hotkey(const u32 hkey, bool *send_acpi_ev)
{
- /* 0x1000-0x1FFF: key presses */
- unsigned int scancode = hkey & 0xfff;
- *send_acpi_ev = true;
- *ignore_acpi_ev = false;
+ /* Never send ACPI netlink events for original hotkeys (hkey: 0x1001 - 0x1020) */
+ if (hkey >= TP_HKEY_EV_ORIG_KEY_START && hkey <= TP_HKEY_EV_ORIG_KEY_END) {
+ *send_acpi_ev = false;
- /*
- * Original events are in the 0x10XX range, the adaptive keyboard
- * found in 2014 X1 Carbon emits events are of 0x11XX. In 2017
- * models, additional keys are emitted through 0x13XX.
- */
- switch ((hkey >> 8) & 0xf) {
- case 0:
- if (scancode > 0 &&
- scancode <= TP_ACPI_HOTKEYSCAN_ADAPTIVE_START) {
- /* HKEY event 0x1001 is scancode 0x00 */
- scancode--;
- if (!(hotkey_source_mask & (1 << scancode))) {
- tpacpi_input_send_key_masked(scancode);
- *send_acpi_ev = false;
- } else {
- *ignore_acpi_ev = true;
- }
+ /* Original hotkeys may be polled from NVRAM instead */
+ unsigned int scancode = hkey - TP_HKEY_EV_ORIG_KEY_START;
+ if (hotkey_source_mask & (1 << scancode))
return true;
- }
- break;
-
- case 1:
- return adaptive_keyboard_hotkey_notify_hotkey(scancode);
-
- case 3:
- return hotkey_notify_extended_hotkey(hkey);
}
- return false;
+ return tpacpi_input_send_key(hkey, send_acpi_ev);
}
-static bool hotkey_notify_wakeup(const u32 hkey,
- bool *send_acpi_ev,
- bool *ignore_acpi_ev)
+/* 0x2000-0x2FFF: Wakeup reason */
+static bool hotkey_notify_wakeup(const u32 hkey, bool *send_acpi_ev)
{
- /* 0x2000-0x2FFF: Wakeup reason */
- *send_acpi_ev = true;
- *ignore_acpi_ev = false;
-
switch (hkey) {
case TP_HKEY_EV_WKUP_S3_UNDOCK: /* suspend, undock */
case TP_HKEY_EV_WKUP_S4_UNDOCK: /* hibernation, undock */
hotkey_wakeup_reason = TP_ACPI_WAKEUP_UNDOCK;
- *ignore_acpi_ev = true;
+ *send_acpi_ev = false;
break;
case TP_HKEY_EV_WKUP_S3_BAYEJ: /* suspend, bay eject */
case TP_HKEY_EV_WKUP_S4_BAYEJ: /* hibernation, bay eject */
hotkey_wakeup_reason = TP_ACPI_WAKEUP_BAYEJ;
- *ignore_acpi_ev = true;
+ *send_acpi_ev = false;
break;
case TP_HKEY_EV_WKUP_S3_BATLOW: /* Battery on critical low level/S3 */
@@ -3829,14 +3681,9 @@ static bool hotkey_notify_wakeup(const u32 hkey,
return true;
}
-static bool hotkey_notify_dockevent(const u32 hkey,
- bool *send_acpi_ev,
- bool *ignore_acpi_ev)
+/* 0x4000-0x4FFF: dock-related events */
+static bool hotkey_notify_dockevent(const u32 hkey, bool *send_acpi_ev)
{
- /* 0x4000-0x4FFF: dock-related events */
- *send_acpi_ev = true;
- *ignore_acpi_ev = false;
-
switch (hkey) {
case TP_HKEY_EV_UNDOCK_ACK:
/* ACPI undock operation completed after wakeup */
@@ -3866,7 +3713,6 @@ static bool hotkey_notify_dockevent(const u32 hkey,
case TP_HKEY_EV_KBD_COVER_ATTACH:
case TP_HKEY_EV_KBD_COVER_DETACH:
*send_acpi_ev = false;
- *ignore_acpi_ev = true;
return true;
default:
@@ -3874,14 +3720,9 @@ static bool hotkey_notify_dockevent(const u32 hkey,
}
}
-static bool hotkey_notify_usrevent(const u32 hkey,
- bool *send_acpi_ev,
- bool *ignore_acpi_ev)
+/* 0x5000-0x5FFF: human interface helpers */
+static bool hotkey_notify_usrevent(const u32 hkey, bool *send_acpi_ev)
{
- /* 0x5000-0x5FFF: human interface helpers */
- *send_acpi_ev = true;
- *ignore_acpi_ev = false;
-
switch (hkey) {
case TP_HKEY_EV_PEN_INSERTED: /* X61t: tablet pen inserted into bay */
case TP_HKEY_EV_PEN_REMOVED: /* X61t: tablet pen removed from bay */
@@ -3898,7 +3739,7 @@ static bool hotkey_notify_usrevent(const u32 hkey,
case TP_HKEY_EV_LID_OPEN: /* Lid opened */
case TP_HKEY_EV_BRGHT_CHANGED: /* brightness changed */
/* do not propagate these events */
- *ignore_acpi_ev = true;
+ *send_acpi_ev = false;
return true;
default:
@@ -3909,14 +3750,9 @@ static bool hotkey_notify_usrevent(const u32 hkey,
static void thermal_dump_all_sensors(void);
static void palmsensor_refresh(void);
-static bool hotkey_notify_6xxx(const u32 hkey,
- bool *send_acpi_ev,
- bool *ignore_acpi_ev)
+/* 0x6000-0x6FFF: thermal alarms/notices and keyboard events */
+static bool hotkey_notify_6xxx(const u32 hkey, bool *send_acpi_ev)
{
- /* 0x6000-0x6FFF: thermal alarms/notices and keyboard events */
- *send_acpi_ev = true;
- *ignore_acpi_ev = false;
-
switch (hkey) {
case TP_HKEY_EV_THM_TABLE_CHANGED:
pr_debug("EC reports: Thermal Table has changed\n");
@@ -3962,14 +3798,12 @@ static bool hotkey_notify_6xxx(const u32 hkey,
/* key press events, we just ignore them as long as the EC
* is still reporting them in the normal keyboard stream */
*send_acpi_ev = false;
- *ignore_acpi_ev = true;
return true;
case TP_HKEY_EV_KEY_FN_ESC:
/* Get the media key status to force the status LED to update */
acpi_evalf(hkey_handle, NULL, "GMKS", "v");
*send_acpi_ev = false;
- *ignore_acpi_ev = true;
return true;
case TP_HKEY_EV_TABLET_CHANGED:
@@ -3993,11 +3827,23 @@ static bool hotkey_notify_6xxx(const u32 hkey,
return true;
}
+static bool hotkey_notify_8xxx(const u32 hkey, bool *send_acpi_ev)
+{
+ switch (hkey) {
+ case TP_HKEY_EV_TRACK_DOUBLETAP:
+ if (tp_features.trackpoint_doubletap)
+ tpacpi_input_send_key(hkey, send_acpi_ev);
+
+ return true;
+ default:
+ return false;
+ }
+}
+
static void hotkey_notify(struct ibm_struct *ibm, u32 event)
{
u32 hkey;
bool send_acpi_ev;
- bool ignore_acpi_ev;
bool known_ev;
if (event != 0x80) {
@@ -4022,18 +3868,16 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
}
send_acpi_ev = true;
- ignore_acpi_ev = false;
+ known_ev = false;
switch (hkey >> 12) {
case 1:
/* 0x1000-0x1FFF: key presses */
- known_ev = hotkey_notify_hotkey(hkey, &send_acpi_ev,
- &ignore_acpi_ev);
+ known_ev = hotkey_notify_hotkey(hkey, &send_acpi_ev);
break;
case 2:
/* 0x2000-0x2FFF: Wakeup reason */
- known_ev = hotkey_notify_wakeup(hkey, &send_acpi_ev,
- &ignore_acpi_ev);
+ known_ev = hotkey_notify_wakeup(hkey, &send_acpi_ev);
break;
case 3:
/* 0x3000-0x3FFF: bay-related wakeups */
@@ -4048,38 +3892,34 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
/* FIXME: kick libata if SATA link offline */
known_ev = true;
break;
- default:
- known_ev = false;
}
break;
case 4:
/* 0x4000-0x4FFF: dock-related events */
- known_ev = hotkey_notify_dockevent(hkey, &send_acpi_ev,
- &ignore_acpi_ev);
+ known_ev = hotkey_notify_dockevent(hkey, &send_acpi_ev);
break;
case 5:
/* 0x5000-0x5FFF: human interface helpers */
- known_ev = hotkey_notify_usrevent(hkey, &send_acpi_ev,
- &ignore_acpi_ev);
+ known_ev = hotkey_notify_usrevent(hkey, &send_acpi_ev);
break;
case 6:
/* 0x6000-0x6FFF: thermal alarms/notices and
* keyboard events */
- known_ev = hotkey_notify_6xxx(hkey, &send_acpi_ev,
- &ignore_acpi_ev);
+ known_ev = hotkey_notify_6xxx(hkey, &send_acpi_ev);
break;
case 7:
/* 0x7000-0x7FFF: misc */
if (tp_features.hotkey_wlsw &&
hkey == TP_HKEY_EV_RFKILL_CHANGED) {
tpacpi_send_radiosw_update();
- send_acpi_ev = 0;
+ send_acpi_ev = false;
known_ev = true;
- break;
}
- fallthrough; /* to default */
- default:
- known_ev = false;
+ break;
+ case 8:
+ /* 0x8000-0x8FFF: misc2 */
+ known_ev = hotkey_notify_8xxx(hkey, &send_acpi_ev);
+ break;
}
if (!known_ev) {
pr_notice("unhandled HKEY event 0x%04x\n", hkey);
@@ -4088,7 +3928,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
}
/* netlink events */
- if (!ignore_acpi_ev && send_acpi_ev) {
+ if (send_acpi_ev) {
acpi_bus_generate_netlink_event(
ibm->acpi->device->pnp.device_class,
dev_name(&ibm->acpi->device->dev),
@@ -6126,12 +5966,15 @@ enum thermal_access_mode {
TPACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */
TPACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */
TPACPI_THERMAL_TPEC_8, /* Use ACPI EC regs, 8 sensors */
+ TPACPI_THERMAL_TPEC_12, /* Use ACPI EC regs, 12 sensors */
TPACPI_THERMAL_TPEC_16, /* Use ACPI EC regs, 16 sensors */
};
enum { /* TPACPI_THERMAL_TPEC_* */
TP_EC_THERMAL_TMP0 = 0x78, /* ACPI EC regs TMP 0..7 */
TP_EC_THERMAL_TMP8 = 0xC0, /* ACPI EC regs TMP 8..15 */
+ TP_EC_THERMAL_TMP0_NS = 0xA8, /* ACPI EC Non-Standard regs TMP 0..7 */
+ TP_EC_THERMAL_TMP8_NS = 0xB8, /* ACPI EC Non-standard regs TMP 8..11 */
TP_EC_FUNCREV = 0xEF, /* ACPI EC Functional revision */
TP_EC_THERMAL_TMP_NA = -128, /* ACPI EC sensor not available */
@@ -6144,8 +5987,104 @@ struct ibm_thermal_sensors_struct {
s32 temp[TPACPI_MAX_THERMAL_SENSORS];
};
+static const struct tpacpi_quirk thermal_quirk_table[] __initconst = {
+ /* Non-standard address for thermal registers on some ThinkPads */
+ TPACPI_Q_LNV3('R', '1', 'F', true), /* L13 Yoga Gen 2 */
+ TPACPI_Q_LNV3('N', '2', 'U', true), /* X13 Yoga Gen 2*/
+ TPACPI_Q_LNV3('R', '0', 'R', true), /* L380 */
+ TPACPI_Q_LNV3('R', '1', '5', true), /* L13 Yoga Gen 1*/
+ TPACPI_Q_LNV3('R', '1', '0', true), /* L390 */
+ TPACPI_Q_LNV3('N', '2', 'L', true), /* X13 Yoga Gen 1*/
+ TPACPI_Q_LNV3('R', '0', 'T', true), /* 11e Gen5 GL*/
+ TPACPI_Q_LNV3('R', '1', 'D', true), /* 11e Gen5 GL-R*/
+ TPACPI_Q_LNV3('R', '0', 'V', true), /* 11e Gen5 KL-Y*/
+};
+
static enum thermal_access_mode thermal_read_mode;
static bool thermal_use_labels;
+static bool thermal_with_ns_address; /* Non-standard thermal reg address */
+
+/* Function to check thermal read mode */
+static enum thermal_access_mode __init thermal_read_mode_check(void)
+{
+ u8 t, ta1, ta2, ver = 0;
+ int i;
+ int acpi_tmp7;
+
+ acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv");
+
+ if (thinkpad_id.ec_model) {
+ /*
+ * Direct EC access mode: sensors at registers 0x78-0x7F,
+ * 0xC0-0xC7. Registers return 0x00 for non-implemented,
+ * thermal sensors return 0x80 when not available.
+ *
+ * In some special cases (when Power Supply ID is 0xC2)
+ * above rule causes thermal control issues. Offset 0xEF
+ * determines EC version. 0xC0-0xC7 are not thermal registers
+ * in Ver 3.
+ */
+ if (!acpi_ec_read(TP_EC_FUNCREV, &ver))
+ pr_warn("Thinkpad ACPI EC unable to access EC version\n");
+
+ /* Quirks to check non-standard EC */
+ thermal_with_ns_address = tpacpi_check_quirks(thermal_quirk_table,
+ ARRAY_SIZE(thermal_quirk_table));
+
+ /* Support for Thinkpads with non-standard address */
+ if (thermal_with_ns_address) {
+ pr_info("ECFW with non-standard thermal registers found\n");
+ return TPACPI_THERMAL_TPEC_12;
+ }
+
+ ta1 = ta2 = 0;
+ for (i = 0; i < 8; i++) {
+ if (acpi_ec_read(TP_EC_THERMAL_TMP0 + i, &t)) {
+ ta1 |= t;
+ } else {
+ ta1 = 0;
+ break;
+ }
+ if (ver < 3) {
+ if (acpi_ec_read(TP_EC_THERMAL_TMP8 + i, &t)) {
+ ta2 |= t;
+ } else {
+ ta1 = 0;
+ break;
+ }
+ }
+ }
+
+ if (ta1 == 0) {
+ /* This is sheer paranoia, but we handle it anyway */
+ if (acpi_tmp7) {
+ pr_err("ThinkPad ACPI EC access misbehaving, falling back to ACPI TMPx access mode\n");
+ return TPACPI_THERMAL_ACPI_TMP07;
+ }
+ pr_err("ThinkPad ACPI EC access misbehaving, disabling thermal sensors access\n");
+ return TPACPI_THERMAL_NONE;
+ }
+
+ if (ver >= 3) {
+ thermal_use_labels = true;
+ return TPACPI_THERMAL_TPEC_8;
+ }
+
+ return (ta2 != 0) ? TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8;
+ }
+
+ if (acpi_tmp7) {
+ if (tpacpi_is_ibm() && acpi_evalf(ec_handle, NULL, "UPDT", "qv")) {
+ /* 600e/x, 770e, 770x */
+ return TPACPI_THERMAL_ACPI_UPDT;
+ }
+ /* IBM/LENOVO DSDT EC.TMPx access, max 8 sensors */
+ return TPACPI_THERMAL_ACPI_TMP07;
+ }
+
+ /* temperatures not supported on 570, G4x, R30, R31, R32 */
+ return TPACPI_THERMAL_NONE;
+}
/* idx is zero-based */
static int thermal_get_sensor(int idx, s32 *value)
@@ -6174,6 +6113,20 @@ static int thermal_get_sensor(int idx, s32 *value)
}
break;
+ /* The Non-standard EC uses 12 Thermal areas */
+ case TPACPI_THERMAL_TPEC_12:
+ if (idx >= 12)
+ return -EINVAL;
+
+ t = idx < 8 ? TP_EC_THERMAL_TMP0_NS + idx :
+ TP_EC_THERMAL_TMP8_NS + (idx - 8);
+
+ if (!acpi_ec_read(t, &tmp))
+ return -EIO;
+
+ *value = tmp * MILLIDEGREE_PER_DEGREE;
+ return 0;
+
case TPACPI_THERMAL_ACPI_UPDT:
if (idx <= 7) {
snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx);
@@ -6208,17 +6161,17 @@ static int thermal_get_sensor(int idx, s32 *value)
static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s)
{
- int res, i;
- int n;
-
- n = 8;
- i = 0;
+ int res, i, n;
if (!s)
return -EINVAL;
if (thermal_read_mode == TPACPI_THERMAL_TPEC_16)
n = 16;
+ else if (thermal_read_mode == TPACPI_THERMAL_TPEC_12)
+ n = 12;
+ else
+ n = 8;
for (i = 0 ; i < n; i++) {
res = thermal_get_sensor(i, &s->temp[i]);
@@ -6317,18 +6270,36 @@ static struct attribute *thermal_temp_input_attr[] = {
NULL
};
+#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
+
static umode_t thermal_attr_is_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
- if (thermal_read_mode == TPACPI_THERMAL_NONE)
+ struct device_attribute *dev_attr = to_dev_attr(attr);
+ struct sensor_device_attribute *sensor_attr =
+ to_sensor_dev_attr(dev_attr);
+
+ int idx = sensor_attr->index;
+
+ switch (thermal_read_mode) {
+ case TPACPI_THERMAL_NONE:
return 0;
- if (attr == THERMAL_ATTRS(8) || attr == THERMAL_ATTRS(9) ||
- attr == THERMAL_ATTRS(10) || attr == THERMAL_ATTRS(11) ||
- attr == THERMAL_ATTRS(12) || attr == THERMAL_ATTRS(13) ||
- attr == THERMAL_ATTRS(14) || attr == THERMAL_ATTRS(15)) {
- if (thermal_read_mode != TPACPI_THERMAL_TPEC_16)
+ case TPACPI_THERMAL_ACPI_TMP07:
+ case TPACPI_THERMAL_ACPI_UPDT:
+ case TPACPI_THERMAL_TPEC_8:
+ if (idx >= 8)
+ return 0;
+ break;
+
+ case TPACPI_THERMAL_TPEC_12:
+ if (idx >= 12)
return 0;
+ break;
+
+ default:
+ break;
+
}
return attr->mode;
@@ -6375,78 +6346,9 @@ static const struct attribute_group temp_label_attr_group = {
static int __init thermal_init(struct ibm_init_struct *iibm)
{
- u8 t, ta1, ta2, ver = 0;
- int i;
- int acpi_tmp7;
-
vdbg_printk(TPACPI_DBG_INIT, "initializing thermal subdriver\n");
- acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv");
-
- if (thinkpad_id.ec_model) {
- /*
- * Direct EC access mode: sensors at registers
- * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for
- * non-implemented, thermal sensors return 0x80 when
- * not available
- * The above rule is unfortunately flawed. This has been seen with
- * 0xC2 (power supply ID) causing thermal control problems.
- * The EC version can be determined by offset 0xEF and at least for
- * version 3 the Lenovo firmware team confirmed that registers 0xC0-0xC7
- * are not thermal registers.
- */
- if (!acpi_ec_read(TP_EC_FUNCREV, &ver))
- pr_warn("Thinkpad ACPI EC unable to access EC version\n");
-
- ta1 = ta2 = 0;
- for (i = 0; i < 8; i++) {
- if (acpi_ec_read(TP_EC_THERMAL_TMP0 + i, &t)) {
- ta1 |= t;
- } else {
- ta1 = 0;
- break;
- }
- if (ver < 3) {
- if (acpi_ec_read(TP_EC_THERMAL_TMP8 + i, &t)) {
- ta2 |= t;
- } else {
- ta1 = 0;
- break;
- }
- }
- }
- if (ta1 == 0) {
- /* This is sheer paranoia, but we handle it anyway */
- if (acpi_tmp7) {
- pr_err("ThinkPad ACPI EC access misbehaving, falling back to ACPI TMPx access mode\n");
- thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
- } else {
- pr_err("ThinkPad ACPI EC access misbehaving, disabling thermal sensors access\n");
- thermal_read_mode = TPACPI_THERMAL_NONE;
- }
- } else {
- if (ver >= 3) {
- thermal_read_mode = TPACPI_THERMAL_TPEC_8;
- thermal_use_labels = true;
- } else {
- thermal_read_mode =
- (ta2 != 0) ?
- TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8;
- }
- }
- } else if (acpi_tmp7) {
- if (tpacpi_is_ibm() &&
- acpi_evalf(ec_handle, NULL, "UPDT", "qv")) {
- /* 600e/x, 770e, 770x */
- thermal_read_mode = TPACPI_THERMAL_ACPI_UPDT;
- } else {
- /* IBM/LENOVO DSDT EC.TMPx access, max 8 sensors */
- thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
- }
- } else {
- /* temperatures not supported on 570, G4x, R30, R31, R32 */
- thermal_read_mode = TPACPI_THERMAL_NONE;
- }
+ thermal_read_mode = thermal_read_mode_check();
vdbg_printk(TPACPI_DBG_INIT, "thermal is %s, mode %d\n",
str_supported(thermal_read_mode != TPACPI_THERMAL_NONE),
@@ -8767,6 +8669,13 @@ static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
TPACPI_Q_LNV3('N', '3', '7', TPACPI_FAN_2CTL), /* T15g (2nd gen) */
TPACPI_Q_LNV3('R', '1', 'F', TPACPI_FAN_NS), /* L13 Yoga Gen 2 */
TPACPI_Q_LNV3('N', '2', 'U', TPACPI_FAN_NS), /* X13 Yoga Gen 2*/
+ TPACPI_Q_LNV3('R', '0', 'R', TPACPI_FAN_NS), /* L380 */
+ TPACPI_Q_LNV3('R', '1', '5', TPACPI_FAN_NS), /* L13 Yoga Gen 1 */
+ TPACPI_Q_LNV3('R', '1', '0', TPACPI_FAN_NS), /* L390 */
+ TPACPI_Q_LNV3('N', '2', 'L', TPACPI_FAN_NS), /* X13 Yoga Gen 1 */
+ TPACPI_Q_LNV3('R', '0', 'T', TPACPI_FAN_NS), /* 11e Gen5 GL */
+ TPACPI_Q_LNV3('R', '1', 'D', TPACPI_FAN_NS), /* 11e Gen5 GL-R */
+ 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) */
};
@@ -9285,7 +9194,6 @@ static int mute_led_init(struct ibm_init_struct *iibm)
continue;
}
- mute_led_cdev[i].brightness = ledtrig_audio_get(i);
err = led_classdev_register(&tpacpi_pdev->dev, &mute_led_cdev[i]);
if (err < 0) {
while (i--)
@@ -9718,7 +9626,7 @@ static ssize_t tpacpi_battery_show(int what,
battery = BAT_PRIMARY;
if (tpacpi_battery_get(what, battery, &ret))
return -ENODEV;
- return sprintf(buf, "%d\n", ret);
+ return sysfs_emit(buf, "%d\n", ret);
}
static ssize_t charge_control_start_threshold_show(struct device *device,
@@ -10920,13 +10828,7 @@ static struct ibm_struct auxmac_data = {
.name = "auxmac",
};
-static ssize_t auxmac_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- return sysfs_emit(buf, "%s\n", auxmac);
-}
-static DEVICE_ATTR_RO(auxmac);
+static DEVICE_STRING_ATTR_RO(auxmac, 0444, auxmac);
static umode_t auxmac_attr_is_visible(struct kobject *kobj,
struct attribute *attr, int n)
@@ -10935,7 +10837,7 @@ static umode_t auxmac_attr_is_visible(struct kobject *kobj,
}
static struct attribute *auxmac_attributes[] = {
- &dev_attr_auxmac.attr,
+ &dev_attr_auxmac.attr.attr,
NULL
};
@@ -11055,76 +10957,93 @@ static struct platform_driver tpacpi_hwmon_pdriver = {
* HKEY event callout for other subdrivers go here
* (yes, it is ugly, but it is quick, safe, and gets the job done
*/
-static void tpacpi_driver_event(const unsigned int hkey_event)
+static bool tpacpi_driver_event(const unsigned int hkey_event)
{
- if (ibm_backlight_device) {
- switch (hkey_event) {
- case TP_HKEY_EV_BRGHT_UP:
- case TP_HKEY_EV_BRGHT_DOWN:
+ switch (hkey_event) {
+ case TP_HKEY_EV_BRGHT_UP:
+ case TP_HKEY_EV_BRGHT_DOWN:
+ if (ibm_backlight_device)
tpacpi_brightness_notify_change();
- }
- }
- if (alsa_card) {
- switch (hkey_event) {
- case TP_HKEY_EV_VOL_UP:
- case TP_HKEY_EV_VOL_DOWN:
- case TP_HKEY_EV_VOL_MUTE:
+ /*
+ * Key press events are suppressed by default hotkey_user_mask
+ * and should still be reported if explicitly requested.
+ */
+ return false;
+ case TP_HKEY_EV_VOL_UP:
+ case TP_HKEY_EV_VOL_DOWN:
+ case TP_HKEY_EV_VOL_MUTE:
+ if (alsa_card)
volume_alsa_notify_change();
- }
- }
- if (tp_features.kbdlight && hkey_event == TP_HKEY_EV_KBD_LIGHT) {
- enum led_brightness brightness;
- mutex_lock(&kbdlight_mutex);
+ /* Key events are suppressed by default hotkey_user_mask */
+ return false;
+ case TP_HKEY_EV_KBD_LIGHT:
+ if (tp_features.kbdlight) {
+ enum led_brightness brightness;
- /*
- * Check the brightness actually changed, setting the brightness
- * through kbdlight_set_level() also triggers this event.
- */
- brightness = kbdlight_sysfs_get(NULL);
- if (kbdlight_brightness != brightness) {
- kbdlight_brightness = brightness;
- led_classdev_notify_brightness_hw_changed(
- &tpacpi_led_kbdlight.led_classdev, brightness);
- }
+ mutex_lock(&kbdlight_mutex);
- mutex_unlock(&kbdlight_mutex);
- }
+ /*
+ * Check the brightness actually changed, setting the brightness
+ * through kbdlight_set_level() also triggers this event.
+ */
+ brightness = kbdlight_sysfs_get(NULL);
+ if (kbdlight_brightness != brightness) {
+ kbdlight_brightness = brightness;
+ led_classdev_notify_brightness_hw_changed(
+ &tpacpi_led_kbdlight.led_classdev, brightness);
+ }
- if (hkey_event == TP_HKEY_EV_THM_CSM_COMPLETED) {
+ mutex_unlock(&kbdlight_mutex);
+ }
+ /* Key events are suppressed by default hotkey_user_mask */
+ return false;
+ case TP_HKEY_EV_DFR_CHANGE_ROW:
+ adaptive_keyboard_change_row();
+ return true;
+ case TP_HKEY_EV_DFR_S_QUICKVIEW_ROW:
+ adaptive_keyboard_s_quickview_row();
+ return true;
+ case TP_HKEY_EV_THM_CSM_COMPLETED:
lapsensor_refresh();
/* If we are already accessing DYTC then skip dytc update */
if (!atomic_add_unless(&dytc_ignore_event, -1, 0))
dytc_profile_refresh();
- }
-
- if (lcdshadow_dev && hkey_event == TP_HKEY_EV_PRIVACYGUARD_TOGGLE) {
- enum drm_privacy_screen_status old_hw_state;
- bool changed;
- mutex_lock(&lcdshadow_dev->lock);
- old_hw_state = lcdshadow_dev->hw_state;
- lcdshadow_get_hw_state(lcdshadow_dev);
- changed = lcdshadow_dev->hw_state != old_hw_state;
- mutex_unlock(&lcdshadow_dev->lock);
-
- if (changed)
- drm_privacy_screen_call_notifier_chain(lcdshadow_dev);
- }
- if (hkey_event == TP_HKEY_EV_AMT_TOGGLE) {
+ return true;
+ case TP_HKEY_EV_PRIVACYGUARD_TOGGLE:
+ if (lcdshadow_dev) {
+ enum drm_privacy_screen_status old_hw_state;
+ bool changed;
+
+ mutex_lock(&lcdshadow_dev->lock);
+ old_hw_state = lcdshadow_dev->hw_state;
+ lcdshadow_get_hw_state(lcdshadow_dev);
+ changed = lcdshadow_dev->hw_state != old_hw_state;
+ mutex_unlock(&lcdshadow_dev->lock);
+
+ if (changed)
+ drm_privacy_screen_call_notifier_chain(lcdshadow_dev);
+ }
+ return true;
+ case TP_HKEY_EV_AMT_TOGGLE:
/* If we're enabling AMT we need to force balanced mode */
if (!dytc_amt_active)
/* This will also set AMT mode enabled */
dytc_profile_set(NULL, PLATFORM_PROFILE_BALANCED);
else
dytc_control_amt(!dytc_amt_active);
- }
-}
+ return true;
+ case TP_HKEY_EV_DOUBLETAP_TOGGLE:
+ tp_features.trackpoint_doubletap = !tp_features.trackpoint_doubletap;
+ return true;
+ case TP_HKEY_EV_PROFILE_TOGGLE:
+ platform_profile_cycle();
+ return true;
+ }
-static void hotkey_driver_event(const unsigned int scancode)
-{
- tpacpi_driver_event(TP_HKEY_EV_HOTKEY_BASE + scancode);
+ return false;
}
/* --------------------------------------------------------------------- */
@@ -11727,7 +11646,6 @@ static void thinkpad_acpi_module_exit(void)
input_unregister_device(tpacpi_inputdev);
else
input_free_device(tpacpi_inputdev);
- kfree(hotkey_keycode_map);
}
if (tpacpi_sensors_pdev)
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index 291f14ef67..78a5aac2dc 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -57,6 +57,11 @@ module_param(turn_on_panel_on_resume, int, 0644);
MODULE_PARM_DESC(turn_on_panel_on_resume,
"Call HCI_PANEL_POWER_ON on resume (-1 = auto, 0 = no, 1 = yes");
+static int hci_hotkey_quickstart = -1;
+module_param(hci_hotkey_quickstart, int, 0644);
+MODULE_PARM_DESC(hci_hotkey_quickstart,
+ "Call HCI_HOTKEY_EVENT with value 0x5 for quickstart button support (-1 = auto, 0 = no, 1 = yes");
+
#define TOSHIBA_WMI_EVENT_GUID "59142400-C6A3-40FA-BADB-8A2652834100"
/* Scan code for Fn key on TOS1900 models */
@@ -136,6 +141,7 @@ MODULE_PARM_DESC(turn_on_panel_on_resume,
#define HCI_ACCEL_MASK 0x7fff
#define HCI_ACCEL_DIRECTION_MASK 0x8000
#define HCI_HOTKEY_DISABLE 0x0b
+#define HCI_HOTKEY_ENABLE_QUICKSTART 0x05
#define HCI_HOTKEY_ENABLE 0x09
#define HCI_HOTKEY_SPECIAL_FUNCTIONS 0x10
#define HCI_LCD_BRIGHTNESS_BITS 3
@@ -264,6 +270,7 @@ static const struct key_entry toshiba_acpi_keymap[] = {
{ KE_KEY, 0xb32, { KEY_NEXTSONG } },
{ KE_KEY, 0xb33, { KEY_PLAYPAUSE } },
{ KE_KEY, 0xb5a, { KEY_MEDIA } },
+ { KE_IGNORE, 0x0e00, { KEY_RESERVED } }, /* Wake from sleep */
{ KE_IGNORE, 0x1430, { KEY_RESERVED } }, /* Wake from sleep */
{ KE_IGNORE, 0x1501, { KEY_RESERVED } }, /* Output changed */
{ KE_IGNORE, 0x1502, { KEY_RESERVED } }, /* HDMI plugged/unplugged */
@@ -1814,12 +1821,7 @@ static DECLARE_WORK(kbd_bl_work, toshiba_acpi_kbd_bl_work);
/*
* Sysfs files
*/
-static ssize_t version_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return sprintf(buf, "%s\n", TOSHIBA_ACPI_VERSION);
-}
-static DEVICE_ATTR_RO(version);
+static DEVICE_STRING_ATTR_RO(version, 0444, TOSHIBA_ACPI_VERSION);
static ssize_t fan_store(struct device *dev,
struct device_attribute *attr,
@@ -2428,7 +2430,7 @@ static ssize_t cooling_method_store(struct device *dev,
static DEVICE_ATTR_RW(cooling_method);
static struct attribute *toshiba_attributes[] = {
- &dev_attr_version.attr,
+ &dev_attr_version.attr.attr,
&dev_attr_fan.attr,
&dev_attr_kbd_backlight_mode.attr,
&dev_attr_kbd_type.attr,
@@ -2730,10 +2732,15 @@ static int toshiba_acpi_enable_hotkeys(struct toshiba_acpi_dev *dev)
return -ENODEV;
/*
+ * Enable quickstart buttons if supported.
+ *
* Enable the "Special Functions" mode only if they are
* supported and if they are activated.
*/
- if (dev->kbd_function_keys_supported && dev->special_functions)
+ if (hci_hotkey_quickstart)
+ result = hci_write(dev, HCI_HOTKEY_EVENT,
+ HCI_HOTKEY_ENABLE_QUICKSTART);
+ else if (dev->kbd_function_keys_supported && dev->special_functions)
result = hci_write(dev, HCI_HOTKEY_EVENT,
HCI_HOTKEY_SPECIAL_FUNCTIONS);
else
@@ -3257,7 +3264,14 @@ static const char *find_hci_method(acpi_handle handle)
* works. toshiba_acpi_resume() uses HCI_PANEL_POWER_ON to avoid changing
* the configured brightness level.
*/
-static const struct dmi_system_id turn_on_panel_on_resume_dmi_ids[] = {
+#define QUIRK_TURN_ON_PANEL_ON_RESUME BIT(0)
+/*
+ * Some Toshibas use "quickstart" keys. On these, HCI_HOTKEY_EVENT must use
+ * the value HCI_HOTKEY_ENABLE_QUICKSTART.
+ */
+#define QUIRK_HCI_HOTKEY_QUICKSTART BIT(1)
+
+static const struct dmi_system_id toshiba_dmi_quirks[] __initconst = {
{
/* Toshiba Portégé R700 */
/* https://bugzilla.kernel.org/show_bug.cgi?id=21012 */
@@ -3265,6 +3279,7 @@ static const struct dmi_system_id turn_on_panel_on_resume_dmi_ids[] = {
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE R700"),
},
+ .driver_data = (void *)QUIRK_TURN_ON_PANEL_ON_RESUME,
},
{
/* Toshiba Satellite/Portégé R830 */
@@ -3274,6 +3289,7 @@ static const struct dmi_system_id turn_on_panel_on_resume_dmi_ids[] = {
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
DMI_MATCH(DMI_PRODUCT_NAME, "R830"),
},
+ .driver_data = (void *)QUIRK_TURN_ON_PANEL_ON_RESUME,
},
{
/* Toshiba Satellite/Portégé Z830 */
@@ -3281,7 +3297,9 @@ static const struct dmi_system_id turn_on_panel_on_resume_dmi_ids[] = {
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
DMI_MATCH(DMI_PRODUCT_NAME, "Z830"),
},
+ .driver_data = (void *)(QUIRK_TURN_ON_PANEL_ON_RESUME | QUIRK_HCI_HOTKEY_QUICKSTART),
},
+ { }
};
static int toshiba_acpi_add(struct acpi_device *acpi_dev)
@@ -3441,9 +3459,6 @@ iio_error:
}
#endif
- if (turn_on_panel_on_resume == -1)
- turn_on_panel_on_resume = dmi_check_system(turn_on_panel_on_resume_dmi_ids);
-
toshiba_wwan_available(dev);
if (dev->wwan_supported)
toshiba_acpi_setup_wwan_rfkill(dev);
@@ -3523,9 +3538,10 @@ static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event)
(dev->kbd_mode == SCI_KBD_MODE_ON) ?
LED_FULL : LED_OFF);
break;
+ case 0x8e: /* Power button pressed */
+ break;
case 0x85: /* Unknown */
case 0x8d: /* Unknown */
- case 0x8e: /* Unknown */
case 0x94: /* Unknown */
case 0x95: /* Unknown */
default:
@@ -3581,7 +3597,6 @@ static SIMPLE_DEV_PM_OPS(toshiba_acpi_pm,
static struct acpi_driver toshiba_acpi_driver = {
.name = "Toshiba ACPI driver",
- .owner = THIS_MODULE,
.ids = toshiba_device_ids,
.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
.ops = {
@@ -3592,10 +3607,27 @@ static struct acpi_driver toshiba_acpi_driver = {
.drv.pm = &toshiba_acpi_pm,
};
+static void __init toshiba_dmi_init(void)
+{
+ const struct dmi_system_id *dmi_id;
+ long quirks = 0;
+
+ dmi_id = dmi_first_match(toshiba_dmi_quirks);
+ if (dmi_id)
+ quirks = (long)dmi_id->driver_data;
+
+ if (turn_on_panel_on_resume == -1)
+ turn_on_panel_on_resume = !!(quirks & QUIRK_TURN_ON_PANEL_ON_RESUME);
+
+ if (hci_hotkey_quickstart == -1)
+ hci_hotkey_quickstart = !!(quirks & QUIRK_HCI_HOTKEY_QUICKSTART);
+}
+
static int __init toshiba_acpi_init(void)
{
int ret;
+ toshiba_dmi_init();
toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir);
if (!toshiba_proc_dir) {
pr_err("Unable to create proc dir " PROC_TOSHIBA "\n");
diff --git a/drivers/platform/x86/toshiba_bluetooth.c b/drivers/platform/x86/toshiba_bluetooth.c
index d8f81962a2..dad2c3e559 100644
--- a/drivers/platform/x86/toshiba_bluetooth.c
+++ b/drivers/platform/x86/toshiba_bluetooth.c
@@ -59,7 +59,6 @@ static struct acpi_driver toshiba_bt_rfkill_driver = {
.remove = toshiba_bt_rfkill_remove,
.notify = toshiba_bt_rfkill_notify,
},
- .owner = THIS_MODULE,
.drv.pm = &toshiba_bt_pm,
};
diff --git a/drivers/platform/x86/toshiba_haps.c b/drivers/platform/x86/toshiba_haps.c
index 8c9f76286b..03dfddeee0 100644
--- a/drivers/platform/x86/toshiba_haps.c
+++ b/drivers/platform/x86/toshiba_haps.c
@@ -251,7 +251,6 @@ MODULE_DEVICE_TABLE(acpi, haps_device_ids);
static struct acpi_driver toshiba_haps_driver = {
.name = "Toshiba HAPS",
- .owner = THIS_MODULE,
.ids = haps_device_ids,
.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
.ops = {
diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c
index c6a10ec2c8..f74af0a689 100644
--- a/drivers/platform/x86/touchscreen_dmi.c
+++ b/drivers/platform/x86/touchscreen_dmi.c
@@ -9,10 +9,13 @@
*/
#include <linux/acpi.h>
+#include <linux/ctype.h>
#include <linux/device.h>
#include <linux/dmi.h>
#include <linux/efi_embedded_fw.h>
#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kstrtox.h>
#include <linux/notifier.h>
#include <linux/property.h>
#include <linux/string.h>
@@ -31,7 +34,6 @@ static const struct property_entry archos_101_cesium_educ_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 1280),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-archos-101-cesium-educ.fw"),
{ }
@@ -46,7 +48,6 @@ static const struct property_entry bush_bush_windows_tablet_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1850),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1280),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-bush-bush-windows-tablet.fw"),
{ }
@@ -76,7 +77,6 @@ static const struct property_entry chuwi_hi8_air_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 1148),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-chuwi-hi8-air.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
{ }
};
@@ -92,7 +92,6 @@ static const struct property_entry chuwi_hi8_pro_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 1148),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-chuwi-hi8-pro.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -120,7 +119,6 @@ static const struct property_entry chuwi_hi10_air_props[] = {
PROPERTY_ENTRY_U32("touchscreen-fuzz-x", 5),
PROPERTY_ENTRY_U32("touchscreen-fuzz-y", 4),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi10-air.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -136,7 +134,6 @@ static const struct property_entry chuwi_hi10_plus_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1908),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1270),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi10plus.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
PROPERTY_ENTRY_BOOL("silead,pen-supported"),
PROPERTY_ENTRY_U32("silead,pen-resolution-x", 8),
@@ -168,7 +165,6 @@ static const struct property_entry chuwi_hi10_pro_props[] = {
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi10-pro.fw"),
PROPERTY_ENTRY_U32_ARRAY("silead,efi-fw-min-max", chuwi_hi10_pro_efi_min_max),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
PROPERTY_ENTRY_BOOL("silead,pen-supported"),
PROPERTY_ENTRY_U32("silead,pen-resolution-x", 8),
@@ -198,7 +194,6 @@ static const struct property_entry chuwi_hibook_props[] = {
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hibook.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -224,7 +219,6 @@ static const struct property_entry chuwi_vi8_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 1140),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-chuwi-vi8.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -252,7 +246,6 @@ static const struct property_entry chuwi_vi10_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1858),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1280),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-chuwi-vi10.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -268,7 +261,6 @@ static const struct property_entry chuwi_surbook_mini_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 2040),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1524),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-surbook-mini.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
{ }
};
@@ -286,7 +278,6 @@ static const struct property_entry connect_tablet9_props[] = {
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-connect-tablet9.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
{ }
};
@@ -303,7 +294,6 @@ static const struct property_entry csl_panther_tab_hd_props[] = {
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-csl-panther-tab-hd.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
{ }
};
@@ -319,7 +309,6 @@ static const struct property_entry cube_iwork8_air_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 896),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-cube-iwork8-air.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
{ }
};
@@ -343,7 +332,6 @@ static const struct property_entry cube_knote_i1101_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1961),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1513),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-cube-knote-i1101.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -357,7 +345,6 @@ static const struct property_entry dexp_ursus_7w_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 890),
PROPERTY_ENTRY_U32("touchscreen-size-y", 630),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1686-dexp-ursus-7w.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -373,7 +360,6 @@ static const struct property_entry dexp_ursus_kx210i_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1720),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1137),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-dexp-ursus-kx210i.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -388,7 +374,6 @@ static const struct property_entry digma_citi_e200_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 1500),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1686-digma_citi_e200.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -447,7 +432,6 @@ static const struct property_entry irbis_tw90_props[] = {
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-irbis_tw90.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -463,7 +447,6 @@ static const struct property_entry irbis_tw118_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1960),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1510),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-irbis-tw118.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
{ }
};
@@ -480,7 +463,6 @@ static const struct property_entry itworks_tw891_props[] = {
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-itworks-tw891.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
{ }
};
@@ -493,7 +475,6 @@ static const struct property_entry jumper_ezpad_6_pro_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1980),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1500),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-jumper-ezpad-6-pro.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -508,7 +489,6 @@ static const struct property_entry jumper_ezpad_6_pro_b_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 1500),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-jumper-ezpad-6-pro-b.fw"),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -524,7 +504,6 @@ static const struct property_entry jumper_ezpad_6_m4_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1950),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1525),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-jumper-ezpad-6-m4.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -541,7 +520,6 @@ static const struct property_entry jumper_ezpad_7_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 1526),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-jumper-ezpad-7.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,stuck-controller-bug"),
{ }
};
@@ -558,7 +536,6 @@ static const struct property_entry jumper_ezpad_mini3_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 1138),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-jumper-ezpad-mini3.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
{ }
};
@@ -575,7 +552,6 @@ static const struct property_entry mpman_converter9_props[] = {
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-mpman-converter9.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
{ }
};
@@ -591,7 +567,6 @@ static const struct property_entry mpman_mpwin895cl_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 1150),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-mpman-mpwin895cl.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -608,7 +583,6 @@ static const struct property_entry myria_my8307_props[] = {
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-myria-my8307.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -625,7 +599,6 @@ static const struct property_entry onda_obook_20_plus_props[] = {
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-onda-obook-20-plus.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -642,7 +615,6 @@ static const struct property_entry onda_v80_plus_v3_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 1140),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-onda-v80-plus-v3.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -666,7 +638,6 @@ static const struct property_entry onda_v820w_32g_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 1140),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-onda-v820w-32g.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -684,7 +655,6 @@ static const struct property_entry onda_v891_v5_props[] = {
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name",
"gsl3676-onda-v891-v5.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -700,7 +670,6 @@ static const struct property_entry onda_v891w_v1_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1676),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1130),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-onda-v891w-v1.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -717,7 +686,6 @@ static const struct property_entry onda_v891w_v3_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 1135),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-onda-v891w-v3.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -756,7 +724,6 @@ static const struct property_entry pipo_w11_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1984),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1532),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-pipo-w11.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -772,7 +739,6 @@ static const struct property_entry positivo_c4128b_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1915),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1269),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-positivo-c4128b.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
{ }
};
@@ -788,7 +754,6 @@ static const struct property_entry pov_mobii_wintab_p800w_v20_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 1146),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-pov-mobii-wintab-p800w-v20.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -805,7 +770,6 @@ static const struct property_entry pov_mobii_wintab_p800w_v21_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 1148),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-pov-mobii-wintab-p800w.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -822,7 +786,6 @@ static const struct property_entry pov_mobii_wintab_p1006w_v10_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 1520),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-pov-mobii-wintab-p1006w-v10.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -839,7 +802,6 @@ static const struct property_entry predia_basic_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 1144),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-predia-basic.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -856,7 +818,6 @@ static const struct property_entry rca_cambio_w101_v2_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 874),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-rca-cambio-w101-v2.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
{ }
};
@@ -871,7 +832,6 @@ static const struct property_entry rwc_nanote_p8_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 1140),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-rwc-nanote-p8.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
{ }
};
@@ -887,7 +847,6 @@ static const struct property_entry schneider_sct101ctm_props[] = {
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-schneider-sct101ctm.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -897,6 +856,21 @@ static const struct ts_dmi_data schneider_sct101ctm_data = {
.properties = schneider_sct101ctm_props,
};
+static const struct property_entry globalspace_solt_ivw116_props[] = {
+ PROPERTY_ENTRY_U32("touchscreen-min-x", 7),
+ PROPERTY_ENTRY_U32("touchscreen-min-y", 22),
+ PROPERTY_ENTRY_U32("touchscreen-size-x", 1723),
+ PROPERTY_ENTRY_U32("touchscreen-size-y", 1077),
+ PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-globalspace-solt-ivw116.fw"),
+ PROPERTY_ENTRY_BOOL("silead,home-button"),
+ { }
+};
+
+static const struct ts_dmi_data globalspace_solt_ivw116_data = {
+ .acpi_name = "MSSL1680:00",
+ .properties = globalspace_solt_ivw116_props,
+};
+
static const struct property_entry techbite_arc_11_6_props[] = {
PROPERTY_ENTRY_U32("touchscreen-min-x", 5),
PROPERTY_ENTRY_U32("touchscreen-min-y", 7),
@@ -904,7 +878,6 @@ static const struct property_entry techbite_arc_11_6_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 1270),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-techbite-arc-11-6.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
{ }
};
@@ -920,7 +893,6 @@ static const struct property_entry teclast_tbook11_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 1264),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-teclast-tbook11.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -946,7 +918,6 @@ static const struct property_entry teclast_x16_plus_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 1264),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-teclast-x16-plus.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -969,7 +940,6 @@ static const struct property_entry teclast_x3_plus_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1980),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1500),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-teclast-x3-plus.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -985,7 +955,6 @@ static const struct property_entry teclast_x98plus2_props[] = {
PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1686-teclast_x98plus2.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
{ }
};
@@ -999,7 +968,6 @@ static const struct property_entry trekstor_primebook_c11_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 1530),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-trekstor-primebook-c11.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -1013,7 +981,6 @@ static const struct property_entry trekstor_primebook_c13_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 2624),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1920),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-trekstor-primebook-c13.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -1027,7 +994,6 @@ static const struct property_entry trekstor_primetab_t13b_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 2500),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1900),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-trekstor-primetab-t13b.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
{ }
@@ -1055,7 +1021,6 @@ static const struct property_entry trekstor_surftab_twin_10_1_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-y", 1280),
PROPERTY_ENTRY_U32("touchscreen-inverted-y", 1),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-surftab-twin-10-1-st10432-8.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -1071,7 +1036,6 @@ static const struct property_entry trekstor_surftab_wintron70_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 884),
PROPERTY_ENTRY_U32("touchscreen-size-y", 632),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1686-surftab-wintron70-st70416-6.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -1088,7 +1052,6 @@ static const struct property_entry viglen_connect_10_props[] = {
PROPERTY_ENTRY_U32("touchscreen-fuzz-y", 6),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-viglen-connect-10.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -1102,7 +1065,6 @@ static const struct property_entry vinga_twizzle_j116_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1920),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1280),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-vinga-twizzle_j116.fw"),
- PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
@@ -1386,6 +1348,17 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
},
},
{
+ /* Jumper EZpad 6s Pro */
+ .driver_data = (void *)&jumper_ezpad_6_pro_b_data,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Jumper"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Ezpad"),
+ /* Above matches are too generic, add bios match */
+ DMI_MATCH(DMI_BIOS_VERSION, "E.WSA116_8.E1.042.bin"),
+ DMI_MATCH(DMI_BIOS_DATE, "01/08/2020"),
+ },
+ },
+ {
/* Jumper EZpad 6 m4 */
.driver_data = (void *)&jumper_ezpad_6_m4_data,
.matches = {
@@ -1625,6 +1598,15 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
},
},
{
+ /* GlobalSpace SoLT IVW 11.6" */
+ .driver_data = (void *)&globalspace_solt_ivw116_data,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Globalspace Tech Pvt Ltd"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "SolTIVW"),
+ DMI_MATCH(DMI_PRODUCT_SKU, "PN20170413488"),
+ },
+ },
+ {
/* Techbite Arc 11.6 */
.driver_data = (void *)&techbite_arc_11_6_data,
.matches = {
@@ -1817,7 +1799,7 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
{ }
};
-static const struct ts_dmi_data *ts_data;
+static struct ts_dmi_data *ts_data;
static void ts_dmi_add_props(struct i2c_client *client)
{
@@ -1852,6 +1834,64 @@ static int ts_dmi_notifier_call(struct notifier_block *nb,
return 0;
}
+#define MAX_CMDLINE_PROPS 16
+
+static struct property_entry ts_cmdline_props[MAX_CMDLINE_PROPS + 1];
+
+static struct ts_dmi_data ts_cmdline_data = {
+ .properties = ts_cmdline_props,
+};
+
+static int __init ts_parse_props(char *str)
+{
+ /* Save the original str to show it on syntax errors */
+ char orig_str[256];
+ char *name, *value;
+ u32 u32val;
+ int i, ret;
+
+ strscpy(orig_str, str);
+
+ /*
+ * str is part of the static_command_line from init/main.c and poking
+ * holes in that by writing 0 to it is allowed, as is taking long
+ * lasting references to it.
+ */
+ ts_cmdline_data.acpi_name = strsep(&str, ":");
+
+ for (i = 0; i < MAX_CMDLINE_PROPS; i++) {
+ name = strsep(&str, ":");
+ if (!name || !name[0])
+ break;
+
+ /* Replace '=' with 0 and make value point past '=' or NULL */
+ value = name;
+ strsep(&value, "=");
+ if (!value) {
+ ts_cmdline_props[i] = PROPERTY_ENTRY_BOOL(name);
+ } else if (isdigit(value[0])) {
+ ret = kstrtou32(value, 0, &u32val);
+ if (ret)
+ goto syntax_error;
+
+ ts_cmdline_props[i] = PROPERTY_ENTRY_U32(name, u32val);
+ } else {
+ ts_cmdline_props[i] = PROPERTY_ENTRY_STRING(name, value);
+ }
+ }
+
+ if (!i || str)
+ goto syntax_error;
+
+ ts_data = &ts_cmdline_data;
+ return 1;
+
+syntax_error:
+ pr_err("Invalid '%s' value for 'i2c_touchscreen_props='\n", orig_str);
+ return 1; /* "i2c_touchscreen_props=" is still a known parameter */
+}
+__setup("i2c_touchscreen_props=", ts_parse_props);
+
static struct notifier_block ts_dmi_notifier = {
.notifier_call = ts_dmi_notifier_call,
};
@@ -1859,13 +1899,25 @@ static struct notifier_block ts_dmi_notifier = {
static int __init ts_dmi_init(void)
{
const struct dmi_system_id *dmi_id;
+ struct ts_dmi_data *ts_data_dmi;
int error;
dmi_id = dmi_first_match(touchscreen_dmi_table);
- if (!dmi_id)
+ ts_data_dmi = dmi_id ? dmi_id->driver_data : NULL;
+
+ if (ts_data) {
+ /*
+ * Kernel cmdline provided data takes precedence, copy over
+ * DMI efi_embedded_fw info if available.
+ */
+ if (ts_data_dmi)
+ ts_data->embedded_fw = ts_data_dmi->embedded_fw;
+ } else if (ts_data_dmi) {
+ ts_data = ts_data_dmi;
+ } else {
return 0; /* Not an error */
+ }
- ts_data = dmi_id->driver_data;
/* Some dmi table entries only provide an efi_embedded_fw_desc */
if (!ts_data->properties)
return 0;
diff --git a/drivers/platform/x86/uv_sysfs.c b/drivers/platform/x86/uv_sysfs.c
index 38d1b692d3..f6a0627f36 100644
--- a/drivers/platform/x86/uv_sysfs.c
+++ b/drivers/platform/x86/uv_sysfs.c
@@ -11,6 +11,7 @@
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/kobject.h>
+#include <linux/vmalloc.h>
#include <asm/uv/bios.h>
#include <asm/uv/uv.h>
#include <asm/uv/uv_hub.h>
@@ -129,22 +130,22 @@ static ssize_t hub_location_show(struct uv_bios_hub_info *hub_info, char *buf)
static ssize_t hub_partition_show(struct uv_bios_hub_info *hub_info, char *buf)
{
- return sprintf(buf, "%d\n", hub_info->f.fields.this_part);
+ return sysfs_emit(buf, "%d\n", hub_info->f.fields.this_part);
}
static ssize_t hub_shared_show(struct uv_bios_hub_info *hub_info, char *buf)
{
- return sprintf(buf, "%d\n", hub_info->f.fields.is_shared);
+ return sysfs_emit(buf, "%d\n", hub_info->f.fields.is_shared);
}
static ssize_t hub_nasid_show(struct uv_bios_hub_info *hub_info, char *buf)
{
int cnode = get_obj_to_cnode(hub_info->id);
- return sprintf(buf, "%d\n", ordinal_to_nasid(cnode));
+ return sysfs_emit(buf, "%d\n", ordinal_to_nasid(cnode));
}
static ssize_t hub_cnode_show(struct uv_bios_hub_info *hub_info, char *buf)
{
- return sprintf(buf, "%d\n", get_obj_to_cnode(hub_info->id));
+ return sysfs_emit(buf, "%d\n", get_obj_to_cnode(hub_info->id));
}
struct hub_sysfs_entry {
@@ -304,12 +305,12 @@ struct uv_port {
static ssize_t uv_port_conn_hub_show(struct uv_bios_port_info *port, char *buf)
{
- return sprintf(buf, "%d\n", port->conn_id);
+ return sysfs_emit(buf, "%d\n", port->conn_id);
}
static ssize_t uv_port_conn_port_show(struct uv_bios_port_info *port, char *buf)
{
- return sprintf(buf, "%d\n", port->conn_port);
+ return sysfs_emit(buf, "%d\n", port->conn_port);
}
struct uv_port_sysfs_entry {
@@ -470,7 +471,7 @@ static ssize_t uv_pci_location_show(struct uv_pci_top_obj *top_obj, char *buf)
static ssize_t uv_pci_iio_stack_show(struct uv_pci_top_obj *top_obj, char *buf)
{
- return sprintf(buf, "%d\n", top_obj->iio_stack);
+ return sysfs_emit(buf, "%d\n", top_obj->iio_stack);
}
static ssize_t uv_pci_ppb_addr_show(struct uv_pci_top_obj *top_obj, char *buf)
@@ -480,7 +481,7 @@ static ssize_t uv_pci_ppb_addr_show(struct uv_pci_top_obj *top_obj, char *buf)
static ssize_t uv_pci_slot_show(struct uv_pci_top_obj *top_obj, char *buf)
{
- return sprintf(buf, "%d\n", top_obj->slot);
+ return sysfs_emit(buf, "%d\n", top_obj->slot);
}
struct uv_pci_top_sysfs_entry {
@@ -725,13 +726,13 @@ static void pci_topology_exit(void)
static ssize_t partition_id_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
- return sprintf(buf, "%ld\n", sn_partition_id);
+ return sysfs_emit(buf, "%ld\n", sn_partition_id);
}
static ssize_t coherence_id_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
- return sprintf(buf, "%ld\n", sn_coherency_id);
+ return sysfs_emit(buf, "%ld\n", sn_coherency_id);
}
static ssize_t uv_type_show(struct kobject *kobj,
@@ -928,4 +929,5 @@ module_init(uv_sysfs_init);
module_exit(uv_sysfs_exit);
MODULE_AUTHOR("Hewlett Packard Enterprise");
+MODULE_DESCRIPTION("Sysfs structure for HPE UV systems");
MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/wireless-hotkey.c b/drivers/platform/x86/wireless-hotkey.c
index 4422863f47..a220fe4f9e 100644
--- a/drivers/platform/x86/wireless-hotkey.c
+++ b/drivers/platform/x86/wireless-hotkey.c
@@ -14,11 +14,13 @@
#include <linux/acpi.h>
#include <acpi/acpi_bus.h>
+MODULE_DESCRIPTION("Airplane mode button for AMD, HP & Xiaomi laptops");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alex Hung");
MODULE_ALIAS("acpi*:HPQ6001:*");
MODULE_ALIAS("acpi*:WSTADEF:*");
MODULE_ALIAS("acpi*:AMDI0051:*");
+MODULE_ALIAS("acpi*:LGEX0815:*");
struct wl_button {
struct input_dev *input_dev;
@@ -29,6 +31,7 @@ static const struct acpi_device_id wl_ids[] = {
{"HPQ6001", 0},
{"WSTADEF", 0},
{"AMDI0051", 0},
+ {"LGEX0815", 0},
{"", 0},
};
@@ -110,7 +113,6 @@ static void wl_remove(struct acpi_device *device)
static struct acpi_driver wl_driver = {
.name = "wireless-hotkey",
- .owner = THIS_MODULE,
.ids = wl_ids,
.ops = {
.add = wl_add,
diff --git a/drivers/platform/x86/wmi-bmof.c b/drivers/platform/x86/wmi-bmof.c
index 644d2fd889..df6f0ae6e6 100644
--- a/drivers/platform/x86/wmi-bmof.c
+++ b/drivers/platform/x86/wmi-bmof.c
@@ -94,6 +94,7 @@ static struct wmi_driver wmi_bmof_driver = {
.probe = wmi_bmof_probe,
.remove = wmi_bmof_remove,
.id_table = wmi_bmof_id_table,
+ .no_singleton = true,
};
module_wmi_driver(wmi_bmof_driver);
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index 3c288e8f40..d21f3fa258 100644
--- a/drivers/platform/x86/wmi.c
+++ b/drivers/platform/x86/wmi.c
@@ -57,6 +57,8 @@ static_assert(__alignof__(struct guid_block) == 1);
enum { /* wmi_block flags */
WMI_READ_TAKES_NO_ARGS,
+ WMI_GUID_DUPLICATED,
+ WMI_NO_EVENT_DATA,
};
struct wmi_block {
@@ -88,16 +90,6 @@ static const struct acpi_device_id wmi_device_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, wmi_device_ids);
-/* allow duplicate GUIDs as these device drivers use struct wmi_driver */
-static const char * const allow_duplicates[] = {
- "05901221-D566-11D1-B2F0-00A0C9062910", /* wmi-bmof */
- "8A42EA14-4F2A-FD45-6422-0087F7A7E608", /* dell-wmi-ddv */
- "44FADEB1-B204-40F2-8581-394BBDC1B651", /* intel-wmi-sbl-fw-update */
- "86CCFD48-205E-4A77-9C48-2021CBEDE341", /* intel-wmi-thunderbolt */
- "F1DDEE52-063C-4784-A11E-8A06684B9B01", /* dell-smm-hwmon */
- NULL
-};
-
#define dev_to_wblock(__dev) container_of_const(__dev, struct wmi_block, dev.dev)
#define dev_to_wdev(__dev) container_of_const(__dev, struct wmi_device, dev)
@@ -132,26 +124,6 @@ static const void *find_guid_context(struct wmi_block *wblock,
return NULL;
}
-static int get_subobj_info(acpi_handle handle, const char *pathname,
- struct acpi_device_info **info)
-{
- acpi_handle subobj_handle;
- acpi_status status;
-
- status = acpi_get_handle(handle, pathname, &subobj_handle);
- if (status == AE_NOT_FOUND)
- return -ENOENT;
-
- if (ACPI_FAILURE(status))
- return -EIO;
-
- status = acpi_get_object_info(subobj_handle, info);
- if (ACPI_FAILURE(status))
- return -EIO;
-
- return 0;
-}
-
static acpi_status wmi_method_enable(struct wmi_block *wblock, bool enable)
{
struct guid_block *block;
@@ -215,6 +187,12 @@ static int wmidev_match_guid(struct device *dev, const void *data)
struct wmi_block *wblock = dev_to_wblock(dev);
const guid_t *guid = data;
+ /* Legacy GUID-based functions are restricted to only see
+ * a single WMI device for each GUID.
+ */
+ if (test_bit(WMI_GUID_DUPLICATED, &wblock->flags))
+ return 0;
+
if (guid_equal(guid, &wblock->gblock.guid))
return 1;
@@ -226,13 +204,19 @@ static int wmidev_match_notify_id(struct device *dev, const void *data)
struct wmi_block *wblock = dev_to_wblock(dev);
const u32 *notify_id = data;
+ /* Legacy GUID-based functions are restricted to only see
+ * a single WMI device for each GUID.
+ */
+ if (test_bit(WMI_GUID_DUPLICATED, &wblock->flags))
+ return 0;
+
if (wblock->gblock.flags & ACPI_WMI_EVENT && wblock->gblock.notify_id == *notify_id)
return 1;
return 0;
}
-static struct bus_type wmi_bus_type;
+static const struct bus_type wmi_bus_type;
static struct wmi_device *wmi_find_device_by_guid(const char *guid_string)
{
@@ -316,7 +300,7 @@ EXPORT_SYMBOL_GPL(wmidev_instance_count);
* @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
* @instance: Instance index
* @method_id: Method ID to call
- * @in: Buffer containing input for the method call
+ * @in: Mandatory buffer containing input for the method call
* @out: Empty buffer to return the method results
*
* Call an ACPI-WMI method, the caller must free @out.
@@ -346,7 +330,7 @@ EXPORT_SYMBOL_GPL(wmi_evaluate_method);
* @wdev: A wmi bus device from a driver
* @instance: Instance index
* @method_id: Method ID to call
- * @in: Buffer containing input for the method call
+ * @in: Mandatory buffer containing input for the method call
* @out: Empty buffer to return the method results
*
* Call an ACPI-WMI method, the caller must free @out.
@@ -367,26 +351,25 @@ acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, u32 met
block = &wblock->gblock;
handle = wblock->acpi_device->handle;
+ if (!in)
+ return AE_BAD_DATA;
+
if (!(block->flags & ACPI_WMI_METHOD))
return AE_BAD_DATA;
if (block->instance_count <= instance)
return AE_BAD_PARAMETER;
- input.count = 2;
+ input.count = 3;
input.pointer = params;
+
params[0].type = ACPI_TYPE_INTEGER;
params[0].integer.value = instance;
params[1].type = ACPI_TYPE_INTEGER;
params[1].integer.value = method_id;
-
- if (in) {
- input.count = 3;
-
- params[2].type = get_param_acpi_type(wblock);
- params[2].buffer.length = in->length;
- params[2].buffer.pointer = in->pointer;
- }
+ params[2].type = get_param_acpi_type(wblock);
+ params[2].buffer.length = in->length;
+ params[2].buffer.pointer = in->pointer;
get_acpi_method_name(wblock, 'M', method);
@@ -890,6 +873,23 @@ static int wmi_dev_probe(struct device *dev)
struct wmi_driver *wdriver = drv_to_wdrv(dev->driver);
int ret = 0;
+ /* Some older WMI drivers will break if instantiated multiple times,
+ * so they are blocked from probing WMI devices with a duplicated GUID.
+ *
+ * New WMI drivers should support being instantiated multiple times.
+ */
+ if (test_bit(WMI_GUID_DUPLICATED, &wblock->flags) && !wdriver->no_singleton) {
+ dev_warn(dev, "Legacy driver %s cannot be instantiated multiple times\n",
+ dev->driver->name);
+
+ return -ENODEV;
+ }
+
+ if (wdriver->notify) {
+ if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags) && !wdriver->no_notify_data)
+ return -ENODEV;
+ }
+
if (ACPI_FAILURE(wmi_method_enable(wblock, true)))
dev_warn(dev, "failed to enable device -- probing anyway\n");
@@ -931,7 +931,7 @@ static struct class wmi_bus_class = {
.name = "wmi_bus",
};
-static struct bus_type wmi_bus_type = {
+static const struct bus_type wmi_bus_type = {
.name = "wmi",
.dev_groups = wmi_groups,
.match = wmi_dev_match,
@@ -979,9 +979,10 @@ static int wmi_create_device(struct device *wmi_bus_dev,
struct wmi_block *wblock,
struct acpi_device *device)
{
- struct acpi_device_info *info;
char method[WMI_ACPI_METHOD_NAME_SIZE];
- int result;
+ struct acpi_device_info *info;
+ acpi_handle method_handle;
+ acpi_status status;
uint count;
if (wblock->gblock.flags & ACPI_WMI_EVENT) {
@@ -990,6 +991,15 @@ static int wmi_create_device(struct device *wmi_bus_dev,
}
if (wblock->gblock.flags & ACPI_WMI_METHOD) {
+ get_acpi_method_name(wblock, 'M', method);
+ if (!acpi_has_method(device->handle, method)) {
+ dev_warn(wmi_bus_dev,
+ FW_BUG "%s method block execution control method not found\n",
+ method);
+
+ return -ENXIO;
+ }
+
wblock->dev.dev.type = &wmi_type_method;
goto out_init;
}
@@ -1000,15 +1010,19 @@ static int wmi_create_device(struct device *wmi_bus_dev,
* we ignore this data block.
*/
get_acpi_method_name(wblock, 'Q', method);
- result = get_subobj_info(device->handle, method, &info);
-
- if (result) {
+ status = acpi_get_handle(device->handle, method, &method_handle);
+ if (ACPI_FAILURE(status)) {
dev_warn(wmi_bus_dev,
- "%s data block query control method not found\n",
+ FW_BUG "%s data block query control method not found\n",
method);
- return result;
+
+ return -ENXIO;
}
+ status = acpi_get_object_info(method_handle, &info);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
wblock->dev.dev.type = &wmi_type_data;
/*
@@ -1037,10 +1051,12 @@ static int wmi_create_device(struct device *wmi_bus_dev,
wblock->dev.dev.parent = wmi_bus_dev;
count = guid_count(&wblock->gblock.guid);
- if (count)
+ if (count) {
dev_set_name(&wblock->dev.dev, "%pUL-%d", &wblock->gblock.guid, count);
- else
+ set_bit(WMI_GUID_DUPLICATED, &wblock->flags);
+ } else {
dev_set_name(&wblock->dev.dev, "%pUL", &wblock->gblock.guid);
+ }
device_initialize(&wblock->dev.dev);
@@ -1067,32 +1083,6 @@ static int wmi_add_device(struct platform_device *pdev, struct wmi_device *wdev)
return device_add(&wdev->dev);
}
-static bool guid_already_parsed_for_legacy(struct acpi_device *device, const guid_t *guid)
-{
- struct wmi_block *wblock;
-
- list_for_each_entry(wblock, &wmi_block_list, list) {
- /* skip warning and register if we know the driver will use struct wmi_driver */
- for (int i = 0; allow_duplicates[i] != NULL; i++) {
- if (guid_parse_and_compare(allow_duplicates[i], guid))
- return false;
- }
- if (guid_equal(&wblock->gblock.guid, guid)) {
- /*
- * Because we historically didn't track the relationship
- * between GUIDs and ACPI nodes, we don't know whether
- * we need to suppress GUIDs that are unique on a
- * given node but duplicated across nodes.
- */
- dev_warn(&device->dev, "duplicate WMI GUID %pUL (first instance was on %s)\n",
- guid, dev_name(&wblock->acpi_device->dev));
- return true;
- }
- }
-
- return false;
-}
-
/*
* Parse the _WDG method for the GUID data blocks
*/
@@ -1101,6 +1091,7 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev)
struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
const struct guid_block *gblock;
+ bool event_data_available;
struct wmi_block *wblock;
union acpi_object *obj;
acpi_status status;
@@ -1120,6 +1111,7 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev)
return -ENXIO;
}
+ event_data_available = acpi_has_method(device->handle, "_WED");
gblock = (const struct guid_block *)obj->buffer.pointer;
total = obj->buffer.length / sizeof(struct guid_block);
@@ -1129,17 +1121,14 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev)
continue;
}
- if (guid_already_parsed_for_legacy(device, &gblock[i].guid))
- continue;
-
wblock = kzalloc(sizeof(*wblock), GFP_KERNEL);
- if (!wblock) {
- dev_err(wmi_bus_dev, "Failed to allocate %pUL\n", &gblock[i].guid);
+ if (!wblock)
continue;
- }
wblock->acpi_device = device;
wblock->gblock = gblock[i];
+ if (gblock[i].flags & ACPI_WMI_EVENT && !event_data_available)
+ set_bit(WMI_NO_EVENT_DATA, &wblock->flags);
retval = wmi_create_device(wmi_bus_dev, wblock, device);
if (retval) {
@@ -1164,71 +1153,46 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev)
return 0;
}
-/*
- * WMI can have EmbeddedControl access regions. In which case, we just want to
- * hand these off to the EC driver.
- */
-static acpi_status
-acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
- u32 bits, u64 *value,
- void *handler_context, void *region_context)
+static int wmi_get_notify_data(struct wmi_block *wblock, union acpi_object **obj)
{
- int result = 0;
- u8 temp = 0;
-
- if ((address > 0xFF) || !value)
- return AE_BAD_PARAMETER;
-
- if (function != ACPI_READ && function != ACPI_WRITE)
- return AE_BAD_PARAMETER;
-
- if (bits != 8)
- return AE_BAD_PARAMETER;
+ struct acpi_buffer data = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_status status;
- if (function == ACPI_READ) {
- result = ec_read(address, &temp);
- *value = temp;
- } else {
- temp = 0xff & *value;
- result = ec_write(address, temp);
+ if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags)) {
+ *obj = NULL;
+ return 0;
}
- switch (result) {
- case -EINVAL:
- return AE_BAD_PARAMETER;
- case -ENODEV:
- return AE_NOT_FOUND;
- case -ETIME:
- return AE_TIME;
- default:
- return AE_OK;
+ status = get_event_data(wblock, &data);
+ if (ACPI_FAILURE(status)) {
+ dev_warn(&wblock->dev.dev, "Failed to get event data\n");
+ return -EIO;
}
+
+ *obj = data.pointer;
+
+ return 0;
}
-static void wmi_notify_driver(struct wmi_block *wblock)
+static void wmi_notify_driver(struct wmi_block *wblock, union acpi_object *obj)
{
struct wmi_driver *driver = drv_to_wdrv(wblock->dev.dev.driver);
- struct acpi_buffer data = { ACPI_ALLOCATE_BUFFER, NULL };
- acpi_status status;
- if (!driver->no_notify_data) {
- status = get_event_data(wblock, &data);
- if (ACPI_FAILURE(status)) {
- dev_warn(&wblock->dev.dev, "Failed to get event data\n");
- return;
- }
+ if (!obj && !driver->no_notify_data) {
+ dev_warn(&wblock->dev.dev, "Event contains no event data\n");
+ return;
}
if (driver->notify)
- driver->notify(&wblock->dev, data.pointer);
-
- kfree(data.pointer);
+ driver->notify(&wblock->dev, obj);
}
static int wmi_notify_device(struct device *dev, void *data)
{
struct wmi_block *wblock = dev_to_wblock(dev);
+ union acpi_object *obj;
u32 *event = data;
+ int ret;
if (!(wblock->gblock.flags & ACPI_WMI_EVENT && wblock->gblock.notify_id == *event))
return 0;
@@ -1238,15 +1202,36 @@ static int wmi_notify_device(struct device *dev, void *data)
* Because of this the WMI driver notify handler takes precedence.
*/
if (wblock->dev.dev.driver && wblock->driver_ready) {
- wmi_notify_driver(wblock);
+ ret = wmi_get_notify_data(wblock, &obj);
+ if (ret >= 0) {
+ wmi_notify_driver(wblock, obj);
+ kfree(obj);
+ }
} else {
- if (wblock->handler)
+ if (wblock->handler) {
wblock->handler(*event, wblock->handler_data);
+ } else {
+ /* The ACPI WMI specification says that _WED should be
+ * evaluated every time an notification is received, even
+ * if no consumers are present.
+ *
+ * Some firmware implementations actually depend on this
+ * by using a queue for events which will fill up if the
+ * WMI driver core stops evaluating _WED due to missing
+ * WMI event consumers.
+ *
+ * Because of this we need this seemingly useless call to
+ * wmi_get_notify_data() which in turn evaluates _WED.
+ */
+ ret = wmi_get_notify_data(wblock, &obj);
+ if (ret >= 0)
+ kfree(obj);
+ }
+
}
up_read(&wblock->notify_lock);
- acpi_bus_generate_netlink_event(wblock->acpi_device->pnp.device_class,
- dev_name(&wblock->dev.dev), *event, 0);
+ acpi_bus_generate_netlink_event("wmi", acpi_dev_name(wblock->acpi_device), *event, 0);
return -EBUSY;
}
@@ -1282,14 +1267,6 @@ static void acpi_wmi_remove_notify_handler(void *data)
acpi_remove_notify_handler(acpi_device->handle, ACPI_ALL_NOTIFY, acpi_wmi_notify_handler);
}
-static void acpi_wmi_remove_address_space_handler(void *data)
-{
- struct acpi_device *acpi_device = data;
-
- acpi_remove_address_space_handler(acpi_device->handle, ACPI_ADR_SPACE_EC,
- &acpi_wmi_ec_space_handler);
-}
-
static void acpi_wmi_remove_bus_device(void *data)
{
struct device *wmi_bus_dev = data;
@@ -1321,19 +1298,6 @@ static int acpi_wmi_probe(struct platform_device *device)
dev_set_drvdata(&device->dev, wmi_bus_dev);
- status = acpi_install_address_space_handler(acpi_device->handle,
- ACPI_ADR_SPACE_EC,
- &acpi_wmi_ec_space_handler,
- NULL, NULL);
- if (ACPI_FAILURE(status)) {
- dev_err(&device->dev, "Error installing EC region handler\n");
- return -ENODEV;
- }
- error = devm_add_action_or_reset(&device->dev, acpi_wmi_remove_address_space_handler,
- acpi_device);
- if (error < 0)
- return error;
-
status = acpi_install_notify_handler(acpi_device->handle, ACPI_ALL_NOTIFY,
acpi_wmi_notify_handler, wmi_bus_dev);
if (ACPI_FAILURE(status)) {
@@ -1347,7 +1311,7 @@ static int acpi_wmi_probe(struct platform_device *device)
error = parse_wdg(wmi_bus_dev, device);
if (error) {
- pr_err("Failed to parse WDG method\n");
+ dev_err(&device->dev, "Failed to parse _WDG method\n");
return error;
}
diff --git a/drivers/platform/x86/x86-android-tablets/Kconfig b/drivers/platform/x86/x86-android-tablets/Kconfig
index 6603461d42..b591419de8 100644
--- a/drivers/platform/x86/x86-android-tablets/Kconfig
+++ b/drivers/platform/x86/x86-android-tablets/Kconfig
@@ -6,6 +6,8 @@
config X86_ANDROID_TABLETS
tristate "X86 Android tablet support"
depends on I2C && SPI && SERIAL_DEV_BUS && ACPI && EFI && GPIOLIB && PMIC_OPREGION
+ select NEW_LEDS
+ select LEDS_CLASS
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/core.c b/drivers/platform/x86/x86-android-tablets/core.c
index a3415f1c0b..919ef44712 100644
--- a/drivers/platform/x86/x86-android-tablets/core.c
+++ b/drivers/platform/x86/x86-android-tablets/core.c
@@ -52,10 +52,8 @@ int x86_android_tablet_get_gpiod(const char *chip, int pin, const char *con_id,
return -ENOMEM;
lookup->dev_id = KBUILD_MODNAME;
- lookup->table[0].key = chip;
- lookup->table[0].chip_hwnum = pin;
- lookup->table[0].con_id = con_id;
- lookup->table[0].flags = active_low ? GPIO_ACTIVE_LOW : GPIO_ACTIVE_HIGH;
+ lookup->table[0] =
+ GPIO_LOOKUP(chip, pin, con_id, active_low ? GPIO_ACTIVE_LOW : GPIO_ACTIVE_HIGH);
gpiod_add_lookup_table(lookup);
gpiod = devm_gpiod_get(&x86_android_tablet_device->dev, con_id, dflags);
@@ -278,25 +276,25 @@ static void x86_android_tablet_remove(struct platform_device *pdev)
{
int i;
- for (i = 0; i < serdev_count; i++) {
+ for (i = serdev_count - 1; i >= 0; i--) {
if (serdevs[i])
serdev_device_remove(serdevs[i]);
}
kfree(serdevs);
- for (i = 0; i < pdev_count; i++)
+ for (i = pdev_count - 1; i >= 0; i--)
platform_device_unregister(pdevs[i]);
kfree(pdevs);
kfree(buttons);
- for (i = 0; i < spi_dev_count; i++)
+ for (i = spi_dev_count - 1; i >= 0; i--)
spi_unregister_device(spi_devs[i]);
kfree(spi_devs);
- for (i = 0; i < i2c_client_count; i++)
+ for (i = i2c_client_count - 1; i >= 0; i--)
i2c_unregister_device(i2c_clients[i]);
kfree(i2c_clients);
@@ -343,7 +341,7 @@ static __init int x86_android_tablet_probe(struct platform_device *pdev)
gpiod_add_lookup_table(gpiod_lookup_tables[i]);
if (dev_info->init) {
- ret = dev_info->init();
+ ret = dev_info->init(&pdev->dev);
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 5d6c12494f..141a2d25e8 100644
--- a/drivers/platform/x86/x86-android-tablets/dmi.c
+++ b/drivers/platform/x86/x86-android-tablets/dmi.c
@@ -106,6 +106,24 @@ const struct dmi_system_id x86_android_tablet_ids[] __initconst = {
},
{
/*
+ * Lenovo Yoga Tablet 2 Pro 1380F/L (13") This has more or less
+ * the same BIOS as the 830F/L or 1050F/L (8" and 10") below,
+ * but unlike the 8" / 10" models which share the same mainboard
+ * this model has a different mainboard.
+ * This match for the 13" model MUST come before the 8" + 10"
+ * match since that one will also match the 13" model!
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "VALLEYVIEW C0 PLATFORM"),
+ DMI_MATCH(DMI_BOARD_NAME, "BYT-T FFD8"),
+ /* Full match so as to NOT match the 830/1050 BIOS */
+ DMI_MATCH(DMI_BIOS_VERSION, "BLADE_21.X64.0005.R00.1504101516"),
+ },
+ .driver_data = (void *)&lenovo_yoga_tab2_1380_info,
+ },
+ {
+ /*
* Lenovo Yoga Tablet 2 830F/L or 1050F/L (The 8" and 10"
* Lenovo Yoga Tablet 2 use the same mainboard)
*/
diff --git a/drivers/platform/x86/x86-android-tablets/lenovo.c b/drivers/platform/x86/x86-android-tablets/lenovo.c
index c297391955..74f39b658d 100644
--- a/drivers/platform/x86/x86-android-tablets/lenovo.c
+++ b/drivers/platform/x86/x86-android-tablets/lenovo.c
@@ -19,6 +19,7 @@
#include <linux/pinctrl/machine.h>
#include <linux/platform_data/lp855x.h>
#include <linux/platform_device.h>
+#include <linux/power/bq24190_charger.h>
#include <linux/reboot.h>
#include <linux/rmi.h>
#include <linux/spi/spi.h>
@@ -229,7 +230,7 @@ static struct gpiod_lookup_table * const lenovo_yb1_x90_gpios[] = {
NULL
};
-static int __init lenovo_yb1_x90_init(void)
+static int __init lenovo_yb1_x90_init(struct device *dev)
{
/* Enable the regulators used by the touchscreens */
@@ -411,7 +412,7 @@ static struct gpiod_lookup_table * const lenovo_yoga_tab2_830_1050_gpios[] = {
NULL
};
-static int __init lenovo_yoga_tab2_830_1050_init(void);
+static int __init lenovo_yoga_tab2_830_1050_init(struct device *dev);
static void lenovo_yoga_tab2_830_1050_exit(void);
const struct x86_dev_info lenovo_yoga_tab2_830_1050_info __initconst = {
@@ -533,7 +534,7 @@ static int lenovo_yoga_tab2_830_1050_power_off(struct sys_off_data *data)
return NOTIFY_DONE;
}
-static int __init lenovo_yoga_tab2_830_1050_init(void)
+static int __init lenovo_yoga_tab2_830_1050_init(struct device *dev)
{
int ret;
@@ -565,6 +566,221 @@ static void lenovo_yoga_tab2_830_1050_exit(void)
}
}
+/*
+ * Lenovo Yoga Tablet 2 Pro 1380F/L
+ *
+ * The Lenovo Yoga Tablet 2 Pro 1380F/L mostly has the same design as the 830F/L
+ * and the 1050F/L so this re-uses some of the handling for that from above.
+ */
+static const char * const lc824206xa_chg_det_psy[] = { "lc824206xa-charger-detect" };
+
+static const struct property_entry lenovo_yoga_tab2_1380_bq24190_props[] = {
+ PROPERTY_ENTRY_STRING_ARRAY("supplied-from", lc824206xa_chg_det_psy),
+ PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node),
+ PROPERTY_ENTRY_BOOL("omit-battery-class"),
+ PROPERTY_ENTRY_BOOL("disable-reset"),
+ { }
+};
+
+static const struct software_node lenovo_yoga_tab2_1380_bq24190_node = {
+ .properties = lenovo_yoga_tab2_1380_bq24190_props,
+};
+
+/* For enabling the bq24190 5V boost based on id-pin */
+static struct regulator_consumer_supply lc824206xa_consumer = {
+ .supply = "vbus",
+ .dev_name = "i2c-lc824206xa",
+};
+
+static const struct regulator_init_data lenovo_yoga_tab2_1380_bq24190_vbus_init_data = {
+ .constraints = {
+ .name = "bq24190_vbus",
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .consumer_supplies = &lc824206xa_consumer,
+ .num_consumer_supplies = 1,
+};
+
+struct bq24190_platform_data lenovo_yoga_tab2_1380_bq24190_pdata = {
+ .regulator_init_data = &lenovo_yoga_tab2_1380_bq24190_vbus_init_data,
+};
+
+static const struct property_entry lenovo_yoga_tab2_1380_lc824206xa_props[] = {
+ PROPERTY_ENTRY_BOOL("onnn,enable-miclr-for-dcp"),
+ { }
+};
+
+static const struct software_node lenovo_yoga_tab2_1380_lc824206xa_node = {
+ .properties = lenovo_yoga_tab2_1380_lc824206xa_props,
+};
+
+static const char * const lenovo_yoga_tab2_1380_lms303d_mount_matrix[] = {
+ "0", "-1", "0",
+ "-1", "0", "0",
+ "0", "0", "1"
+};
+
+static const struct property_entry lenovo_yoga_tab2_1380_lms303d_props[] = {
+ PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", lenovo_yoga_tab2_1380_lms303d_mount_matrix),
+ { }
+};
+
+static const struct software_node lenovo_yoga_tab2_1380_lms303d_node = {
+ .properties = lenovo_yoga_tab2_1380_lms303d_props,
+};
+
+static const struct x86_i2c_client_info lenovo_yoga_tab2_1380_i2c_clients[] __initconst = {
+ {
+ /* BQ27541 fuel-gauge */
+ .board_info = {
+ .type = "bq27541",
+ .addr = 0x55,
+ .dev_name = "bq27541",
+ .swnode = &fg_bq24190_supply_node,
+ },
+ .adapter_path = "\\_SB_.I2C1",
+ }, {
+ /* bq24292i battery charger */
+ .board_info = {
+ .type = "bq24190",
+ .addr = 0x6b,
+ .dev_name = "bq24292i",
+ .swnode = &lenovo_yoga_tab2_1380_bq24190_node,
+ .platform_data = &lenovo_yoga_tab2_1380_bq24190_pdata,
+ },
+ .adapter_path = "\\_SB_.I2C1",
+ .irq_data = {
+ .type = X86_ACPI_IRQ_TYPE_GPIOINT,
+ .chip = "INT33FC:02",
+ .index = 2,
+ .trigger = ACPI_EDGE_SENSITIVE,
+ .polarity = ACPI_ACTIVE_HIGH,
+ .con_id = "bq24292i_irq",
+ },
+ }, {
+ /* LP8557 Backlight controller */
+ .board_info = {
+ .type = "lp8557",
+ .addr = 0x2c,
+ .dev_name = "lp8557",
+ .platform_data = &lenovo_lp8557_pwm_and_reg_pdata,
+ },
+ .adapter_path = "\\_SB_.I2C3",
+ }, {
+ /* LC824206XA Micro USB Switch */
+ .board_info = {
+ .type = "lc824206xa",
+ .addr = 0x48,
+ .dev_name = "lc824206xa",
+ .swnode = &lenovo_yoga_tab2_1380_lc824206xa_node,
+ },
+ .adapter_path = "\\_SB_.I2C3",
+ .irq_data = {
+ .type = X86_ACPI_IRQ_TYPE_GPIOINT,
+ .chip = "INT33FC:02",
+ .index = 1,
+ .trigger = ACPI_LEVEL_SENSITIVE,
+ .polarity = ACPI_ACTIVE_LOW,
+ .con_id = "lc824206xa_irq",
+ },
+ }, {
+ /* AL3320A ambient light sensor */
+ .board_info = {
+ .type = "al3320a",
+ .addr = 0x1c,
+ .dev_name = "al3320a",
+ },
+ .adapter_path = "\\_SB_.I2C5",
+ }, {
+ /* LSM303DA accelerometer + magnetometer */
+ .board_info = {
+ .type = "lsm303d",
+ .addr = 0x1d,
+ .dev_name = "lsm303d",
+ .swnode = &lenovo_yoga_tab2_1380_lms303d_node,
+ },
+ .adapter_path = "\\_SB_.I2C5",
+ }, {
+ /* Synaptics RMI touchscreen */
+ .board_info = {
+ .type = "rmi4_i2c",
+ .addr = 0x38,
+ .dev_name = "rmi4_i2c",
+ .platform_data = &lenovo_yoga_tab2_830_1050_rmi_pdata,
+ },
+ .adapter_path = "\\_SB_.I2C6",
+ .irq_data = {
+ .type = X86_ACPI_IRQ_TYPE_APIC,
+ .index = 0x45,
+ .trigger = ACPI_EDGE_SENSITIVE,
+ .polarity = ACPI_ACTIVE_HIGH,
+ },
+ }
+};
+
+static const struct platform_device_info lenovo_yoga_tab2_1380_pdevs[] __initconst = {
+ {
+ /* For the Tablet 2 Pro 1380's custom fast charging driver */
+ .name = "lenovo-yoga-tab2-pro-1380-fastcharger",
+ .id = PLATFORM_DEVID_NONE,
+ },
+};
+
+const char * const lenovo_yoga_tab2_1380_modules[] __initconst = {
+ "bq24190_charger", /* For the Vbus regulator for lc824206xa */
+ NULL
+};
+
+static int __init lenovo_yoga_tab2_1380_init(struct device *dev)
+{
+ int ret;
+
+ /* To verify that the DMI matching works vs the 830 / 1050 models */
+ pr_info("detected Lenovo Yoga Tablet 2 Pro 1380F/L\n");
+
+ ret = lenovo_yoga_tab2_830_1050_init_codec();
+ if (ret)
+ return ret;
+
+ /* SYS_OFF_PRIO_FIRMWARE + 1 so that it runs before acpi_power_off */
+ lenovo_yoga_tab2_830_1050_sys_off_handler =
+ register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_FIRMWARE + 1,
+ lenovo_yoga_tab2_830_1050_power_off, NULL);
+ if (IS_ERR(lenovo_yoga_tab2_830_1050_sys_off_handler))
+ return PTR_ERR(lenovo_yoga_tab2_830_1050_sys_off_handler);
+
+ return 0;
+}
+
+static struct gpiod_lookup_table lenovo_yoga_tab2_1380_fc_gpios = {
+ .dev_id = "serial0-0",
+ .table = {
+ GPIO_LOOKUP("INT33FC:00", 57, "uart3_txd", GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP("INT33FC:00", 61, "uart3_rxd", GPIO_ACTIVE_HIGH),
+ { }
+ },
+};
+
+static struct gpiod_lookup_table * const lenovo_yoga_tab2_1380_gpios[] = {
+ &lenovo_yoga_tab2_830_1050_codec_gpios,
+ &lenovo_yoga_tab2_1380_fc_gpios,
+ NULL
+};
+
+const struct x86_dev_info lenovo_yoga_tab2_1380_info __initconst = {
+ .i2c_client_info = lenovo_yoga_tab2_1380_i2c_clients,
+ .i2c_client_count = ARRAY_SIZE(lenovo_yoga_tab2_1380_i2c_clients),
+ .pdev_info = lenovo_yoga_tab2_1380_pdevs,
+ .pdev_count = ARRAY_SIZE(lenovo_yoga_tab2_1380_pdevs),
+ .gpio_button = &lenovo_yoga_tab2_830_1050_lid,
+ .gpio_button_count = 1,
+ .gpiod_lookup_tables = lenovo_yoga_tab2_1380_gpios,
+ .bat_swnode = &generic_lipo_hv_4v35_battery_node,
+ .modules = lenovo_yoga_tab2_1380_modules,
+ .init = lenovo_yoga_tab2_1380_init,
+ .exit = lenovo_yoga_tab2_830_1050_exit,
+};
+
/* Lenovo Yoga Tab 3 Pro YT3-X90F */
/*
@@ -762,7 +978,7 @@ static const struct x86_spi_dev_info lenovo_yt3_spi_devs[] __initconst = {
}
};
-static int __init lenovo_yt3_init(void)
+static int __init lenovo_yt3_init(struct device *dev)
{
int ret;
diff --git a/drivers/platform/x86/x86-android-tablets/other.c b/drivers/platform/x86/x86-android-tablets/other.c
index 278402dcb8..eb0e55c69d 100644
--- a/drivers/platform/x86/x86-android-tablets/other.c
+++ b/drivers/platform/x86/x86-android-tablets/other.c
@@ -11,7 +11,11 @@
#include <linux/acpi.h>
#include <linux/gpio/machine.h>
#include <linux/input.h>
+#include <linux/leds.h>
#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+#include <dt-bindings/leds/common.h>
#include "shared-psy-info.h"
#include "x86-android-tablets.h"
@@ -181,7 +185,7 @@ static const struct x86_i2c_client_info chuwi_hi8_i2c_clients[] __initconst = {
},
};
-static int __init chuwi_hi8_init(void)
+static int __init chuwi_hi8_init(struct device *dev)
{
/*
* Avoid the acpi_unregister_gsi() call in x86_acpi_irq_helper_get()
@@ -242,7 +246,7 @@ const struct x86_dev_info cyberbook_t116_info __initconst = {
#define CZC_EC_EXTRA_PORT 0x68
#define CZC_EC_ANDROID_KEYS 0x63
-static int __init czc_p10t_init(void)
+static int __init czc_p10t_init(struct device *dev)
{
/*
* The device boots up in "Windows 7" mode, when the home button sends a
@@ -594,6 +598,128 @@ const struct x86_dev_info whitelabel_tm800a550l_info __initconst = {
};
/*
+ * The fwnode for ktd2026 on Xaomi pad2. It composed of a RGB LED node
+ * with three subnodes for each color (B/G/R). The RGB LED node is named
+ * "multi-led" to align with the name in the device tree.
+ */
+
+/* main fwnode for ktd2026 */
+static const struct software_node ktd2026_node = {
+ .name = "ktd2026",
+};
+
+static const struct property_entry ktd2026_rgb_led_props[] = {
+ PROPERTY_ENTRY_U32("reg", 0),
+ PROPERTY_ENTRY_U32("color", LED_COLOR_ID_RGB),
+ PROPERTY_ENTRY_STRING("label", "mipad2:rgb:indicator"),
+ PROPERTY_ENTRY_STRING("linux,default-trigger", "bq27520-0-charging-orange-full-green"),
+ { }
+};
+
+static const struct software_node ktd2026_rgb_led_node = {
+ .name = "multi-led",
+ .properties = ktd2026_rgb_led_props,
+ .parent = &ktd2026_node,
+};
+
+static const struct property_entry ktd2026_blue_led_props[] = {
+ PROPERTY_ENTRY_U32("reg", 0),
+ PROPERTY_ENTRY_U32("color", LED_COLOR_ID_BLUE),
+ { }
+};
+
+static const struct software_node ktd2026_blue_led_node = {
+ .properties = ktd2026_blue_led_props,
+ .parent = &ktd2026_rgb_led_node,
+};
+
+static const struct property_entry ktd2026_green_led_props[] = {
+ PROPERTY_ENTRY_U32("reg", 1),
+ PROPERTY_ENTRY_U32("color", LED_COLOR_ID_GREEN),
+ { }
+};
+
+static const struct software_node ktd2026_green_led_node = {
+ .properties = ktd2026_green_led_props,
+ .parent = &ktd2026_rgb_led_node,
+};
+
+static const struct property_entry ktd2026_red_led_props[] = {
+ PROPERTY_ENTRY_U32("reg", 2),
+ PROPERTY_ENTRY_U32("color", LED_COLOR_ID_RED),
+ { }
+};
+
+static const struct software_node ktd2026_red_led_node = {
+ .properties = ktd2026_red_led_props,
+ .parent = &ktd2026_rgb_led_node,
+};
+
+static const struct software_node *ktd2026_node_group[] = {
+ &ktd2026_node,
+ &ktd2026_rgb_led_node,
+ &ktd2026_red_led_node,
+ &ktd2026_green_led_node,
+ &ktd2026_blue_led_node,
+ NULL
+};
+
+/*
+ * For the LEDs which backlight the menu / home / back capacitive buttons on
+ * the bottom bezel. These are attached to a TPS61158 LED controller which
+ * is controlled by the "pwm_soc_lpss_2" PWM output.
+ */
+#define XIAOMI_MIPAD2_LED_PERIOD_NS 19200
+#define XIAOMI_MIPAD2_LED_DEFAULT_DUTY 6000 /* From Android kernel */
+
+static struct pwm_device *xiaomi_mipad2_led_pwm;
+
+static int xiaomi_mipad2_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness val)
+{
+ struct pwm_state state = {
+ .period = XIAOMI_MIPAD2_LED_PERIOD_NS,
+ .duty_cycle = val,
+ /* Always set PWM enabled to avoid the pin floating */
+ .enabled = true,
+ };
+
+ return pwm_apply_might_sleep(xiaomi_mipad2_led_pwm, &state);
+}
+
+static int __init xiaomi_mipad2_init(struct device *dev)
+{
+ struct led_classdev *led_cdev;
+ int ret;
+
+ xiaomi_mipad2_led_pwm = devm_pwm_get(dev, "pwm_soc_lpss_2");
+ if (IS_ERR(xiaomi_mipad2_led_pwm))
+ return dev_err_probe(dev, PTR_ERR(xiaomi_mipad2_led_pwm), "getting pwm\n");
+
+ led_cdev = devm_kzalloc(dev, sizeof(*led_cdev), GFP_KERNEL);
+ if (!led_cdev)
+ return -ENOMEM;
+
+ led_cdev->name = "mipad2:white:touch-buttons-backlight";
+ led_cdev->max_brightness = XIAOMI_MIPAD2_LED_PERIOD_NS;
+ /* "input-events" trigger uses blink_brightness */
+ led_cdev->blink_brightness = XIAOMI_MIPAD2_LED_DEFAULT_DUTY;
+ led_cdev->default_trigger = "input-events";
+ led_cdev->brightness_set_blocking = xiaomi_mipad2_brightness_set;
+
+ ret = devm_led_classdev_register(dev, led_cdev);
+ if (ret)
+ return dev_err_probe(dev, ret, "registering LED\n");
+
+ return software_node_register_node_group(ktd2026_node_group);
+}
+
+static void xiaomi_mipad2_exit(void)
+{
+ software_node_unregister_node_group(ktd2026_node_group);
+}
+
+/*
* If the EFI bootloader is not Xiaomi's own signed Android loader, then the
* Xiaomi Mi Pad 2 X86 tablet sets OSID in the DSDT to 1 (Windows), causing
* a bunch of devices to be hidden.
@@ -616,6 +742,7 @@ static const struct x86_i2c_client_info xiaomi_mipad2_i2c_clients[] __initconst
.type = "ktd2026",
.addr = 0x30,
.dev_name = "ktd2026",
+ .swnode = &ktd2026_node,
},
.adapter_path = "\\_SB_.PCI0.I2C3",
},
@@ -624,4 +751,6 @@ static const struct x86_i2c_client_info xiaomi_mipad2_i2c_clients[] __initconst
const struct x86_dev_info xiaomi_mipad2_info __initconst = {
.i2c_client_info = xiaomi_mipad2_i2c_clients,
.i2c_client_count = ARRAY_SIZE(xiaomi_mipad2_i2c_clients),
+ .init = xiaomi_mipad2_init,
+ .exit = xiaomi_mipad2_exit,
};
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 468993edfe..86402b9b46 100644
--- a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h
+++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h
@@ -89,7 +89,7 @@ struct x86_dev_info {
int pdev_count;
int serdev_count;
int gpio_button_count;
- int (*init)(void);
+ int (*init)(struct device *dev);
void (*exit)(void);
};
@@ -112,6 +112,7 @@ extern const struct x86_dev_info czc_p10t;
extern const struct x86_dev_info lenovo_yogabook_x90_info;
extern const struct x86_dev_info lenovo_yogabook_x91_info;
extern const struct x86_dev_info lenovo_yoga_tab2_830_1050_info;
+extern const struct x86_dev_info lenovo_yoga_tab2_1380_info;
extern const struct x86_dev_info lenovo_yt3_info;
extern const struct x86_dev_info medion_lifetab_s10346_info;
extern const struct x86_dev_info nextbook_ares8_info;
diff --git a/drivers/platform/x86/xiaomi-wmi.c b/drivers/platform/x86/xiaomi-wmi.c
index be80f0bda9..cbed29ca50 100644
--- a/drivers/platform/x86/xiaomi-wmi.c
+++ b/drivers/platform/x86/xiaomi-wmi.c
@@ -38,7 +38,7 @@ static int xiaomi_wmi_probe(struct wmi_device *wdev, const void *context)
struct xiaomi_wmi *data;
int ret;
- if (wdev == NULL || context == NULL)
+ if (!context)
return -EINVAL;
data = devm_kzalloc(&wdev->dev, sizeof(struct xiaomi_wmi), GFP_KERNEL);
@@ -66,14 +66,7 @@ static int xiaomi_wmi_probe(struct wmi_device *wdev, const void *context)
static void xiaomi_wmi_notify(struct wmi_device *wdev, union acpi_object *dummy)
{
- struct xiaomi_wmi *data;
-
- if (wdev == NULL)
- return;
-
- data = dev_get_drvdata(&wdev->dev);
- if (data == NULL)
- return;
+ struct xiaomi_wmi *data = dev_get_drvdata(&wdev->dev);
mutex_lock(&data->key_lock);
input_report_key(data->input_dev, data->key_code, 1);
@@ -101,6 +94,7 @@ static struct wmi_driver xiaomi_wmi_driver = {
.id_table = xiaomi_wmi_id_table,
.probe = xiaomi_wmi_probe,
.notify = xiaomi_wmi_notify,
+ .no_singleton = true,
};
module_wmi_driver(xiaomi_wmi_driver);
diff --git a/drivers/platform/x86/xo1-rfkill.c b/drivers/platform/x86/xo1-rfkill.c
index e64d5646b4..5fe6829650 100644
--- a/drivers/platform/x86/xo1-rfkill.c
+++ b/drivers/platform/x86/xo1-rfkill.c
@@ -74,5 +74,6 @@ static struct platform_driver xo1_rfkill_driver = {
module_platform_driver(xo1_rfkill_driver);
MODULE_AUTHOR("Daniel Drake <dsd@laptop.org>");
+MODULE_DESCRIPTION("OLPC XO-1 software RF kill switch");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:xo1-rfkill");