lede/target/linux/silicon/patches-5.19/0143-power-supply-macsmc_power-Add-more-props-rework-othe.patch

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