1*4b6b6433SSamuel Kayode // SPDX-License-Identifier: GPL-2.0 2*4b6b6433SSamuel Kayode /* 3*4b6b6433SSamuel Kayode * charger driver for the PF1550 4*4b6b6433SSamuel Kayode * 5*4b6b6433SSamuel Kayode * Copyright (C) 2016 Freescale Semiconductor, Inc. 6*4b6b6433SSamuel Kayode * Robin Gong <yibin.gong@freescale.com> 7*4b6b6433SSamuel Kayode * 8*4b6b6433SSamuel Kayode * Portions Copyright (c) 2025 Savoir-faire Linux Inc. 9*4b6b6433SSamuel Kayode * Samuel Kayode <samuel.kayode@savoirfairelinux.com> 10*4b6b6433SSamuel Kayode */ 11*4b6b6433SSamuel Kayode 12*4b6b6433SSamuel Kayode #include <linux/devm-helpers.h> 13*4b6b6433SSamuel Kayode #include <linux/interrupt.h> 14*4b6b6433SSamuel Kayode #include <linux/mfd/pf1550.h> 15*4b6b6433SSamuel Kayode #include <linux/module.h> 16*4b6b6433SSamuel Kayode #include <linux/platform_device.h> 17*4b6b6433SSamuel Kayode #include <linux/power_supply.h> 18*4b6b6433SSamuel Kayode 19*4b6b6433SSamuel Kayode #define PF1550_DEFAULT_CONSTANT_VOLT 4200000 20*4b6b6433SSamuel Kayode #define PF1550_DEFAULT_MIN_SYSTEM_VOLT 3500000 21*4b6b6433SSamuel Kayode #define PF1550_DEFAULT_THERMAL_TEMP 95 22*4b6b6433SSamuel Kayode #define PF1550_CHARGER_IRQ_NR 5 23*4b6b6433SSamuel Kayode 24*4b6b6433SSamuel Kayode struct pf1550_charger { 25*4b6b6433SSamuel Kayode struct device *dev; 26*4b6b6433SSamuel Kayode const struct pf1550_ddata *pf1550; 27*4b6b6433SSamuel Kayode struct power_supply *charger; 28*4b6b6433SSamuel Kayode struct power_supply *battery; 29*4b6b6433SSamuel Kayode struct delayed_work vbus_sense_work; 30*4b6b6433SSamuel Kayode struct delayed_work chg_sense_work; 31*4b6b6433SSamuel Kayode struct delayed_work bat_sense_work; 32*4b6b6433SSamuel Kayode int virqs[PF1550_CHARGER_IRQ_NR]; 33*4b6b6433SSamuel Kayode 34*4b6b6433SSamuel Kayode u32 constant_volt; 35*4b6b6433SSamuel Kayode u32 min_system_volt; 36*4b6b6433SSamuel Kayode u32 thermal_regulation_temp; 37*4b6b6433SSamuel Kayode }; 38*4b6b6433SSamuel Kayode 39*4b6b6433SSamuel Kayode static int pf1550_get_charger_state(struct regmap *regmap, int *val) 40*4b6b6433SSamuel Kayode { 41*4b6b6433SSamuel Kayode unsigned int data; 42*4b6b6433SSamuel Kayode int ret; 43*4b6b6433SSamuel Kayode 44*4b6b6433SSamuel Kayode ret = regmap_read(regmap, PF1550_CHARG_REG_CHG_SNS, &data); 45*4b6b6433SSamuel Kayode if (ret < 0) 46*4b6b6433SSamuel Kayode return ret; 47*4b6b6433SSamuel Kayode 48*4b6b6433SSamuel Kayode data &= PF1550_CHG_SNS_MASK; 49*4b6b6433SSamuel Kayode 50*4b6b6433SSamuel Kayode switch (data) { 51*4b6b6433SSamuel Kayode case PF1550_CHG_PRECHARGE: 52*4b6b6433SSamuel Kayode case PF1550_CHG_CONSTANT_CURRENT: 53*4b6b6433SSamuel Kayode case PF1550_CHG_CONSTANT_VOL: 54*4b6b6433SSamuel Kayode case PF1550_CHG_EOC: 55*4b6b6433SSamuel Kayode *val = POWER_SUPPLY_STATUS_CHARGING; 56*4b6b6433SSamuel Kayode break; 57*4b6b6433SSamuel Kayode case PF1550_CHG_DONE: 58*4b6b6433SSamuel Kayode *val = POWER_SUPPLY_STATUS_FULL; 59*4b6b6433SSamuel Kayode break; 60*4b6b6433SSamuel Kayode case PF1550_CHG_TIMER_FAULT: 61*4b6b6433SSamuel Kayode case PF1550_CHG_SUSPEND: 62*4b6b6433SSamuel Kayode *val = POWER_SUPPLY_STATUS_NOT_CHARGING; 63*4b6b6433SSamuel Kayode break; 64*4b6b6433SSamuel Kayode case PF1550_CHG_OFF_INV: 65*4b6b6433SSamuel Kayode case PF1550_CHG_OFF_TEMP: 66*4b6b6433SSamuel Kayode case PF1550_CHG_LINEAR_ONLY: 67*4b6b6433SSamuel Kayode *val = POWER_SUPPLY_STATUS_DISCHARGING; 68*4b6b6433SSamuel Kayode break; 69*4b6b6433SSamuel Kayode default: 70*4b6b6433SSamuel Kayode *val = POWER_SUPPLY_STATUS_UNKNOWN; 71*4b6b6433SSamuel Kayode } 72*4b6b6433SSamuel Kayode 73*4b6b6433SSamuel Kayode return 0; 74*4b6b6433SSamuel Kayode } 75*4b6b6433SSamuel Kayode 76*4b6b6433SSamuel Kayode static int pf1550_get_charge_type(struct regmap *regmap, int *val) 77*4b6b6433SSamuel Kayode { 78*4b6b6433SSamuel Kayode unsigned int data; 79*4b6b6433SSamuel Kayode int ret; 80*4b6b6433SSamuel Kayode 81*4b6b6433SSamuel Kayode ret = regmap_read(regmap, PF1550_CHARG_REG_CHG_SNS, &data); 82*4b6b6433SSamuel Kayode if (ret < 0) 83*4b6b6433SSamuel Kayode return ret; 84*4b6b6433SSamuel Kayode 85*4b6b6433SSamuel Kayode data &= PF1550_CHG_SNS_MASK; 86*4b6b6433SSamuel Kayode 87*4b6b6433SSamuel Kayode switch (data) { 88*4b6b6433SSamuel Kayode case PF1550_CHG_SNS_MASK: 89*4b6b6433SSamuel Kayode *val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; 90*4b6b6433SSamuel Kayode break; 91*4b6b6433SSamuel Kayode case PF1550_CHG_CONSTANT_CURRENT: 92*4b6b6433SSamuel Kayode case PF1550_CHG_CONSTANT_VOL: 93*4b6b6433SSamuel Kayode case PF1550_CHG_EOC: 94*4b6b6433SSamuel Kayode *val = POWER_SUPPLY_CHARGE_TYPE_FAST; 95*4b6b6433SSamuel Kayode break; 96*4b6b6433SSamuel Kayode case PF1550_CHG_DONE: 97*4b6b6433SSamuel Kayode case PF1550_CHG_TIMER_FAULT: 98*4b6b6433SSamuel Kayode case PF1550_CHG_SUSPEND: 99*4b6b6433SSamuel Kayode case PF1550_CHG_OFF_INV: 100*4b6b6433SSamuel Kayode case PF1550_CHG_BAT_OVER: 101*4b6b6433SSamuel Kayode case PF1550_CHG_OFF_TEMP: 102*4b6b6433SSamuel Kayode case PF1550_CHG_LINEAR_ONLY: 103*4b6b6433SSamuel Kayode *val = POWER_SUPPLY_CHARGE_TYPE_NONE; 104*4b6b6433SSamuel Kayode break; 105*4b6b6433SSamuel Kayode default: 106*4b6b6433SSamuel Kayode *val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; 107*4b6b6433SSamuel Kayode } 108*4b6b6433SSamuel Kayode 109*4b6b6433SSamuel Kayode return 0; 110*4b6b6433SSamuel Kayode } 111*4b6b6433SSamuel Kayode 112*4b6b6433SSamuel Kayode /* 113*4b6b6433SSamuel Kayode * Supported health statuses: 114*4b6b6433SSamuel Kayode * - POWER_SUPPLY_HEALTH_DEAD 115*4b6b6433SSamuel Kayode * - POWER_SUPPLY_HEALTH_GOOD 116*4b6b6433SSamuel Kayode * - POWER_SUPPLY_HEALTH_OVERVOLTAGE 117*4b6b6433SSamuel Kayode * - POWER_SUPPLY_HEALTH_UNKNOWN 118*4b6b6433SSamuel Kayode */ 119*4b6b6433SSamuel Kayode static int pf1550_get_battery_health(struct regmap *regmap, int *val) 120*4b6b6433SSamuel Kayode { 121*4b6b6433SSamuel Kayode unsigned int data; 122*4b6b6433SSamuel Kayode int ret; 123*4b6b6433SSamuel Kayode 124*4b6b6433SSamuel Kayode ret = regmap_read(regmap, PF1550_CHARG_REG_BATT_SNS, &data); 125*4b6b6433SSamuel Kayode if (ret < 0) 126*4b6b6433SSamuel Kayode return ret; 127*4b6b6433SSamuel Kayode 128*4b6b6433SSamuel Kayode data &= PF1550_BAT_SNS_MASK; 129*4b6b6433SSamuel Kayode 130*4b6b6433SSamuel Kayode switch (data) { 131*4b6b6433SSamuel Kayode case PF1550_BAT_NO_DETECT: 132*4b6b6433SSamuel Kayode *val = POWER_SUPPLY_HEALTH_NO_BATTERY; 133*4b6b6433SSamuel Kayode break; 134*4b6b6433SSamuel Kayode case PF1550_BAT_NO_VBUS: 135*4b6b6433SSamuel Kayode case PF1550_BAT_LOW_THAN_PRECHARG: 136*4b6b6433SSamuel Kayode case PF1550_BAT_CHARG_FAIL: 137*4b6b6433SSamuel Kayode case PF1550_BAT_HIGH_THAN_PRECHARG: 138*4b6b6433SSamuel Kayode *val = POWER_SUPPLY_HEALTH_GOOD; 139*4b6b6433SSamuel Kayode break; 140*4b6b6433SSamuel Kayode case PF1550_BAT_OVER_VOL: 141*4b6b6433SSamuel Kayode *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 142*4b6b6433SSamuel Kayode break; 143*4b6b6433SSamuel Kayode default: 144*4b6b6433SSamuel Kayode *val = POWER_SUPPLY_HEALTH_UNKNOWN; 145*4b6b6433SSamuel Kayode break; 146*4b6b6433SSamuel Kayode } 147*4b6b6433SSamuel Kayode 148*4b6b6433SSamuel Kayode return 0; 149*4b6b6433SSamuel Kayode } 150*4b6b6433SSamuel Kayode 151*4b6b6433SSamuel Kayode static int pf1550_get_present(struct regmap *regmap, int *val) 152*4b6b6433SSamuel Kayode { 153*4b6b6433SSamuel Kayode unsigned int data; 154*4b6b6433SSamuel Kayode int ret; 155*4b6b6433SSamuel Kayode 156*4b6b6433SSamuel Kayode ret = regmap_read(regmap, PF1550_CHARG_REG_BATT_SNS, &data); 157*4b6b6433SSamuel Kayode if (ret < 0) 158*4b6b6433SSamuel Kayode return ret; 159*4b6b6433SSamuel Kayode 160*4b6b6433SSamuel Kayode data &= PF1550_BAT_SNS_MASK; 161*4b6b6433SSamuel Kayode *val = (data == PF1550_BAT_NO_DETECT) ? 0 : 1; 162*4b6b6433SSamuel Kayode 163*4b6b6433SSamuel Kayode return 0; 164*4b6b6433SSamuel Kayode } 165*4b6b6433SSamuel Kayode 166*4b6b6433SSamuel Kayode static int pf1550_get_online(struct regmap *regmap, int *val) 167*4b6b6433SSamuel Kayode { 168*4b6b6433SSamuel Kayode unsigned int data; 169*4b6b6433SSamuel Kayode int ret; 170*4b6b6433SSamuel Kayode 171*4b6b6433SSamuel Kayode ret = regmap_read(regmap, PF1550_CHARG_REG_VBUS_SNS, &data); 172*4b6b6433SSamuel Kayode if (ret < 0) 173*4b6b6433SSamuel Kayode return ret; 174*4b6b6433SSamuel Kayode 175*4b6b6433SSamuel Kayode *val = (data & PF1550_VBUS_VALID) ? 1 : 0; 176*4b6b6433SSamuel Kayode 177*4b6b6433SSamuel Kayode return 0; 178*4b6b6433SSamuel Kayode } 179*4b6b6433SSamuel Kayode 180*4b6b6433SSamuel Kayode static void pf1550_chg_bat_work(struct work_struct *work) 181*4b6b6433SSamuel Kayode { 182*4b6b6433SSamuel Kayode struct pf1550_charger *chg = container_of(to_delayed_work(work), 183*4b6b6433SSamuel Kayode struct pf1550_charger, 184*4b6b6433SSamuel Kayode bat_sense_work); 185*4b6b6433SSamuel Kayode unsigned int data; 186*4b6b6433SSamuel Kayode 187*4b6b6433SSamuel Kayode if (regmap_read(chg->pf1550->regmap, PF1550_CHARG_REG_BATT_SNS, &data)) { 188*4b6b6433SSamuel Kayode dev_err(chg->dev, "Read BATT_SNS error.\n"); 189*4b6b6433SSamuel Kayode return; 190*4b6b6433SSamuel Kayode } 191*4b6b6433SSamuel Kayode 192*4b6b6433SSamuel Kayode switch (data & PF1550_BAT_SNS_MASK) { 193*4b6b6433SSamuel Kayode case PF1550_BAT_NO_VBUS: 194*4b6b6433SSamuel Kayode dev_dbg(chg->dev, "No valid VBUS input.\n"); 195*4b6b6433SSamuel Kayode break; 196*4b6b6433SSamuel Kayode case PF1550_BAT_LOW_THAN_PRECHARG: 197*4b6b6433SSamuel Kayode dev_dbg(chg->dev, "VBAT < VPRECHG.LB.\n"); 198*4b6b6433SSamuel Kayode break; 199*4b6b6433SSamuel Kayode case PF1550_BAT_CHARG_FAIL: 200*4b6b6433SSamuel Kayode dev_dbg(chg->dev, "Battery charging failed.\n"); 201*4b6b6433SSamuel Kayode break; 202*4b6b6433SSamuel Kayode case PF1550_BAT_HIGH_THAN_PRECHARG: 203*4b6b6433SSamuel Kayode dev_dbg(chg->dev, "VBAT > VPRECHG.LB.\n"); 204*4b6b6433SSamuel Kayode break; 205*4b6b6433SSamuel Kayode case PF1550_BAT_OVER_VOL: 206*4b6b6433SSamuel Kayode dev_dbg(chg->dev, "VBAT > VBATOV.\n"); 207*4b6b6433SSamuel Kayode break; 208*4b6b6433SSamuel Kayode case PF1550_BAT_NO_DETECT: 209*4b6b6433SSamuel Kayode dev_dbg(chg->dev, "Battery not detected.\n"); 210*4b6b6433SSamuel Kayode break; 211*4b6b6433SSamuel Kayode default: 212*4b6b6433SSamuel Kayode dev_err(chg->dev, "Unknown value read:%x\n", 213*4b6b6433SSamuel Kayode data & PF1550_CHG_SNS_MASK); 214*4b6b6433SSamuel Kayode } 215*4b6b6433SSamuel Kayode } 216*4b6b6433SSamuel Kayode 217*4b6b6433SSamuel Kayode static void pf1550_chg_chg_work(struct work_struct *work) 218*4b6b6433SSamuel Kayode { 219*4b6b6433SSamuel Kayode struct pf1550_charger *chg = container_of(to_delayed_work(work), 220*4b6b6433SSamuel Kayode struct pf1550_charger, 221*4b6b6433SSamuel Kayode chg_sense_work); 222*4b6b6433SSamuel Kayode unsigned int data; 223*4b6b6433SSamuel Kayode 224*4b6b6433SSamuel Kayode if (regmap_read(chg->pf1550->regmap, PF1550_CHARG_REG_CHG_SNS, &data)) { 225*4b6b6433SSamuel Kayode dev_err(chg->dev, "Read CHG_SNS error.\n"); 226*4b6b6433SSamuel Kayode return; 227*4b6b6433SSamuel Kayode } 228*4b6b6433SSamuel Kayode 229*4b6b6433SSamuel Kayode switch (data & PF1550_CHG_SNS_MASK) { 230*4b6b6433SSamuel Kayode case PF1550_CHG_PRECHARGE: 231*4b6b6433SSamuel Kayode dev_dbg(chg->dev, "In pre-charger mode.\n"); 232*4b6b6433SSamuel Kayode break; 233*4b6b6433SSamuel Kayode case PF1550_CHG_CONSTANT_CURRENT: 234*4b6b6433SSamuel Kayode dev_dbg(chg->dev, "In fast-charge constant current mode.\n"); 235*4b6b6433SSamuel Kayode break; 236*4b6b6433SSamuel Kayode case PF1550_CHG_CONSTANT_VOL: 237*4b6b6433SSamuel Kayode dev_dbg(chg->dev, "In fast-charge constant voltage mode.\n"); 238*4b6b6433SSamuel Kayode break; 239*4b6b6433SSamuel Kayode case PF1550_CHG_EOC: 240*4b6b6433SSamuel Kayode dev_dbg(chg->dev, "In EOC mode.\n"); 241*4b6b6433SSamuel Kayode break; 242*4b6b6433SSamuel Kayode case PF1550_CHG_DONE: 243*4b6b6433SSamuel Kayode dev_dbg(chg->dev, "In DONE mode.\n"); 244*4b6b6433SSamuel Kayode break; 245*4b6b6433SSamuel Kayode case PF1550_CHG_TIMER_FAULT: 246*4b6b6433SSamuel Kayode dev_info(chg->dev, "In timer fault mode.\n"); 247*4b6b6433SSamuel Kayode break; 248*4b6b6433SSamuel Kayode case PF1550_CHG_SUSPEND: 249*4b6b6433SSamuel Kayode dev_info(chg->dev, "In thermistor suspend mode.\n"); 250*4b6b6433SSamuel Kayode break; 251*4b6b6433SSamuel Kayode case PF1550_CHG_OFF_INV: 252*4b6b6433SSamuel Kayode dev_info(chg->dev, "Input invalid, charger off.\n"); 253*4b6b6433SSamuel Kayode break; 254*4b6b6433SSamuel Kayode case PF1550_CHG_BAT_OVER: 255*4b6b6433SSamuel Kayode dev_warn(chg->dev, "Battery over-voltage.\n"); 256*4b6b6433SSamuel Kayode break; 257*4b6b6433SSamuel Kayode case PF1550_CHG_OFF_TEMP: 258*4b6b6433SSamuel Kayode dev_info(chg->dev, "Temp high, charger off.\n"); 259*4b6b6433SSamuel Kayode break; 260*4b6b6433SSamuel Kayode case PF1550_CHG_LINEAR_ONLY: 261*4b6b6433SSamuel Kayode dev_dbg(chg->dev, "In Linear mode, not charging.\n"); 262*4b6b6433SSamuel Kayode break; 263*4b6b6433SSamuel Kayode default: 264*4b6b6433SSamuel Kayode dev_err(chg->dev, "Unknown value read:%x\n", 265*4b6b6433SSamuel Kayode data & PF1550_CHG_SNS_MASK); 266*4b6b6433SSamuel Kayode } 267*4b6b6433SSamuel Kayode } 268*4b6b6433SSamuel Kayode 269*4b6b6433SSamuel Kayode static void pf1550_chg_vbus_work(struct work_struct *work) 270*4b6b6433SSamuel Kayode { 271*4b6b6433SSamuel Kayode struct pf1550_charger *chg = container_of(to_delayed_work(work), 272*4b6b6433SSamuel Kayode struct pf1550_charger, 273*4b6b6433SSamuel Kayode vbus_sense_work); 274*4b6b6433SSamuel Kayode unsigned int data; 275*4b6b6433SSamuel Kayode 276*4b6b6433SSamuel Kayode if (regmap_read(chg->pf1550->regmap, PF1550_CHARG_REG_VBUS_SNS, &data)) { 277*4b6b6433SSamuel Kayode dev_err(chg->dev, "Read VBUS_SNS error.\n"); 278*4b6b6433SSamuel Kayode return; 279*4b6b6433SSamuel Kayode } 280*4b6b6433SSamuel Kayode 281*4b6b6433SSamuel Kayode if (data & PF1550_VBUS_UVLO) { 282*4b6b6433SSamuel Kayode dev_dbg(chg->dev, "VBUS detached.\n"); 283*4b6b6433SSamuel Kayode power_supply_changed(chg->battery); 284*4b6b6433SSamuel Kayode } 285*4b6b6433SSamuel Kayode if (data & PF1550_VBUS_IN2SYS) 286*4b6b6433SSamuel Kayode dev_dbg(chg->dev, "VBUS_IN2SYS_SNS.\n"); 287*4b6b6433SSamuel Kayode if (data & PF1550_VBUS_OVLO) 288*4b6b6433SSamuel Kayode dev_dbg(chg->dev, "VBUS_OVLO_SNS.\n"); 289*4b6b6433SSamuel Kayode if (data & PF1550_VBUS_VALID) { 290*4b6b6433SSamuel Kayode dev_dbg(chg->dev, "VBUS attached.\n"); 291*4b6b6433SSamuel Kayode power_supply_changed(chg->charger); 292*4b6b6433SSamuel Kayode } 293*4b6b6433SSamuel Kayode } 294*4b6b6433SSamuel Kayode 295*4b6b6433SSamuel Kayode static irqreturn_t pf1550_charger_irq_handler(int irq, void *data) 296*4b6b6433SSamuel Kayode { 297*4b6b6433SSamuel Kayode struct pf1550_charger *chg = data; 298*4b6b6433SSamuel Kayode struct device *dev = chg->dev; 299*4b6b6433SSamuel Kayode int i, irq_type = -1; 300*4b6b6433SSamuel Kayode 301*4b6b6433SSamuel Kayode for (i = 0; i < PF1550_CHARGER_IRQ_NR; i++) 302*4b6b6433SSamuel Kayode if (irq == chg->virqs[i]) 303*4b6b6433SSamuel Kayode irq_type = i; 304*4b6b6433SSamuel Kayode 305*4b6b6433SSamuel Kayode switch (irq_type) { 306*4b6b6433SSamuel Kayode case PF1550_CHARG_IRQ_BAT2SOCI: 307*4b6b6433SSamuel Kayode dev_info(dev, "BAT to SYS Overcurrent interrupt.\n"); 308*4b6b6433SSamuel Kayode break; 309*4b6b6433SSamuel Kayode case PF1550_CHARG_IRQ_BATI: 310*4b6b6433SSamuel Kayode schedule_delayed_work(&chg->bat_sense_work, 311*4b6b6433SSamuel Kayode msecs_to_jiffies(10)); 312*4b6b6433SSamuel Kayode break; 313*4b6b6433SSamuel Kayode case PF1550_CHARG_IRQ_CHGI: 314*4b6b6433SSamuel Kayode schedule_delayed_work(&chg->chg_sense_work, 315*4b6b6433SSamuel Kayode msecs_to_jiffies(10)); 316*4b6b6433SSamuel Kayode break; 317*4b6b6433SSamuel Kayode case PF1550_CHARG_IRQ_VBUSI: 318*4b6b6433SSamuel Kayode schedule_delayed_work(&chg->vbus_sense_work, 319*4b6b6433SSamuel Kayode msecs_to_jiffies(10)); 320*4b6b6433SSamuel Kayode break; 321*4b6b6433SSamuel Kayode case PF1550_CHARG_IRQ_THMI: 322*4b6b6433SSamuel Kayode dev_info(dev, "Thermal interrupt.\n"); 323*4b6b6433SSamuel Kayode break; 324*4b6b6433SSamuel Kayode default: 325*4b6b6433SSamuel Kayode dev_err(dev, "unknown interrupt occurred.\n"); 326*4b6b6433SSamuel Kayode } 327*4b6b6433SSamuel Kayode 328*4b6b6433SSamuel Kayode return IRQ_HANDLED; 329*4b6b6433SSamuel Kayode } 330*4b6b6433SSamuel Kayode 331*4b6b6433SSamuel Kayode static enum power_supply_property pf1550_charger_props[] = { 332*4b6b6433SSamuel Kayode POWER_SUPPLY_PROP_ONLINE, 333*4b6b6433SSamuel Kayode POWER_SUPPLY_PROP_MODEL_NAME, 334*4b6b6433SSamuel Kayode POWER_SUPPLY_PROP_MANUFACTURER, 335*4b6b6433SSamuel Kayode }; 336*4b6b6433SSamuel Kayode 337*4b6b6433SSamuel Kayode static enum power_supply_property pf1550_battery_props[] = { 338*4b6b6433SSamuel Kayode POWER_SUPPLY_PROP_STATUS, 339*4b6b6433SSamuel Kayode POWER_SUPPLY_PROP_CHARGE_TYPE, 340*4b6b6433SSamuel Kayode POWER_SUPPLY_PROP_HEALTH, 341*4b6b6433SSamuel Kayode POWER_SUPPLY_PROP_PRESENT, 342*4b6b6433SSamuel Kayode POWER_SUPPLY_PROP_MODEL_NAME, 343*4b6b6433SSamuel Kayode POWER_SUPPLY_PROP_MANUFACTURER, 344*4b6b6433SSamuel Kayode }; 345*4b6b6433SSamuel Kayode 346*4b6b6433SSamuel Kayode static int pf1550_charger_get_property(struct power_supply *psy, 347*4b6b6433SSamuel Kayode enum power_supply_property psp, 348*4b6b6433SSamuel Kayode union power_supply_propval *val) 349*4b6b6433SSamuel Kayode { 350*4b6b6433SSamuel Kayode struct pf1550_charger *chg = power_supply_get_drvdata(psy); 351*4b6b6433SSamuel Kayode struct regmap *regmap = chg->pf1550->regmap; 352*4b6b6433SSamuel Kayode int ret = 0; 353*4b6b6433SSamuel Kayode 354*4b6b6433SSamuel Kayode switch (psp) { 355*4b6b6433SSamuel Kayode case POWER_SUPPLY_PROP_STATUS: 356*4b6b6433SSamuel Kayode ret = pf1550_get_charger_state(regmap, &val->intval); 357*4b6b6433SSamuel Kayode break; 358*4b6b6433SSamuel Kayode case POWER_SUPPLY_PROP_CHARGE_TYPE: 359*4b6b6433SSamuel Kayode ret = pf1550_get_charge_type(regmap, &val->intval); 360*4b6b6433SSamuel Kayode break; 361*4b6b6433SSamuel Kayode case POWER_SUPPLY_PROP_HEALTH: 362*4b6b6433SSamuel Kayode ret = pf1550_get_battery_health(regmap, &val->intval); 363*4b6b6433SSamuel Kayode break; 364*4b6b6433SSamuel Kayode case POWER_SUPPLY_PROP_PRESENT: 365*4b6b6433SSamuel Kayode ret = pf1550_get_present(regmap, &val->intval); 366*4b6b6433SSamuel Kayode break; 367*4b6b6433SSamuel Kayode case POWER_SUPPLY_PROP_ONLINE: 368*4b6b6433SSamuel Kayode ret = pf1550_get_online(regmap, &val->intval); 369*4b6b6433SSamuel Kayode break; 370*4b6b6433SSamuel Kayode case POWER_SUPPLY_PROP_MODEL_NAME: 371*4b6b6433SSamuel Kayode val->strval = "PF1550"; 372*4b6b6433SSamuel Kayode break; 373*4b6b6433SSamuel Kayode case POWER_SUPPLY_PROP_MANUFACTURER: 374*4b6b6433SSamuel Kayode val->strval = "NXP"; 375*4b6b6433SSamuel Kayode break; 376*4b6b6433SSamuel Kayode default: 377*4b6b6433SSamuel Kayode return -EINVAL; 378*4b6b6433SSamuel Kayode } 379*4b6b6433SSamuel Kayode 380*4b6b6433SSamuel Kayode return ret; 381*4b6b6433SSamuel Kayode } 382*4b6b6433SSamuel Kayode 383*4b6b6433SSamuel Kayode static const struct power_supply_desc pf1550_charger_desc = { 384*4b6b6433SSamuel Kayode .name = "pf1550-charger", 385*4b6b6433SSamuel Kayode .type = POWER_SUPPLY_TYPE_MAINS, 386*4b6b6433SSamuel Kayode .properties = pf1550_charger_props, 387*4b6b6433SSamuel Kayode .num_properties = ARRAY_SIZE(pf1550_charger_props), 388*4b6b6433SSamuel Kayode .get_property = pf1550_charger_get_property, 389*4b6b6433SSamuel Kayode }; 390*4b6b6433SSamuel Kayode 391*4b6b6433SSamuel Kayode static const struct power_supply_desc pf1550_battery_desc = { 392*4b6b6433SSamuel Kayode .name = "pf1550-battery", 393*4b6b6433SSamuel Kayode .type = POWER_SUPPLY_TYPE_BATTERY, 394*4b6b6433SSamuel Kayode .properties = pf1550_battery_props, 395*4b6b6433SSamuel Kayode .num_properties = ARRAY_SIZE(pf1550_battery_props), 396*4b6b6433SSamuel Kayode .get_property = pf1550_charger_get_property, 397*4b6b6433SSamuel Kayode }; 398*4b6b6433SSamuel Kayode 399*4b6b6433SSamuel Kayode static int pf1550_set_constant_volt(struct pf1550_charger *chg, 400*4b6b6433SSamuel Kayode unsigned int uvolt) 401*4b6b6433SSamuel Kayode { 402*4b6b6433SSamuel Kayode unsigned int data; 403*4b6b6433SSamuel Kayode 404*4b6b6433SSamuel Kayode if (uvolt >= 3500000 && uvolt <= 4440000) 405*4b6b6433SSamuel Kayode data = 8 + (uvolt - 3500000) / 20000; 406*4b6b6433SSamuel Kayode else 407*4b6b6433SSamuel Kayode return dev_err_probe(chg->dev, -EINVAL, 408*4b6b6433SSamuel Kayode "Wrong value for constant voltage\n"); 409*4b6b6433SSamuel Kayode 410*4b6b6433SSamuel Kayode dev_dbg(chg->dev, "Charging constant voltage: %u (0x%x)\n", uvolt, 411*4b6b6433SSamuel Kayode data); 412*4b6b6433SSamuel Kayode 413*4b6b6433SSamuel Kayode return regmap_update_bits(chg->pf1550->regmap, 414*4b6b6433SSamuel Kayode PF1550_CHARG_REG_BATT_REG, 415*4b6b6433SSamuel Kayode PF1550_CHARG_REG_BATT_REG_CHGCV_MASK, data); 416*4b6b6433SSamuel Kayode } 417*4b6b6433SSamuel Kayode 418*4b6b6433SSamuel Kayode static int pf1550_set_min_system_volt(struct pf1550_charger *chg, 419*4b6b6433SSamuel Kayode unsigned int uvolt) 420*4b6b6433SSamuel Kayode { 421*4b6b6433SSamuel Kayode unsigned int data; 422*4b6b6433SSamuel Kayode 423*4b6b6433SSamuel Kayode switch (uvolt) { 424*4b6b6433SSamuel Kayode case 3500000: 425*4b6b6433SSamuel Kayode data = 0x0; 426*4b6b6433SSamuel Kayode break; 427*4b6b6433SSamuel Kayode case 3700000: 428*4b6b6433SSamuel Kayode data = 0x1; 429*4b6b6433SSamuel Kayode break; 430*4b6b6433SSamuel Kayode case 4300000: 431*4b6b6433SSamuel Kayode data = 0x2; 432*4b6b6433SSamuel Kayode break; 433*4b6b6433SSamuel Kayode default: 434*4b6b6433SSamuel Kayode return dev_err_probe(chg->dev, -EINVAL, 435*4b6b6433SSamuel Kayode "Wrong value for minimum system voltage\n"); 436*4b6b6433SSamuel Kayode } 437*4b6b6433SSamuel Kayode 438*4b6b6433SSamuel Kayode data <<= PF1550_CHARG_REG_BATT_REG_VMINSYS_SHIFT; 439*4b6b6433SSamuel Kayode 440*4b6b6433SSamuel Kayode dev_dbg(chg->dev, "Minimum system regulation voltage: %u (0x%x)\n", 441*4b6b6433SSamuel Kayode uvolt, data); 442*4b6b6433SSamuel Kayode 443*4b6b6433SSamuel Kayode return regmap_update_bits(chg->pf1550->regmap, 444*4b6b6433SSamuel Kayode PF1550_CHARG_REG_BATT_REG, 445*4b6b6433SSamuel Kayode PF1550_CHARG_REG_BATT_REG_VMINSYS_MASK, data); 446*4b6b6433SSamuel Kayode } 447*4b6b6433SSamuel Kayode 448*4b6b6433SSamuel Kayode static int pf1550_set_thermal_regulation_temp(struct pf1550_charger *chg, 449*4b6b6433SSamuel Kayode unsigned int cells) 450*4b6b6433SSamuel Kayode { 451*4b6b6433SSamuel Kayode unsigned int data; 452*4b6b6433SSamuel Kayode 453*4b6b6433SSamuel Kayode switch (cells) { 454*4b6b6433SSamuel Kayode case 80: 455*4b6b6433SSamuel Kayode data = 0x0; 456*4b6b6433SSamuel Kayode break; 457*4b6b6433SSamuel Kayode case 95: 458*4b6b6433SSamuel Kayode data = 0x1; 459*4b6b6433SSamuel Kayode break; 460*4b6b6433SSamuel Kayode case 110: 461*4b6b6433SSamuel Kayode data = 0x2; 462*4b6b6433SSamuel Kayode break; 463*4b6b6433SSamuel Kayode case 125: 464*4b6b6433SSamuel Kayode data = 0x3; 465*4b6b6433SSamuel Kayode break; 466*4b6b6433SSamuel Kayode default: 467*4b6b6433SSamuel Kayode return dev_err_probe(chg->dev, -EINVAL, 468*4b6b6433SSamuel Kayode "Wrong value for thermal temperature\n"); 469*4b6b6433SSamuel Kayode } 470*4b6b6433SSamuel Kayode 471*4b6b6433SSamuel Kayode data <<= PF1550_CHARG_REG_THM_REG_CNFG_REGTEMP_SHIFT; 472*4b6b6433SSamuel Kayode 473*4b6b6433SSamuel Kayode dev_dbg(chg->dev, "Thermal regulation loop temperature: %u (0x%x)\n", 474*4b6b6433SSamuel Kayode cells, data); 475*4b6b6433SSamuel Kayode 476*4b6b6433SSamuel Kayode return regmap_update_bits(chg->pf1550->regmap, 477*4b6b6433SSamuel Kayode PF1550_CHARG_REG_THM_REG_CNFG, 478*4b6b6433SSamuel Kayode PF1550_CHARG_REG_THM_REG_CNFG_REGTEMP_MASK, 479*4b6b6433SSamuel Kayode data); 480*4b6b6433SSamuel Kayode } 481*4b6b6433SSamuel Kayode 482*4b6b6433SSamuel Kayode /* 483*4b6b6433SSamuel Kayode * Sets charger registers to proper and safe default values. 484*4b6b6433SSamuel Kayode */ 485*4b6b6433SSamuel Kayode static int pf1550_reg_init(struct pf1550_charger *chg) 486*4b6b6433SSamuel Kayode { 487*4b6b6433SSamuel Kayode struct power_supply_battery_info *info; 488*4b6b6433SSamuel Kayode struct device *dev = chg->dev; 489*4b6b6433SSamuel Kayode int ret; 490*4b6b6433SSamuel Kayode 491*4b6b6433SSamuel Kayode /* Unmask charger interrupt, mask DPMI and reserved bit */ 492*4b6b6433SSamuel Kayode ret = regmap_write(chg->pf1550->regmap, PF1550_CHARG_REG_CHG_INT_MASK, 493*4b6b6433SSamuel Kayode PF1550_CHG_INT_MASK); 494*4b6b6433SSamuel Kayode if (ret) 495*4b6b6433SSamuel Kayode return dev_err_probe(dev, ret, 496*4b6b6433SSamuel Kayode "Error unmask charger interrupt\n"); 497*4b6b6433SSamuel Kayode 498*4b6b6433SSamuel Kayode ret = pf1550_set_constant_volt(chg, chg->constant_volt); 499*4b6b6433SSamuel Kayode if (ret) 500*4b6b6433SSamuel Kayode return ret; 501*4b6b6433SSamuel Kayode 502*4b6b6433SSamuel Kayode ret = pf1550_set_min_system_volt(chg, chg->min_system_volt); 503*4b6b6433SSamuel Kayode if (ret) 504*4b6b6433SSamuel Kayode return ret; 505*4b6b6433SSamuel Kayode 506*4b6b6433SSamuel Kayode ret = pf1550_set_thermal_regulation_temp(chg, 507*4b6b6433SSamuel Kayode chg->thermal_regulation_temp); 508*4b6b6433SSamuel Kayode if (ret) 509*4b6b6433SSamuel Kayode return ret; 510*4b6b6433SSamuel Kayode 511*4b6b6433SSamuel Kayode /* 512*4b6b6433SSamuel Kayode * The PF1550 charger has 3 modes of operation. By default, the charger 513*4b6b6433SSamuel Kayode * is in mode 1; it remains off. Appropriate for applications not using 514*4b6b6433SSamuel Kayode * a battery. The other supported mode is mode 2, the charger is turned 515*4b6b6433SSamuel Kayode * on to charge a battery when present. 516*4b6b6433SSamuel Kayode */ 517*4b6b6433SSamuel Kayode if (power_supply_get_battery_info(chg->charger, &info)) { 518*4b6b6433SSamuel Kayode ret = regmap_write(chg->pf1550->regmap, 519*4b6b6433SSamuel Kayode PF1550_CHARG_REG_CHG_OPER, 520*4b6b6433SSamuel Kayode PF1550_CHG_BAT_ON); 521*4b6b6433SSamuel Kayode if (ret) 522*4b6b6433SSamuel Kayode return dev_err_probe(dev, ret, 523*4b6b6433SSamuel Kayode "Error turn on charger\n"); 524*4b6b6433SSamuel Kayode } 525*4b6b6433SSamuel Kayode 526*4b6b6433SSamuel Kayode return 0; 527*4b6b6433SSamuel Kayode } 528*4b6b6433SSamuel Kayode 529*4b6b6433SSamuel Kayode static void pf1550_dt_parse_dev_info(struct pf1550_charger *chg) 530*4b6b6433SSamuel Kayode { 531*4b6b6433SSamuel Kayode struct power_supply_battery_info *info; 532*4b6b6433SSamuel Kayode struct device *dev = chg->dev; 533*4b6b6433SSamuel Kayode 534*4b6b6433SSamuel Kayode if (device_property_read_u32(dev->parent, "nxp,min-system-microvolt", 535*4b6b6433SSamuel Kayode &chg->min_system_volt)) 536*4b6b6433SSamuel Kayode chg->min_system_volt = PF1550_DEFAULT_MIN_SYSTEM_VOLT; 537*4b6b6433SSamuel Kayode 538*4b6b6433SSamuel Kayode if (device_property_read_u32(dev->parent, 539*4b6b6433SSamuel Kayode "nxp,thermal-regulation-celsius", 540*4b6b6433SSamuel Kayode &chg->thermal_regulation_temp)) 541*4b6b6433SSamuel Kayode chg->thermal_regulation_temp = PF1550_DEFAULT_THERMAL_TEMP; 542*4b6b6433SSamuel Kayode 543*4b6b6433SSamuel Kayode if (power_supply_get_battery_info(chg->charger, &info)) 544*4b6b6433SSamuel Kayode chg->constant_volt = PF1550_DEFAULT_CONSTANT_VOLT; 545*4b6b6433SSamuel Kayode else 546*4b6b6433SSamuel Kayode chg->constant_volt = info->constant_charge_voltage_max_uv; 547*4b6b6433SSamuel Kayode } 548*4b6b6433SSamuel Kayode 549*4b6b6433SSamuel Kayode static int pf1550_charger_probe(struct platform_device *pdev) 550*4b6b6433SSamuel Kayode { 551*4b6b6433SSamuel Kayode const struct pf1550_ddata *pf1550 = dev_get_drvdata(pdev->dev.parent); 552*4b6b6433SSamuel Kayode struct power_supply_config psy_cfg = {}; 553*4b6b6433SSamuel Kayode struct pf1550_charger *chg; 554*4b6b6433SSamuel Kayode int i, irq, ret; 555*4b6b6433SSamuel Kayode 556*4b6b6433SSamuel Kayode chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL); 557*4b6b6433SSamuel Kayode if (!chg) 558*4b6b6433SSamuel Kayode return -ENOMEM; 559*4b6b6433SSamuel Kayode 560*4b6b6433SSamuel Kayode chg->dev = &pdev->dev; 561*4b6b6433SSamuel Kayode chg->pf1550 = pf1550; 562*4b6b6433SSamuel Kayode 563*4b6b6433SSamuel Kayode if (!chg->pf1550->regmap) 564*4b6b6433SSamuel Kayode return dev_err_probe(&pdev->dev, -ENODEV, 565*4b6b6433SSamuel Kayode "failed to get regmap\n"); 566*4b6b6433SSamuel Kayode 567*4b6b6433SSamuel Kayode platform_set_drvdata(pdev, chg); 568*4b6b6433SSamuel Kayode 569*4b6b6433SSamuel Kayode ret = devm_delayed_work_autocancel(chg->dev, &chg->vbus_sense_work, 570*4b6b6433SSamuel Kayode pf1550_chg_vbus_work); 571*4b6b6433SSamuel Kayode if (ret) 572*4b6b6433SSamuel Kayode return dev_err_probe(chg->dev, ret, 573*4b6b6433SSamuel Kayode "failed to add vbus sense work\n"); 574*4b6b6433SSamuel Kayode 575*4b6b6433SSamuel Kayode ret = devm_delayed_work_autocancel(chg->dev, &chg->chg_sense_work, 576*4b6b6433SSamuel Kayode pf1550_chg_chg_work); 577*4b6b6433SSamuel Kayode if (ret) 578*4b6b6433SSamuel Kayode return dev_err_probe(chg->dev, ret, 579*4b6b6433SSamuel Kayode "failed to add charger sense work\n"); 580*4b6b6433SSamuel Kayode 581*4b6b6433SSamuel Kayode ret = devm_delayed_work_autocancel(chg->dev, &chg->bat_sense_work, 582*4b6b6433SSamuel Kayode pf1550_chg_bat_work); 583*4b6b6433SSamuel Kayode if (ret) 584*4b6b6433SSamuel Kayode return dev_err_probe(chg->dev, ret, 585*4b6b6433SSamuel Kayode "failed to add battery sense work\n"); 586*4b6b6433SSamuel Kayode 587*4b6b6433SSamuel Kayode for (i = 0; i < PF1550_CHARGER_IRQ_NR; i++) { 588*4b6b6433SSamuel Kayode irq = platform_get_irq(pdev, i); 589*4b6b6433SSamuel Kayode if (irq < 0) 590*4b6b6433SSamuel Kayode return irq; 591*4b6b6433SSamuel Kayode 592*4b6b6433SSamuel Kayode chg->virqs[i] = irq; 593*4b6b6433SSamuel Kayode 594*4b6b6433SSamuel Kayode ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, 595*4b6b6433SSamuel Kayode pf1550_charger_irq_handler, 596*4b6b6433SSamuel Kayode IRQF_NO_SUSPEND, 597*4b6b6433SSamuel Kayode "pf1550-charger", chg); 598*4b6b6433SSamuel Kayode if (ret) 599*4b6b6433SSamuel Kayode return dev_err_probe(&pdev->dev, ret, 600*4b6b6433SSamuel Kayode "failed irq request\n"); 601*4b6b6433SSamuel Kayode } 602*4b6b6433SSamuel Kayode 603*4b6b6433SSamuel Kayode psy_cfg.drv_data = chg; 604*4b6b6433SSamuel Kayode 605*4b6b6433SSamuel Kayode chg->charger = devm_power_supply_register(&pdev->dev, 606*4b6b6433SSamuel Kayode &pf1550_charger_desc, 607*4b6b6433SSamuel Kayode &psy_cfg); 608*4b6b6433SSamuel Kayode if (IS_ERR(chg->charger)) 609*4b6b6433SSamuel Kayode return dev_err_probe(&pdev->dev, PTR_ERR(chg->charger), 610*4b6b6433SSamuel Kayode "failed: power supply register\n"); 611*4b6b6433SSamuel Kayode 612*4b6b6433SSamuel Kayode chg->battery = devm_power_supply_register(&pdev->dev, 613*4b6b6433SSamuel Kayode &pf1550_battery_desc, 614*4b6b6433SSamuel Kayode &psy_cfg); 615*4b6b6433SSamuel Kayode if (IS_ERR(chg->battery)) 616*4b6b6433SSamuel Kayode return dev_err_probe(&pdev->dev, PTR_ERR(chg->battery), 617*4b6b6433SSamuel Kayode "failed: power supply register\n"); 618*4b6b6433SSamuel Kayode 619*4b6b6433SSamuel Kayode pf1550_dt_parse_dev_info(chg); 620*4b6b6433SSamuel Kayode 621*4b6b6433SSamuel Kayode return pf1550_reg_init(chg); 622*4b6b6433SSamuel Kayode } 623*4b6b6433SSamuel Kayode 624*4b6b6433SSamuel Kayode static const struct platform_device_id pf1550_charger_id[] = { 625*4b6b6433SSamuel Kayode { "pf1550-charger", }, 626*4b6b6433SSamuel Kayode { /* sentinel */ } 627*4b6b6433SSamuel Kayode }; 628*4b6b6433SSamuel Kayode MODULE_DEVICE_TABLE(platform, pf1550_charger_id); 629*4b6b6433SSamuel Kayode 630*4b6b6433SSamuel Kayode static struct platform_driver pf1550_charger_driver = { 631*4b6b6433SSamuel Kayode .driver = { 632*4b6b6433SSamuel Kayode .name = "pf1550-charger", 633*4b6b6433SSamuel Kayode }, 634*4b6b6433SSamuel Kayode .probe = pf1550_charger_probe, 635*4b6b6433SSamuel Kayode .id_table = pf1550_charger_id, 636*4b6b6433SSamuel Kayode }; 637*4b6b6433SSamuel Kayode module_platform_driver(pf1550_charger_driver); 638*4b6b6433SSamuel Kayode 639*4b6b6433SSamuel Kayode MODULE_AUTHOR("Robin Gong <yibin.gong@freescale.com>"); 640*4b6b6433SSamuel Kayode MODULE_DESCRIPTION("PF1550 charger driver"); 641*4b6b6433SSamuel Kayode MODULE_LICENSE("GPL"); 642