From 3ff5873602a874035ba28826852bd45393002a08 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 5 Mar 2024 10:45:00 +0100 Subject: platform/x86: p2sb: Make p2sb_get_devfn() return void p2sb_get_devfn() always succeeds, make it return void and remove error checking from its callers. Reviewed-by: Shin'ichiro Kawasaki Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20240305094500.23778-1-hdegoede@redhat.com --- drivers/platform/x86/p2sb.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/platform/x86/p2sb.c b/drivers/platform/x86/p2sb.c index 3d66e1d4eb1f52..53fe96b99ab7ec 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,7 +53,6 @@ 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) @@ -135,9 +134,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 +191,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) -- cgit 1.2.3-korg From 6d9b262afe0ec1d6e0ef99321ca9d6b921310471 Mon Sep 17 00:00:00 2001 From: Ai Chao Date: Thu, 14 Mar 2024 14:37:03 +0800 Subject: platform/x86: hp-wmi: use sysfs_emit() instead of sprintf() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow the advice in Documentation/filesystems/sysfs.rst: show() should only use sysfs_emit() or sysfs_emit_at() when formatting the value to be returned to user space. Signed-off-by: Ai Chao Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240314063703.315841-1-aichao@kylinos.cn Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/hp/hp-wmi.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 630519c086171d..5fa55302384269 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -681,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, @@ -691,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, @@ -701,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, @@ -711,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, @@ -721,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, @@ -732,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, -- cgit 1.2.3-korg From 79bd127f9662ead1ceea7970ef36fbe985a6d7ab Mon Sep 17 00:00:00 2001 From: Ai Chao Date: Tue, 19 Mar 2024 13:56:36 +0800 Subject: platform/x86: asus-wmi: use sysfs_emit() instead of sprintf() This changes all *_show attributes in asus-wmi.c to use sysfs_emit() instead of the older method of writing to the output buffer manually. Follow the advice in Documentation/filesystems/sysfs.rst: show() should only use sysfs_emit() or sysfs_emit_at() when formatting the value to be returned to user space. Signed-off-by: Ai Chao Link: https://lore.kernel.org/r/20240319055636.150289-1-aichao@kylinos.cn Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/asus-wmi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 3f07bbf809ef01..df4c103459daae 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -2326,7 +2326,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, @@ -2512,8 +2512,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 */ @@ -4061,7 +4061,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) \ -- cgit 1.2.3-korg From 415c33d20a2d985fa9be34bccca2a780c72b14cc Mon Sep 17 00:00:00 2001 From: Ai Chao Date: Tue, 19 Mar 2024 14:42:43 +0800 Subject: platform/x86: huawei-wmi: use sysfs_emit() instead of sprintf() Follow the advice in Documentation/filesystems/sysfs.rst: show() should only use sysfs_emit() or sysfs_emit_at() when formatting the value to be returned to user space. Signed-off-by: Ai Chao Link: https://lore.kernel.org/r/20240319064243.297320-1-aichao@kylinos.cn Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/huawei-wmi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c index dde139c69945eb..09d476dd832e8b 100644 --- a/drivers/platform/x86/huawei-wmi.c +++ b/drivers/platform/x86/huawei-wmi.c @@ -379,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, @@ -392,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, @@ -405,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, @@ -562,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, -- cgit 1.2.3-korg From d439311264981fcc90e30993c7746108be45586d Mon Sep 17 00:00:00 2001 From: Ai Chao Date: Tue, 19 Mar 2024 15:00:38 +0800 Subject: platform/x86: uv_sysfs: use sysfs_emit() instead of sprintf() Follow the advice in Documentation/filesystems/sysfs.rst: show() should only use sysfs_emit() or sysfs_emit_at() when formatting the value to be returned to user space. Signed-off-by: Ai Chao Link: https://lore.kernel.org/r/20240319070038.309683-1-aichao@kylinos.cn Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/uv_sysfs.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/platform/x86/uv_sysfs.c b/drivers/platform/x86/uv_sysfs.c index 38d1b692d3c0aa..3f6d52dea5c91e 100644 --- a/drivers/platform/x86/uv_sysfs.c +++ b/drivers/platform/x86/uv_sysfs.c @@ -129,22 +129,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 +304,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 +470,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 +480,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 +725,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, -- cgit 1.2.3-korg From 6c4d24d60eb887015ae0356b4fc58cd06e74da31 Mon Sep 17 00:00:00 2001 From: Nikita Travkin Date: Fri, 15 Mar 2024 18:51:15 +0500 Subject: dt-bindings: platform: Add Acer Aspire 1 EC Add binding for the EC found in the Acer Aspire 1 laptop. Reviewed-by: Rob Herring Signed-off-by: Nikita Travkin Link: https://lore.kernel.org/r/20240315-aspire1-ec-v5-1-f93381deff39@trvn.ru Signed-off-by: Hans de Goede --- .../bindings/platform/acer,aspire1-ec.yaml | 60 ++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 Documentation/devicetree/bindings/platform/acer,aspire1-ec.yaml diff --git a/Documentation/devicetree/bindings/platform/acer,aspire1-ec.yaml b/Documentation/devicetree/bindings/platform/acer,aspire1-ec.yaml new file mode 100644 index 00000000000000..7cb0134134ffa6 --- /dev/null +++ b/Documentation/devicetree/bindings/platform/acer,aspire1-ec.yaml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/platform/acer,aspire1-ec.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Acer Aspire 1 Embedded Controller + +maintainers: + - Nikita Travkin + +description: + The Acer Aspire 1 laptop uses an embedded controller to control battery + and charging as well as to provide a set of misc features such as the + laptop lid status and HPD events for the USB Type-C DP alt mode. + +properties: + compatible: + const: acer,aspire1-ec + + reg: + const: 0x76 + + interrupts: + maxItems: 1 + + connector: + $ref: /schemas/connector/usb-connector.yaml# + +required: + - compatible + - reg + - interrupts + +additionalProperties: false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + embedded-controller@76 { + compatible = "acer,aspire1-ec"; + reg = <0x76>; + + interrupts-extended = <&tlmm 30 IRQ_TYPE_LEVEL_LOW>; + + connector { + compatible = "usb-c-connector"; + + port { + ec_dp_in: endpoint { + remote-endpoint = <&mdss_dp_out>; + }; + }; + }; + }; + }; -- cgit 1.2.3-korg From 363c8aea25728604537b170a1cc24e2f46844896 Mon Sep 17 00:00:00 2001 From: Nikita Travkin Date: Fri, 15 Mar 2024 18:51:16 +0500 Subject: platform: Add ARM64 platform directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some ARM64 based laptops and computers require vendor/board specific drivers for their embedded controllers. Even though usually the most important functionality of those devices is implemented inside ACPI, unfortunately Linux doesn't currently have great support for ACPI on platforms like Qualcomm Snapdragon that are used in most ARM64 laptops today. Instead Linux relies on Device Tree for Qualcomm based devices and it's significantly easier to reimplement the EC functionality in a dedicated driver than to make use of ACPI code. This commit introduces a new platform/arm64 subdirectory to give a place to such drivers for EC-like devices. A new MAINTAINERS entry is added for this directory. Patches to files in this directory will be taken up by the platform-drivers-x86 team (i.e. Hans de Goede and Ilpo Järvinen) with additional review from Bryan O'Donoghue to represent ARM64 maintainers. Signed-off-by: Nikita Travkin Acked-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240315-aspire1-ec-v5-2-f93381deff39@trvn.ru Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- MAINTAINERS | 10 ++++++++++ drivers/platform/Kconfig | 2 ++ drivers/platform/Makefile | 1 + drivers/platform/arm64/Kconfig | 19 +++++++++++++++++++ drivers/platform/arm64/Makefile | 6 ++++++ 5 files changed, 38 insertions(+) create mode 100644 drivers/platform/arm64/Kconfig create mode 100644 drivers/platform/arm64/Makefile diff --git a/MAINTAINERS b/MAINTAINERS index aa3b947fb0801d..74a93c0b816b92 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3051,6 +3051,16 @@ F: drivers/mmc/host/sdhci-of-arasan.c N: zynq N: xilinx +ARM64 PLATFORM DRIVERS +M: Hans de Goede +M: Ilpo Järvinen +R: Bryan O'Donoghue +L: platform-driver-x86@vger.kernel.org +S: Maintained +Q: https://patchwork.kernel.org/project/platform-driver-x86/list/ +T: git git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git +F: drivers/platform/arm64/ + ARM64 PORT (AARCH64 ARCHITECTURE) M: Catalin Marinas M: Will Deacon diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig index 868b20361769c3..81a298517df2db 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 41640172975a79..fbbe4f77aa5d7d 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) += arm64/ diff --git a/drivers/platform/arm64/Kconfig b/drivers/platform/arm64/Kconfig new file mode 100644 index 00000000000000..644b83ede0933e --- /dev/null +++ b/drivers/platform/arm64/Kconfig @@ -0,0 +1,19 @@ +# 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 + +endif # ARM64_PLATFORM_DEVICES diff --git a/drivers/platform/arm64/Makefile b/drivers/platform/arm64/Makefile new file mode 100644 index 00000000000000..f91cdc7155e2d6 --- /dev/null +++ b/drivers/platform/arm64/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for linux/drivers/platform/arm64 +# +# This dir should only include drivers for EC-like devices. +# -- cgit 1.2.3-korg From 2b3efb7c515111eaa009f014b16bce8417fb2828 Mon Sep 17 00:00:00 2001 From: Nikita Travkin Date: Fri, 15 Mar 2024 18:51:17 +0500 Subject: platform: arm64: Add Acer Aspire 1 embedded controller driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Acer Aspire 1 is a Snapdragon 7c based laptop. It uses an embedded controller to perform a set of various functions, such as: - Battery and charger monitoring; - Keyboard layout control (i.e. fn_lock settings); - USB Type-C DP alt mode HPD notifications; - Laptop lid status. Unfortunately, while all this functionality is implemented in ACPI, it's currently not possible to use ACPI to boot Linux on such Qualcomm devices. To allow Linux to still support the features provided by EC, this driver reimplments the relevant ACPI parts. This allows us to boot the laptop with Device Tree and retain all the features. Reviewed-by: Bryan O'Donoghue Signed-off-by: Nikita Travkin Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20240315-aspire1-ec-v5-3-f93381deff39@trvn.ru Signed-off-by: Hans de Goede --- MAINTAINERS | 6 + drivers/platform/arm64/Kconfig | 16 + drivers/platform/arm64/Makefile | 2 + drivers/platform/arm64/acer-aspire1-ec.c | 562 +++++++++++++++++++++++++++++++ 4 files changed, 586 insertions(+) create mode 100644 drivers/platform/arm64/acer-aspire1-ec.c diff --git a/MAINTAINERS b/MAINTAINERS index 74a93c0b816b92..b5514c83c7dce2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -258,6 +258,12 @@ L: linux-acenic@sunsite.dk S: Maintained F: drivers/net/ethernet/alteon/acenic* +ACER ASPIRE 1 EMBEDDED CONTROLLER DRIVER +M: Nikita Travkin +S: Maintained +F: Documentation/devicetree/bindings/platform/acer,aspire1-ec.yaml +F: drivers/platform/arm64/acer-aspire1-ec.c + ACER ASPIRE ONE TEMPERATURE AND FAN DRIVER M: Peter Kaestle L: platform-driver-x86@vger.kernel.org diff --git a/drivers/platform/arm64/Kconfig b/drivers/platform/arm64/Kconfig index 644b83ede0933e..8fdca0f8e9097d 100644 --- a/drivers/platform/arm64/Kconfig +++ b/drivers/platform/arm64/Kconfig @@ -16,4 +16,20 @@ menuconfig ARM64_PLATFORM_DEVICES 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 index f91cdc7155e2d6..4fcc9855579be0 100644 --- a/drivers/platform/arm64/Makefile +++ b/drivers/platform/arm64/Makefile @@ -4,3 +4,5 @@ # # 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 00000000000000..dbb1cce139654a --- /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 */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_LICENSE("GPL"); -- cgit 1.2.3-korg From 0cd33df4e406ee4b705ee15d942a1ae8387a1d8b Mon Sep 17 00:00:00 2001 From: Nikita Travkin Date: Fri, 15 Mar 2024 18:51:18 +0500 Subject: arm64: dts: qcom: acer-aspire1: Add embedded controller The laptop contains an embedded controller that provides a set of features: - Battery and charger monitoring - USB Type-C DP alt mode HPD monitoring - Lid status detection - Small amount of keyboard configuration* [*] The keyboard is handled by the same EC but it has a dedicated i2c bus and is already enabled. This port only provides fn key behavior configuration. Add the EC to the device tree and describe the relationship between the EC-managed type-c port and the SoC DisplayPort. Reviewed-by: Konrad Dybcio Signed-off-by: Nikita Travkin Link: https://lore.kernel.org/r/20240315-aspire1-ec-v5-4-f93381deff39@trvn.ru Signed-off-by: Hans de Goede --- arch/arm64/boot/dts/qcom/sc7180-acer-aspire1.dts | 40 +++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sc7180-acer-aspire1.dts b/arch/arm64/boot/dts/qcom/sc7180-acer-aspire1.dts index 5afcb8212f4900..3f0d3e33894a07 100644 --- a/arch/arm64/boot/dts/qcom/sc7180-acer-aspire1.dts +++ b/arch/arm64/boot/dts/qcom/sc7180-acer-aspire1.dts @@ -255,7 +255,25 @@ clock-frequency = <400000>; status = "okay"; - /* embedded-controller@76 */ + embedded-controller@76 { + compatible = "acer,aspire1-ec"; + reg = <0x76>; + + interrupts-extended = <&tlmm 30 IRQ_TYPE_LEVEL_LOW>; + + pinctrl-0 = <&ec_int_default>; + pinctrl-names = "default"; + + connector { + compatible = "usb-c-connector"; + + port { + ec_dp_in: endpoint { + remote-endpoint = <&mdss_dp_out>; + }; + }; + }; + }; }; &i2c4 { @@ -419,6 +437,19 @@ status = "okay"; }; +&mdss_dp { + data-lanes = <0 1>; + + vdda-1p2-supply = <&vreg_l3c_1p2>; + vdda-0p9-supply = <&vreg_l4a_0p8>; + + status = "okay"; +}; + +&mdss_dp_out { + remote-endpoint = <&ec_dp_in>; +}; + &mdss_dsi0 { vdda-supply = <&vreg_l3c_1p2>; status = "okay"; @@ -857,6 +888,13 @@ bias-disable; }; + ec_int_default: ec-int-default-state { + pins = "gpio30"; + function = "gpio"; + drive-strength = <2>; + bias-disable; + }; + edp_bridge_irq_default: edp-bridge-irq-default-state { pins = "gpio11"; function = "gpio"; -- cgit 1.2.3-korg From c663b26972eae7d2a614f584c92a266fe9a2d44c Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Thu, 14 Mar 2024 19:45:37 +0100 Subject: platform/x86: wmi: Support reading/writing 16 bit EC values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ACPI EC address space handler currently only supports reading/writing 8 bit values. Some firmware implementations however want to access for example 16 bit values, which is perfectly legal according to the ACPI spec. Add support for reading/writing such values. Tested on a Dell Inspiron 3505 and a Asus Prime B650-Plus. Reviewed-by: Ilpo Järvinen Signed-off-by: Armin Wolf Reviewed-by: Kuppuswamy Sathyanarayanan Link: https://lore.kernel.org/r/20240314184538.2933-1-W_Armin@gmx.de Signed-off-by: Hans de Goede --- drivers/platform/x86/wmi.c | 54 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 1920e115da893a..9602658711cf31 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -1153,6 +1153,34 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev) return 0; } +static int ec_read_multiple(u8 address, u8 *buffer, size_t bytes) +{ + size_t i; + int ret; + + for (i = 0; i < bytes; i++) { + ret = ec_read(address + i, &buffer[i]); + if (ret < 0) + return ret; + } + + return 0; +} + +static int ec_write_multiple(u8 address, u8 *buffer, size_t bytes) +{ + size_t i; + int ret; + + for (i = 0; i < bytes; i++) { + ret = ec_write(address + i, buffer[i]); + if (ret < 0) + return ret; + } + + return 0; +} + /* * WMI can have EmbeddedControl access regions. In which case, we just want to * hand these off to the EC driver. @@ -1162,27 +1190,27 @@ acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, u32 bits, u64 *value, void *handler_context, void *region_context) { - int result = 0; - u8 temp = 0; + int bytes = bits / BITS_PER_BYTE; + int ret; - if ((address > 0xFF) || !value) + if (!value) + return AE_NULL_ENTRY; + + if (!bytes || bytes > sizeof(*value)) return AE_BAD_PARAMETER; - if (function != ACPI_READ && function != ACPI_WRITE) + if (address > U8_MAX || address + bytes - 1 > U8_MAX) return AE_BAD_PARAMETER; - if (bits != 8) + if (function != ACPI_READ && function != ACPI_WRITE) return AE_BAD_PARAMETER; - if (function == ACPI_READ) { - result = ec_read(address, &temp); - *value = temp; - } else { - temp = 0xff & *value; - result = ec_write(address, temp); - } + if (function == ACPI_READ) + ret = ec_read_multiple(address, (u8 *)value, bytes); + else + ret = ec_write_multiple(address, (u8 *)value, bytes); - switch (result) { + switch (ret) { case -EINVAL: return AE_BAD_PARAMETER; case -ENODEV: -- cgit 1.2.3-korg From e526da8f8875267ccb8f4c4782668ebfa160b2c0 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Thu, 14 Mar 2024 19:45:38 +0100 Subject: platform/x86: wmi: Avoid returning AE_OK upon unknown error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If an error code other than EINVAL, ENODEV or ETIME is returned by ec_read()/ec_write(), then AE_OK is wrongly returned. Fix this by only returning AE_OK if the return code is 0, and return AE_ERROR otherwise. Tested on a Dell Inspiron 3505 and a Asus Prime B650-Plus. Reviewed-by: Hans de Goede Reviewed-by: Ilpo Järvinen Reviewed-by: Kuppuswamy Sathyanarayanan Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240314184538.2933-2-W_Armin@gmx.de Signed-off-by: Hans de Goede --- drivers/platform/x86/wmi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 9602658711cf31..060e4236f932b1 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -1217,8 +1217,10 @@ acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, return AE_NOT_FOUND; case -ETIME: return AE_TIME; - default: + case 0: return AE_OK; + default: + return AE_ERROR; } } -- cgit 1.2.3-korg From 3427c443a6dc2f6171616c2381d037d004af1df0 Mon Sep 17 00:00:00 2001 From: Ivor Wanders Date: Thu, 14 Mar 2024 18:37:33 -0400 Subject: platform/surface: platform_profile: add fan profile switching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change naming from tmp to platform profile to clarify the module may interact with both the TMP and FAN subystems. Add functionality that switches the fan profile when the platform profile is changed when a fan is present. Signed-off-by: Ivor Wanders Link: https://github.com/linux-surface/kernel/pull/145 Reviewed-by: Maximilian Luz Link: https://lore.kernel.org/r/20240314223733.6236-2-ivor@iwanders.net Reviewed-by: Ilpo Järvinen Signed-off-by: Hans de Goede --- .../platform/surface/surface_aggregator_registry.c | 36 ++++++--- .../platform/surface/surface_platform_profile.c | 88 ++++++++++++++++++---- 2 files changed, 100 insertions(+), 24 deletions(-) diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c index 035d6b4105cd63..79e52eddabd0cf 100644 --- a/drivers/platform/surface/surface_aggregator_registry.c +++ b/drivers/platform/surface/surface_aggregator_registry.c @@ -68,12 +68,26 @@ 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, +}; + /* Fan speed function. */ static const struct software_node ssam_node_fan_speed = { .name = "ssam:01:05:01:01:01", @@ -208,7 +222,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, }; @@ -219,7 +233,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, @@ -233,7 +247,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, @@ -245,7 +259,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, @@ -258,7 +272,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, @@ -274,7 +288,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, }; @@ -283,7 +297,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, }; @@ -293,7 +307,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, @@ -310,7 +324,7 @@ 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_fan_speed, &ssam_node_pos_tablet_switch, &ssam_node_hid_kip_keyboard, diff --git a/drivers/platform/surface/surface_platform_profile.c b/drivers/platform/surface/surface_platform_profile.c index a5a3941b3f43af..3de864bc66108d 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 */ @@ -14,6 +14,7 @@ #include +// 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); -- cgit 1.2.3-korg From fe7af61159b85c2e78b4626be282e7fc9c487d46 Mon Sep 17 00:00:00 2001 From: Ai Chao Date: Wed, 27 Mar 2024 16:27:37 +0800 Subject: platform/x86: add lenovo WMI camera button driver Add lenovo WMI camera button driver to support camera button. The Camera button is a GPIO device. This driver receives ACPI notifications when the camera button is switched on/off. This driver is used in Lenovo A70, it is a Computer integrated machine. Signed-off-by: Ai Chao Link: https://lore.kernel.org/r/20240327082737.336992-1-aichao@kylinos.cn Reviewed-by: Armin Wolf Signed-off-by: Hans de Goede --- drivers/platform/x86/Kconfig | 12 +++ drivers/platform/x86/Makefile | 1 + drivers/platform/x86/lenovo-wmi-camera.c | 127 +++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+) create mode 100644 drivers/platform/x86/lenovo-wmi-camera.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 7e9251fc33416e..cf9aa220529c7e 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -996,6 +996,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 1de432e8861eac..217e94d7c87745 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -66,6 +66,7 @@ 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_LENOVO_WMI_CAMERA) += lenovo-wmi-camera.o # Intel obj-y += intel/ diff --git a/drivers/platform/x86/lenovo-wmi-camera.c b/drivers/platform/x86/lenovo-wmi-camera.c new file mode 100644 index 00000000000000..0c0bedaf740710 --- /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 + * Copyright (C) 2024 KylinSoft Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_DESCRIPTION("Lenovo WMI Camera Button Driver"); +MODULE_LICENSE("GPL"); -- cgit 1.2.3-korg From fcc6220ddc7e54d8442287273d0cb8c415ada022 Mon Sep 17 00:00:00 2001 From: Kate Hsuan Date: Fri, 22 Mar 2024 11:37:31 +0800 Subject: platform/x86: x86-android-tablets: Add swnode for Xiaomi pad2 indicator LED There is a KTD2026 LED controller to manage the indicator LED for Xiaomi pad2. The ACPI for it is not properly made so the kernel can't get a correct description of it. This work adds a description for this RGB LED controller and also sets a trigger to indicate the charging event (bq27520-0-charging). When it is charging, the indicator LED will be turned on. Signed-off-by: Kate Hsuan Link: https://lore.kernel.org/r/20240322033736.9344-2-hpa@redhat.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/x86-android-tablets/other.c | 82 ++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/drivers/platform/x86/x86-android-tablets/other.c b/drivers/platform/x86/x86-android-tablets/other.c index 278402dcb808c5..ce487b3c972cb0 100644 --- a/drivers/platform/x86/x86-android-tablets/other.c +++ b/drivers/platform/x86/x86-android-tablets/other.c @@ -13,6 +13,8 @@ #include #include +#include + #include "shared-psy-info.h" #include "x86-android-tablets.h" @@ -593,6 +595,83 @@ const struct x86_dev_info whitelabel_tm800a550l_info __initconst = { .gpiod_lookup_tables = whitelabel_tm800a550l_gpios, }; +/* + * 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("function", "indicator"), + PROPERTY_ENTRY_STRING("linux,default-trigger", "bq27520-0-charging"), + { } +}; + +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_green_led_node, + &ktd2026_blue_led_node, + &ktd2026_red_led_node, + NULL +}; + +static int __init xiaomi_mipad2_init(void) +{ + 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 @@ -616,6 +695,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 +704,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, }; -- cgit 1.2.3-korg From f1cacd216dea03e14999c782cd8429b03c90e0f8 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 31 Jan 2024 12:16:40 +0100 Subject: platform/x86: Add ACPI quickstart button (PNP0C32) driver This drivers supports the ACPI quickstart button device, which is used to send manufacturer-specific events to userspace. Since the meaning of those events is not standardized, userspace has to use for example hwdb to decode them. The driver itself is based on an earlier proposal, but contains some improvements and uses the device wakeup API instead of a custom sysfs file. Signed-off-by: Armin Wolf Tested-by: Hans de Goede Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20240131111641.4418-2-W_Armin@gmx.de Signed-off-by: Hans de Goede --- MAINTAINERS | 6 + drivers/platform/x86/Kconfig | 13 +++ drivers/platform/x86/Makefile | 3 + drivers/platform/x86/quickstart.c | 225 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 247 insertions(+) create mode 100644 drivers/platform/x86/quickstart.c diff --git a/MAINTAINERS b/MAINTAINERS index b5514c83c7dce2..26b1d8ade546eb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -360,6 +360,12 @@ B: https://bugzilla.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm F: drivers/acpi/pmic/ +ACPI QUICKSTART DRIVER +M: Armin Wolf +L: platform-driver-x86@vger.kernel.org +S: Maintained +F: drivers/platform/x86/quickstart.c + ACPI SERIAL MULTI INSTANTIATE DRIVER M: Hans de Goede L: platform-driver-x86@vger.kernel.org diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index cf9aa220529c7e..cfceaaa827a761 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -642,6 +642,19 @@ 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_SPARSE_KEYMAP + 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 MSI_EC tristate "MSI EC Extras" depends on ACPI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 217e94d7c87745..8076bf3a7e83f7 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -71,6 +71,9 @@ obj-$(CONFIG_LENOVO_WMI_CAMERA) += lenovo-wmi-camera.o # Intel obj-y += intel/ +# Microsoft +obj-$(CONFIG_ACPI_QUICKSTART) += quickstart.o + # MSI obj-$(CONFIG_MSI_EC) += msi-ec.o obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o diff --git a/drivers/platform/x86/quickstart.c b/drivers/platform/x86/quickstart.c new file mode 100644 index 00000000000000..ba3a7a25dda70f --- /dev/null +++ b/drivers/platform/x86/quickstart.c @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * quickstart.c - ACPI Direct App Launch driver + * + * Copyright (C) 2024 Armin Wolf + * Copyright (C) 2022 Arvid Norlander + * Copyright (C) 2007-2010 Angelo Arrifano + * + * Information gathered from disassembled dsdt and from here: + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 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: + sparse_keymap_report_event(data->input_device, 0x1, 1, true); + 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 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); + + /* 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", 0 }, + { } +}; +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 "); +MODULE_AUTHOR("Arvid Norlander "); +MODULE_AUTHOR("Angelo Arrifano"); +MODULE_DESCRIPTION("ACPI Direct App Launch driver"); +MODULE_LICENSE("GPL"); -- cgit 1.2.3-korg From 23f1d8b47d125dcd8c1ec62a91164e6bc5d691d0 Mon Sep 17 00:00:00 2001 From: Arvid Norlander Date: Wed, 31 Jan 2024 12:16:41 +0100 Subject: platform/x86: toshiba_acpi: Add quirk for buttons on Z830 The Z830 has some buttons that will only work properly as "quickstart" buttons. To enable them in that mode, a value between 1 and 7 must be used for HCI_HOTKEY_EVENT. Windows uses 0x5 on this laptop so use that for maximum predictability and compatibility. As there is not yet a known way of auto detection, this patch uses a DMI quirk table. A module parameter is exposed to allow setting this on other models for testing. Signed-off-by: Arvid Norlander Tested-by: Hans de Goede Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20240131111641.4418-3-W_Armin@gmx.de Signed-off-by: Hans de Goede --- drivers/platform/x86/toshiba_acpi.c | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 291f14ef67024a..2a5a651235fe6d 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 @@ -2730,10 +2736,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 +3268,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[] = { { /* Toshiba Portégé R700 */ /* https://bugzilla.kernel.org/show_bug.cgi?id=21012 */ @@ -3265,6 +3283,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 +3293,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,6 +3301,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, "Z830"), }, + .driver_data = (void *)(QUIRK_TURN_ON_PANEL_ON_RESUME | QUIRK_HCI_HOTKEY_QUICKSTART), }, }; @@ -3289,6 +3310,8 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) struct toshiba_acpi_dev *dev; const char *hci_method; u32 dummy; + const struct dmi_system_id *dmi_id; + long quirks = 0; int ret = 0; if (toshiba_acpi) @@ -3441,8 +3464,15 @@ iio_error: } #endif + 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 = dmi_check_system(turn_on_panel_on_resume_dmi_ids); + 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); toshiba_wwan_available(dev); if (dev->wwan_supported) -- cgit 1.2.3-korg From 33b0e895aa318c58f54fc2f0b6034c531610e8f0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 27 Mar 2024 09:08:33 +0100 Subject: platform/x86/amd/hsmp: switch to use device_add_groups() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit devm_device_add_groups() is being removed from the kernel, so move the hsmp driver to use device_add_groups() instead. The logic is identical, when the device is removed the driver core will properly clean up and remove the groups, and the memory used by the attribute groups will be freed because it was created with dev_* calls, so this is functionally identical overall. Cc: Naveen Krishna Chatradhi Cc: Carlos Bilbao Cc: Hans de Goede Cc: "Ilpo Järvinen" Cc: platform-driver-x86@vger.kernel.org Signed-off-by: Greg Kroah-Hartman Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/2024032732-thigh-smite-f5dd@gregkh Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/hsmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/amd/hsmp.c b/drivers/platform/x86/amd/hsmp.c index 1927be901108e3..d84ea66eecc6b6 100644 --- a/drivers/platform/x86/amd/hsmp.c +++ b/drivers/platform/x86/amd/hsmp.c @@ -693,7 +693,7 @@ static int hsmp_create_non_acpi_sysfs_if(struct device *dev) hsmp_create_attr_list(attr_grp, dev, i); } - return devm_device_add_groups(dev, hsmp_attr_grps); + return device_add_groups(dev, hsmp_attr_grps); } static int hsmp_create_acpi_sysfs_if(struct device *dev) -- cgit 1.2.3-korg From 609bf4bd8e597f3eca631a6382f59331972905e0 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 27 Mar 2024 09:14:34 +0100 Subject: platform/x86: MAINTAINERS: drop Daniel Oliveira Nascimento Emails to Daniel Oliveira Nascimento bounce: "550 5.1.1 The email account that you tried to reach does not exist." Signed-off-by: Krzysztof Kozlowski Acked-by: Thadeu Lima de Souza Cascardo Link: https://lore.kernel.org/r/20240327081434.306106-1-krzysztof.kozlowski@linaro.org Signed-off-by: Hans de Goede --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 26b1d8ade546eb..3fb0fa67576d7f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5229,7 +5229,6 @@ F: lib/closure.c CMPC ACPI DRIVER M: Thadeu Lima de Souza Cascardo -M: Daniel Oliveira Nascimento L: platform-driver-x86@vger.kernel.org S: Supported F: drivers/platform/x86/classmate-laptop.c -- cgit 1.2.3-korg From 1d86d946d3413436edcce7fd22d803af9c5b39e8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 27 Mar 2024 23:52:08 +0200 Subject: platform/x86: quickstart: Miscellaneous improvements There is a mix of a few improvements to the driver. I have done this instead of review, so it can quickly be folded into the original code (partially or fully). Signed-off-by: Andy Shevchenko Reviewed-by: Armin Wolf Link: https://lore.kernel.org/r/20240327215208.649020-1-andy.shevchenko@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/quickstart.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/quickstart.c b/drivers/platform/x86/quickstart.c index ba3a7a25dda70f..f686942662ccc4 100644 --- a/drivers/platform/x86/quickstart.c +++ b/drivers/platform/x86/quickstart.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * quickstart.c - ACPI Direct App Launch driver + * ACPI Direct App Launch driver * * Copyright (C) 2024 Armin Wolf * Copyright (C) 2022 Arvid Norlander @@ -10,15 +10,18 @@ * */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include +#include +#include #include #include #include -#include +#include #include #include +#include +#include +#include #include #include @@ -165,7 +168,8 @@ static int quickstart_probe(struct platform_device *pdev) data->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, data); - /* We have to initialize the device wakeup before evaluating GHID because + /* + * 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. */ @@ -202,7 +206,7 @@ static int quickstart_probe(struct platform_device *pdev) } static const struct acpi_device_id quickstart_device_ids[] = { - { "PNP0C32", 0 }, + { "PNP0C32" }, { } }; MODULE_DEVICE_TABLE(acpi, quickstart_device_ids); -- cgit 1.2.3-korg From 83cfe6d8b6b986d430941d22797ca636a6789ba9 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 4 Apr 2024 14:34:30 +0200 Subject: platform/x86: quickstart: fix Kconfig selects The new driver Kconfig entry has a typo that causes a link failure when CONFIG_INPUT_SPARSEKMAP is disabled: x86_64-linux-ld: drivers/platform/x86/quickstart.o: in function `quickstart_notify': quickstart.c:(.text+0x96): undefined reference to `sparse_keymap_report_event' x86_64-linux-ld: drivers/platform/x86/quickstart.o: in function `quickstart_probe': quickstart.c:(.text+0x1da): undefined reference to `sparse_keymap_setup' Select this symbol instead of the incorrect INPUT_SPARSE_KEYMAP. Fixes: afd66f2a739e ("platform/x86: Add ACPI quickstart button (PNP0C32) driver") Signed-off-by: Arnd Bergmann Reviewed-by: Armin Wolf Reviewed-by: Kuppuswamy Sathyanarayanan Link: https://lore.kernel.org/r/20240404123435.2684819-1-arnd@kernel.org Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index cfceaaa827a761..168b57df0a6a61 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -646,7 +646,7 @@ config ACPI_QUICKSTART tristate "ACPI Quickstart button driver" depends on ACPI depends on INPUT - select INPUT_SPARSE_KEYMAP + 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 -- cgit 1.2.3-korg From 10eba55febd4784cf54bbb411636f3929723bfc0 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 27 Mar 2024 22:45:24 +0100 Subject: platform/x86: quickstart: Fix race condition when reporting input event Since commit e2ffcda16290 ("ACPI: OSL: Allow Notify () handlers to run on all CPUs"), the ACPI core allows multiple notify calls to be active at the same time. This means that two instances of quickstart_notify() running at the same time can mess which each others input sequences. Fix this by protecting the input sequence with a mutex. Compile-tested only. Fixes: afd66f2a739e ("platform/x86: Add ACPI quickstart button (PNP0C32) driver") Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240327214524.123935-1-W_Armin@gmx.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/quickstart.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/platform/x86/quickstart.c b/drivers/platform/x86/quickstart.c index f686942662ccc4..df496c7e717142 100644 --- a/drivers/platform/x86/quickstart.c +++ b/drivers/platform/x86/quickstart.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,7 @@ 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]; @@ -76,7 +78,10 @@ static void quickstart_notify(acpi_handle handle, u32 event, void *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: @@ -150,6 +155,13 @@ static void quickstart_notify_remove(void *context) 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; @@ -168,6 +180,11 @@ static int quickstart_probe(struct platform_device *pdev) 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 -- cgit 1.2.3-korg From 7ad58be75fcd4ef7d9b637cc3b6c48b26082eef3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 27 Mar 2024 20:57:12 +0100 Subject: platform/x86: intel-vbtn: Log event code on unexpected button events When logging the warning about receiving a button event on a device without buttons log the event code which triggered the warning. Signed-off-by: Hans de Goede Reviewed-by: Kuppuswamy Sathyanarayanan Link: https://lore.kernel.org/r/20240327195712.43851-1-hdegoede@redhat.com --- drivers/platform/x86/intel/vbtn.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/vbtn.c b/drivers/platform/x86/intel/vbtn.c index 084c355c86f5fa..de4decbb13baa3 100644 --- a/drivers/platform/x86/intel/vbtn.c +++ b/drivers/platform/x86/intel/vbtn.c @@ -158,7 +158,8 @@ static void notify_handler(acpi_handle handle, u32 event, void *context) 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; -- cgit 1.2.3-korg From 428a03523d70b951e04822a49952fb32f2b508a4 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 30 Mar 2024 12:24:02 +0100 Subject: platform/surface: aggregator_registry: Add support for thermal sensors on the Surface Pro 9 The Surface Pro 9 has thermal sensors connected via the Surface Aggregator Module. Add a device node to support those. Signed-off-by: Maximilian Luz Link: https://lore.kernel.org/r/20240330112409.3402943-4-luzmaximilian@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/surface/surface_aggregator_registry.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c index 79e52eddabd0cf..1c4d74db08c954 100644 --- a/drivers/platform/surface/surface_aggregator_registry.c +++ b/drivers/platform/surface/surface_aggregator_registry.c @@ -88,6 +88,12 @@ static const struct software_node ssam_node_tmp_perf_profile_with_fan = { .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", @@ -325,6 +331,7 @@ static const struct software_node *ssam_node_group_sp9[] = { &ssam_node_bat_ac, &ssam_node_bat_main, &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, -- cgit 1.2.3-korg From c347fd4fe84ab831db8b4361977437b587526165 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Tue, 2 Apr 2024 16:30:56 +0200 Subject: platform/x86: wmi: Mark simple WMI drivers as legacy-free The inspur_platform_profile driver and the xiaomi-wmi driver both meet the requirements for modern WMI drivers, as they both do not use the legacy GUID-based interface and can be safely instantiated multiple times. Mark them both as legacy-free using the no_singleton flag. Compile-tested only. Reviewed-by: Kuppuswamy Sathyanarayanan Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240402143059.8456-1-W_Armin@gmx.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/inspur_platform_profile.c | 1 + drivers/platform/x86/xiaomi-wmi.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/platform/x86/inspur_platform_profile.c b/drivers/platform/x86/inspur_platform_profile.c index 743705bddda368..8440defa67886a 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/xiaomi-wmi.c b/drivers/platform/x86/xiaomi-wmi.c index 54a2546bb93bf8..1f5f108d87c000 100644 --- a/drivers/platform/x86/xiaomi-wmi.c +++ b/drivers/platform/x86/xiaomi-wmi.c @@ -83,6 +83,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); -- cgit 1.2.3-korg From 290680c2da8061e410bcaec4b21584ed951479af Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Tue, 2 Apr 2024 16:30:57 +0200 Subject: platform/x86: xiaomi-wmi: Fix race condition when reporting key events Multiple WMI events can be received concurrently, so multiple instances of xiaomi_wmi_notify() can be active at the same time. Since the input device is shared between those handlers, the key input sequence can be disturbed. Fix this by protecting the key input sequence with a mutex. Compile-tested only. Fixes: edb73f4f0247 ("platform/x86: wmi: add Xiaomi WMI key driver") Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240402143059.8456-2-W_Armin@gmx.de Reviewed-by: Kuppuswamy Sathyanarayanan Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/xiaomi-wmi.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/platform/x86/xiaomi-wmi.c b/drivers/platform/x86/xiaomi-wmi.c index 1f5f108d87c000..7efbdc111803c2 100644 --- a/drivers/platform/x86/xiaomi-wmi.c +++ b/drivers/platform/x86/xiaomi-wmi.c @@ -2,8 +2,10 @@ /* WMI driver for Xiaomi Laptops */ #include +#include #include #include +#include #include #include @@ -20,12 +22,21 @@ struct xiaomi_wmi { struct input_dev *input_dev; + struct mutex key_lock; /* Protects the key event sequence */ unsigned int key_code; }; +static void xiaomi_mutex_destroy(void *data) +{ + struct mutex *lock = data; + + mutex_destroy(lock); +} + static int xiaomi_wmi_probe(struct wmi_device *wdev, const void *context) { struct xiaomi_wmi *data; + int ret; if (wdev == NULL || context == NULL) return -EINVAL; @@ -35,6 +46,11 @@ static int xiaomi_wmi_probe(struct wmi_device *wdev, const void *context) return -ENOMEM; dev_set_drvdata(&wdev->dev, data); + mutex_init(&data->key_lock); + ret = devm_add_action_or_reset(&wdev->dev, xiaomi_mutex_destroy, &data->key_lock); + if (ret < 0) + return ret; + data->input_dev = devm_input_allocate_device(&wdev->dev); if (data->input_dev == NULL) return -ENOMEM; @@ -59,10 +75,12 @@ static void xiaomi_wmi_notify(struct wmi_device *wdev, union acpi_object *dummy) if (data == NULL) return; + mutex_lock(&data->key_lock); input_report_key(data->input_dev, data->key_code, 1); input_sync(data->input_dev); input_report_key(data->input_dev, data->key_code, 0); input_sync(data->input_dev); + mutex_unlock(&data->key_lock); } static const struct wmi_device_id xiaomi_wmi_id_table[] = { -- cgit 1.2.3-korg From c5e160ff34b4493452cba80c684f5e7c8fc1c9e2 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Tue, 2 Apr 2024 16:30:58 +0200 Subject: platform/x86: xiaomi-wmi: Drop unnecessary NULL checks The WMI driver core already makes sure that: - a valid WMI device is passed to each callback - the notify() callback runs after the probe() callback succeeds Remove the unnecessary NULL checks. Compile-tested only. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240402143059.8456-3-W_Armin@gmx.de Reviewed-by: Kuppuswamy Sathyanarayanan Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/xiaomi-wmi.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/platform/x86/xiaomi-wmi.c b/drivers/platform/x86/xiaomi-wmi.c index 7efbdc111803c2..cbed29ca502a60 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); -- cgit 1.2.3-korg From a582a43e0d2e0afb695cd22ce46554f4a3d8b7bc Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Tue, 2 Apr 2024 16:30:59 +0200 Subject: platform/x86: wmi: Add driver development guide Since 2010, an LWN article covering WMI drivers exists: https://lwn.net/Articles/391230/ Since the introduction of the modern bus-based interface and other userspace tooling (bmfdec, lswmi, ...), this article is outdated and causes people to still submit new WMI drivers using the deprecated GUID-based interface. Fix this by adding a short guide on how to develop WMI drivers using the modern bus-based interface. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20240402143059.8456-4-W_Armin@gmx.de Reviewed-by: Kuppuswamy Sathyanarayanan Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- Documentation/wmi/driver-development-guide.rst | 178 +++++++++++++++++++++++++ Documentation/wmi/index.rst | 1 + 2 files changed, 179 insertions(+) create mode 100644 Documentation/wmi/driver-development-guide.rst diff --git a/Documentation/wmi/driver-development-guide.rst b/Documentation/wmi/driver-development-guide.rst new file mode 100644 index 00000000000000..429137b2f63236 --- /dev/null +++ b/Documentation/wmi/driver-development-guide.rst @@ -0,0 +1,178 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +============================ +WMI driver development guide +============================ + +The WMI subsystem provides a rich driver API for implementing WMI drivers, +documented at Documentation/driver-api/wmi.rst. This document will serve +as an introductory guide for WMI driver writers using this API. It is supposed +to be a successor to the original LWN article [1]_ which deals with WMI drivers +using the deprecated GUID-based WMI interface. + +Obtaining WMI device information +-------------------------------- + +Before developing an WMI driver, information about the WMI device in question +must be obtained. The `lswmi `_ utility can be +used to extract detailed WMI device information using the following command: + +:: + + lswmi -V + +The resulting output will contain information about all WMI devices available on +a given machine, plus some extra information. + +In order to find out more about the interface used to communicate with a WMI device, +the `bmfdec `_ utilities can be used to decode +the Binary MOF (Managed Object Format) information used to describe WMI devices. +The ``wmi-bmof`` driver exposes this information to userspace, see +Documentation/wmi/devices/wmi-bmof.rst. + +In order to retrieve the decoded Binary MOF information, use the following command (requires root): + +:: + + ./bmf2mof /sys/bus/wmi/devices/05901221-D566-11D1-B2F0-00A0C9062910[-X]/bmof + +Sometimes, looking at the disassembled ACPI tables used to describe the WMI device +helps in understanding how the WMI device is supposed to work. The path of the ACPI +method associated with a given WMI device can be retrieved using the ``lswmi`` utility +as mentioned above. + +Basic WMI driver structure +-------------------------- + +The basic WMI driver is build around the struct wmi_driver, which is then bound +to matching WMI devices using a struct wmi_device_id table: + +:: + + static const struct wmi_device_id foo_id_table[] = { + { "936DA01F-9ABD-4D9D-80C7-02AF85C822A8", NULL }, + { } + }; + MODULE_DEVICE_TABLE(wmi, foo_id_table); + + static struct wmi_driver foo_driver = { + .driver = { + .name = "foo", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, /* recommended */ + .pm = pm_sleep_ptr(&foo_dev_pm_ops), /* optional */ + }, + .id_table = foo_id_table, + .probe = foo_probe, + .remove = foo_remove, /* optional, devres is preferred */ + .notify = foo_notify, /* optional, for event handling */ + .no_notify_data = true, /* optional, enables events containing no additional data */ + .no_singleton = true, /* required for new WMI drivers */ + }; + module_wmi_driver(foo_driver); + +The probe() callback is called when the WMI driver is bound to a matching WMI device. Allocating +driver-specific data structures and initialising interfaces to other kernel subsystems should +normally be done in this function. + +The remove() callback is then called when the WMI driver is unbound from a WMI device. In order +to unregister interfaces to other kernel subsystems and release resources, devres should be used. +This simplifies error handling during probe and often allows to omit this callback entirely, see +Documentation/driver-api/driver-model/devres.rst for details. + +Please note that new WMI drivers are required to be able to be instantiated multiple times, +and are forbidden from using any deprecated GUID-based WMI functions. This means that the +WMI driver should be prepared for the scenario that multiple matching WMI devices are present +on a given machine. + +Because of this, WMI drivers should use the state container design pattern as described in +Documentation/driver-api/driver-model/design-patterns.rst. + +WMI method drivers +------------------ + +WMI drivers can call WMI device methods using wmidev_evaluate_method(), the +structure of the ACPI buffer passed to this function is device-specific and usually +needs some tinkering to get right. Looking at the ACPI tables containing the WMI +device usually helps here. The method id and instance number passed to this function +are also device-specific, looking at the decoded Binary MOF is usually enough to +find the right values. + +The maximum instance number can be retrieved during runtime using wmidev_instance_count(). + +Take a look at drivers/platform/x86/inspur_platform_profile.c for an example WMI method driver. + +WMI data block drivers +---------------------- + +WMI drivers can query WMI device data blocks using wmidev_block_query(), the +structure of the returned ACPI object is again device-specific. Some WMI devices +also allow for setting data blocks using wmidev_block_set(). + +The maximum instance number can also be retrieved using wmidev_instance_count(). + +Take a look at drivers/platform/x86/intel/wmi/sbl-fw-update.c for an example +WMI data block driver. + +WMI event drivers +----------------- + +WMI drivers can receive WMI events via the notify() callback inside the struct wmi_driver. +The WMI subsystem will then take care of setting up the WMI event accordingly. Please note that +the structure of the ACPI object passed to this callback is device-specific, and freeing the +ACPI object is being done by the WMI subsystem, not the driver. + +The WMI driver core will take care that the notify() callback will only be called after +the probe() callback has been called, and that no events are being received by the driver +right before and after calling its remove() callback. + +However WMI driver developers should be aware that multiple WMI events can be received concurrently, +so any locking (if necessary) needs to be provided by the WMI driver itself. + +In order to be able to receive WMI events containing no additional event data, +the ``no_notify_data`` flag inside struct wmi_driver should be set to ``true``. + +Take a look at drivers/platform/x86/xiaomi-wmi.c for an example WMI event driver. + +Handling multiple WMI devices at once +------------------------------------- + +There are many cases of firmware vendors using multiple WMI devices to control different aspects +of a single physical device. This can make developing WMI drivers complicated, as those drivers +might need to communicate with each other to present a unified interface to userspace. + +On such case involves a WMI event device which needs to talk to a WMI data block device or WMI +method device upon receiving an WMI event. In such a case, two WMI drivers should be developed, +one for the WMI event device and one for the other WMI device. + +The WMI event device driver has only one purpose: to receive WMI events, validate any additional +event data and invoke a notifier chain. The other WMI driver adds itself to this notifier chain +during probing and thus gets notified every time a WMI event is received. This WMI driver might +then process the event further for example by using an input device. + +For other WMI device constellations, similar mechanisms can be used. + +Things to avoid +--------------- + +When developing WMI drivers, there are a couple of things which should be avoided: + +- usage of the deprecated GUID-based WMI interface which uses GUIDs instead of WMI device structs +- bypassing of the WMI subsystem when talking to WMI devices +- WMI drivers which cannot be instantiated multiple times. + +Many older WMI drivers violate one or more points from this list. The reason for +this is that the WMI subsystem evolved significantly over the last two decades, +so there is a lot of legacy cruft inside older WMI drivers. + +New WMI drivers are also required to conform to the linux kernel coding style as specified in +Documentation/process/coding-style.rst. The checkpatch utility can catch many common coding style +violations, you can invoke it with the following command: + +:: + + ./scripts/checkpatch.pl --strict + +References +========== + +.. [1] https://lwn.net/Articles/391230/ diff --git a/Documentation/wmi/index.rst b/Documentation/wmi/index.rst index 537cff188e14ce..fec4b6ae97b3b5 100644 --- a/Documentation/wmi/index.rst +++ b/Documentation/wmi/index.rst @@ -8,6 +8,7 @@ WMI Subsystem :maxdepth: 1 acpi-interface + driver-development-guide devices/index .. only:: subproject and html -- cgit 1.2.3-korg From f81d13df1aa8b02fa8c6ac4b396dcda92fdfdac0 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Thu, 4 Apr 2024 13:16:44 +1300 Subject: platform/x86: asus-wmi: add support for 2024 ROG Mini-LED MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support the 2024 mini-led backlight and adjust the related functions to select the relevant dev-id. Also add `available_mini_led_mode` to the platform sysfs since the available mini-led levels can be different. Reviewed-by: Ilpo Järvinen signed-off-by: Luke D. Jones Link: https://lore.kernel.org/r/20240404001652.86207-2-luke@ljones.dev Signed-off-by: Hans de Goede --- Documentation/ABI/testing/sysfs-platform-asus-wmi | 8 ++ drivers/platform/x86/asus-wmi.c | 96 ++++++++++++++++++++--- include/linux/platform_data/x86/asus-wmi.h | 1 + 3 files changed, 95 insertions(+), 10 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi index 8a7e25bde08531..ef1ac1a20a71d8 100644 --- a/Documentation/ABI/testing/sysfs-platform-asus-wmi +++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi @@ -126,6 +126,14 @@ Description: Change the mini-LED mode: * 0 - Single-zone, * 1 - Multi-zone + * 2 - Multi-zone strong (available on newer generation mini-led) + +What: /sys/devices/platform//available_mini_led_mode +Date: Apr 2024 +KernelVersion: 6.10 +Contact: "Luke Jones" +Description: + List the available mini-led modes. What: /sys/devices/platform//ppt_pl1_spl Date: Jun 2023 diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index df4c103459daae..e8fbd7f9d343c1 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -126,6 +126,17 @@ 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 */ @@ -288,7 +299,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; @@ -2108,13 +2119,33 @@ static ssize_t mini_led_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct asus_wmi *asus = dev_get_drvdata(dev); - int result; + u32 value; + int err; - result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_MINI_LED_MODE); - if (result < 0) - return result; + err = asus_wmi_get_devstate(asus, asus->mini_led_dev_id, &value); + if (err < 0) + return err; + value = value & ASUS_MINI_LED_MODE_MASK; - return sysfs_emit(buf, "%d\n", result); + /* + * 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, @@ -2130,11 +2161,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; @@ -2151,6 +2203,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) @@ -4139,6 +4208,7 @@ static struct attribute *platform_attributes[] = { &dev_attr_nv_temp_target.attr, &dev_attr_panel_od.attr, &dev_attr_mini_led_mode.attr, + &dev_attr_available_mini_led_mode.attr, NULL }; @@ -4191,7 +4261,9 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, else if (attr == &dev_attr_panel_od.attr) ok = asus->panel_overdrive_available; 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); @@ -4444,10 +4516,14 @@ static int asus_wmi_add(struct platform_device *pdev) 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; + err = fan_boost_mode_check_present(asus); if (err) goto fail_fan_boost_mode; diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index ab1c7deff118f3..9cadce10ad9ac5 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -71,6 +71,7 @@ #define ASUS_WMI_DEVID_LID_FLIP 0x00060062 #define ASUS_WMI_DEVID_LID_FLIP_ROG 0x00060077 #define ASUS_WMI_DEVID_MINI_LED_MODE 0x0005001E +#define ASUS_WMI_DEVID_MINI_LED_MODE2 0x0005002E /* Storage */ #define ASUS_WMI_DEVID_CARDREADER 0x00080013 -- cgit 1.2.3-korg From eb3bac90549a226df204a57eac45bbe5d6b4a0cf Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Thu, 4 Apr 2024 13:16:45 +1300 Subject: platform/x86: asus-wmi: add support for Vivobook GPU MUX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the Vivobook dgpu MUX available on the ASUS Viviobook and some of the other ranges (Zen). This MUX functions exactly the same as the existing ROG MUX support so the existing functionality now detects which MUX is available and uses that for control. Reviewed-by: Ilpo Järvinen Signed-off-by: Luke D. Jones Link: https://lore.kernel.org/r/20240404001652.86207-3-luke@ljones.dev Signed-off-by: Hans de Goede --- drivers/platform/x86/asus-wmi.c | 22 +++++++++++++--------- include/linux/platform_data/x86/asus-wmi.h | 1 + 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index e8fbd7f9d343c1..51e4df08104666 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -270,7 +270,7 @@ struct asus_wmi { 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; @@ -693,8 +693,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; @@ -759,8 +759,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); @@ -813,7 +813,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; @@ -859,7 +859,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; @@ -4239,7 +4239,7 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, 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) @@ -4505,7 +4505,6 @@ static int asus_wmi_add(struct platform_device *pdev) 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); @@ -4524,6 +4523,11 @@ static int asus_wmi_add(struct platform_device *pdev) 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; + err = fan_boost_mode_check_present(asus); if (err) goto fail_fan_boost_mode; diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 9cadce10ad9ac5..b48b024dd8445c 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -128,6 +128,7 @@ /* gpu mux switch, 0 = dGPU, 1 = Optimus */ #define ASUS_WMI_DEVID_GPU_MUX 0x00090016 +#define ASUS_WMI_DEVID_GPU_MUX_VIVO 0x00090026 /* TUF laptop RGB modes/colours */ #define ASUS_WMI_DEVID_TUF_RGB_MODE 0x00100056 -- cgit 1.2.3-korg From ae834a549ec1d4cf372ffba1af1c14148807af55 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Thu, 4 Apr 2024 13:16:46 +1300 Subject: platform/x86: asus-wmi: add support variant of TUF RGB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds support for a second TUF RGB wmi call that some versions of the TUF laptop come with. Also adjusts existing support to select whichever is available. Reviewed-by: Ilpo Järvinen Signed-off-by: Luke D. Jones Link: https://lore.kernel.org/r/20240404001652.86207-4-luke@ljones.dev Signed-off-by: Hans de Goede --- drivers/platform/x86/asus-wmi.c | 13 +++++++++---- include/linux/platform_data/x86/asus-wmi.h | 1 + 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 51e4df08104666..113a80ae0efef2 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -281,7 +281,7 @@ struct asus_wmi { bool nv_dyn_boost_available; bool nv_temp_tgt_available; - bool kbd_rgb_mode_available; + u32 kbd_rgb_dev; bool kbd_rgb_state_available; bool throttle_thermal_policy_available; @@ -881,6 +881,7 @@ static ssize_t kbd_rgb_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + struct asus_wmi *asus = dev_get_drvdata(dev); u32 cmd, mode, r, g, b, speed; int err; @@ -917,7 +918,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; @@ -1560,7 +1561,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; @@ -4505,7 +4506,6 @@ static int asus_wmi_add(struct platform_device *pdev) 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->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); @@ -4528,6 +4528,11 @@ static int asus_wmi_add(struct platform_device *pdev) 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; diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index b48b024dd8445c..3e9a01467c6762 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -132,6 +132,7 @@ /* TUF laptop RGB modes/colours */ #define ASUS_WMI_DEVID_TUF_RGB_MODE 0x00100056 +#define ASUS_WMI_DEVID_TUF_RGB_MODE2 0x0010005A /* TUF laptop RGB power/state */ #define ASUS_WMI_DEVID_TUF_RGB_STATE 0x00100057 -- cgit 1.2.3-korg From e0ae0ecce4869f841ea7cb20fca1b2c865c13339 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Thu, 4 Apr 2024 13:16:47 +1300 Subject: platform/x86: asus-wmi: support toggling POST sound MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for toggling the BIOS POST sound on some ASUS laptops. Reviewed-by: Ilpo Järvinen Signed-off-by: Luke D. Jones Link: https://lore.kernel.org/r/20240404001652.86207-5-luke@ljones.dev Signed-off-by: Hans de Goede --- Documentation/ABI/testing/sysfs-platform-asus-wmi | 9 ++++ drivers/platform/x86/asus-wmi.c | 51 +++++++++++++++++++++++ include/linux/platform_data/x86/asus-wmi.h | 3 ++ 3 files changed, 63 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi index ef1ac1a20a71d8..72933527d2e49a 100644 --- a/Documentation/ABI/testing/sysfs-platform-asus-wmi +++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi @@ -194,3 +194,12 @@ Contact: "Luke Jones" Description: Set the target temperature limit of the Nvidia dGPU: * min=75, max=87 + +What: /sys/devices/platform//boot_sound +Date: Apr 2024 +KernelVersion: 6.10 +Contact: "Luke Jones" +Description: + Set if the BIOS POST sound is played on boot. + * 0 - False, + * 1 - True diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 113a80ae0efef2..1dbe473294f783 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -2115,6 +2115,54 @@ static ssize_t panel_od_store(struct device *dev, } static DEVICE_ATTR_RW(panel_od); +/* 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_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) @@ -4207,6 +4255,7 @@ 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_boot_sound.attr, &dev_attr_panel_od.attr, &dev_attr_mini_led_mode.attr, &dev_attr_available_mini_led_mode.attr, @@ -4259,6 +4308,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, ok = asus->nv_dyn_boost_available; else if (attr == &dev_attr_nv_temp_target.attr) ok = asus->nv_temp_tgt_available; + 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; else if (attr == &dev_attr_mini_led_mode.attr) diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 3e9a01467c6762..3eb5cd6773ad41 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -137,6 +137,9 @@ /* TUF laptop RGB power/state */ #define ASUS_WMI_DEVID_TUF_RGB_STATE 0x00100057 +/* Bootup sound control */ +#define ASUS_WMI_DEVID_BOOT_SOUND 0x00130022 + /* DSTS masks */ #define ASUS_WMI_DSTS_STATUS_BIT 0x00000001 #define ASUS_WMI_DSTS_UNKNOWN_BIT 0x00000002 -- cgit 1.2.3-korg From 5fc378183d94186a768d6727fe5610d34f0dc4f6 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Thu, 4 Apr 2024 13:16:48 +1300 Subject: platform/x86: asus-wmi: store a min default for ppt options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Laptops with any of the ppt or nv tunables default to the minimum setting on boot so we can safely assume a stored value is correct. This patch adds storing of those values in the local struct, and enables reading of those values back. To prevent creating a series of byte holes in the struct the "_available" bool is removed and `asus_sysfs_is_visible()` uses the `ASUS_WMI_DEVID_` directly. Reviewed-by: Ilpo Järvinen Signed-off-by: Luke D. Jones Link: https://lore.kernel.org/r/20240404001652.86207-6-luke@ljones.dev Signed-off-by: Hans de Goede --- drivers/platform/x86/asus-wmi.c | 127 +++++++++++++++++++++++++++++++--------- 1 file changed, 99 insertions(+), 28 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 1dbe473294f783..b34edc9a8f4673 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -273,13 +273,13 @@ struct asus_wmi { 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; + 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; @@ -1031,11 +1031,21 @@ 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, @@ -1065,11 +1075,20 @@ 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, @@ -1099,11 +1118,21 @@ 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, @@ -1133,11 +1162,21 @@ 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, @@ -1167,11 +1206,21 @@ 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, @@ -1201,11 +1250,21 @@ 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, @@ -1235,11 +1294,21 @@ 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); /* Battery ********************************************************************/ @@ -4295,19 +4364,19 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, 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_boot_sound.attr) devid = ASUS_WMI_DEVID_BOOT_SOUND; else if (attr == &dev_attr_panel_od.attr) @@ -4553,18 +4622,20 @@ static int asus_wmi_add(struct platform_device *pdev) if (err) goto fail_platform; + /* 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->charge_mode_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_CHARGE_MODE); 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->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->ally_mcu_usb_switch = acpi_has_method(NULL, ASUS_USB0_PWR_EC0_CSEE) && dmi_match(DMI_BOARD_NAME, "RC71L"); -- cgit 1.2.3-korg From 892fc4b57dc576326a25b55833ae63d1310f113d Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Thu, 4 Apr 2024 13:16:49 +1300 Subject: platform/x86: asus-wmi: adjust formatting of ppt-() functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shift the call to dev_get_drvdata() up to top of the function block in all of the ppt_() functions as part of a minor cleanup. Reviewed-by: Ilpo Järvinen Signed-off-by: Luke D. Jones Link: https://lore.kernel.org/r/20240404001652.86207-7-luke@ljones.dev Signed-off-by: Hans de Goede --- drivers/platform/x86/asus-wmi.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index b34edc9a8f4673..dac818842be4a4 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1008,11 +1008,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; @@ -1052,11 +1051,10 @@ 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; @@ -1095,11 +1093,10 @@ 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; @@ -1139,11 +1136,10 @@ 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; @@ -1183,11 +1179,10 @@ 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; @@ -1227,11 +1222,10 @@ 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; @@ -1271,11 +1265,10 @@ 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; -- cgit 1.2.3-korg From 7e7a5dee49732ed01a3a17c9a3edf027fb9457fe Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Thu, 4 Apr 2024 13:16:50 +1300 Subject: platform/x86: asus-wmi: ROG Ally increase wait time, allow MCU powersave MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous work to allow the MCU to be resumed correctly after sleep and resume tried to take the shortest possible time. However as work continues in various other parts of the s2idle subsystems it has shown that it wasn't entirely reliable. If the MCU disable/enable call is done correctly the MCU fully removes its USB endpoints, and this shows as a full USB device reconnection on resume. When we tried to short this as much as possible sometimes the MCU doesn't get to complete what it needs to do before going to low-power and this affected the reconnection. Through trial it is found that the minimum time required is approx 1200ms to allow a proper disconnect and disable, and the same amount of time on resume is required to prevent a rapid disconnect/reconnect happening on seemingly random occasions. To be safe the time is now 1500ms for msleep. Reviewed-by: Ilpo Järvinen Signed-off-by: Luke D. Jones Link: https://lore.kernel.org/r/20240404001652.86207-8-luke@ljones.dev Signed-off-by: Hans de Goede --- drivers/platform/x86/asus-wmi.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index dac818842be4a4..f1aff0d449a0c6 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -140,7 +140,7 @@ module_param(fnlock_default, bool, 0444); /* 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 }; @@ -4829,6 +4829,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 @@ -4840,17 +4841,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"); -- cgit 1.2.3-korg From a94e8a56f9e1258d2f4ce613976207d0c02eb181 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Thu, 4 Apr 2024 13:16:51 +1300 Subject: platform/x86: asus-wmi: Add support for MCU powersave MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for an MCU powersave WMI call. This is intended to set the MCU in to a low-power mode when sleeping. This mode can cut sleep power use by around half. Reviewed-by: Ilpo Järvinen Signed-off-by: Luke D. Jones Link: https://lore.kernel.org/r/20240404001652.86207-9-luke@ljones.dev Signed-off-by: Hans de Goede --- Documentation/ABI/testing/sysfs-platform-asus-wmi | 9 ++++ drivers/platform/x86/asus-wmi.c | 50 +++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi index 72933527d2e49a..28144371a0f1a3 100644 --- a/Documentation/ABI/testing/sysfs-platform-asus-wmi +++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi @@ -203,3 +203,12 @@ Description: Set if the BIOS POST sound is played on boot. * 0 - False, * 1 - True + +What: /sys/devices/platform//mcu_powersave +Date: Apr 2024 +KernelVersion: 6.10 +Contact: "Luke Jones" +Description: + Set if the MCU can go in to low-power mode on system sleep + * 0 - False, + * 1 - True diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index f1aff0d449a0c6..e9e5724746773a 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1303,6 +1303,53 @@ static ssize_t nv_temp_target_show(struct device *dev, } 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 ********************************************************************/ /* The battery maximum charging percentage */ @@ -4317,6 +4364,7 @@ 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, @@ -4370,6 +4418,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, devid = ASUS_WMI_DEVID_NV_DYN_BOOST; else if (attr == &dev_attr_nv_temp_target.attr) 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) -- cgit 1.2.3-korg From 88c0ef69dd881d8acceb62c48b66674367c962b7 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Thu, 4 Apr 2024 13:16:52 +1300 Subject: platform/x86: asus-wmi: cleanup main struct to avoid some holes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reorganises some attr-available calls to remove a few unrequired booleans in the main driver struct which combined with some reorganisation prevents a series of large holes seen with pahole. Reviewed-by: Ilpo Järvinen Signed-off-by: Luke D. Jones Link: https://lore.kernel.org/r/20240404001652.86207-10-luke@ljones.dev Signed-off-by: Hans de Goede --- drivers/platform/x86/asus-wmi.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index e9e5724746773a..727dbdec45f458 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -254,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; @@ -266,9 +269,7 @@ 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; u32 gpu_mux_dev; @@ -309,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; @@ -4391,11 +4389,11 @@ 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) @@ -4423,7 +4421,7 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, 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_dev_id != 0; else if (attr == &dev_attr_available_mini_led_mode.attr) @@ -4674,12 +4672,9 @@ static int asus_wmi_add(struct platform_device *pdev) asus->nv_dynamic_boost = 5; asus->nv_temp_target = 75; - asus->charge_mode_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_CHARGE_MODE); 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->kbd_rgb_state_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_TUF_RGB_STATE); - asus->panel_overdrive_available = asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_PANEL_OD); asus->ally_mcu_usb_switch = acpi_has_method(NULL, ASUS_USB0_PWR_EC0_CSEE) && dmi_match(DMI_BOARD_NAME, "RC71L"); -- cgit 1.2.3-korg