Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 126 additions & 34 deletions drivers/power/supply/macsmc-power.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,13 @@ struct macsmc_power {
char model_name[MAX_STRING_LENGTH];
char serial_number[MAX_STRING_LENGTH];
char mfg_date[MAX_STRING_LENGTH];

bool has_chwa;
bool has_chls;
bool has_ch0i;
bool has_ch0c;
bool has_chte;

u8 num_cells;
int nominal_voltage_mv;

Expand All @@ -57,8 +62,8 @@ struct macsmc_power {
static int macsmc_log_power_set(const char *val, const struct kernel_param *kp);

static const struct kernel_param_ops macsmc_log_power_ops = {
.set = macsmc_log_power_set,
.get = param_get_bool,
.set = macsmc_log_power_set,
.get = param_get_bool,
};

static bool log_power = false;
Expand Down Expand Up @@ -242,6 +247,7 @@ static int macsmc_battery_get_status(struct macsmc_power *power)
*/
if (power->has_chls) {
u16 vu16;

ret = apple_smc_read_u16(power->smc, SMC_KEY(CHLS), &vu16);
if (ret == sizeof(vu16) && (vu16 & 0xff) >= CHLS_MIN_END_THRESHOLD)
charge_limit = (vu16 & 0xff) - CHWA_CHLS_FIXED_START_OFFSET;
Expand All @@ -253,6 +259,7 @@ static int macsmc_battery_get_status(struct macsmc_power *power)

if (charge_limit > 0) {
u8 buic = 0;

if (apple_smc_read_u8(power->smc, SMC_KEY(BUIC), &buic) >= 0 &&
buic >= charge_limit)
limited = true;
Expand Down Expand Up @@ -291,55 +298,113 @@ static int macsmc_battery_get_status(struct macsmc_power *power)
static int macsmc_battery_get_charge_behaviour(struct macsmc_power *power)
{
int ret;
u8 val;
u8 val8;
u8 chte_buf[4];

if (power->has_ch0i) {
/* CH0I returns a bitmask like the low byte of CH0R */
ret = apple_smc_read_u8(power->smc, SMC_KEY(CH0I), &val8);
if (ret)
return ret;
if (val8 & CH0R_NOAC_CH0I)
return POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE;
}

/* 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 & CH0R_NOAC_CH0I)
return POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE;
/* Prefer CHTE available in newer firmwares */
if (power->has_chte) {
ret = apple_smc_read(power->smc, SMC_KEY(CHTE), chte_buf, 4);
if (ret < 0)
return ret;

if (chte_buf[0] == 0x01)
return POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE;

} else if (power->has_ch0c) {
/* CH0C returns a bitmask containing CH0B/CH0C flags */
ret = apple_smc_read_u8(power->smc, SMC_KEY(CH0C), &val8);
if (ret)
return ret;
if (val8 & CH0X_CH0C)
return POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE;
}

/* 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 & CH0X_CH0C)
return POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE;
else
return POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO;
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.
* apple_smc_write_u32 does weird things with endianess,
* so we write raw bytes to ensure correctness of CHTE
*/
u8 chte_inhibit[4] = {0x01, 0x00, 0x00, 0x00};
u8 chte_auto[4] = {0x00, 0x00, 0x00, 0x00};

/*
* CH0I/CH0C/CHTE 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;
if (power->has_ch0i) {
ret = apple_smc_write_u8(power->smc, SMC_KEY(CH0I), 0);
if (ret)
return ret;
}

if (power->has_chte) {
ret = apple_smc_write(power->smc, SMC_KEY(CHTE), chte_auto, 4);
if (ret)
return ret;
} else if (power->has_ch0c) {
ret = apple_smc_write_u8(power->smc, SMC_KEY(CH0C), 0);
if (ret)
return ret;
}
break;

case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE:
ch0i = 0;
ch0c = 1;
if (power->has_ch0i) {
ret = apple_smc_write_u8(power->smc, SMC_KEY(CH0I), 0);
if (ret)
return ret;
}

/* Prefer CHTE available in newer firmwares */
if (power->has_chte)
return apple_smc_write(power->smc, SMC_KEY(CHTE), chte_inhibit, 4);
else if (power->has_ch0c)
return apple_smc_write_u8(power->smc, SMC_KEY(CH0C), 1);
else
return -EINVAL;
break;

case POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE:
ch0i = 1;
ch0c = 0;
break;
if (!power->has_ch0i)
return -EINVAL;

/* Prefer CHTE available in newer firmwares */
if (power->has_chte) {
ret = apple_smc_write(power->smc, SMC_KEY(CHTE), chte_auto, 4);
if (ret)
return ret;
} else if (power->has_ch0c) {
ret = apple_smc_write_u8(power->smc, SMC_KEY(CH0C), 0);
if (ret)
return ret;
}

return apple_smc_write_u8(power->smc, SMC_KEY(CH0I), 1);

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);

return 0;
}

static int macsmc_battery_get_date(const char *s, int *out)
Expand Down Expand Up @@ -539,8 +604,7 @@ static int macsmc_battery_get_property(struct power_supply *psy,
val->intval = vu16 & 0xff;
if (val->intval < CHLS_MIN_END_THRESHOLD || val->intval >= 100)
val->intval = 100;
}
else if (power->has_chwa) {
} else if (power->has_chwa) {
flag = false;
ret = apple_smc_read_flag(power->smc, SMC_KEY(CHWA), &flag);
val->intval = flag ? CHWA_FIXED_END_THRESHOLD : 100;
Expand Down Expand Up @@ -838,8 +902,9 @@ static int macsmc_power_probe(struct platform_device *pdev)
struct power_supply_config psy_cfg = {};
struct macsmc_power *power;
bool flag;
u32 val;
u8 val8;
u16 vu16;
u32 val32;
int ret;

power = devm_kzalloc(&pdev->dev, sizeof(*power), GFP_KERNEL);
Expand All @@ -861,10 +926,37 @@ static int macsmc_power_probe(struct platform_device *pdev)
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);

if (apple_smc_read_u32(power->smc, SMC_KEY(CHTE), &val32) >= 0)
power->has_chte = true;

if (apple_smc_read_u8(power->smc, SMC_KEY(CH0C), &val8) >= 0)
power->has_ch0c = true;

if (apple_smc_read_u8(power->smc, SMC_KEY(CH0I), &val8) >= 0)
power->has_ch0i = true;

/* Turn off the "optimized battery charging" flags, in case macOS left them on */
if (power->has_chte)
apple_smc_write_u32(power->smc, SMC_KEY(CHTE), 0);
else if (power->has_ch0c)
apple_smc_write_u8(power->smc, SMC_KEY(CH0C), 0);

if (power->has_ch0i)
apple_smc_write_u8(power->smc, SMC_KEY(CH0I), 0);

apple_smc_write_u8(power->smc, SMC_KEY(CH0K), 0);
apple_smc_write_u8(power->smc, SMC_KEY(CH0B), 0);

power->batt_desc.charge_behaviours = BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO);

/* Newer firmwares do not have force discharge, so check if it's supported */
if (power->has_ch0i)
power->batt_desc.charge_behaviours |= BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE);

/* Older firmware uses CH0C, and newer firmware uses CHTE, so check if at least one is present*/
if (power->has_chte || power->has_ch0c)
power->batt_desc.charge_behaviours |= BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE);

/*
* Prefer CHWA as the SMC firmware from iBoot-10151.1.1 is not compatible with
* this CHLS usage.
Expand All @@ -882,7 +974,7 @@ static int macsmc_power_probe(struct platform_device *pdev)
power->nominal_voltage_mv = MACSMC_NOMINAL_CELL_VOLTAGE_MV * power->num_cells;

/* Doing one read of this flag enables critical shutdown notifications */
apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &val);
apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &val32);

psy_cfg.drv_data = power;
power->batt = devm_power_supply_register(&pdev->dev, &power->batt_desc, &psy_cfg);
Expand Down