lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20240129175714.164326-2-szfabian@bluemarch.art>
Date: Mon, 29 Jan 2024 18:00:23 +0000
From: Szilard Fabian <szfabian@...emarch.art>
To: linux-kernel@...r.kernel.org, platform-driver-x86@...r.kernel.org, jwoithe@...t42.net, hdegoede@...hat.com, ilpo.jarvinen@...ux.intel.com
Cc: Szilard Fabian <szfabian@...emarch.art>
Subject: [RFC PATCH v2] platform/x86/fujitsu-laptop: Add battery charge control support

This patch adds battery charge control support on Fujitsu notebooks
via the S006 method of the FUJ02E3 ACPI device. With this method it's
possible to set charge_control_end_threshold between 50 and 100%.

Tested on Lifebook E5411 and Lifebook U728. Sadly I can't test this
patch on a dual battery one, but I didn't find any clue about
independent battery charge control on dual battery Fujitsu notebooks
either. And by that I mean checking the DSDT table of various Lifebook
notebooks and reverse engineering FUJ02E3.dll.

Signed-off-by: Szilard Fabian <szfabian@...emarch.art>
---
v2:
Forgot to sign-off the original commit. Fixed, sorry for the
inconvenience.
---
 drivers/platform/x86/fujitsu-laptop.c | 95 +++++++++++++++++++++++++++
 1 file changed, 95 insertions(+)

diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c
index 085e044e888e..bf3df74e4d63 100644
--- a/drivers/platform/x86/fujitsu-laptop.c
+++ b/drivers/platform/x86/fujitsu-laptop.c
@@ -49,6 +49,8 @@
 #include <linux/kfifo.h>
 #include <linux/leds.h>
 #include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <acpi/battery.h>
 #include <acpi/video.h>
 
 #define FUJITSU_DRIVER_VERSION		"0.6.0"
@@ -97,6 +99,10 @@
 #define BACKLIGHT_OFF			(BIT(0) | BIT(1))
 #define BACKLIGHT_ON			0
 
+/* FUNC interface - battery control interface */
+#define FUNC_S006_METHOD		0x1006
+#define CHARGE_CONTROL_RW		0x21
+
 /* Scancodes read from the GIRB register */
 #define KEY1_CODE			0x410
 #define KEY2_CODE			0x411
@@ -164,6 +170,91 @@ static int call_fext_func(struct acpi_device *device,
 	return value;
 }
 
+/* Battery charge control code */
+
+static ssize_t charge_control_end_threshold_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	int value, ret;
+
+	ret = kstrtouint(buf, 10, &value);
+	if (ret)
+		return ret;
+
+	if (value < 50 || value > 100)
+		return -EINVAL;
+
+	int cc_end_value, s006_cc_return;
+
+	cc_end_value = value * 0x100 + 0x20;
+	s006_cc_return = call_fext_func(fext, FUNC_S006_METHOD,
+					CHARGE_CONTROL_RW, cc_end_value, 0x0);
+
+	/*
+	 * The S006 0x21 method returns 0x00 in case the provided value
+	 * is invalid.
+	 */
+	if (s006_cc_return == 0x00)
+		return -EINVAL;
+
+	return count;
+}
+
+static ssize_t charge_control_end_threshold_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	int status;
+	status = call_fext_func(fext, FUNC_S006_METHOD,
+				CHARGE_CONTROL_RW, 0x21, 0x0);
+
+	return sprintf(buf, "%d\n", status);
+}
+
+static DEVICE_ATTR_RW(charge_control_end_threshold);
+
+/* ACPI battery hook */
+
+static int fujitsu_battery_add(struct power_supply *battery,
+			       struct acpi_battery_hook *hook)
+{
+	/* Check if there is an existing FUJ02E3 ACPI device. */
+	if (fext == NULL)
+		return -ENODEV;
+
+	/*
+	 * Check if the S006 0x21 method exists by trying to get the current
+	 * battery charge limit.
+	 */
+	int s006_cc_return;
+	s006_cc_return = call_fext_func(fext, FUNC_S006_METHOD,
+					CHARGE_CONTROL_RW, 0x21, 0x0);
+	if (s006_cc_return == UNSUPPORTED_CMD)
+		return -ENODEV;
+
+	if (device_create_file(&battery->dev,
+			       &dev_attr_charge_control_end_threshold))
+		return -ENODEV;
+
+	return 0;
+}
+
+static int fujitsu_battery_remove(struct power_supply *battery,
+				  struct acpi_battery_hook *hook)
+{
+	device_remove_file(&battery->dev,
+			   &dev_attr_charge_control_end_threshold);
+
+	return 0;
+}
+
+static struct acpi_battery_hook battery_hook = {
+	.add_battery = fujitsu_battery_add,
+	.remove_battery = fujitsu_battery_remove,
+	.name = "Fujitsu Battery Extension",
+};
+
 /* Hardware access for LCD brightness control */
 
 static int set_lcd_level(struct acpi_device *device, int level)
@@ -839,6 +930,8 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device)
 	if (ret)
 		goto err_free_fifo;
 
+	battery_hook_register(&battery_hook);
+
 	return 0;
 
 err_free_fifo:
@@ -851,6 +944,8 @@ static void acpi_fujitsu_laptop_remove(struct acpi_device *device)
 {
 	struct fujitsu_laptop *priv = acpi_driver_data(device);
 
+	battery_hook_unregister(&battery_hook);
+
 	fujitsu_laptop_platform_remove(device);
 
 	kfifo_free(&priv->fifo);
-- 
2.43.0



Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ