mirror of
https://github.com/coolsnowwolf/lede.git
synced 2025-07-30 02:16:59 +08:00
335 lines
10 KiB
Diff
335 lines
10 KiB
Diff
From 2226f112885322e19b76f56acef517bea6bd399c Mon Sep 17 00:00:00 2001
|
|
From: Hector Martin <marcan@marcan.st>
|
|
Date: Tue, 15 Feb 2022 02:20:20 +0900
|
|
Subject: [PATCH 143/171] power: supply: macsmc_power: Add more props, rework
|
|
others
|
|
|
|
Signed-off-by: Hector Martin <marcan@marcan.st>
|
|
---
|
|
drivers/power/supply/macsmc_power.c | 224 +++++++++++++++++++++++++---
|
|
1 file changed, 202 insertions(+), 22 deletions(-)
|
|
|
|
diff --git a/drivers/power/supply/macsmc_power.c b/drivers/power/supply/macsmc_power.c
|
|
index aa67762144d8..2cb1644055f4 100644
|
|
--- a/drivers/power/supply/macsmc_power.c
|
|
+++ b/drivers/power/supply/macsmc_power.c
|
|
@@ -4,6 +4,7 @@
|
|
* Copyright The Asahi Linux Contributors
|
|
*/
|
|
|
|
+#include <linux/ctype.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/platform_device.h>
|
|
@@ -19,50 +20,178 @@ struct macsmc_power {
|
|
struct power_supply *psy;
|
|
char model_name[MAX_STRING_LENGTH];
|
|
char serial_number[MAX_STRING_LENGTH];
|
|
+ char mfg_date[MAX_STRING_LENGTH];
|
|
|
|
struct notifier_block nb;
|
|
};
|
|
|
|
+#define CHNC_BATTERY_FULL BIT(0)
|
|
+#define CHNC_NO_CHARGER BIT(7)
|
|
+#define CHNC_NOCHG_CH0C BIT(14)
|
|
+#define CHNC_NOCHG_CH0B_CH0K BIT(15)
|
|
+#define CHNC_BATTERY_FULL_2 BIT(18)
|
|
+#define CHNC_BMS_BUSY BIT(23)
|
|
+#define CHNC_NOAC_CH0J BIT(53)
|
|
+#define CHNC_NOAC_CH0I BIT(54)
|
|
+
|
|
+#define CH0R_LOWER_FLAGS GENMASK(15, 0)
|
|
+#define CH0R_NOAC_CH0I BIT(0)
|
|
+#define CH0R_NOAC_CH0J BIT(5)
|
|
+#define CH0R_BMS_BUSY BIT(8)
|
|
+#define CH0R_NOAC_CH0K BIT(9)
|
|
+
|
|
+#define CH0X_CH0C BIT(0)
|
|
+#define CH0X_CH0B BIT(1)
|
|
+
|
|
static int macsmc_battery_get_status(struct macsmc_power *power)
|
|
{
|
|
- u8 val;
|
|
+ u64 nocharge_flags;
|
|
+ u32 nopower_flags;
|
|
+ u16 ac_current;
|
|
int ret;
|
|
|
|
- ret = apple_smc_read_u8(power->smc, SMC_KEY(BSFC), &val);
|
|
- if (ret)
|
|
+ /*
|
|
+ * Note: there are fallbacks in case some of these SMC keys disappear in the future
|
|
+ * or are not present on some machines. We treat the absence of the CHCE/CHCC/BSFC/CHSC
|
|
+ * flags as an error, since they are quite fundamental and simple booleans.
|
|
+ */
|
|
+
|
|
+ /*
|
|
+ * If power input is inhibited, we are definitely discharging.
|
|
+ * However, if the only reason is the BMS is doing a balancing cycle,
|
|
+ * go ahead and ignore that one to avoid spooking users.
|
|
+ */
|
|
+ ret = apple_smc_read_u32(power->smc, SMC_KEY(CH0R), &nopower_flags);
|
|
+ if (!ret && (nopower_flags & CH0R_LOWER_FLAGS & ~CH0R_BMS_BUSY))
|
|
+ return POWER_SUPPLY_STATUS_DISCHARGING;
|
|
+
|
|
+ /* If no charger is present, we are definitely discharging. */
|
|
+ ret = apple_smc_read_flag(power->smc, SMC_KEY(CHCE));
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ else if (!ret)
|
|
+ return POWER_SUPPLY_STATUS_DISCHARGING;
|
|
+
|
|
+ /* If AC is not charge capable, we are definitely discharging. */
|
|
+ ret = apple_smc_read_flag(power->smc, SMC_KEY(CHCC));
|
|
+ if (ret < 0)
|
|
return ret;
|
|
- if (val == 1)
|
|
+ else if (!ret)
|
|
+ return POWER_SUPPLY_STATUS_DISCHARGING;
|
|
+
|
|
+ /*
|
|
+ * If the AC input current limit is tiny or 0, we are discharging no matter
|
|
+ * how much the BMS believes it can charge.
|
|
+ */
|
|
+ ret = apple_smc_read_u16(power->smc, SMC_KEY(AC-i), &ac_current);
|
|
+ if (!ret && ac_current < 100)
|
|
+ return POWER_SUPPLY_STATUS_DISCHARGING;
|
|
+
|
|
+ /* If the battery is full, report it as such. */
|
|
+ ret = apple_smc_read_flag(power->smc, SMC_KEY(BSFC));
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ else if (ret)
|
|
return POWER_SUPPLY_STATUS_FULL;
|
|
|
|
- ret = apple_smc_read_u8(power->smc, SMC_KEY(CHSC), &val);
|
|
- if (ret)
|
|
+ /* If there are reasons we aren't charging... */
|
|
+ ret = apple_smc_read_u64(power->smc, SMC_KEY(CHNC), &nocharge_flags);
|
|
+ if (!ret) {
|
|
+ /* Perhaps the battery is full after all */
|
|
+ if (nocharge_flags & CHNC_BATTERY_FULL)
|
|
+ return POWER_SUPPLY_STATUS_FULL;
|
|
+ /* Or maybe the BMS is just busy doing something, if so call it charging anyway */
|
|
+ else if (nocharge_flags == CHNC_BMS_BUSY)
|
|
+ return POWER_SUPPLY_STATUS_CHARGING;
|
|
+ /* If we have other reasons we aren't charging, say we aren't */
|
|
+ else if (nocharge_flags)
|
|
+ return POWER_SUPPLY_STATUS_NOT_CHARGING;
|
|
+ /* Else we're either charging or about to charge */
|
|
+ else
|
|
+ return POWER_SUPPLY_STATUS_CHARGING;
|
|
+ }
|
|
+
|
|
+ /* As a fallback, use the system charging flag. */
|
|
+ ret = apple_smc_read_flag(power->smc, SMC_KEY(CHSC));
|
|
+ if (ret < 0)
|
|
return ret;
|
|
- if (val == 1)
|
|
+ if (!ret)
|
|
+ return POWER_SUPPLY_STATUS_NOT_CHARGING;
|
|
+ else
|
|
return POWER_SUPPLY_STATUS_CHARGING;
|
|
+}
|
|
|
|
- ret = apple_smc_read_u8(power->smc, SMC_KEY(CHCC), &val);
|
|
+static int macsmc_battery_get_charge_behaviour(struct macsmc_power *power)
|
|
+{
|
|
+ int ret;
|
|
+ u8 val;
|
|
+
|
|
+ /* CH0I returns a bitmask like the low byte of CH0R */
|
|
+ ret = apple_smc_read_u8(power->smc, SMC_KEY(CH0I), &val);
|
|
if (ret)
|
|
return ret;
|
|
- if (val == 0)
|
|
- return POWER_SUPPLY_STATUS_DISCHARGING;
|
|
+ if (val & CH0R_NOAC_CH0I)
|
|
+ return POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE;
|
|
|
|
- ret = apple_smc_read_u8(power->smc, SMC_KEY(CHCE), &val);
|
|
+ /* CH0C returns a bitmask containing CH0B/CH0C flags */
|
|
+ ret = apple_smc_read_u8(power->smc, SMC_KEY(CH0C), &val);
|
|
if (ret)
|
|
return ret;
|
|
- if (val == 0)
|
|
- return POWER_SUPPLY_STATUS_DISCHARGING;
|
|
+ if (val & CH0X_CH0C)
|
|
+ return POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE;
|
|
else
|
|
- return POWER_SUPPLY_STATUS_NOT_CHARGING;
|
|
+ return POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO;
|
|
+}
|
|
|
|
-
|
|
+static int macsmc_battery_set_charge_behaviour(struct macsmc_power *power, int val)
|
|
+{
|
|
+ u8 ch0i, ch0c;
|
|
+ int ret;
|
|
+
|
|
+ /*
|
|
+ * CH0I/CH0C are "hard" controls that will allow the battery to run down to 0.
|
|
+ * CH0K/CH0B are "soft" controls that are reset to 0 when SOC drops below 50%;
|
|
+ * we don't expose these yet.
|
|
+ */
|
|
+
|
|
+ switch (val) {
|
|
+ case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO:
|
|
+ ch0i = ch0c = 0;
|
|
+ break;
|
|
+ case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE:
|
|
+ ch0i = 0;
|
|
+ ch0c = 1;
|
|
+ break;
|
|
+ case POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE:
|
|
+ ch0i = 1;
|
|
+ ch0c = 0;
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ret = apple_smc_write_u8(power->smc, SMC_KEY(CH0I), ch0i);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ return apple_smc_write_u8(power->smc, SMC_KEY(CH0C), ch0c);
|
|
+}
|
|
+
|
|
+static int macsmc_battery_get_date(const char *s, int *out)
|
|
+{
|
|
+ if (!isdigit(s[0]) || !isdigit(s[1]))
|
|
+ return -ENOTSUPP;
|
|
+
|
|
+ *out = (s[0] - '0') * 10 + s[1] - '0';
|
|
+ return 0;
|
|
}
|
|
|
|
static int macsmc_battery_get_property(struct power_supply *psy,
|
|
- enum power_supply_property psp,
|
|
- union power_supply_propval *val)
|
|
+ enum power_supply_property psp,
|
|
+ union power_supply_propval *val)
|
|
{
|
|
struct macsmc_power *power = power_supply_get_drvdata(psy);
|
|
int ret = 0;
|
|
+ u8 vu8;
|
|
u16 vu16;
|
|
u32 vu32;
|
|
s16 vs16;
|
|
@@ -77,6 +206,10 @@ static int macsmc_battery_get_property(struct power_supply *psy,
|
|
case POWER_SUPPLY_PROP_PRESENT:
|
|
val->intval = 1;
|
|
break;
|
|
+ case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
|
|
+ val->intval = macsmc_battery_get_charge_behaviour(power);
|
|
+ ret = val->intval < 0 ? val->intval : 0;
|
|
+ break;
|
|
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
|
|
ret = apple_smc_read_u16(power->smc, SMC_KEY(B0TE), &vu16);
|
|
val->intval = vu16 == 0xffff ? 0 : vu16 * 60;
|
|
@@ -145,6 +278,9 @@ static int macsmc_battery_get_property(struct power_supply *psy,
|
|
ret = apple_smc_read_u16(power->smc, SMC_KEY(B0CT), &vu16);
|
|
val->intval = vu16;
|
|
break;
|
|
+ case POWER_SUPPLY_PROP_SCOPE:
|
|
+ val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
|
|
+ break;
|
|
case POWER_SUPPLY_PROP_HEALTH:
|
|
ret = apple_smc_read_flag(power->smc, SMC_KEY(BBAD));
|
|
val->intval = ret == 1 ? POWER_SUPPLY_HEALTH_DEAD : POWER_SUPPLY_HEALTH_GOOD;
|
|
@@ -156,6 +292,16 @@ static int macsmc_battery_get_property(struct power_supply *psy,
|
|
case POWER_SUPPLY_PROP_SERIAL_NUMBER:
|
|
val->strval = power->serial_number;
|
|
break;
|
|
+ case POWER_SUPPLY_PROP_MANUFACTURE_YEAR:
|
|
+ ret = macsmc_battery_get_date(&power->mfg_date[0], &val->intval);
|
|
+ val->intval += 2000 - 8; /* -8 is a fixup for a firmware bug... */
|
|
+ break;
|
|
+ case POWER_SUPPLY_PROP_MANUFACTURE_MONTH:
|
|
+ ret = macsmc_battery_get_date(&power->mfg_date[2], &val->intval);
|
|
+ break;
|
|
+ case POWER_SUPPLY_PROP_MANUFACTURE_DAY:
|
|
+ ret = macsmc_battery_get_date(&power->mfg_date[4], &val->intval);
|
|
+ break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
@@ -163,9 +309,36 @@ static int macsmc_battery_get_property(struct power_supply *psy,
|
|
return ret;
|
|
}
|
|
|
|
+static int macsmc_battery_set_property(struct power_supply *psy,
|
|
+ enum power_supply_property psp,
|
|
+ const union power_supply_propval *val)
|
|
+{
|
|
+ struct macsmc_power *power = power_supply_get_drvdata(psy);
|
|
+
|
|
+ switch (psp) {
|
|
+ case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
|
|
+ return macsmc_battery_set_charge_behaviour(power, val->intval);
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int macsmc_battery_property_is_writeable(struct power_supply *psy,
|
|
+ enum power_supply_property psp)
|
|
+{
|
|
+ switch (psp) {
|
|
+ case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
|
|
+ return true;
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
static enum power_supply_property macsmc_battery_props[] = {
|
|
POWER_SUPPLY_PROP_STATUS,
|
|
POWER_SUPPLY_PROP_PRESENT,
|
|
+ POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR,
|
|
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
|
|
POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
|
|
POWER_SUPPLY_PROP_CAPACITY,
|
|
@@ -183,17 +356,23 @@ static enum power_supply_property macsmc_battery_props[] = {
|
|
POWER_SUPPLY_PROP_TEMP,
|
|
POWER_SUPPLY_PROP_CHARGE_COUNTER,
|
|
POWER_SUPPLY_PROP_CYCLE_COUNT,
|
|
+ POWER_SUPPLY_PROP_SCOPE,
|
|
POWER_SUPPLY_PROP_HEALTH,
|
|
POWER_SUPPLY_PROP_MODEL_NAME,
|
|
POWER_SUPPLY_PROP_SERIAL_NUMBER,
|
|
+ POWER_SUPPLY_PROP_MANUFACTURE_YEAR,
|
|
+ POWER_SUPPLY_PROP_MANUFACTURE_MONTH,
|
|
+ POWER_SUPPLY_PROP_MANUFACTURE_DAY,
|
|
};
|
|
|
|
static const struct power_supply_desc macsmc_battery_desc = {
|
|
- .name = "macsmc-battery",
|
|
- .type = POWER_SUPPLY_TYPE_BATTERY,
|
|
- .get_property = macsmc_battery_get_property,
|
|
- .properties = macsmc_battery_props,
|
|
- .num_properties = ARRAY_SIZE(macsmc_battery_props),
|
|
+ .name = "macsmc-battery",
|
|
+ .type = POWER_SUPPLY_TYPE_BATTERY,
|
|
+ .get_property = macsmc_battery_get_property,
|
|
+ .set_property = macsmc_battery_set_property,
|
|
+ .property_is_writeable = macsmc_battery_property_is_writeable,
|
|
+ .properties = macsmc_battery_props,
|
|
+ .num_properties = ARRAY_SIZE(macsmc_battery_props),
|
|
};
|
|
|
|
static int macsmc_power_event(struct notifier_block *nb, unsigned long event, void *data)
|
|
@@ -234,6 +413,7 @@ static int macsmc_power_probe(struct platform_device *pdev)
|
|
/* Fetch string properties */
|
|
apple_smc_read(smc, SMC_KEY(BMDN), power->model_name, sizeof(power->model_name) - 1);
|
|
apple_smc_read(smc, SMC_KEY(BMSN), power->serial_number, sizeof(power->serial_number) - 1);
|
|
+ apple_smc_read(smc, SMC_KEY(BMDT), power->mfg_date, sizeof(power->mfg_date) - 1);
|
|
|
|
psy_cfg.drv_data = power;
|
|
power->psy = devm_power_supply_register(&pdev->dev, &macsmc_battery_desc, &psy_cfg);
|
|
--
|
|
2.34.1
|
|
|