Skip to content

Commit b55d416

Browse files
t-8chjwrdegoede
authored andcommitted
platform/x86: thinkpad_acpi: support force-discharge
This adds support for the force-discharge charge_behaviour through the embedded controller of ThinkPads. Co-developed-by: Thomas Koch <linrunner@gmx.net> Signed-off-by: Thomas Koch <linrunner@gmx.net> Co-developed-by: Nicolò Piazzalunga <nicolopiazzalunga@gmail.com> Signed-off-by: Nicolò Piazzalunga <nicolopiazzalunga@gmail.com> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net> Acked-by: Sebastian Reichel <sebastian.reichel@collabora.com> Link: https://lore.kernel.org/r/20211123232704.25394-4-linux@weissschuh.net Reviewed-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
1 parent 539b9c9 commit b55d416

File tree

1 file changed

+127
-4
lines changed

1 file changed

+127
-4
lines changed

drivers/platform/x86/thinkpad_acpi.c

+127-4
Original file line numberDiff line numberDiff line change
@@ -9214,6 +9214,8 @@ static struct ibm_struct mute_led_driver_data = {
92149214
#define SET_START "BCCS"
92159215
#define GET_STOP "BCSG"
92169216
#define SET_STOP "BCSS"
9217+
#define GET_DISCHARGE "BDSG"
9218+
#define SET_DISCHARGE "BDSS"
92179219

92189220
enum {
92199221
BAT_ANY = 0,
@@ -9230,13 +9232,15 @@ enum {
92309232
/* This is used in the get/set helpers */
92319233
THRESHOLD_START,
92329234
THRESHOLD_STOP,
9235+
FORCE_DISCHARGE,
92339236
};
92349237

92359238
struct tpacpi_battery_data {
92369239
int charge_start;
92379240
int start_support;
92389241
int charge_stop;
92399242
int stop_support;
9243+
unsigned int charge_behaviours;
92409244
};
92419245

92429246
struct tpacpi_battery_driver_data {
@@ -9294,6 +9298,12 @@ static int tpacpi_battery_get(int what, int battery, int *ret)
92949298
if (*ret == 0)
92959299
*ret = 100;
92969300
return 0;
9301+
case FORCE_DISCHARGE:
9302+
if ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_DISCHARGE, ret, battery))
9303+
return -ENODEV;
9304+
/* The force discharge status is in bit 0 */
9305+
*ret = *ret & 0x01;
9306+
return 0;
92979307
default:
92989308
pr_crit("wrong parameter: %d", what);
92999309
return -EINVAL;
@@ -9322,12 +9332,49 @@ static int tpacpi_battery_set(int what, int battery, int value)
93229332
return -ENODEV;
93239333
}
93249334
return 0;
9335+
case FORCE_DISCHARGE:
9336+
/* Force discharge is in bit 0,
9337+
* break on AC attach is in bit 1 (won't work on some ThinkPads),
9338+
* battery ID is in bits 8-9, 2 bits.
9339+
*/
9340+
if (ACPI_FAILURE(tpacpi_battery_acpi_eval(SET_DISCHARGE, &ret, param))) {
9341+
pr_err("failed to set force discharge on %d", battery);
9342+
return -ENODEV;
9343+
}
9344+
return 0;
93259345
default:
93269346
pr_crit("wrong parameter: %d", what);
93279347
return -EINVAL;
93289348
}
93299349
}
93309350

9351+
static int tpacpi_battery_set_validate(int what, int battery, int value)
9352+
{
9353+
int ret, v;
9354+
9355+
ret = tpacpi_battery_set(what, battery, value);
9356+
if (ret < 0)
9357+
return ret;
9358+
9359+
ret = tpacpi_battery_get(what, battery, &v);
9360+
if (ret < 0)
9361+
return ret;
9362+
9363+
if (v == value)
9364+
return 0;
9365+
9366+
msleep(500);
9367+
9368+
ret = tpacpi_battery_get(what, battery, &v);
9369+
if (ret < 0)
9370+
return ret;
9371+
9372+
if (v == value)
9373+
return 0;
9374+
9375+
return -EIO;
9376+
}
9377+
93319378
static int tpacpi_battery_probe(int battery)
93329379
{
93339380
int ret = 0;
@@ -9340,6 +9387,8 @@ static int tpacpi_battery_probe(int battery)
93409387
* 2) Check for support
93419388
* 3) Get the current stop threshold
93429389
* 4) Check for support
9390+
* 5) Get the current force discharge status
9391+
* 6) Check for support
93439392
*/
93449393
if (acpi_has_method(hkey_handle, GET_START)) {
93459394
if ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_START, &ret, battery)) {
@@ -9376,10 +9425,25 @@ static int tpacpi_battery_probe(int battery)
93769425
return -ENODEV;
93779426
}
93789427
}
9379-
pr_info("battery %d registered (start %d, stop %d)",
9380-
battery,
9381-
battery_info.batteries[battery].charge_start,
9382-
battery_info.batteries[battery].charge_stop);
9428+
if (acpi_has_method(hkey_handle, GET_DISCHARGE)) {
9429+
if (ACPI_FAILURE(tpacpi_battery_acpi_eval(GET_DISCHARGE, &ret, battery))) {
9430+
pr_err("Error probing battery discharge; %d\n", battery);
9431+
return -ENODEV;
9432+
}
9433+
/* Support is marked in bit 8 */
9434+
if (ret & BIT(8))
9435+
battery_info.batteries[battery].charge_behaviours |=
9436+
BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE);
9437+
}
9438+
9439+
battery_info.batteries[battery].charge_behaviours |=
9440+
BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO);
9441+
9442+
pr_info("battery %d registered (start %d, stop %d, behaviours: 0x%x)\n",
9443+
battery,
9444+
battery_info.batteries[battery].charge_start,
9445+
battery_info.batteries[battery].charge_stop,
9446+
battery_info.batteries[battery].charge_behaviours);
93839447

93849448
return 0;
93859449
}
@@ -9514,6 +9578,28 @@ static ssize_t charge_control_end_threshold_show(struct device *device,
95149578
return tpacpi_battery_show(THRESHOLD_STOP, device, buf);
95159579
}
95169580

9581+
static ssize_t charge_behaviour_show(struct device *dev,
9582+
struct device_attribute *attr,
9583+
char *buf)
9584+
{
9585+
enum power_supply_charge_behaviour active = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO;
9586+
struct power_supply *supply = to_power_supply(dev);
9587+
unsigned int available;
9588+
int ret, battery;
9589+
9590+
battery = tpacpi_battery_get_id(supply->desc->name);
9591+
available = battery_info.batteries[battery].charge_behaviours;
9592+
9593+
if (available & BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE)) {
9594+
if (tpacpi_battery_get(FORCE_DISCHARGE, battery, &ret))
9595+
return -ENODEV;
9596+
if (ret)
9597+
active = POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE;
9598+
}
9599+
9600+
return power_supply_charge_behaviour_show(dev, available, active, buf);
9601+
}
9602+
95179603
static ssize_t charge_control_start_threshold_store(struct device *dev,
95189604
struct device_attribute *attr,
95199605
const char *buf, size_t count)
@@ -9528,8 +9614,44 @@ static ssize_t charge_control_end_threshold_store(struct device *dev,
95289614
return tpacpi_battery_store(THRESHOLD_STOP, dev, buf, count);
95299615
}
95309616

9617+
static ssize_t charge_behaviour_store(struct device *dev,
9618+
struct device_attribute *attr,
9619+
const char *buf, size_t count)
9620+
{
9621+
struct power_supply *supply = to_power_supply(dev);
9622+
int selected, battery, ret = 0;
9623+
unsigned int available;
9624+
9625+
battery = tpacpi_battery_get_id(supply->desc->name);
9626+
available = battery_info.batteries[battery].charge_behaviours;
9627+
selected = power_supply_charge_behaviour_parse(available, buf);
9628+
9629+
if (selected < 0)
9630+
return selected;
9631+
9632+
switch (selected) {
9633+
case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO:
9634+
if (available & BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE))
9635+
ret = tpacpi_battery_set_validate(FORCE_DISCHARGE, battery, 0);
9636+
if (ret < 0)
9637+
return ret;
9638+
break;
9639+
case POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE:
9640+
ret = tpacpi_battery_set_validate(FORCE_DISCHARGE, battery, 1);
9641+
if (ret < 0)
9642+
return ret;
9643+
break;
9644+
default:
9645+
dev_err(dev, "Unexpected charge behaviour: %d\n", selected);
9646+
return -EINVAL;
9647+
}
9648+
9649+
return count;
9650+
}
9651+
95319652
static DEVICE_ATTR_RW(charge_control_start_threshold);
95329653
static DEVICE_ATTR_RW(charge_control_end_threshold);
9654+
static DEVICE_ATTR_RW(charge_behaviour);
95339655
static struct device_attribute dev_attr_charge_start_threshold = __ATTR(
95349656
charge_start_threshold,
95359657
0644,
@@ -9548,6 +9670,7 @@ static struct attribute *tpacpi_battery_attrs[] = {
95489670
&dev_attr_charge_control_end_threshold.attr,
95499671
&dev_attr_charge_start_threshold.attr,
95509672
&dev_attr_charge_stop_threshold.attr,
9673+
&dev_attr_charge_behaviour.attr,
95519674
NULL,
95529675
};
95539676

0 commit comments

Comments
 (0)