X86 platform drivers
 help / color / mirror / Atom feed
From: Julian Andres Klode <jak-4HMq4SXA452hPH1hqNUYSQ@public.gmane.org>
To: Henrique de Moraes Holschuh
	<ibm-acpi-N3TV7GIv+o9fyO9Q7EP/yw@public.gmane.org>,
	Matthew Garrett
	<matthew.garrett-05XSO3Yj/JvQT0dZR+AlfA@public.gmane.org>
Cc: Julian Andres Klode <jak-4HMq4SXA452hPH1hqNUYSQ@public.gmane.org>,
	"open list:THINKPAD ACPI EXT..."
	<ibm-acpi-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>,
	open list <linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>,
	"open list:THINKPAD ACPI EXT..."
	<platform-driver-x86-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>
Subject: [PATCH 1/4] thinkpad_acpi: Add support for controlling charge thresholds
Date: Mon, 11 Nov 2013 14:56:30 +0100	[thread overview]
Message-ID: <1384178195-12218-2-git-send-email-jak@jak-linux.org> (raw)
In-Reply-To: <1384178195-12218-1-git-send-email-jak-4HMq4SXA452hPH1hqNUYSQ@public.gmane.org>

Add support for battery charge thresholds in new Sandy Bridge and Ivy Bridge
ThinkPads. Based on the unofficial documentation in tpacpi-bat.

The threshold files support the values '0' for the controller's default,
and 1-99 as percentages. Values outside of that range are rejected. The
behaviour of '0' might be confusing, especially for the stop case where
it basically seems to mean '100'.

Signed-off-by: Julian Andres Klode <jak-4HMq4SXA452hPH1hqNUYSQ@public.gmane.org>
---
 Documentation/laptops/thinkpad-acpi.txt |  17 ++++
 drivers/platform/x86/thinkpad_acpi.c    | 173 ++++++++++++++++++++++++++++++++
 2 files changed, 190 insertions(+)

diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt
index 86c5236..97b150a 100644
--- a/Documentation/laptops/thinkpad-acpi.txt
+++ b/Documentation/laptops/thinkpad-acpi.txt
@@ -46,6 +46,7 @@ detailed description):
 	- Fan control and monitoring: fan speed, fan enable/disable
 	- WAN enable and disable
 	- UWB enable and disable
+	- Charging control
 
 A compatibility table by model and feature is maintained on the web
 site, http://ibm-acpi.sf.net/. I appreciate any success or failure
@@ -1352,6 +1353,22 @@ Sysfs notes:
 	rfkill controller switch "tpacpi_uwb_sw": refer to
 	Documentation/rfkill.txt for details.
 
+Charging control
+----------------
+sysfs attribute groups: BAT0, BAT1, and so on, depending on how many batteries
+are supported by the embedded controller.
+
+This feature controls the battery charging process.
+
+battery sysfs attribute: start_charge_thresh
+
+	A percentage value from 1-99%, controlling when charging should start, or
+	0 for the default value.
+
+battery sysfs attribute: stop_charge_thresh
+
+	A percentage value from 1-99%, controlling when charging should stop, or
+	0 for the default value.
 
 Multiple Commands, Module Parameters
 ------------------------------------
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index 03ca6c1..66e629e 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -323,6 +323,7 @@ static struct {
 	u32 sensors_pdrv_attrs_registered:1;
 	u32 sensors_pdev_attrs_registered:1;
 	u32 hotkey_poll_active:1;
+	u32 battery:1;
 } tp_features;
 
 static struct {
@@ -8350,6 +8351,174 @@ static struct ibm_struct fan_driver_data = {
 	.resume = fan_resume,
 };
 
+
+/*************************************************************************
+ * Battery subdriver
+ */
+
+/* Modify battery_init() if you modify them */
+#define BATTERY_MAX_COUNT 3
+#define BATTERY_MAX_ATTRS 2
+
+static struct battery {
+	char name[3 + 1 + 1];
+	struct attribute_set *set;
+	struct dev_ext_attribute attributes[BATTERY_MAX_ATTRS];
+} batteries[BATTERY_MAX_COUNT];
+
+static int battery_attribute_get_battery(struct device_attribute *attr)
+{
+	return (int) (unsigned long) container_of(attr,
+						  struct dev_ext_attribute,
+						  attr)->var;
+}
+
+static ssize_t battery_start_charge_thresh_store(struct device *dev,
+						 struct device_attribute *attr,
+						 const char *buf, size_t count)
+{
+	int bat = battery_attribute_get_battery(attr);
+	int res = -EINVAL;
+	unsigned long value;
+
+	res = kstrtoul(buf, 0, &value);
+	if (res || value > 99)
+		return res ? res : -EINVAL;
+
+	if (!hkey_handle || !acpi_evalf(hkey_handle, &res, "BCCS", "dd",
+					(int) value | (bat << 8)) || res < 0)
+		return -EIO;
+	return count;
+}
+
+static ssize_t battery_stop_charge_thresh_store(struct device *dev,
+						struct device_attribute *attr,
+						const char *buf, size_t count)
+{
+	int bat = battery_attribute_get_battery(attr);
+	int res = -EINVAL;
+	unsigned long value;
+
+	res = kstrtoul(buf, 0, &value);
+	if (res || value > 99)
+		return res ? res : -EINVAL;
+
+	if (!hkey_handle || !acpi_evalf(hkey_handle, &res, "BCSS", "dd",
+					(int) value | (bat << 8)) || res < 0)
+		return -EIO;
+	return count;
+}
+
+static ssize_t battery_start_charge_thresh_show(struct device *dev,
+						struct device_attribute *attr,
+						char *buf)
+{
+	int bat = battery_attribute_get_battery(attr);
+	int value;
+
+	if (!hkey_handle || !acpi_evalf(hkey_handle, &value, "BCTG",
+					"dd", bat))
+		return -EIO;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", value & 0xFF);
+}
+
+static ssize_t battery_stop_charge_thresh_show(struct device *dev,
+					       struct device_attribute *attr,
+					       char *buf)
+{
+	int bat = battery_attribute_get_battery(attr);
+	int value;
+
+	if (!hkey_handle || !acpi_evalf(hkey_handle, &value, "BCSG",
+					"dd", bat))
+		return -EIO;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", value & 0xFF);
+}
+
+static int __init battery_init(struct ibm_init_struct *iibm)
+{
+	int res;
+	int i;
+	int state;
+
+	vdbg_printk(TPACPI_DBG_INIT,
+		    "initializing battery commands subdriver\n");
+
+	TPACPI_ACPIHANDLE_INIT(hkey);
+
+	/* Check whether getter for start threshold exists */
+	tp_features.battery = hkey_handle &&
+	    acpi_evalf(hkey_handle, &state, "BCTG", "qdd", 1);
+
+	vdbg_printk(TPACPI_DBG_INIT, "battery commands are %s\n",
+		    str_supported(tp_features.battery));
+
+	if (!tp_features.battery)
+		return 1;
+
+	for (i = 0; i < BATTERY_MAX_COUNT; i++) {
+		int j = 0;
+		if (!acpi_evalf(hkey_handle, &state, "BCTG", "qdd", i + 1))
+			continue;
+		/* If the sign bit was set, we could not get the start charge
+		 * threshold of that battery. Let's assume that this battery
+		 * (and all following ones) do not exist */
+		if (state < 0)
+			break;
+		/* Modify BATTERY_MAX_ATTRS if you add an attribute */
+		batteries[i].attributes[j++] = (struct dev_ext_attribute) {
+			.attr = __ATTR(start_charge_tresh,
+				       S_IWUSR | S_IRUGO,
+				       battery_start_charge_thresh_show,
+				       battery_start_charge_thresh_store),
+			.var = (void *) (unsigned long) (i + 1)
+		};
+		batteries[i].attributes[j++] = (struct dev_ext_attribute) {
+			.attr = __ATTR(stop_charge_tresh,
+				       S_IWUSR | S_IRUGO,
+				       battery_stop_charge_thresh_show,
+				       battery_stop_charge_thresh_store),
+			.var = (void *) (unsigned long) (i + 1)
+		};
+
+		strncpy(batteries[i].name, "BAT", 3);
+		batteries[i].name[3] = '0' + i;
+		batteries[i].name[4] = '\0';
+		batteries[i].set = create_attr_set(j - 1, batteries[i].name);
+
+		for (j = j - 1; j >= 0; j--)
+			add_to_attr_set(batteries[i].set,
+					&batteries[i].attributes[j].attr.attr);
+
+		res = register_attr_set_with_sysfs(batteries[i].set,
+						   &tpacpi_pdev->dev.kobj);
+
+		if (res)
+			return res;
+	}
+
+	return 0;
+}
+
+static void battery_exit(void)
+{
+	int i;
+
+	for (i = 0; i < BATTERY_MAX_COUNT; i++) {
+		if (batteries[i].set != NULL) {
+			delete_attr_set(batteries[i].set,
+					&tpacpi_pdev->dev.kobj);
+		}
+	}
+}
+
+static struct ibm_struct battery_driver_data = {
+	.name = "battery",
+	.exit = battery_exit,
+};
+
 /****************************************************************************
  ****************************************************************************
  *
@@ -8741,6 +8910,10 @@ static struct ibm_init_struct ibms_init[] __initdata = {
 		.data = &light_driver_data,
 	},
 	{
+		.init = battery_init,
+		.data = &battery_driver_data,
+	},
+	{
 		.init = cmos_init,
 		.data = &cmos_driver_data,
 	},
-- 
1.8.4.2


------------------------------------------------------------------------------
November Webinars for C, C++, Fortran Developers
Accelerate application performance with scalable programming models. Explore
techniques for threading, error checking, porting, and tuning. Get the most 
from the latest Intel processors and coprocessors. See abstracts and register
http://pubads.g.doubleclick.net/gampad/clk?id=60136231&iu=/4140/ostg.clktrk

  parent reply	other threads:[~2013-11-11 13:56 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-11-11 13:56 [PATCH 0/4] thinkpad_acpi: Add support for controlling charge thresholds Julian Andres Klode
     [not found] ` <1384178195-12218-1-git-send-email-jak-4HMq4SXA452hPH1hqNUYSQ@public.gmane.org>
2013-11-11 13:56   ` Julian Andres Klode [this message]
2013-11-13 13:50     ` [PATCH 1/4] " Julian Andres Klode
2013-12-30 13:29     ` Julian Andres Klode
2013-12-30 21:58       ` Henrique de Moraes Holschuh
2013-12-30 22:40         ` Henrique de Moraes Holschuh
2013-12-31  0:01           ` Julian Andres Klode
2013-12-31 12:12             ` [ibm-acpi-devel] " Henrique de Moraes Holschuh
2013-12-31 22:46               ` Julian Andres Klode
2014-04-06 12:14                 ` Julian Andres Klode
2014-04-09 18:00                   ` Henrique de Moraes Holschuh
2013-11-11 13:56   ` [PATCH 2/4] thinkpad_acpi: battery: Add force_discharge attribute Julian Andres Klode
2013-11-11 13:56   ` [PATCH 3/4] thinkpad_acpi: battery: Add force_discharge_ac_break attribute Julian Andres Klode
2013-11-11 13:56   ` [PATCH 4/4] thinkpad_acpi: battery: Add inhibit_charge_minutes attribute Julian Andres Klode
2013-11-25 14:59 ` [PATCH 0/4] thinkpad_acpi: Add support for controlling charge thresholds Julian Andres Klode
2013-12-28 21:10 ` Julian Andres Klode
2013-12-28 22:10   ` Henrique de Moraes Holschuh
2013-12-30 13:26     ` Julian Andres Klode
2013-12-30 20:06       ` Henrique de Moraes Holschuh
2014-01-20 21:22       ` Pavel Machek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1384178195-12218-2-git-send-email-jak@jak-linux.org \
    --to=jak-4hmq4sxa452hph1hqnuysq@public.gmane.org \
    --cc=ibm-acpi-N3TV7GIv+o9fyO9Q7EP/yw@public.gmane.org \
    --cc=ibm-acpi-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org \
    --cc=linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=matthew.garrett-05XSO3Yj/JvQT0dZR+AlfA@public.gmane.org \
    --cc=platform-driver-x86-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox