This source file includes following definitions.
- axp288_charger_set_cc
- axp288_charger_set_cv
- axp288_charger_get_vbus_inlmt
- axp288_charger_set_vbus_inlmt
- axp288_charger_vbus_path_select
- axp288_charger_enable_charger
- axp288_charger_is_present
- axp288_charger_is_online
- axp288_get_charger_health
- axp288_charger_usb_set_property
- axp288_charger_usb_get_property
- axp288_charger_property_is_writeable
- axp288_charger_irq_thread_handler
- axp288_charger_extcon_evt_worker
- axp288_charger_handle_cable_evt
- axp288_charger_otg_evt_worker
- axp288_charger_handle_otg_evt
- charger_init_hw_regs
- axp288_charger_cancel_work
- axp288_charger_probe
   1 
   2 
   3 
   4 
   5 
   6 
   7 
   8 
   9 
  10 #include <linux/acpi.h>
  11 #include <linux/bitops.h>
  12 #include <linux/module.h>
  13 #include <linux/device.h>
  14 #include <linux/regmap.h>
  15 #include <linux/workqueue.h>
  16 #include <linux/delay.h>
  17 #include <linux/platform_device.h>
  18 #include <linux/usb/otg.h>
  19 #include <linux/notifier.h>
  20 #include <linux/power_supply.h>
  21 #include <linux/property.h>
  22 #include <linux/mfd/axp20x.h>
  23 #include <linux/extcon.h>
  24 #include <linux/dmi.h>
  25 
  26 #define PS_STAT_VBUS_TRIGGER            BIT(0)
  27 #define PS_STAT_BAT_CHRG_DIR            BIT(2)
  28 #define PS_STAT_VBAT_ABOVE_VHOLD        BIT(3)
  29 #define PS_STAT_VBUS_VALID              BIT(4)
  30 #define PS_STAT_VBUS_PRESENT            BIT(5)
  31 
  32 #define CHRG_STAT_BAT_SAFE_MODE         BIT(3)
  33 #define CHRG_STAT_BAT_VALID             BIT(4)
  34 #define CHRG_STAT_BAT_PRESENT           BIT(5)
  35 #define CHRG_STAT_CHARGING              BIT(6)
  36 #define CHRG_STAT_PMIC_OTP              BIT(7)
  37 
  38 #define VBUS_ISPOUT_CUR_LIM_MASK        0x03
  39 #define VBUS_ISPOUT_CUR_LIM_BIT_POS     0
  40 #define VBUS_ISPOUT_CUR_LIM_900MA       0x0     
  41 #define VBUS_ISPOUT_CUR_LIM_1500MA      0x1     
  42 #define VBUS_ISPOUT_CUR_LIM_2000MA      0x2     
  43 #define VBUS_ISPOUT_CUR_NO_LIM          0x3     
  44 #define VBUS_ISPOUT_VHOLD_SET_MASK      0x31
  45 #define VBUS_ISPOUT_VHOLD_SET_BIT_POS   0x3
  46 #define VBUS_ISPOUT_VHOLD_SET_OFFSET    4000    
  47 #define VBUS_ISPOUT_VHOLD_SET_LSB_RES   100     
  48 #define VBUS_ISPOUT_VHOLD_SET_4300MV    0x3     
  49 #define VBUS_ISPOUT_VBUS_PATH_DIS       BIT(7)
  50 
  51 #define CHRG_CCCV_CC_MASK               0xf             
  52 #define CHRG_CCCV_CC_BIT_POS            0
  53 #define CHRG_CCCV_CC_OFFSET             200             
  54 #define CHRG_CCCV_CC_LSB_RES            200             
  55 #define CHRG_CCCV_ITERM_20P             BIT(4)          
  56 #define CHRG_CCCV_CV_MASK               0x60            
  57 #define CHRG_CCCV_CV_BIT_POS            5
  58 #define CHRG_CCCV_CV_4100MV             0x0             
  59 #define CHRG_CCCV_CV_4150MV             0x1             
  60 #define CHRG_CCCV_CV_4200MV             0x2             
  61 #define CHRG_CCCV_CV_4350MV             0x3             
  62 #define CHRG_CCCV_CHG_EN                BIT(7)
  63 
  64 #define CNTL2_CC_TIMEOUT_MASK           0x3     
  65 #define CNTL2_CC_TIMEOUT_OFFSET         6       
  66 #define CNTL2_CC_TIMEOUT_LSB_RES        2       
  67 #define CNTL2_CC_TIMEOUT_12HRS          0x3     
  68 #define CNTL2_CHGLED_TYPEB              BIT(4)
  69 #define CNTL2_CHG_OUT_TURNON            BIT(5)
  70 #define CNTL2_PC_TIMEOUT_MASK           0xC0
  71 #define CNTL2_PC_TIMEOUT_OFFSET         40      
  72 #define CNTL2_PC_TIMEOUT_LSB_RES        10      
  73 #define CNTL2_PC_TIMEOUT_70MINS         0x3
  74 
  75 #define CHRG_ILIM_TEMP_LOOP_EN          BIT(3)
  76 #define CHRG_VBUS_ILIM_MASK             0xf0
  77 #define CHRG_VBUS_ILIM_BIT_POS          4
  78 #define CHRG_VBUS_ILIM_100MA            0x0     
  79 #define CHRG_VBUS_ILIM_500MA            0x1     
  80 #define CHRG_VBUS_ILIM_900MA            0x2     
  81 #define CHRG_VBUS_ILIM_1500MA           0x3     
  82 #define CHRG_VBUS_ILIM_2000MA           0x4     
  83 #define CHRG_VBUS_ILIM_2500MA           0x5     
  84 #define CHRG_VBUS_ILIM_3000MA           0x6     
  85 #define CHRG_VBUS_ILIM_3500MA           0x7     
  86 #define CHRG_VBUS_ILIM_4000MA           0x8     
  87 
  88 #define CHRG_VLTFC_0C                   0xA5    
  89 #define CHRG_VHTFC_45C                  0x1F    
  90 
  91 #define FG_CNTL_OCV_ADJ_EN              BIT(3)
  92 
  93 #define CV_4100MV                       4100    
  94 #define CV_4150MV                       4150    
  95 #define CV_4200MV                       4200    
  96 #define CV_4350MV                       4350    
  97 
  98 #define AXP288_EXTCON_DEV_NAME          "axp288_extcon"
  99 #define USB_HOST_EXTCON_HID             "INT3496"
 100 #define USB_HOST_EXTCON_NAME            "INT3496:00"
 101 
 102 enum {
 103         VBUS_OV_IRQ = 0,
 104         CHARGE_DONE_IRQ,
 105         CHARGE_CHARGING_IRQ,
 106         BAT_SAFE_QUIT_IRQ,
 107         BAT_SAFE_ENTER_IRQ,
 108         QCBTU_IRQ,
 109         CBTU_IRQ,
 110         QCBTO_IRQ,
 111         CBTO_IRQ,
 112         CHRG_INTR_END,
 113 };
 114 
 115 struct axp288_chrg_info {
 116         struct platform_device *pdev;
 117         struct regmap *regmap;
 118         struct regmap_irq_chip_data *regmap_irqc;
 119         int irq[CHRG_INTR_END];
 120         struct power_supply *psy_usb;
 121 
 122         
 123         struct {
 124                 struct work_struct work;
 125                 struct extcon_dev *cable;
 126                 struct notifier_block id_nb;
 127                 bool id_short;
 128         } otg;
 129 
 130         
 131         struct {
 132                 struct extcon_dev *edev;
 133                 struct notifier_block nb;
 134                 struct work_struct work;
 135         } cable;
 136 
 137         int cc;
 138         int cv;
 139         int max_cc;
 140         int max_cv;
 141 };
 142 
 143 static inline int axp288_charger_set_cc(struct axp288_chrg_info *info, int cc)
 144 {
 145         u8 reg_val;
 146         int ret;
 147 
 148         if (cc < CHRG_CCCV_CC_OFFSET)
 149                 cc = CHRG_CCCV_CC_OFFSET;
 150         else if (cc > info->max_cc)
 151                 cc = info->max_cc;
 152 
 153         reg_val = (cc - CHRG_CCCV_CC_OFFSET) / CHRG_CCCV_CC_LSB_RES;
 154         cc = (reg_val * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;
 155         reg_val = reg_val << CHRG_CCCV_CC_BIT_POS;
 156 
 157         ret = regmap_update_bits(info->regmap,
 158                                 AXP20X_CHRG_CTRL1,
 159                                 CHRG_CCCV_CC_MASK, reg_val);
 160         if (ret >= 0)
 161                 info->cc = cc;
 162 
 163         return ret;
 164 }
 165 
 166 static inline int axp288_charger_set_cv(struct axp288_chrg_info *info, int cv)
 167 {
 168         u8 reg_val;
 169         int ret;
 170 
 171         if (cv <= CV_4100MV) {
 172                 reg_val = CHRG_CCCV_CV_4100MV;
 173                 cv = CV_4100MV;
 174         } else if (cv <= CV_4150MV) {
 175                 reg_val = CHRG_CCCV_CV_4150MV;
 176                 cv = CV_4150MV;
 177         } else if (cv <= CV_4200MV) {
 178                 reg_val = CHRG_CCCV_CV_4200MV;
 179                 cv = CV_4200MV;
 180         } else {
 181                 reg_val = CHRG_CCCV_CV_4350MV;
 182                 cv = CV_4350MV;
 183         }
 184 
 185         reg_val = reg_val << CHRG_CCCV_CV_BIT_POS;
 186 
 187         ret = regmap_update_bits(info->regmap,
 188                                 AXP20X_CHRG_CTRL1,
 189                                 CHRG_CCCV_CV_MASK, reg_val);
 190 
 191         if (ret >= 0)
 192                 info->cv = cv;
 193 
 194         return ret;
 195 }
 196 
 197 static int axp288_charger_get_vbus_inlmt(struct axp288_chrg_info *info)
 198 {
 199         unsigned int val;
 200         int ret;
 201 
 202         ret = regmap_read(info->regmap, AXP20X_CHRG_BAK_CTRL, &val);
 203         if (ret < 0)
 204                 return ret;
 205 
 206         val >>= CHRG_VBUS_ILIM_BIT_POS;
 207         switch (val) {
 208         case CHRG_VBUS_ILIM_100MA:
 209                 return 100000;
 210         case CHRG_VBUS_ILIM_500MA:
 211                 return 500000;
 212         case CHRG_VBUS_ILIM_900MA:
 213                 return 900000;
 214         case CHRG_VBUS_ILIM_1500MA:
 215                 return 1500000;
 216         case CHRG_VBUS_ILIM_2000MA:
 217                 return 2000000;
 218         case CHRG_VBUS_ILIM_2500MA:
 219                 return 2500000;
 220         case CHRG_VBUS_ILIM_3000MA:
 221                 return 3000000;
 222         case CHRG_VBUS_ILIM_3500MA:
 223                 return 3500000;
 224         default:
 225                 
 226                 return 4000000;
 227         }
 228 }
 229 
 230 static inline int axp288_charger_set_vbus_inlmt(struct axp288_chrg_info *info,
 231                                            int inlmt)
 232 {
 233         int ret;
 234         u8 reg_val;
 235 
 236         if (inlmt >= 4000000)
 237                 reg_val = CHRG_VBUS_ILIM_4000MA << CHRG_VBUS_ILIM_BIT_POS;
 238         else if (inlmt >= 3500000)
 239                 reg_val = CHRG_VBUS_ILIM_3500MA << CHRG_VBUS_ILIM_BIT_POS;
 240         else if (inlmt >= 3000000)
 241                 reg_val = CHRG_VBUS_ILIM_3000MA << CHRG_VBUS_ILIM_BIT_POS;
 242         else if (inlmt >= 2500000)
 243                 reg_val = CHRG_VBUS_ILIM_2500MA << CHRG_VBUS_ILIM_BIT_POS;
 244         else if (inlmt >= 2000000)
 245                 reg_val = CHRG_VBUS_ILIM_2000MA << CHRG_VBUS_ILIM_BIT_POS;
 246         else if (inlmt >= 1500000)
 247                 reg_val = CHRG_VBUS_ILIM_1500MA << CHRG_VBUS_ILIM_BIT_POS;
 248         else if (inlmt >= 900000)
 249                 reg_val = CHRG_VBUS_ILIM_900MA << CHRG_VBUS_ILIM_BIT_POS;
 250         else if (inlmt >= 500000)
 251                 reg_val = CHRG_VBUS_ILIM_500MA << CHRG_VBUS_ILIM_BIT_POS;
 252         else
 253                 reg_val = CHRG_VBUS_ILIM_100MA << CHRG_VBUS_ILIM_BIT_POS;
 254 
 255         ret = regmap_update_bits(info->regmap, AXP20X_CHRG_BAK_CTRL,
 256                                  CHRG_VBUS_ILIM_MASK, reg_val);
 257         if (ret < 0)
 258                 dev_err(&info->pdev->dev, "charger BAK control %d\n", ret);
 259 
 260         return ret;
 261 }
 262 
 263 static int axp288_charger_vbus_path_select(struct axp288_chrg_info *info,
 264                                                                 bool enable)
 265 {
 266         int ret;
 267 
 268         if (enable)
 269                 ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,
 270                                         VBUS_ISPOUT_VBUS_PATH_DIS, 0);
 271         else
 272                 ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,
 273                         VBUS_ISPOUT_VBUS_PATH_DIS, VBUS_ISPOUT_VBUS_PATH_DIS);
 274 
 275         if (ret < 0)
 276                 dev_err(&info->pdev->dev, "axp288 vbus path select %d\n", ret);
 277 
 278         return ret;
 279 }
 280 
 281 static int axp288_charger_enable_charger(struct axp288_chrg_info *info,
 282                                                                 bool enable)
 283 {
 284         int ret;
 285 
 286         if (enable)
 287                 ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,
 288                                 CHRG_CCCV_CHG_EN, CHRG_CCCV_CHG_EN);
 289         else
 290                 ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,
 291                                 CHRG_CCCV_CHG_EN, 0);
 292         if (ret < 0)
 293                 dev_err(&info->pdev->dev, "axp288 enable charger %d\n", ret);
 294 
 295         return ret;
 296 }
 297 
 298 static int axp288_charger_is_present(struct axp288_chrg_info *info)
 299 {
 300         int ret, present = 0;
 301         unsigned int val;
 302 
 303         ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
 304         if (ret < 0)
 305                 return ret;
 306 
 307         if (val & PS_STAT_VBUS_PRESENT)
 308                 present = 1;
 309         return present;
 310 }
 311 
 312 static int axp288_charger_is_online(struct axp288_chrg_info *info)
 313 {
 314         int ret, online = 0;
 315         unsigned int val;
 316 
 317         ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
 318         if (ret < 0)
 319                 return ret;
 320 
 321         if (val & PS_STAT_VBUS_VALID)
 322                 online = 1;
 323         return online;
 324 }
 325 
 326 static int axp288_get_charger_health(struct axp288_chrg_info *info)
 327 {
 328         int ret, pwr_stat, chrg_stat;
 329         int health = POWER_SUPPLY_HEALTH_UNKNOWN;
 330         unsigned int val;
 331 
 332         ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
 333         if ((ret < 0) || !(val & PS_STAT_VBUS_PRESENT))
 334                 goto health_read_fail;
 335         else
 336                 pwr_stat = val;
 337 
 338         ret = regmap_read(info->regmap, AXP20X_PWR_OP_MODE, &val);
 339         if (ret < 0)
 340                 goto health_read_fail;
 341         else
 342                 chrg_stat = val;
 343 
 344         if (!(pwr_stat & PS_STAT_VBUS_VALID))
 345                 health = POWER_SUPPLY_HEALTH_DEAD;
 346         else if (chrg_stat & CHRG_STAT_PMIC_OTP)
 347                 health = POWER_SUPPLY_HEALTH_OVERHEAT;
 348         else if (chrg_stat & CHRG_STAT_BAT_SAFE_MODE)
 349                 health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
 350         else
 351                 health = POWER_SUPPLY_HEALTH_GOOD;
 352 
 353 health_read_fail:
 354         return health;
 355 }
 356 
 357 static int axp288_charger_usb_set_property(struct power_supply *psy,
 358                                     enum power_supply_property psp,
 359                                     const union power_supply_propval *val)
 360 {
 361         struct axp288_chrg_info *info = power_supply_get_drvdata(psy);
 362         int ret = 0;
 363         int scaled_val;
 364 
 365         switch (psp) {
 366         case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
 367                 scaled_val = min(val->intval, info->max_cc);
 368                 scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000);
 369                 ret = axp288_charger_set_cc(info, scaled_val);
 370                 if (ret < 0)
 371                         dev_warn(&info->pdev->dev, "set charge current failed\n");
 372                 break;
 373         case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
 374                 scaled_val = min(val->intval, info->max_cv);
 375                 scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000);
 376                 ret = axp288_charger_set_cv(info, scaled_val);
 377                 if (ret < 0)
 378                         dev_warn(&info->pdev->dev, "set charge voltage failed\n");
 379                 break;
 380         case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
 381                 ret = axp288_charger_set_vbus_inlmt(info, val->intval);
 382                 if (ret < 0)
 383                         dev_warn(&info->pdev->dev, "set input current limit failed\n");
 384                 break;
 385         default:
 386                 ret = -EINVAL;
 387         }
 388 
 389         return ret;
 390 }
 391 
 392 static int axp288_charger_usb_get_property(struct power_supply *psy,
 393                                     enum power_supply_property psp,
 394                                     union power_supply_propval *val)
 395 {
 396         struct axp288_chrg_info *info = power_supply_get_drvdata(psy);
 397         int ret;
 398 
 399         switch (psp) {
 400         case POWER_SUPPLY_PROP_PRESENT:
 401                 
 402                 if (info->otg.id_short) {
 403                         val->intval = 0;
 404                         break;
 405                 }
 406                 ret = axp288_charger_is_present(info);
 407                 if (ret < 0)
 408                         return ret;
 409                 val->intval = ret;
 410                 break;
 411         case POWER_SUPPLY_PROP_ONLINE:
 412                 
 413                 if (info->otg.id_short) {
 414                         val->intval = 0;
 415                         break;
 416                 }
 417                 ret = axp288_charger_is_online(info);
 418                 if (ret < 0)
 419                         return ret;
 420                 val->intval = ret;
 421                 break;
 422         case POWER_SUPPLY_PROP_HEALTH:
 423                 val->intval = axp288_get_charger_health(info);
 424                 break;
 425         case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
 426                 val->intval = info->cc * 1000;
 427                 break;
 428         case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
 429                 val->intval = info->max_cc * 1000;
 430                 break;
 431         case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
 432                 val->intval = info->cv * 1000;
 433                 break;
 434         case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
 435                 val->intval = info->max_cv * 1000;
 436                 break;
 437         case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
 438                 ret = axp288_charger_get_vbus_inlmt(info);
 439                 if (ret < 0)
 440                         return ret;
 441                 val->intval = ret;
 442                 break;
 443         default:
 444                 return -EINVAL;
 445         }
 446 
 447         return 0;
 448 }
 449 
 450 static int axp288_charger_property_is_writeable(struct power_supply *psy,
 451                 enum power_supply_property psp)
 452 {
 453         int ret;
 454 
 455         switch (psp) {
 456         case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
 457         case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
 458         case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
 459                 ret = 1;
 460                 break;
 461         default:
 462                 ret = 0;
 463         }
 464 
 465         return ret;
 466 }
 467 
 468 static enum power_supply_property axp288_usb_props[] = {
 469         POWER_SUPPLY_PROP_PRESENT,
 470         POWER_SUPPLY_PROP_ONLINE,
 471         POWER_SUPPLY_PROP_TYPE,
 472         POWER_SUPPLY_PROP_HEALTH,
 473         POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
 474         POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
 475         POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
 476         POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
 477         POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
 478 };
 479 
 480 static const struct power_supply_desc axp288_charger_desc = {
 481         .name                   = "axp288_charger",
 482         .type                   = POWER_SUPPLY_TYPE_USB,
 483         .properties             = axp288_usb_props,
 484         .num_properties         = ARRAY_SIZE(axp288_usb_props),
 485         .get_property           = axp288_charger_usb_get_property,
 486         .set_property           = axp288_charger_usb_set_property,
 487         .property_is_writeable  = axp288_charger_property_is_writeable,
 488 };
 489 
 490 static irqreturn_t axp288_charger_irq_thread_handler(int irq, void *dev)
 491 {
 492         struct axp288_chrg_info *info = dev;
 493         int i;
 494 
 495         for (i = 0; i < CHRG_INTR_END; i++) {
 496                 if (info->irq[i] == irq)
 497                         break;
 498         }
 499 
 500         if (i >= CHRG_INTR_END) {
 501                 dev_warn(&info->pdev->dev, "spurious interrupt!!\n");
 502                 return IRQ_NONE;
 503         }
 504 
 505         switch (i) {
 506         case VBUS_OV_IRQ:
 507                 dev_dbg(&info->pdev->dev, "VBUS Over Voltage INTR\n");
 508                 break;
 509         case CHARGE_DONE_IRQ:
 510                 dev_dbg(&info->pdev->dev, "Charging Done INTR\n");
 511                 break;
 512         case CHARGE_CHARGING_IRQ:
 513                 dev_dbg(&info->pdev->dev, "Start Charging IRQ\n");
 514                 break;
 515         case BAT_SAFE_QUIT_IRQ:
 516                 dev_dbg(&info->pdev->dev,
 517                         "Quit Safe Mode(restart timer) Charging IRQ\n");
 518                 break;
 519         case BAT_SAFE_ENTER_IRQ:
 520                 dev_dbg(&info->pdev->dev,
 521                         "Enter Safe Mode(timer expire) Charging IRQ\n");
 522                 break;
 523         case QCBTU_IRQ:
 524                 dev_dbg(&info->pdev->dev,
 525                         "Quit Battery Under Temperature(CHRG) INTR\n");
 526                 break;
 527         case CBTU_IRQ:
 528                 dev_dbg(&info->pdev->dev,
 529                         "Hit Battery Under Temperature(CHRG) INTR\n");
 530                 break;
 531         case QCBTO_IRQ:
 532                 dev_dbg(&info->pdev->dev,
 533                         "Quit Battery Over Temperature(CHRG) INTR\n");
 534                 break;
 535         case CBTO_IRQ:
 536                 dev_dbg(&info->pdev->dev,
 537                         "Hit Battery Over Temperature(CHRG) INTR\n");
 538                 break;
 539         default:
 540                 dev_warn(&info->pdev->dev, "Spurious Interrupt!!!\n");
 541                 goto out;
 542         }
 543 
 544         power_supply_changed(info->psy_usb);
 545 out:
 546         return IRQ_HANDLED;
 547 }
 548 
 549 
 550 
 551 
 552 
 553 
 554 
 555 
 556 
 557 
 558 
 559 
 560 
 561 
 562 
 563 
 564 
 565 
 566 
 567 
 568 
 569 
 570 
 571 
 572 
 573 
 574 
 575 
 576 
 577 
 578 
 579 static const struct dmi_system_id axp288_hp_x2_dmi_ids[] = {
 580         {
 581                 
 582 
 583 
 584 
 585                 .matches = {
 586                         DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"),
 587                 },
 588         },
 589         {} 
 590 };
 591 
 592 static void axp288_charger_extcon_evt_worker(struct work_struct *work)
 593 {
 594         struct axp288_chrg_info *info =
 595             container_of(work, struct axp288_chrg_info, cable.work);
 596         int ret, current_limit;
 597         struct extcon_dev *edev = info->cable.edev;
 598         unsigned int val;
 599 
 600         ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
 601         if (ret < 0) {
 602                 dev_err(&info->pdev->dev, "Error reading status (%d)\n", ret);
 603                 return;
 604         }
 605 
 606         
 607         if (!(val & PS_STAT_VBUS_VALID)) {
 608                 dev_dbg(&info->pdev->dev, "USB charger disconnected\n");
 609                 axp288_charger_enable_charger(info, false);
 610                 power_supply_changed(info->psy_usb);
 611                 return;
 612         }
 613 
 614         
 615         if (dmi_check_system(axp288_hp_x2_dmi_ids)) {
 616                 
 617                 dev_dbg(&info->pdev->dev, "HP X2 with Type-C, setting inlmt to 3A\n");
 618                 current_limit = 3000000;
 619         } else if (extcon_get_state(edev, EXTCON_CHG_USB_SDP) > 0) {
 620                 dev_dbg(&info->pdev->dev, "USB SDP charger is connected\n");
 621                 current_limit = 500000;
 622         } else if (extcon_get_state(edev, EXTCON_CHG_USB_CDP) > 0) {
 623                 dev_dbg(&info->pdev->dev, "USB CDP charger is connected\n");
 624                 current_limit = 1500000;
 625         } else if (extcon_get_state(edev, EXTCON_CHG_USB_DCP) > 0) {
 626                 dev_dbg(&info->pdev->dev, "USB DCP charger is connected\n");
 627                 current_limit = 2000000;
 628         } else {
 629                 
 630                 return;
 631         }
 632 
 633         
 634         ret = axp288_charger_set_vbus_inlmt(info, current_limit);
 635         if (ret == 0)
 636                 axp288_charger_enable_charger(info, true);
 637         else
 638                 dev_err(&info->pdev->dev,
 639                         "error setting current limit (%d)\n", ret);
 640 
 641         power_supply_changed(info->psy_usb);
 642 }
 643 
 644 static int axp288_charger_handle_cable_evt(struct notifier_block *nb,
 645                                            unsigned long event, void *param)
 646 {
 647         struct axp288_chrg_info *info =
 648                 container_of(nb, struct axp288_chrg_info, cable.nb);
 649         schedule_work(&info->cable.work);
 650         return NOTIFY_OK;
 651 }
 652 
 653 static void axp288_charger_otg_evt_worker(struct work_struct *work)
 654 {
 655         struct axp288_chrg_info *info =
 656             container_of(work, struct axp288_chrg_info, otg.work);
 657         struct extcon_dev *edev = info->otg.cable;
 658         int ret, usb_host = extcon_get_state(edev, EXTCON_USB_HOST);
 659 
 660         dev_dbg(&info->pdev->dev, "external connector USB-Host is %s\n",
 661                                 usb_host ? "attached" : "detached");
 662 
 663         
 664 
 665 
 666 
 667         info->otg.id_short = usb_host;
 668 
 669         
 670         ret = axp288_charger_vbus_path_select(info, !info->otg.id_short);
 671         if (ret < 0)
 672                 dev_warn(&info->pdev->dev, "vbus path disable failed\n");
 673 }
 674 
 675 static int axp288_charger_handle_otg_evt(struct notifier_block *nb,
 676                                    unsigned long event, void *param)
 677 {
 678         struct axp288_chrg_info *info =
 679             container_of(nb, struct axp288_chrg_info, otg.id_nb);
 680 
 681         schedule_work(&info->otg.work);
 682 
 683         return NOTIFY_OK;
 684 }
 685 
 686 static int charger_init_hw_regs(struct axp288_chrg_info *info)
 687 {
 688         int ret, cc, cv;
 689         unsigned int val;
 690 
 691         
 692         ret = regmap_write(info->regmap, AXP20X_V_LTF_CHRG, CHRG_VLTFC_0C);
 693         if (ret < 0) {
 694                 dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
 695                                                         AXP20X_V_LTF_CHRG, ret);
 696                 return ret;
 697         }
 698 
 699         ret = regmap_write(info->regmap, AXP20X_V_HTF_CHRG, CHRG_VHTFC_45C);
 700         if (ret < 0) {
 701                 dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
 702                                                         AXP20X_V_HTF_CHRG, ret);
 703                 return ret;
 704         }
 705 
 706         
 707         ret = regmap_update_bits(info->regmap,
 708                                 AXP20X_CHRG_CTRL2,
 709                                 CNTL2_CHG_OUT_TURNON, CNTL2_CHG_OUT_TURNON);
 710         if (ret < 0) {
 711                 dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
 712                                                 AXP20X_CHRG_CTRL2, ret);
 713                 return ret;
 714         }
 715 
 716         
 717         ret = regmap_update_bits(info->regmap,
 718                                 AXP20X_CHRG_CTRL1,
 719                                 CHRG_CCCV_ITERM_20P, 0);
 720         if (ret < 0) {
 721                 dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
 722                                                 AXP20X_CHRG_CTRL1, ret);
 723                 return ret;
 724         }
 725 
 726         
 727         ret = regmap_update_bits(info->regmap,
 728                                 AXP20X_CC_CTRL,
 729                                 FG_CNTL_OCV_ADJ_EN, 0);
 730         if (ret < 0) {
 731                 dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
 732                                                 AXP20X_CC_CTRL, ret);
 733                 return ret;
 734         }
 735 
 736         if (dmi_check_system(axp288_hp_x2_dmi_ids)) {
 737                 
 738                 ret = axp288_charger_vbus_path_select(info, true);
 739                 if (ret < 0)
 740                         return ret;
 741         }
 742 
 743         
 744         ret = regmap_read(info->regmap, AXP20X_CHRG_CTRL1, &val);
 745         if (ret < 0) {
 746                 dev_err(&info->pdev->dev, "register(%x) read error(%d)\n",
 747                         AXP20X_CHRG_CTRL1, ret);
 748                 return ret;
 749         }
 750 
 751         
 752         cv = (val & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS;
 753         switch (cv) {
 754         case CHRG_CCCV_CV_4100MV:
 755                 info->cv = CV_4100MV;
 756                 break;
 757         case CHRG_CCCV_CV_4150MV:
 758                 info->cv = CV_4150MV;
 759                 break;
 760         case CHRG_CCCV_CV_4200MV:
 761                 info->cv = CV_4200MV;
 762                 break;
 763         case CHRG_CCCV_CV_4350MV:
 764                 info->cv = CV_4350MV;
 765                 break;
 766         }
 767 
 768         
 769         cc = (val & CHRG_CCCV_CC_MASK) >> CHRG_CCCV_CC_BIT_POS;
 770         cc = (cc * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;
 771         info->cc = cc;
 772 
 773         
 774 
 775 
 776 
 777         info->max_cv = info->cv;
 778         info->max_cc = info->cc;
 779 
 780         return 0;
 781 }
 782 
 783 static void axp288_charger_cancel_work(void *data)
 784 {
 785         struct axp288_chrg_info *info = data;
 786 
 787         cancel_work_sync(&info->otg.work);
 788         cancel_work_sync(&info->cable.work);
 789 }
 790 
 791 static int axp288_charger_probe(struct platform_device *pdev)
 792 {
 793         int ret, i, pirq;
 794         struct axp288_chrg_info *info;
 795         struct device *dev = &pdev->dev;
 796         struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
 797         struct power_supply_config charger_cfg = {};
 798         unsigned int val;
 799 
 800         
 801 
 802 
 803 
 804         ret = regmap_read(axp20x->regmap, AXP20X_CC_CTRL, &val);
 805         if (ret < 0)
 806                 return ret;
 807         if (val == 0)
 808                 return -ENODEV;
 809 
 810         info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
 811         if (!info)
 812                 return -ENOMEM;
 813 
 814         info->pdev = pdev;
 815         info->regmap = axp20x->regmap;
 816         info->regmap_irqc = axp20x->regmap_irqc;
 817 
 818         info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME);
 819         if (info->cable.edev == NULL) {
 820                 dev_dbg(&pdev->dev, "%s is not ready, probe deferred\n",
 821                         AXP288_EXTCON_DEV_NAME);
 822                 return -EPROBE_DEFER;
 823         }
 824 
 825         if (acpi_dev_present(USB_HOST_EXTCON_HID, NULL, -1)) {
 826                 info->otg.cable = extcon_get_extcon_dev(USB_HOST_EXTCON_NAME);
 827                 if (info->otg.cable == NULL) {
 828                         dev_dbg(dev, "EXTCON_USB_HOST is not ready, probe deferred\n");
 829                         return -EPROBE_DEFER;
 830                 }
 831                 dev_info(&pdev->dev,
 832                          "Using " USB_HOST_EXTCON_HID " extcon for usb-id\n");
 833         }
 834 
 835         platform_set_drvdata(pdev, info);
 836 
 837         ret = charger_init_hw_regs(info);
 838         if (ret)
 839                 return ret;
 840 
 841         
 842         charger_cfg.drv_data = info;
 843         info->psy_usb = devm_power_supply_register(dev, &axp288_charger_desc,
 844                                                    &charger_cfg);
 845         if (IS_ERR(info->psy_usb)) {
 846                 ret = PTR_ERR(info->psy_usb);
 847                 dev_err(dev, "failed to register power supply: %d\n", ret);
 848                 return ret;
 849         }
 850 
 851         
 852         ret = devm_add_action(dev, axp288_charger_cancel_work, info);
 853         if (ret)
 854                 return ret;
 855 
 856         
 857         INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker);
 858         info->cable.nb.notifier_call = axp288_charger_handle_cable_evt;
 859         ret = devm_extcon_register_notifier_all(dev, info->cable.edev,
 860                                                 &info->cable.nb);
 861         if (ret) {
 862                 dev_err(dev, "failed to register cable extcon notifier\n");
 863                 return ret;
 864         }
 865         schedule_work(&info->cable.work);
 866 
 867         
 868         INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker);
 869         info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt;
 870         if (info->otg.cable) {
 871                 ret = devm_extcon_register_notifier(&pdev->dev, info->otg.cable,
 872                                         EXTCON_USB_HOST, &info->otg.id_nb);
 873                 if (ret) {
 874                         dev_err(dev, "failed to register EXTCON_USB_HOST notifier\n");
 875                         return ret;
 876                 }
 877                 schedule_work(&info->otg.work);
 878         }
 879 
 880         
 881         for (i = 0; i < CHRG_INTR_END; i++) {
 882                 pirq = platform_get_irq(info->pdev, i);
 883                 if (pirq < 0) {
 884                         dev_err(&pdev->dev, "Failed to get IRQ: %d\n", pirq);
 885                         return pirq;
 886                 }
 887                 info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq);
 888                 if (info->irq[i] < 0) {
 889                         dev_warn(&info->pdev->dev,
 890                                 "failed to get virtual interrupt=%d\n", pirq);
 891                         return info->irq[i];
 892                 }
 893                 ret = devm_request_threaded_irq(&info->pdev->dev, info->irq[i],
 894                                         NULL, axp288_charger_irq_thread_handler,
 895                                         IRQF_ONESHOT, info->pdev->name, info);
 896                 if (ret) {
 897                         dev_err(&pdev->dev, "failed to request interrupt=%d\n",
 898                                                                 info->irq[i]);
 899                         return ret;
 900                 }
 901         }
 902 
 903         return 0;
 904 }
 905 
 906 static const struct platform_device_id axp288_charger_id_table[] = {
 907         { .name = "axp288_charger" },
 908         {},
 909 };
 910 MODULE_DEVICE_TABLE(platform, axp288_charger_id_table);
 911 
 912 static struct platform_driver axp288_charger_driver = {
 913         .probe = axp288_charger_probe,
 914         .id_table = axp288_charger_id_table,
 915         .driver = {
 916                 .name = "axp288_charger",
 917         },
 918 };
 919 
 920 module_platform_driver(axp288_charger_driver);
 921 
 922 MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");
 923 MODULE_DESCRIPTION("X-power AXP288 Charger Driver");
 924 MODULE_LICENSE("GPL v2");