[RFC PATCH] thinkpad_acpi: Add support for controlling charge thresholds

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxxxxx>
---
Does this look OK so far? There are some other things I want to add
(force_discharge and others), but I thought I'd gather some comments
in case I'm doing it all wrong...

I'm slightly abusing struct dev_ext_attribute here, as I'm storing the
battery number in the 'var' pointer.

Note that I export the batteries starting at BAT0, but the EC starts
counting at 1 (0 is reserved for setting all batteries at once, but
that functionality is not provided here).

 drivers/platform/x86/thinkpad_acpi.c | 158 +++++++++++++++++++++++++++++++++++
 1 file changed, 158 insertions(+)

diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index 03ca6c1..1e17222 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -528,6 +528,12 @@ static acpi_handle ec_handle;
 TPACPI_HANDLE(ecrd, ec, "ECRD");	/* 570 */
 TPACPI_HANDLE(ecwr, ec, "ECWR");	/* 570 */
 
+TPACPI_HANDLE(battery, root, "\\_SB.PCI0.LPC.EC.HKEY",
+	   "\\_SB.PCI0.LPCB.EC.HKEY",		/* X121e, T430u */
+	   "\\_SB.PCI0.LPCB.H_EC.HKEY",		/* L430 */
+	   "\\_SB.PCI0.LPCB.EC0.HKEY",		/* Edge/S series */
+	   );
+
 TPACPI_HANDLE(cmos, root, "\\UCMS",	/* R50, R50e, R50p, R51, */
 					/* T4x, X31, X40 */
 	   "\\CMOS",		/* A3x, G4x, R32, T23, T30, X22-24, X30 */
@@ -8350,6 +8356,154 @@ static struct ibm_struct fan_driver_data = {
 	.resume = fan_resume,
 };
 
+
+/*************************************************************************
+ * Battery subdriver
+ */
+
+/* Define a new battery, _BAT is a number >= 0 */
+#define DEFINE_BATTERY(_BAT) \
+static struct dev_ext_attribute bat##_BAT##_attribute_start_charge_thresh = { \
+	.attr = __ATTR(start_charge_tresh, (S_IWUSR | S_IRUGO), \
+		       battery_start_charge_thresh_show, \
+		       battery_start_charge_thresh_store), \
+	.var = (void *) (_BAT + 1) \
+}; \
+static struct dev_ext_attribute bat##_BAT##_attribute_stop_charge_thresh = { \
+	.attr = __ATTR(stop_charge_tresh, (S_IWUSR | S_IRUGO), \
+		       battery_stop_charge_thresh_show, \
+		       battery_stop_charge_thresh_store), \
+	.var = (void *) (_BAT + 1) \
+}; \
+static struct attribute *bat##_BAT##_attributes[] = { \
+	&bat##_BAT##_attribute_start_charge_thresh.attr.attr, \
+	&bat##_BAT##_attribute_stop_charge_thresh.attr.attr, \
+	NULL \
+}; \
+\
+static struct attribute_group bat##_BAT##_attribute_group = { \
+	.name  = "BAT" #_BAT, \
+	.attrs = bat##_BAT##_attributes \
+};
+
+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 (!battery_handle || !acpi_evalf(battery_handle, &res, "BCCS",
+					   "dd", (int) value | (bat << 8)))
+		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 (!battery_handle || !acpi_evalf(battery_handle, &res, "BCSS",
+					   "dd", (int) value | (bat << 8)))
+		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 (!battery_handle || !acpi_evalf(battery_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 (!battery_handle || !acpi_evalf(battery_handle, &value, "BCSG",
+					   "dd", bat))
+		return -EIO;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", value & 0xFF);
+}
+
+DEFINE_BATTERY(0);
+DEFINE_BATTERY(1);
+
+static struct attribute_group *bat_attribute_groups[] = {
+	&bat0_attribute_group,
+	&bat1_attribute_group,
+};
+
+static int __init battery_init(struct ibm_init_struct *iibm)
+{
+	int res;
+	int i;
+
+	vdbg_printk(TPACPI_DBG_INIT,
+		"initializing battery commands subdriver\n");
+
+	TPACPI_ACPIHANDLE_INIT(battery);
+
+	vdbg_printk(TPACPI_DBG_INIT, "battery commands are %s\n",
+		str_supported(battery_handle != NULL));
+
+	for (i = 0; i < ARRAY_SIZE(bat_attribute_groups); i++) {
+		res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
+					 bat_attribute_groups[i]);
+		if (res)
+			return res;
+	}
+
+	return (battery_handle) ? 0 : 1;
+}
+
+static void battery_exit(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(bat_attribute_groups); i++)
+		sysfs_remove_group(&tpacpi_pdev->dev.kobj,
+				   bat_attribute_groups[i]);
+}
+
+static struct ibm_struct battery_driver_data = {
+	.name = "battery",
+	.exit = battery_exit,
+};
+
 /****************************************************************************
  ****************************************************************************
  *
@@ -8741,6 +8895,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


------------------------------------------------------------------------------
Android is increasing in popularity, but the open development platform that
developers love is also attractive to malware creators. Download this white
paper to learn more about secure code signing practices that can help keep
Android apps secure.
http://pubads.g.doubleclick.net/gampad/clk?id=65839951&iu=/4140/ostg.clktrk
_______________________________________________
ibm-acpi-devel mailing list
ibm-acpi-devel@xxxxxxxxxxxxxxxxxxxxx
https://lists.sourceforge.net/lists/listinfo/ibm-acpi-devel




[Index of Archives]     [Linux ACPI]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Photo]     [Yosemite Photos]     [Yosemite Advice]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Device Mapper]

  Powered by Linux