* [RFC PATCH] thinkpad_acpi: Add support for controlling charge thresholds
@ 2013-11-04 21:38 Julian Andres Klode
2013-11-05 10:18 ` Bjørn Mork
0 siblings, 1 reply; 3+ messages in thread
From: Julian Andres Klode @ 2013-11-04 21:38 UTC (permalink / raw)
To: Henrique de Moraes Holschuh, Matthew Garrett
Cc: Julian Andres Klode, open list:THINKPAD ACPI EXT...,
open list:THINKPAD ACPI EXT..., open list
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@jak-linux.org>
---
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
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [RFC PATCH] thinkpad_acpi: Add support for controlling charge thresholds
2013-11-04 21:38 [RFC PATCH] thinkpad_acpi: Add support for controlling charge thresholds Julian Andres Klode
@ 2013-11-05 10:18 ` Bjørn Mork
2013-11-05 11:52 ` Julian Andres Klode
0 siblings, 1 reply; 3+ messages in thread
From: Bjørn Mork @ 2013-11-05 10:18 UTC (permalink / raw)
To: Julian Andres Klode
Cc: Henrique de Moraes Holschuh, Matthew Garrett,
open list:THINKPAD ACPI EXT..., open list:THINKPAD ACPI EXT...,
open list
Julian Andres Klode <jak@jak-linux.org> writes:
>
> +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 */
> + );
> +
Isn't this just the full patch to the existing "hkey_handle" for those
models? Why not just use that handle, like e.g the rfkill driver does?
Supported models could probably be autodetected by checking whether the
methods exist?
> +static struct attribute_group bat##_BAT##_attribute_group = { \
> + .name = "BAT" #_BAT, \
> + .attrs = bat##_BAT##_attributes \
> +};
Are these names guaranteed to match the ACPI battery device(s)?
> +DEFINE_BATTERY(0);
> +DEFINE_BATTERY(1);
Are there always two batteries?
Bjørn
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [RFC PATCH] thinkpad_acpi: Add support for controlling charge thresholds
2013-11-05 10:18 ` Bjørn Mork
@ 2013-11-05 11:52 ` Julian Andres Klode
0 siblings, 0 replies; 3+ messages in thread
From: Julian Andres Klode @ 2013-11-05 11:52 UTC (permalink / raw)
To: Bjørn Mork
Cc: Julian Andres Klode, Henrique de Moraes Holschuh, Matthew Garrett,
open list:THINKPAD ACPI EXT..., open list:THINKPAD ACPI EXT...,
open list
On Tue, Nov 05, 2013 at 11:18:02AM +0100, Bjørn Mork wrote:
> Julian Andres Klode <jak@jak-linux.org> writes:
>
> >
> > +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 */
> > + );
> > +
>
> Isn't this just the full patch to the existing "hkey_handle" for those
> models? Why not just use that handle, like e.g the rfkill driver does?
I did not notice it, thanks for pointing that out.
>
> Supported models could probably be autodetected by checking whether the
> methods exist?
Yes, this makes more sense. I modified it locally to check for existence
of BCTG (get start threshold) and set tp_features.battery accordingly. If
it exists, all features are enabled (I think we can safely assume their
existence, I don't know if there are really thinkpads where you can get
a start threshold but don't have one of the other functions).
>
> > +static struct attribute_group bat##_BAT##_attribute_group = { \
> > + .name = "BAT" #_BAT, \
> > + .attrs = bat##_BAT##_attributes \
> > +};
>
> Are these names guaranteed to match the ACPI battery device(s)?
At least on the Sandy Bridge series and older, the first battery
(BAT0 here) always refers to the internal battery, and BAT1 to
the external one. I think this should match the ACPI battery
devices.
On the Haswell ones, I don't know, because they have one non-removable
built-in and one removable.
>
> > +DEFINE_BATTERY(0);
> > +DEFINE_BATTERY(1);
>
> Are there always two batteries?
As far as I can tell, the controller supports up to 2 batteries. And
they can be configured while they are not plugged in. So, exporting
both of them (all the time) makes sense.
I don't know if the W520 or W530 support 3 batteries, as I don't
have access to them. If they do, I don't know whether they will be
two separate entries or controlled by the same one.
--
Julian Andres Klode - Debian Developer, Ubuntu Member
See http://wiki.debian.org/JulianAndresKlode and http://jak-linux.org/.
Please do not top-post if possible.
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2013-11-05 11:52 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-11-04 21:38 [RFC PATCH] thinkpad_acpi: Add support for controlling charge thresholds Julian Andres Klode
2013-11-05 10:18 ` Bjørn Mork
2013-11-05 11:52 ` Julian Andres Klode
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox