1*0ebf821cSHector Martin // SPDX-License-Identifier: GPL-2.0-only OR MIT 2*0ebf821cSHector Martin /* 3*0ebf821cSHector Martin * Apple SMC Power/Battery Management Driver 4*0ebf821cSHector Martin * 5*0ebf821cSHector Martin * This driver exposes battery telemetry (voltage, current, temperature, health) 6*0ebf821cSHector Martin * and AC adapter status provided by the Apple SMC (System Management Controller) 7*0ebf821cSHector Martin * on Apple Silicon systems. 8*0ebf821cSHector Martin * 9*0ebf821cSHector Martin * Copyright The Asahi Linux Contributors 10*0ebf821cSHector Martin */ 11*0ebf821cSHector Martin 12*0ebf821cSHector Martin #include <linux/ctype.h> 13*0ebf821cSHector Martin #include <linux/delay.h> 14*0ebf821cSHector Martin #include <linux/devm-helpers.h> 15*0ebf821cSHector Martin #include <linux/limits.h> 16*0ebf821cSHector Martin #include <linux/module.h> 17*0ebf821cSHector Martin #include <linux/mfd/macsmc.h> 18*0ebf821cSHector Martin #include <linux/notifier.h> 19*0ebf821cSHector Martin #include <linux/of.h> 20*0ebf821cSHector Martin #include <linux/platform_device.h> 21*0ebf821cSHector Martin #include <linux/power_supply.h> 22*0ebf821cSHector Martin #include <linux/reboot.h> 23*0ebf821cSHector Martin #include <linux/workqueue.h> 24*0ebf821cSHector Martin 25*0ebf821cSHector Martin #define MAX_STRING_LENGTH 256 26*0ebf821cSHector Martin 27*0ebf821cSHector Martin /* 28*0ebf821cSHector Martin * The SMC reports charge in mAh (Coulombs) but energy in mWh (Joules). 29*0ebf821cSHector Martin * We lack a register for "Nominal Voltage" or "Energy Accumulator". 30*0ebf821cSHector Martin * We use a fixed 3.8V/cell constant to approximate energy stats for userspace, 31*0ebf821cSHector Martin * derived from empirical data across supported MacBook models. 32*0ebf821cSHector Martin */ 33*0ebf821cSHector Martin #define MACSMC_NOMINAL_CELL_VOLTAGE_MV 3800 34*0ebf821cSHector Martin 35*0ebf821cSHector Martin /* SMC Key Flags */ 36*0ebf821cSHector Martin #define CHNC_BATTERY_FULL BIT(0) 37*0ebf821cSHector Martin #define CHNC_NO_CHARGER BIT(7) 38*0ebf821cSHector Martin #define CHNC_NOCHG_CH0C BIT(14) 39*0ebf821cSHector Martin #define CHNC_NOCHG_CH0B_CH0K BIT(15) 40*0ebf821cSHector Martin #define CHNC_BATTERY_FULL_2 BIT(18) 41*0ebf821cSHector Martin #define CHNC_BMS_BUSY BIT(23) 42*0ebf821cSHector Martin #define CHNC_CHLS_LIMIT BIT(24) 43*0ebf821cSHector Martin #define CHNC_NOAC_CH0J BIT(53) 44*0ebf821cSHector Martin #define CHNC_NOAC_CH0I BIT(54) 45*0ebf821cSHector Martin 46*0ebf821cSHector Martin #define CH0R_LOWER_FLAGS GENMASK(15, 0) 47*0ebf821cSHector Martin #define CH0R_NOAC_CH0I BIT(0) 48*0ebf821cSHector Martin #define CH0R_NOAC_DISCONNECTED BIT(4) 49*0ebf821cSHector Martin #define CH0R_NOAC_CH0J BIT(5) 50*0ebf821cSHector Martin #define CH0R_BMS_BUSY BIT(8) 51*0ebf821cSHector Martin #define CH0R_NOAC_CH0K BIT(9) 52*0ebf821cSHector Martin #define CH0R_NOAC_CHWA BIT(11) 53*0ebf821cSHector Martin 54*0ebf821cSHector Martin #define CH0X_CH0C BIT(0) 55*0ebf821cSHector Martin #define CH0X_CH0B BIT(1) 56*0ebf821cSHector Martin 57*0ebf821cSHector Martin #define ACSt_CAN_BOOT_AP BIT(2) 58*0ebf821cSHector Martin #define ACSt_CAN_BOOT_IBOOT BIT(1) 59*0ebf821cSHector Martin 60*0ebf821cSHector Martin #define CHWA_CHLS_FIXED_START_OFFSET 5 61*0ebf821cSHector Martin #define CHLS_MIN_END_THRESHOLD 10 62*0ebf821cSHector Martin #define CHLS_FORCE_DISCHARGE 0x100 63*0ebf821cSHector Martin #define CHWA_FIXED_END_THRESHOLD 80 64*0ebf821cSHector Martin #define CHWA_PROP_WRITE_THRESHOLD 95 65*0ebf821cSHector Martin 66*0ebf821cSHector Martin #define MACSMC_MAX_BATT_PROPS 50 67*0ebf821cSHector Martin #define MACSMC_MAX_AC_PROPS 10 68*0ebf821cSHector Martin 69*0ebf821cSHector Martin struct macsmc_power { 70*0ebf821cSHector Martin struct device *dev; 71*0ebf821cSHector Martin struct apple_smc *smc; 72*0ebf821cSHector Martin 73*0ebf821cSHector Martin struct power_supply_desc ac_desc; 74*0ebf821cSHector Martin struct power_supply_desc batt_desc; 75*0ebf821cSHector Martin 76*0ebf821cSHector Martin struct power_supply *batt; 77*0ebf821cSHector Martin struct power_supply *ac; 78*0ebf821cSHector Martin 79*0ebf821cSHector Martin char model_name[MAX_STRING_LENGTH]; 80*0ebf821cSHector Martin char serial_number[MAX_STRING_LENGTH]; 81*0ebf821cSHector Martin char mfg_date[MAX_STRING_LENGTH]; 82*0ebf821cSHector Martin 83*0ebf821cSHector Martin /* Supported feature flags based on SMC key presence */ 84*0ebf821cSHector Martin bool has_chwa; /* Charge limit (Modern firmware) */ 85*0ebf821cSHector Martin bool has_chls; /* Charge limit (Older firmware) */ 86*0ebf821cSHector Martin bool has_ch0i; /* Force discharge (Older firmware) */ 87*0ebf821cSHector Martin bool has_ch0c; /* Inhibit charge (Older firmware) */ 88*0ebf821cSHector Martin bool has_chte; /* Inhibit charge (Modern firmware) */ 89*0ebf821cSHector Martin 90*0ebf821cSHector Martin u8 num_cells; 91*0ebf821cSHector Martin int nominal_voltage_mv; 92*0ebf821cSHector Martin 93*0ebf821cSHector Martin struct notifier_block nb; 94*0ebf821cSHector Martin struct work_struct critical_work; 95*0ebf821cSHector Martin bool emergency_shutdown_triggered; 96*0ebf821cSHector Martin bool orderly_shutdown_triggered; 97*0ebf821cSHector Martin }; 98*0ebf821cSHector Martin 99*0ebf821cSHector Martin static int macsmc_battery_get_status(struct macsmc_power *power) 100*0ebf821cSHector Martin { 101*0ebf821cSHector Martin u64 nocharge_flags; 102*0ebf821cSHector Martin u32 nopower_flags; 103*0ebf821cSHector Martin u16 ac_current; 104*0ebf821cSHector Martin int charge_limit = 0; 105*0ebf821cSHector Martin bool limited = false; 106*0ebf821cSHector Martin bool flag; 107*0ebf821cSHector Martin int ret; 108*0ebf821cSHector Martin 109*0ebf821cSHector Martin /* 110*0ebf821cSHector Martin * B0AV (Voltage) is fundamental. If we can't read it, we assume the 111*0ebf821cSHector Martin * battery is gone. CHCE (Hardware charger present) / CHCC (Hardware 112*0ebf821cSHector Martin * charger capable) are fundamental status flags. 113*0ebf821cSHector Martin * BSFC (System full charge) / CHSC (System charging) are fundamental 114*0ebf821cSHector Martin * status flags. 115*0ebf821cSHector Martin */ 116*0ebf821cSHector Martin 117*0ebf821cSHector Martin /* Check if power input is inhibited (e.g. BMS balancing cycle) */ 118*0ebf821cSHector Martin ret = apple_smc_read_u32(power->smc, SMC_KEY(CH0R), &nopower_flags); 119*0ebf821cSHector Martin if (!ret && (nopower_flags & CH0R_LOWER_FLAGS & ~CH0R_BMS_BUSY)) 120*0ebf821cSHector Martin return POWER_SUPPLY_STATUS_DISCHARGING; 121*0ebf821cSHector Martin 122*0ebf821cSHector Martin /* Check if charger is present */ 123*0ebf821cSHector Martin ret = apple_smc_read_flag(power->smc, SMC_KEY(CHCE), &flag); 124*0ebf821cSHector Martin if (ret < 0) 125*0ebf821cSHector Martin return ret; 126*0ebf821cSHector Martin if (!flag) 127*0ebf821cSHector Martin return POWER_SUPPLY_STATUS_DISCHARGING; 128*0ebf821cSHector Martin 129*0ebf821cSHector Martin /* Check if AC is charge capable */ 130*0ebf821cSHector Martin ret = apple_smc_read_flag(power->smc, SMC_KEY(CHCC), &flag); 131*0ebf821cSHector Martin if (ret < 0) 132*0ebf821cSHector Martin return ret; 133*0ebf821cSHector Martin if (!flag) 134*0ebf821cSHector Martin return POWER_SUPPLY_STATUS_DISCHARGING; 135*0ebf821cSHector Martin 136*0ebf821cSHector Martin /* Check if AC input limit is too low */ 137*0ebf821cSHector Martin ret = apple_smc_read_u16(power->smc, SMC_KEY(AC-i), &ac_current); 138*0ebf821cSHector Martin if (!ret && ac_current < 100) 139*0ebf821cSHector Martin return POWER_SUPPLY_STATUS_DISCHARGING; 140*0ebf821cSHector Martin 141*0ebf821cSHector Martin /* Check if battery is full */ 142*0ebf821cSHector Martin ret = apple_smc_read_flag(power->smc, SMC_KEY(BSFC), &flag); 143*0ebf821cSHector Martin if (ret < 0) 144*0ebf821cSHector Martin return ret; 145*0ebf821cSHector Martin if (flag) 146*0ebf821cSHector Martin return POWER_SUPPLY_STATUS_FULL; 147*0ebf821cSHector Martin 148*0ebf821cSHector Martin /* Check for user-defined charge limits */ 149*0ebf821cSHector Martin if (power->has_chls) { 150*0ebf821cSHector Martin u16 vu16; 151*0ebf821cSHector Martin 152*0ebf821cSHector Martin ret = apple_smc_read_u16(power->smc, SMC_KEY(CHLS), &vu16); 153*0ebf821cSHector Martin if (ret == 0 && (vu16 & 0xff) >= CHLS_MIN_END_THRESHOLD) 154*0ebf821cSHector Martin charge_limit = (vu16 & 0xff) - CHWA_CHLS_FIXED_START_OFFSET; 155*0ebf821cSHector Martin } else if (power->has_chwa) { 156*0ebf821cSHector Martin ret = apple_smc_read_flag(power->smc, SMC_KEY(CHWA), &flag); 157*0ebf821cSHector Martin if (ret == 0 && flag) 158*0ebf821cSHector Martin charge_limit = CHWA_FIXED_END_THRESHOLD - CHWA_CHLS_FIXED_START_OFFSET; 159*0ebf821cSHector Martin } 160*0ebf821cSHector Martin 161*0ebf821cSHector Martin if (charge_limit > 0) { 162*0ebf821cSHector Martin u8 buic = 0; 163*0ebf821cSHector Martin 164*0ebf821cSHector Martin if (apple_smc_read_u8(power->smc, SMC_KEY(BUIC), &buic) >= 0 && 165*0ebf821cSHector Martin buic >= charge_limit) 166*0ebf821cSHector Martin limited = true; 167*0ebf821cSHector Martin } 168*0ebf821cSHector Martin 169*0ebf821cSHector Martin /* Check charging inhibitors */ 170*0ebf821cSHector Martin ret = apple_smc_read_u64(power->smc, SMC_KEY(CHNC), &nocharge_flags); 171*0ebf821cSHector Martin if (!ret) { 172*0ebf821cSHector Martin if (nocharge_flags & CHNC_BATTERY_FULL) 173*0ebf821cSHector Martin return POWER_SUPPLY_STATUS_FULL; 174*0ebf821cSHector Martin /* BMS busy shows up as inhibit, but we treat it as charging */ 175*0ebf821cSHector Martin else if (nocharge_flags == CHNC_BMS_BUSY && !limited) 176*0ebf821cSHector Martin return POWER_SUPPLY_STATUS_CHARGING; 177*0ebf821cSHector Martin else if (nocharge_flags) 178*0ebf821cSHector Martin return POWER_SUPPLY_STATUS_NOT_CHARGING; 179*0ebf821cSHector Martin else 180*0ebf821cSHector Martin return POWER_SUPPLY_STATUS_CHARGING; 181*0ebf821cSHector Martin } 182*0ebf821cSHector Martin 183*0ebf821cSHector Martin /* Fallback: System charging flag */ 184*0ebf821cSHector Martin ret = apple_smc_read_flag(power->smc, SMC_KEY(CHSC), &flag); 185*0ebf821cSHector Martin if (ret < 0) 186*0ebf821cSHector Martin return ret; 187*0ebf821cSHector Martin if (!flag) 188*0ebf821cSHector Martin return POWER_SUPPLY_STATUS_NOT_CHARGING; 189*0ebf821cSHector Martin 190*0ebf821cSHector Martin return POWER_SUPPLY_STATUS_CHARGING; 191*0ebf821cSHector Martin } 192*0ebf821cSHector Martin 193*0ebf821cSHector Martin static int macsmc_battery_get_charge_behaviour(struct macsmc_power *power) 194*0ebf821cSHector Martin { 195*0ebf821cSHector Martin int ret; 196*0ebf821cSHector Martin u8 val8; 197*0ebf821cSHector Martin u8 chte_buf[4]; 198*0ebf821cSHector Martin 199*0ebf821cSHector Martin if (power->has_ch0i) { 200*0ebf821cSHector Martin ret = apple_smc_read_u8(power->smc, SMC_KEY(CH0I), &val8); 201*0ebf821cSHector Martin if (ret) 202*0ebf821cSHector Martin return ret; 203*0ebf821cSHector Martin if (val8 & CH0R_NOAC_CH0I) 204*0ebf821cSHector Martin return POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE; 205*0ebf821cSHector Martin } 206*0ebf821cSHector Martin 207*0ebf821cSHector Martin if (power->has_chte) { 208*0ebf821cSHector Martin ret = apple_smc_read(power->smc, SMC_KEY(CHTE), chte_buf, 4); 209*0ebf821cSHector Martin if (ret < 0) 210*0ebf821cSHector Martin return ret; 211*0ebf821cSHector Martin 212*0ebf821cSHector Martin if (chte_buf[0] == 0x01) 213*0ebf821cSHector Martin return POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE; 214*0ebf821cSHector Martin } else if (power->has_ch0c) { 215*0ebf821cSHector Martin ret = apple_smc_read_u8(power->smc, SMC_KEY(CH0C), &val8); 216*0ebf821cSHector Martin if (ret) 217*0ebf821cSHector Martin return ret; 218*0ebf821cSHector Martin if (val8 & CH0X_CH0C) 219*0ebf821cSHector Martin return POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE; 220*0ebf821cSHector Martin } 221*0ebf821cSHector Martin 222*0ebf821cSHector Martin return POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO; 223*0ebf821cSHector Martin } 224*0ebf821cSHector Martin 225*0ebf821cSHector Martin static int macsmc_battery_set_charge_behaviour(struct macsmc_power *power, int val) 226*0ebf821cSHector Martin { 227*0ebf821cSHector Martin int ret; 228*0ebf821cSHector Martin 229*0ebf821cSHector Martin switch (val) { 230*0ebf821cSHector Martin case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO: 231*0ebf821cSHector Martin /* Reset all inhibitors to a known-good 'auto' state */ 232*0ebf821cSHector Martin if (power->has_ch0i) { 233*0ebf821cSHector Martin ret = apple_smc_write_u8(power->smc, SMC_KEY(CH0I), 0); 234*0ebf821cSHector Martin if (ret) 235*0ebf821cSHector Martin return ret; 236*0ebf821cSHector Martin } 237*0ebf821cSHector Martin 238*0ebf821cSHector Martin if (power->has_chte) { 239*0ebf821cSHector Martin ret = apple_smc_write_u32(power->smc, SMC_KEY(CHTE), 0); 240*0ebf821cSHector Martin if (ret) 241*0ebf821cSHector Martin return ret; 242*0ebf821cSHector Martin } else if (power->has_ch0c) { 243*0ebf821cSHector Martin ret = apple_smc_write_u8(power->smc, SMC_KEY(CH0C), 0); 244*0ebf821cSHector Martin if (ret) 245*0ebf821cSHector Martin return ret; 246*0ebf821cSHector Martin } 247*0ebf821cSHector Martin return 0; 248*0ebf821cSHector Martin 249*0ebf821cSHector Martin case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE: 250*0ebf821cSHector Martin if (power->has_chte) 251*0ebf821cSHector Martin return apple_smc_write_u32(power->smc, SMC_KEY(CHTE), 1); 252*0ebf821cSHector Martin else if (power->has_ch0c) 253*0ebf821cSHector Martin return apple_smc_write_u8(power->smc, SMC_KEY(CH0C), 1); 254*0ebf821cSHector Martin else 255*0ebf821cSHector Martin return -EOPNOTSUPP; 256*0ebf821cSHector Martin 257*0ebf821cSHector Martin case POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE: 258*0ebf821cSHector Martin if (!power->has_ch0i) 259*0ebf821cSHector Martin return -EOPNOTSUPP; 260*0ebf821cSHector Martin return apple_smc_write_u8(power->smc, SMC_KEY(CH0I), 1); 261*0ebf821cSHector Martin 262*0ebf821cSHector Martin default: 263*0ebf821cSHector Martin return -EINVAL; 264*0ebf821cSHector Martin } 265*0ebf821cSHector Martin } 266*0ebf821cSHector Martin 267*0ebf821cSHector Martin static int macsmc_battery_get_date(const char *s, int *out) 268*0ebf821cSHector Martin { 269*0ebf821cSHector Martin if (!isdigit(s[0]) || !isdigit(s[1])) 270*0ebf821cSHector Martin return -EOPNOTSUPP; 271*0ebf821cSHector Martin 272*0ebf821cSHector Martin *out = (s[0] - '0') * 10 + s[1] - '0'; 273*0ebf821cSHector Martin return 0; 274*0ebf821cSHector Martin } 275*0ebf821cSHector Martin 276*0ebf821cSHector Martin static int macsmc_battery_get_capacity_level(struct macsmc_power *power) 277*0ebf821cSHector Martin { 278*0ebf821cSHector Martin bool flag; 279*0ebf821cSHector Martin u32 val; 280*0ebf821cSHector Martin int ret; 281*0ebf821cSHector Martin 282*0ebf821cSHector Martin /* Check for emergency shutdown condition */ 283*0ebf821cSHector Martin if (apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &val) >= 0 && val) 284*0ebf821cSHector Martin return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; 285*0ebf821cSHector Martin 286*0ebf821cSHector Martin /* Check AC status for whether we could boot in this state */ 287*0ebf821cSHector Martin if (apple_smc_read_u32(power->smc, SMC_KEY(ACSt), &val) >= 0) { 288*0ebf821cSHector Martin if (!(val & ACSt_CAN_BOOT_IBOOT)) 289*0ebf821cSHector Martin return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; 290*0ebf821cSHector Martin 291*0ebf821cSHector Martin if (!(val & ACSt_CAN_BOOT_AP)) 292*0ebf821cSHector Martin return POWER_SUPPLY_CAPACITY_LEVEL_LOW; 293*0ebf821cSHector Martin } 294*0ebf821cSHector Martin 295*0ebf821cSHector Martin /* BSFC = Battery System Full Charge */ 296*0ebf821cSHector Martin ret = apple_smc_read_flag(power->smc, SMC_KEY(BSFC), &flag); 297*0ebf821cSHector Martin if (ret < 0) 298*0ebf821cSHector Martin return POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; 299*0ebf821cSHector Martin 300*0ebf821cSHector Martin if (flag) 301*0ebf821cSHector Martin return POWER_SUPPLY_CAPACITY_LEVEL_FULL; 302*0ebf821cSHector Martin else 303*0ebf821cSHector Martin return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; 304*0ebf821cSHector Martin } 305*0ebf821cSHector Martin 306*0ebf821cSHector Martin static int macsmc_battery_get_property(struct power_supply *psy, 307*0ebf821cSHector Martin enum power_supply_property psp, 308*0ebf821cSHector Martin union power_supply_propval *val) 309*0ebf821cSHector Martin { 310*0ebf821cSHector Martin struct macsmc_power *power = power_supply_get_drvdata(psy); 311*0ebf821cSHector Martin int ret = 0; 312*0ebf821cSHector Martin u8 vu8; 313*0ebf821cSHector Martin u16 vu16; 314*0ebf821cSHector Martin s16 vs16; 315*0ebf821cSHector Martin s32 vs32; 316*0ebf821cSHector Martin s64 vs64; 317*0ebf821cSHector Martin bool flag; 318*0ebf821cSHector Martin 319*0ebf821cSHector Martin switch (psp) { 320*0ebf821cSHector Martin case POWER_SUPPLY_PROP_STATUS: 321*0ebf821cSHector Martin val->intval = macsmc_battery_get_status(power); 322*0ebf821cSHector Martin ret = val->intval < 0 ? val->intval : 0; 323*0ebf821cSHector Martin break; 324*0ebf821cSHector Martin case POWER_SUPPLY_PROP_PRESENT: 325*0ebf821cSHector Martin val->intval = 1; 326*0ebf821cSHector Martin break; 327*0ebf821cSHector Martin case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: 328*0ebf821cSHector Martin val->intval = macsmc_battery_get_charge_behaviour(power); 329*0ebf821cSHector Martin ret = val->intval < 0 ? val->intval : 0; 330*0ebf821cSHector Martin break; 331*0ebf821cSHector Martin case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: 332*0ebf821cSHector Martin ret = apple_smc_read_u16(power->smc, SMC_KEY(B0TE), &vu16); 333*0ebf821cSHector Martin val->intval = vu16 == 0xffff ? 0 : vu16 * 60; 334*0ebf821cSHector Martin break; 335*0ebf821cSHector Martin case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: 336*0ebf821cSHector Martin ret = apple_smc_read_u16(power->smc, SMC_KEY(B0TF), &vu16); 337*0ebf821cSHector Martin val->intval = vu16 == 0xffff ? 0 : vu16 * 60; 338*0ebf821cSHector Martin break; 339*0ebf821cSHector Martin case POWER_SUPPLY_PROP_CAPACITY: 340*0ebf821cSHector Martin ret = apple_smc_read_u8(power->smc, SMC_KEY(BUIC), &vu8); 341*0ebf821cSHector Martin val->intval = vu8; 342*0ebf821cSHector Martin break; 343*0ebf821cSHector Martin case POWER_SUPPLY_PROP_CAPACITY_LEVEL: 344*0ebf821cSHector Martin val->intval = macsmc_battery_get_capacity_level(power); 345*0ebf821cSHector Martin ret = val->intval < 0 ? val->intval : 0; 346*0ebf821cSHector Martin break; 347*0ebf821cSHector Martin case POWER_SUPPLY_PROP_VOLTAGE_NOW: 348*0ebf821cSHector Martin ret = apple_smc_read_u16(power->smc, SMC_KEY(B0AV), &vu16); 349*0ebf821cSHector Martin val->intval = vu16 * 1000; 350*0ebf821cSHector Martin break; 351*0ebf821cSHector Martin case POWER_SUPPLY_PROP_CURRENT_NOW: 352*0ebf821cSHector Martin ret = apple_smc_read_s16(power->smc, SMC_KEY(B0AC), &vs16); 353*0ebf821cSHector Martin val->intval = vs16 * 1000; 354*0ebf821cSHector Martin break; 355*0ebf821cSHector Martin case POWER_SUPPLY_PROP_POWER_NOW: 356*0ebf821cSHector Martin ret = apple_smc_read_s32(power->smc, SMC_KEY(B0AP), &vs32); 357*0ebf821cSHector Martin val->intval = vs32 * 1000; 358*0ebf821cSHector Martin break; 359*0ebf821cSHector Martin case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 360*0ebf821cSHector Martin ret = apple_smc_read_u16(power->smc, SMC_KEY(BITV), &vu16); 361*0ebf821cSHector Martin val->intval = vu16 * 1000; 362*0ebf821cSHector Martin break; 363*0ebf821cSHector Martin case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: 364*0ebf821cSHector Martin /* Calculate total max design voltage from per-cell maximum voltage */ 365*0ebf821cSHector Martin ret = apple_smc_read_u16(power->smc, SMC_KEY(BVVN), &vu16); 366*0ebf821cSHector Martin val->intval = vu16 * 1000 * power->num_cells; 367*0ebf821cSHector Martin break; 368*0ebf821cSHector Martin case POWER_SUPPLY_PROP_VOLTAGE_MIN: 369*0ebf821cSHector Martin /* Lifetime min */ 370*0ebf821cSHector Martin ret = apple_smc_read_s16(power->smc, SMC_KEY(BLPM), &vs16); 371*0ebf821cSHector Martin val->intval = vs16 * 1000; 372*0ebf821cSHector Martin break; 373*0ebf821cSHector Martin case POWER_SUPPLY_PROP_VOLTAGE_MAX: 374*0ebf821cSHector Martin /* Lifetime max */ 375*0ebf821cSHector Martin ret = apple_smc_read_s16(power->smc, SMC_KEY(BLPX), &vs16); 376*0ebf821cSHector Martin val->intval = vs16 * 1000; 377*0ebf821cSHector Martin break; 378*0ebf821cSHector Martin case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: 379*0ebf821cSHector Martin ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RC), &vu16); 380*0ebf821cSHector Martin val->intval = vu16 * 1000; 381*0ebf821cSHector Martin break; 382*0ebf821cSHector Martin case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: 383*0ebf821cSHector Martin ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RI), &vu16); 384*0ebf821cSHector Martin val->intval = vu16 * 1000; 385*0ebf821cSHector Martin break; 386*0ebf821cSHector Martin case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 387*0ebf821cSHector Martin ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RV), &vu16); 388*0ebf821cSHector Martin val->intval = vu16 * 1000; 389*0ebf821cSHector Martin break; 390*0ebf821cSHector Martin case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: 391*0ebf821cSHector Martin ret = apple_smc_read_u16(power->smc, SMC_KEY(B0DC), &vu16); 392*0ebf821cSHector Martin val->intval = vu16 * 1000; 393*0ebf821cSHector Martin break; 394*0ebf821cSHector Martin case POWER_SUPPLY_PROP_CHARGE_FULL: 395*0ebf821cSHector Martin ret = apple_smc_read_u16(power->smc, SMC_KEY(B0FC), &vu16); 396*0ebf821cSHector Martin val->intval = vu16 * 1000; 397*0ebf821cSHector Martin break; 398*0ebf821cSHector Martin case POWER_SUPPLY_PROP_CHARGE_NOW: 399*0ebf821cSHector Martin ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RM), &vu16); 400*0ebf821cSHector Martin /* B0RM is Big Endian, likely pass through from TI gas gauge */ 401*0ebf821cSHector Martin val->intval = (s16)swab16(vu16) * 1000; 402*0ebf821cSHector Martin break; 403*0ebf821cSHector Martin case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: 404*0ebf821cSHector Martin ret = apple_smc_read_u16(power->smc, SMC_KEY(B0DC), &vu16); 405*0ebf821cSHector Martin val->intval = vu16 * power->nominal_voltage_mv; 406*0ebf821cSHector Martin break; 407*0ebf821cSHector Martin case POWER_SUPPLY_PROP_ENERGY_FULL: 408*0ebf821cSHector Martin ret = apple_smc_read_u16(power->smc, SMC_KEY(B0FC), &vu16); 409*0ebf821cSHector Martin val->intval = vu16 * power->nominal_voltage_mv; 410*0ebf821cSHector Martin break; 411*0ebf821cSHector Martin case POWER_SUPPLY_PROP_ENERGY_NOW: 412*0ebf821cSHector Martin ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RM), &vu16); 413*0ebf821cSHector Martin /* B0RM is Big Endian, likely pass through from TI gas gauge */ 414*0ebf821cSHector Martin val->intval = (s16)swab16(vu16) * power->nominal_voltage_mv; 415*0ebf821cSHector Martin break; 416*0ebf821cSHector Martin case POWER_SUPPLY_PROP_TEMP: 417*0ebf821cSHector Martin ret = apple_smc_read_u16(power->smc, SMC_KEY(B0AT), &vu16); 418*0ebf821cSHector Martin val->intval = vu16 - 2732; /* Kelvin x10 to Celsius x10 */ 419*0ebf821cSHector Martin break; 420*0ebf821cSHector Martin case POWER_SUPPLY_PROP_CHARGE_COUNTER: 421*0ebf821cSHector Martin ret = apple_smc_read_s64(power->smc, SMC_KEY(BAAC), &vs64); 422*0ebf821cSHector Martin val->intval = vs64; 423*0ebf821cSHector Martin break; 424*0ebf821cSHector Martin case POWER_SUPPLY_PROP_CYCLE_COUNT: 425*0ebf821cSHector Martin ret = apple_smc_read_u16(power->smc, SMC_KEY(B0CT), &vu16); 426*0ebf821cSHector Martin val->intval = vu16; 427*0ebf821cSHector Martin break; 428*0ebf821cSHector Martin case POWER_SUPPLY_PROP_SCOPE: 429*0ebf821cSHector Martin val->intval = POWER_SUPPLY_SCOPE_SYSTEM; 430*0ebf821cSHector Martin break; 431*0ebf821cSHector Martin case POWER_SUPPLY_PROP_HEALTH: 432*0ebf821cSHector Martin flag = false; 433*0ebf821cSHector Martin ret = apple_smc_read_flag(power->smc, SMC_KEY(BBAD), &flag); 434*0ebf821cSHector Martin val->intval = flag ? POWER_SUPPLY_HEALTH_DEAD : POWER_SUPPLY_HEALTH_GOOD; 435*0ebf821cSHector Martin break; 436*0ebf821cSHector Martin case POWER_SUPPLY_PROP_MODEL_NAME: 437*0ebf821cSHector Martin val->strval = power->model_name; 438*0ebf821cSHector Martin break; 439*0ebf821cSHector Martin case POWER_SUPPLY_PROP_SERIAL_NUMBER: 440*0ebf821cSHector Martin val->strval = power->serial_number; 441*0ebf821cSHector Martin break; 442*0ebf821cSHector Martin case POWER_SUPPLY_PROP_MANUFACTURE_YEAR: 443*0ebf821cSHector Martin ret = macsmc_battery_get_date(&power->mfg_date[0], &val->intval); 444*0ebf821cSHector Martin /* The SMC reports the manufacture year as an offset from 1992. */ 445*0ebf821cSHector Martin val->intval += 1992; 446*0ebf821cSHector Martin break; 447*0ebf821cSHector Martin case POWER_SUPPLY_PROP_MANUFACTURE_MONTH: 448*0ebf821cSHector Martin ret = macsmc_battery_get_date(&power->mfg_date[2], &val->intval); 449*0ebf821cSHector Martin break; 450*0ebf821cSHector Martin case POWER_SUPPLY_PROP_MANUFACTURE_DAY: 451*0ebf821cSHector Martin ret = macsmc_battery_get_date(&power->mfg_date[4], &val->intval); 452*0ebf821cSHector Martin break; 453*0ebf821cSHector Martin default: 454*0ebf821cSHector Martin return -EINVAL; 455*0ebf821cSHector Martin } 456*0ebf821cSHector Martin 457*0ebf821cSHector Martin return ret; 458*0ebf821cSHector Martin } 459*0ebf821cSHector Martin 460*0ebf821cSHector Martin static int macsmc_battery_set_property(struct power_supply *psy, 461*0ebf821cSHector Martin enum power_supply_property psp, 462*0ebf821cSHector Martin const union power_supply_propval *val) 463*0ebf821cSHector Martin { 464*0ebf821cSHector Martin struct macsmc_power *power = power_supply_get_drvdata(psy); 465*0ebf821cSHector Martin 466*0ebf821cSHector Martin switch (psp) { 467*0ebf821cSHector Martin case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: 468*0ebf821cSHector Martin return macsmc_battery_set_charge_behaviour(power, val->intval); 469*0ebf821cSHector Martin default: 470*0ebf821cSHector Martin return -EINVAL; 471*0ebf821cSHector Martin } 472*0ebf821cSHector Martin } 473*0ebf821cSHector Martin 474*0ebf821cSHector Martin static int macsmc_battery_property_is_writeable(struct power_supply *psy, 475*0ebf821cSHector Martin enum power_supply_property psp) 476*0ebf821cSHector Martin { 477*0ebf821cSHector Martin switch (psp) { 478*0ebf821cSHector Martin case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: 479*0ebf821cSHector Martin return true; 480*0ebf821cSHector Martin default: 481*0ebf821cSHector Martin return false; 482*0ebf821cSHector Martin } 483*0ebf821cSHector Martin } 484*0ebf821cSHector Martin 485*0ebf821cSHector Martin static const struct power_supply_desc macsmc_battery_desc_template = { 486*0ebf821cSHector Martin .name = "macsmc-battery", 487*0ebf821cSHector Martin .type = POWER_SUPPLY_TYPE_BATTERY, 488*0ebf821cSHector Martin .get_property = macsmc_battery_get_property, 489*0ebf821cSHector Martin .set_property = macsmc_battery_set_property, 490*0ebf821cSHector Martin .property_is_writeable = macsmc_battery_property_is_writeable, 491*0ebf821cSHector Martin }; 492*0ebf821cSHector Martin 493*0ebf821cSHector Martin static int macsmc_ac_get_property(struct power_supply *psy, 494*0ebf821cSHector Martin enum power_supply_property psp, 495*0ebf821cSHector Martin union power_supply_propval *val) 496*0ebf821cSHector Martin { 497*0ebf821cSHector Martin struct macsmc_power *power = power_supply_get_drvdata(psy); 498*0ebf821cSHector Martin int ret = 0; 499*0ebf821cSHector Martin u16 vu16; 500*0ebf821cSHector Martin u32 vu32; 501*0ebf821cSHector Martin 502*0ebf821cSHector Martin switch (psp) { 503*0ebf821cSHector Martin case POWER_SUPPLY_PROP_ONLINE: 504*0ebf821cSHector Martin ret = apple_smc_read_u32(power->smc, SMC_KEY(CHIS), &vu32); 505*0ebf821cSHector Martin val->intval = !!vu32; 506*0ebf821cSHector Martin break; 507*0ebf821cSHector Martin case POWER_SUPPLY_PROP_VOLTAGE_NOW: 508*0ebf821cSHector Martin ret = apple_smc_read_u16(power->smc, SMC_KEY(AC-n), &vu16); 509*0ebf821cSHector Martin val->intval = vu16 * 1000; 510*0ebf821cSHector Martin break; 511*0ebf821cSHector Martin case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 512*0ebf821cSHector Martin ret = apple_smc_read_u16(power->smc, SMC_KEY(AC-i), &vu16); 513*0ebf821cSHector Martin val->intval = vu16 * 1000; 514*0ebf821cSHector Martin break; 515*0ebf821cSHector Martin case POWER_SUPPLY_PROP_INPUT_POWER_LIMIT: 516*0ebf821cSHector Martin ret = apple_smc_read_u32(power->smc, SMC_KEY(ACPW), &vu32); 517*0ebf821cSHector Martin val->intval = vu32 * 1000; 518*0ebf821cSHector Martin break; 519*0ebf821cSHector Martin default: 520*0ebf821cSHector Martin return -EINVAL; 521*0ebf821cSHector Martin } 522*0ebf821cSHector Martin 523*0ebf821cSHector Martin return ret; 524*0ebf821cSHector Martin } 525*0ebf821cSHector Martin 526*0ebf821cSHector Martin static const struct power_supply_desc macsmc_ac_desc_template = { 527*0ebf821cSHector Martin .name = "macsmc-ac", 528*0ebf821cSHector Martin .type = POWER_SUPPLY_TYPE_MAINS, 529*0ebf821cSHector Martin .get_property = macsmc_ac_get_property, 530*0ebf821cSHector Martin }; 531*0ebf821cSHector Martin 532*0ebf821cSHector Martin static void macsmc_power_critical_work(struct work_struct *wrk) 533*0ebf821cSHector Martin { 534*0ebf821cSHector Martin struct macsmc_power *power = container_of(wrk, struct macsmc_power, critical_work); 535*0ebf821cSHector Martin u16 bitv, b0av; 536*0ebf821cSHector Martin u32 bcf0; 537*0ebf821cSHector Martin 538*0ebf821cSHector Martin if (!power->batt) 539*0ebf821cSHector Martin return; 540*0ebf821cSHector Martin 541*0ebf821cSHector Martin /* 542*0ebf821cSHector Martin * Avoid duplicate atempts at emergency shutdown 543*0ebf821cSHector Martin */ 544*0ebf821cSHector Martin if (power->emergency_shutdown_triggered || system_state > SYSTEM_RUNNING) 545*0ebf821cSHector Martin return; 546*0ebf821cSHector Martin 547*0ebf821cSHector Martin /* 548*0ebf821cSHector Martin * EMERGENCY: Check voltage vs design minimum. 549*0ebf821cSHector Martin * If we are below BITV, the battery is physically exhausted. 550*0ebf821cSHector Martin * We must shut down NOW to protect the filesystem. 551*0ebf821cSHector Martin */ 552*0ebf821cSHector Martin if (apple_smc_read_u16(power->smc, SMC_KEY(BITV), &bitv) >= 0 && 553*0ebf821cSHector Martin apple_smc_read_u16(power->smc, SMC_KEY(B0AV), &b0av) >= 0 && 554*0ebf821cSHector Martin b0av < bitv) { 555*0ebf821cSHector Martin power->emergency_shutdown_triggered = true; 556*0ebf821cSHector Martin dev_emerg(power->dev, 557*0ebf821cSHector Martin "Battery voltage (%d mV) below design minimum (%d mV)! Emergency shutdown.\n", 558*0ebf821cSHector Martin b0av, bitv); 559*0ebf821cSHector Martin 560*0ebf821cSHector Martin /* 561*0ebf821cSHector Martin * Shutdown is now imminent. Kick userspace again and give it some 562*0ebf821cSHector Martin * brief time to (hopefully) flush what's needed, before forcing. 563*0ebf821cSHector Martin */ 564*0ebf821cSHector Martin hw_protection_trigger("Battery voltage below design minimum", 1500); 565*0ebf821cSHector Martin } 566*0ebf821cSHector Martin 567*0ebf821cSHector Martin /* 568*0ebf821cSHector Martin * Avoid duplicate attempts at orderly shutdown. 569*0ebf821cSHector Martin * Voltage check is above this as we may want to 570*0ebf821cSHector Martin * "upgrade" an orderly shutdown to a critical power 571*0ebf821cSHector Martin * off if voltage drops. 572*0ebf821cSHector Martin */ 573*0ebf821cSHector Martin if (power->orderly_shutdown_triggered || system_state > SYSTEM_RUNNING) 574*0ebf821cSHector Martin return; 575*0ebf821cSHector Martin 576*0ebf821cSHector Martin /* 577*0ebf821cSHector Martin * Check if SMC flagged the battery as empty. 578*0ebf821cSHector Martin * We trigger a graceful shutdown to let the OS save data. 579*0ebf821cSHector Martin */ 580*0ebf821cSHector Martin if (apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &bcf0) == 0 && bcf0 != 0) { 581*0ebf821cSHector Martin power->orderly_shutdown_triggered = true; 582*0ebf821cSHector Martin dev_crit(power->dev, "Battery critical (empty flag set). Triggering orderly shutdown.\n"); 583*0ebf821cSHector Martin orderly_poweroff(true); 584*0ebf821cSHector Martin } 585*0ebf821cSHector Martin } 586*0ebf821cSHector Martin 587*0ebf821cSHector Martin static int macsmc_power_event(struct notifier_block *nb, unsigned long event, void *data) 588*0ebf821cSHector Martin { 589*0ebf821cSHector Martin struct macsmc_power *power = container_of(nb, struct macsmc_power, nb); 590*0ebf821cSHector Martin 591*0ebf821cSHector Martin /* 592*0ebf821cSHector Martin * SMC Event IDs are correlated to physical events (e.g. charger 593*0ebf821cSHector Martin * connect/disconnect) but the exact meaning of each ID is predicted. 594*0ebf821cSHector Martin * 0x71... indicates power/battery events. 595*0ebf821cSHector Martin */ 596*0ebf821cSHector Martin if ((event & 0xffffff00) == 0x71010100 || /* Charger status change */ 597*0ebf821cSHector Martin (event & 0xffff0000) == 0x71060000 || /* Port charge state change */ 598*0ebf821cSHector Martin (event & 0xffff0000) == 0x71130000) { /* Connector insert/remove event */ 599*0ebf821cSHector Martin if (power->batt) 600*0ebf821cSHector Martin power_supply_changed(power->batt); 601*0ebf821cSHector Martin if (power->ac) 602*0ebf821cSHector Martin power_supply_changed(power->ac); 603*0ebf821cSHector Martin return NOTIFY_OK; 604*0ebf821cSHector Martin } else if (event == 0x71020000) { 605*0ebf821cSHector Martin /* Critical battery warning */ 606*0ebf821cSHector Martin if (power->batt) 607*0ebf821cSHector Martin schedule_work(&power->critical_work); 608*0ebf821cSHector Martin return NOTIFY_OK; 609*0ebf821cSHector Martin } 610*0ebf821cSHector Martin 611*0ebf821cSHector Martin return NOTIFY_DONE; 612*0ebf821cSHector Martin } 613*0ebf821cSHector Martin 614*0ebf821cSHector Martin static int macsmc_power_probe(struct platform_device *pdev) 615*0ebf821cSHector Martin { 616*0ebf821cSHector Martin struct device *dev = &pdev->dev; 617*0ebf821cSHector Martin struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent); 618*0ebf821cSHector Martin struct power_supply_config psy_cfg = {}; 619*0ebf821cSHector Martin struct macsmc_power *power; 620*0ebf821cSHector Martin bool has_battery = false; 621*0ebf821cSHector Martin bool has_ac_adapter = false; 622*0ebf821cSHector Martin int ret = -ENODEV; 623*0ebf821cSHector Martin bool flag; 624*0ebf821cSHector Martin u16 vu16; 625*0ebf821cSHector Martin u32 val32; 626*0ebf821cSHector Martin enum power_supply_property *props; 627*0ebf821cSHector Martin size_t nprops; 628*0ebf821cSHector Martin 629*0ebf821cSHector Martin if (!smc) 630*0ebf821cSHector Martin return -ENODEV; 631*0ebf821cSHector Martin 632*0ebf821cSHector Martin power = devm_kzalloc(dev, sizeof(*power), GFP_KERNEL); 633*0ebf821cSHector Martin if (!power) 634*0ebf821cSHector Martin return -ENOMEM; 635*0ebf821cSHector Martin 636*0ebf821cSHector Martin power->dev = dev; 637*0ebf821cSHector Martin power->smc = smc; 638*0ebf821cSHector Martin dev_set_drvdata(dev, power); 639*0ebf821cSHector Martin 640*0ebf821cSHector Martin INIT_WORK(&power->critical_work, macsmc_power_critical_work); 641*0ebf821cSHector Martin ret = devm_work_autocancel(dev, &power->critical_work, macsmc_power_critical_work); 642*0ebf821cSHector Martin if (ret) 643*0ebf821cSHector Martin return ret; 644*0ebf821cSHector Martin 645*0ebf821cSHector Martin /* 646*0ebf821cSHector Martin * Check for battery presence. 647*0ebf821cSHector Martin * B0AV is a fundamental key. 648*0ebf821cSHector Martin */ 649*0ebf821cSHector Martin if (apple_smc_read_u16(power->smc, SMC_KEY(B0AV), &vu16) == 0 && 650*0ebf821cSHector Martin macsmc_battery_get_status(power) > POWER_SUPPLY_STATUS_UNKNOWN) 651*0ebf821cSHector Martin has_battery = true; 652*0ebf821cSHector Martin 653*0ebf821cSHector Martin /* 654*0ebf821cSHector Martin * Check for AC adapter presence. 655*0ebf821cSHector Martin * CHIS is a fundamental key. 656*0ebf821cSHector Martin */ 657*0ebf821cSHector Martin if (apple_smc_key_exists(smc, SMC_KEY(CHIS))) 658*0ebf821cSHector Martin has_ac_adapter = true; 659*0ebf821cSHector Martin 660*0ebf821cSHector Martin if (!has_battery && !has_ac_adapter) 661*0ebf821cSHector Martin return -ENODEV; 662*0ebf821cSHector Martin 663*0ebf821cSHector Martin if (has_battery) { 664*0ebf821cSHector Martin power->batt_desc = macsmc_battery_desc_template; 665*0ebf821cSHector Martin props = devm_kcalloc(dev, MACSMC_MAX_BATT_PROPS, 666*0ebf821cSHector Martin sizeof(enum power_supply_property), 667*0ebf821cSHector Martin GFP_KERNEL); 668*0ebf821cSHector Martin if (!props) 669*0ebf821cSHector Martin return -ENOMEM; 670*0ebf821cSHector Martin 671*0ebf821cSHector Martin nprops = 0; 672*0ebf821cSHector Martin 673*0ebf821cSHector Martin /* Fundamental properties */ 674*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_STATUS; 675*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_PRESENT; 676*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_NOW; 677*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_CURRENT_NOW; 678*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_POWER_NOW; 679*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_CAPACITY; 680*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_CAPACITY_LEVEL; 681*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_TEMP; 682*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_CYCLE_COUNT; 683*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_HEALTH; 684*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_SCOPE; 685*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_MODEL_NAME; 686*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_SERIAL_NUMBER; 687*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_MANUFACTURE_YEAR; 688*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_MANUFACTURE_MONTH; 689*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_MANUFACTURE_DAY; 690*0ebf821cSHector Martin 691*0ebf821cSHector Martin /* Extended properties usually present */ 692*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW; 693*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_TIME_TO_FULL_NOW; 694*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN; 695*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN; 696*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_MIN; 697*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_MAX; 698*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT; 699*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX; 700*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE; 701*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN; 702*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_CHARGE_FULL; 703*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_CHARGE_NOW; 704*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN; 705*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_ENERGY_FULL; 706*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_ENERGY_NOW; 707*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_CHARGE_COUNTER; 708*0ebf821cSHector Martin 709*0ebf821cSHector Martin /* Detect features based on key availability */ 710*0ebf821cSHector Martin if (apple_smc_key_exists(smc, SMC_KEY(CHTE))) 711*0ebf821cSHector Martin power->has_chte = true; 712*0ebf821cSHector Martin if (apple_smc_key_exists(smc, SMC_KEY(CH0C))) 713*0ebf821cSHector Martin power->has_ch0c = true; 714*0ebf821cSHector Martin if (apple_smc_key_exists(smc, SMC_KEY(CH0I))) 715*0ebf821cSHector Martin power->has_ch0i = true; 716*0ebf821cSHector Martin 717*0ebf821cSHector Martin /* Reset "Optimised Battery Charging" flags to default state */ 718*0ebf821cSHector Martin if (power->has_chte) 719*0ebf821cSHector Martin apple_smc_write_u32(smc, SMC_KEY(CHTE), 0); 720*0ebf821cSHector Martin else if (power->has_ch0c) 721*0ebf821cSHector Martin apple_smc_write_u8(smc, SMC_KEY(CH0C), 0); 722*0ebf821cSHector Martin 723*0ebf821cSHector Martin if (power->has_ch0i) 724*0ebf821cSHector Martin apple_smc_write_u8(smc, SMC_KEY(CH0I), 0); 725*0ebf821cSHector Martin 726*0ebf821cSHector Martin apple_smc_write_u8(smc, SMC_KEY(CH0K), 0); 727*0ebf821cSHector Martin apple_smc_write_u8(smc, SMC_KEY(CH0B), 0); 728*0ebf821cSHector Martin 729*0ebf821cSHector Martin /* Configure charge behaviour if supported */ 730*0ebf821cSHector Martin if (power->has_ch0i || power->has_ch0c || power->has_chte) { 731*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR; 732*0ebf821cSHector Martin 733*0ebf821cSHector Martin power->batt_desc.charge_behaviours = 734*0ebf821cSHector Martin BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO); 735*0ebf821cSHector Martin 736*0ebf821cSHector Martin if (power->has_ch0i) 737*0ebf821cSHector Martin power->batt_desc.charge_behaviours |= 738*0ebf821cSHector Martin BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE); 739*0ebf821cSHector Martin 740*0ebf821cSHector Martin if (power->has_chte || power->has_ch0c) 741*0ebf821cSHector Martin power->batt_desc.charge_behaviours |= 742*0ebf821cSHector Martin BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE); 743*0ebf821cSHector Martin } 744*0ebf821cSHector Martin 745*0ebf821cSHector Martin /* Detect charge limit method (CHWA vs CHLS) */ 746*0ebf821cSHector Martin if (apple_smc_read_flag(power->smc, SMC_KEY(CHWA), &flag) == 0) 747*0ebf821cSHector Martin power->has_chwa = true; 748*0ebf821cSHector Martin else if (apple_smc_read_u16(power->smc, SMC_KEY(CHLS), &vu16) >= 0) 749*0ebf821cSHector Martin power->has_chls = true; 750*0ebf821cSHector Martin 751*0ebf821cSHector Martin if (nprops > MACSMC_MAX_BATT_PROPS) 752*0ebf821cSHector Martin return -ENOMEM; 753*0ebf821cSHector Martin 754*0ebf821cSHector Martin power->batt_desc.properties = props; 755*0ebf821cSHector Martin power->batt_desc.num_properties = nprops; 756*0ebf821cSHector Martin 757*0ebf821cSHector Martin /* Fetch identity strings */ 758*0ebf821cSHector Martin apple_smc_read(smc, SMC_KEY(BMDN), power->model_name, 759*0ebf821cSHector Martin sizeof(power->model_name) - 1); 760*0ebf821cSHector Martin apple_smc_read(smc, SMC_KEY(BMSN), power->serial_number, 761*0ebf821cSHector Martin sizeof(power->serial_number) - 1); 762*0ebf821cSHector Martin apple_smc_read(smc, SMC_KEY(BMDT), power->mfg_date, 763*0ebf821cSHector Martin sizeof(power->mfg_date) - 1); 764*0ebf821cSHector Martin 765*0ebf821cSHector Martin apple_smc_read_u8(power->smc, SMC_KEY(BNCB), &power->num_cells); 766*0ebf821cSHector Martin power->nominal_voltage_mv = MACSMC_NOMINAL_CELL_VOLTAGE_MV * power->num_cells; 767*0ebf821cSHector Martin 768*0ebf821cSHector Martin /* Enable critical shutdown notifications by reading status once */ 769*0ebf821cSHector Martin apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &val32); 770*0ebf821cSHector Martin 771*0ebf821cSHector Martin psy_cfg.drv_data = power; 772*0ebf821cSHector Martin power->batt = devm_power_supply_register(dev, &power->batt_desc, &psy_cfg); 773*0ebf821cSHector Martin if (IS_ERR(power->batt)) { 774*0ebf821cSHector Martin dev_err_probe(dev, PTR_ERR(power->batt), 775*0ebf821cSHector Martin "Failed to register battery\n"); 776*0ebf821cSHector Martin /* Don't return failure yet; try AC registration first */ 777*0ebf821cSHector Martin power->batt = NULL; 778*0ebf821cSHector Martin } 779*0ebf821cSHector Martin } 780*0ebf821cSHector Martin 781*0ebf821cSHector Martin if (has_ac_adapter) { 782*0ebf821cSHector Martin power->ac_desc = macsmc_ac_desc_template; 783*0ebf821cSHector Martin props = devm_kcalloc(dev, MACSMC_MAX_AC_PROPS, 784*0ebf821cSHector Martin sizeof(enum power_supply_property), 785*0ebf821cSHector Martin GFP_KERNEL); 786*0ebf821cSHector Martin if (!props) 787*0ebf821cSHector Martin return -ENOMEM; 788*0ebf821cSHector Martin 789*0ebf821cSHector Martin nprops = 0; 790*0ebf821cSHector Martin 791*0ebf821cSHector Martin /* Online status is fundamental */ 792*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_ONLINE; 793*0ebf821cSHector Martin 794*0ebf821cSHector Martin /* Input power limits are usually available */ 795*0ebf821cSHector Martin if (apple_smc_key_exists(power->smc, SMC_KEY(ACPW))) 796*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_INPUT_POWER_LIMIT; 797*0ebf821cSHector Martin 798*0ebf821cSHector Martin /* macOS 15.4+ firmware dropped legacy AC keys (AC-n, AC-i) */ 799*0ebf821cSHector Martin if (apple_smc_read_u16(power->smc, SMC_KEY(AC-n), &vu16) >= 0) { 800*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_VOLTAGE_NOW; 801*0ebf821cSHector Martin props[nprops++] = POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT; 802*0ebf821cSHector Martin } 803*0ebf821cSHector Martin 804*0ebf821cSHector Martin if (nprops > MACSMC_MAX_AC_PROPS) 805*0ebf821cSHector Martin return -ENOMEM; 806*0ebf821cSHector Martin 807*0ebf821cSHector Martin power->ac_desc.properties = props; 808*0ebf821cSHector Martin power->ac_desc.num_properties = nprops; 809*0ebf821cSHector Martin 810*0ebf821cSHector Martin psy_cfg.drv_data = power; 811*0ebf821cSHector Martin power->ac = devm_power_supply_register(dev, &power->ac_desc, &psy_cfg); 812*0ebf821cSHector Martin if (IS_ERR(power->ac)) { 813*0ebf821cSHector Martin dev_err_probe(dev, PTR_ERR(power->ac), 814*0ebf821cSHector Martin "Failed to register AC adapter\n"); 815*0ebf821cSHector Martin power->ac = NULL; 816*0ebf821cSHector Martin } 817*0ebf821cSHector Martin } 818*0ebf821cSHector Martin 819*0ebf821cSHector Martin /* Final check: did we register anything? */ 820*0ebf821cSHector Martin if (!power->batt && !power->ac) 821*0ebf821cSHector Martin return -ENODEV; 822*0ebf821cSHector Martin 823*0ebf821cSHector Martin power->nb.notifier_call = macsmc_power_event; 824*0ebf821cSHector Martin blocking_notifier_chain_register(&smc->event_handlers, &power->nb); 825*0ebf821cSHector Martin 826*0ebf821cSHector Martin return 0; 827*0ebf821cSHector Martin } 828*0ebf821cSHector Martin 829*0ebf821cSHector Martin static void macsmc_power_remove(struct platform_device *pdev) 830*0ebf821cSHector Martin { 831*0ebf821cSHector Martin struct macsmc_power *power = dev_get_drvdata(&pdev->dev); 832*0ebf821cSHector Martin 833*0ebf821cSHector Martin blocking_notifier_chain_unregister(&power->smc->event_handlers, &power->nb); 834*0ebf821cSHector Martin } 835*0ebf821cSHector Martin 836*0ebf821cSHector Martin static const struct platform_device_id macsmc_power_id[] = { 837*0ebf821cSHector Martin { "macsmc-power" }, 838*0ebf821cSHector Martin { /* sentinel */ } 839*0ebf821cSHector Martin }; 840*0ebf821cSHector Martin MODULE_DEVICE_TABLE(platform, macsmc_power_id); 841*0ebf821cSHector Martin 842*0ebf821cSHector Martin static struct platform_driver macsmc_power_driver = { 843*0ebf821cSHector Martin .driver = { 844*0ebf821cSHector Martin .name = "macsmc-power", 845*0ebf821cSHector Martin }, 846*0ebf821cSHector Martin .id_table = macsmc_power_id, 847*0ebf821cSHector Martin .probe = macsmc_power_probe, 848*0ebf821cSHector Martin .remove = macsmc_power_remove, 849*0ebf821cSHector Martin }; 850*0ebf821cSHector Martin module_platform_driver(macsmc_power_driver); 851*0ebf821cSHector Martin 852*0ebf821cSHector Martin MODULE_LICENSE("Dual MIT/GPL"); 853*0ebf821cSHector Martin MODULE_DESCRIPTION("Apple SMC battery and power management driver"); 854*0ebf821cSHector Martin MODULE_AUTHOR("Hector Martin <marcan@marcan.st>"); 855*0ebf821cSHector Martin MODULE_AUTHOR("Michael Reeves <michael.reeves077@gmail.com>"); 856