0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/acpi.h>
0011 #include <linux/bitops.h>
0012 #include <linux/module.h>
0013 #include <linux/device.h>
0014 #include <linux/regmap.h>
0015 #include <linux/workqueue.h>
0016 #include <linux/delay.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/usb/otg.h>
0019 #include <linux/notifier.h>
0020 #include <linux/power_supply.h>
0021 #include <linux/property.h>
0022 #include <linux/mfd/axp20x.h>
0023 #include <linux/extcon.h>
0024 #include <linux/dmi.h>
0025 #include <asm/iosf_mbi.h>
0026
0027 #define PS_STAT_VBUS_TRIGGER BIT(0)
0028 #define PS_STAT_BAT_CHRG_DIR BIT(2)
0029 #define PS_STAT_VBAT_ABOVE_VHOLD BIT(3)
0030 #define PS_STAT_VBUS_VALID BIT(4)
0031 #define PS_STAT_VBUS_PRESENT BIT(5)
0032
0033 #define CHRG_STAT_BAT_SAFE_MODE BIT(3)
0034 #define CHRG_STAT_BAT_VALID BIT(4)
0035 #define CHRG_STAT_BAT_PRESENT BIT(5)
0036 #define CHRG_STAT_CHARGING BIT(6)
0037 #define CHRG_STAT_PMIC_OTP BIT(7)
0038
0039 #define VBUS_ISPOUT_CUR_LIM_MASK 0x03
0040 #define VBUS_ISPOUT_CUR_LIM_BIT_POS 0
0041 #define VBUS_ISPOUT_CUR_LIM_900MA 0x0
0042 #define VBUS_ISPOUT_CUR_LIM_1500MA 0x1
0043 #define VBUS_ISPOUT_CUR_LIM_2000MA 0x2
0044 #define VBUS_ISPOUT_CUR_NO_LIM 0x3
0045 #define VBUS_ISPOUT_VHOLD_SET_MASK 0x38
0046 #define VBUS_ISPOUT_VHOLD_SET_BIT_POS 0x3
0047 #define VBUS_ISPOUT_VHOLD_SET_OFFSET 4000
0048 #define VBUS_ISPOUT_VHOLD_SET_LSB_RES 100
0049 #define VBUS_ISPOUT_VHOLD_SET_4400MV 0x4
0050 #define VBUS_ISPOUT_VBUS_PATH_DIS BIT(7)
0051
0052 #define CHRG_CCCV_CC_MASK 0xf
0053 #define CHRG_CCCV_CC_BIT_POS 0
0054 #define CHRG_CCCV_CC_OFFSET 200
0055 #define CHRG_CCCV_CC_LSB_RES 200
0056 #define CHRG_CCCV_ITERM_20P BIT(4)
0057 #define CHRG_CCCV_CV_MASK 0x60
0058 #define CHRG_CCCV_CV_BIT_POS 5
0059 #define CHRG_CCCV_CV_4100MV 0x0
0060 #define CHRG_CCCV_CV_4150MV 0x1
0061 #define CHRG_CCCV_CV_4200MV 0x2
0062 #define CHRG_CCCV_CV_4350MV 0x3
0063 #define CHRG_CCCV_CHG_EN BIT(7)
0064
0065 #define CNTL2_CC_TIMEOUT_MASK 0x3
0066 #define CNTL2_CC_TIMEOUT_OFFSET 6
0067 #define CNTL2_CC_TIMEOUT_LSB_RES 2
0068 #define CNTL2_CC_TIMEOUT_12HRS 0x3
0069 #define CNTL2_CHGLED_TYPEB BIT(4)
0070 #define CNTL2_CHG_OUT_TURNON BIT(5)
0071 #define CNTL2_PC_TIMEOUT_MASK 0xC0
0072 #define CNTL2_PC_TIMEOUT_OFFSET 40
0073 #define CNTL2_PC_TIMEOUT_LSB_RES 10
0074 #define CNTL2_PC_TIMEOUT_70MINS 0x3
0075
0076 #define CHRG_ILIM_TEMP_LOOP_EN BIT(3)
0077 #define CHRG_VBUS_ILIM_MASK 0xf0
0078 #define CHRG_VBUS_ILIM_BIT_POS 4
0079 #define CHRG_VBUS_ILIM_100MA 0x0
0080 #define CHRG_VBUS_ILIM_500MA 0x1
0081 #define CHRG_VBUS_ILIM_900MA 0x2
0082 #define CHRG_VBUS_ILIM_1500MA 0x3
0083 #define CHRG_VBUS_ILIM_2000MA 0x4
0084 #define CHRG_VBUS_ILIM_2500MA 0x5
0085 #define CHRG_VBUS_ILIM_3000MA 0x6
0086 #define CHRG_VBUS_ILIM_3500MA 0x7
0087 #define CHRG_VBUS_ILIM_4000MA 0x8
0088
0089 #define CHRG_VLTFC_0C 0xA5
0090 #define CHRG_VHTFC_45C 0x1F
0091
0092 #define FG_CNTL_OCV_ADJ_EN BIT(3)
0093
0094 #define CV_4100MV 4100
0095 #define CV_4150MV 4150
0096 #define CV_4200MV 4200
0097 #define CV_4350MV 4350
0098
0099 #define AXP288_REG_UPDATE_INTERVAL (60 * HZ)
0100
0101 #define AXP288_EXTCON_DEV_NAME "axp288_extcon"
0102 #define USB_HOST_EXTCON_HID "INT3496"
0103 #define USB_HOST_EXTCON_NAME "INT3496:00"
0104
0105 enum {
0106 VBUS_OV_IRQ = 0,
0107 CHARGE_DONE_IRQ,
0108 CHARGE_CHARGING_IRQ,
0109 BAT_SAFE_QUIT_IRQ,
0110 BAT_SAFE_ENTER_IRQ,
0111 QCBTU_IRQ,
0112 CBTU_IRQ,
0113 QCBTO_IRQ,
0114 CBTO_IRQ,
0115 CHRG_INTR_END,
0116 };
0117
0118 struct axp288_chrg_info {
0119 struct platform_device *pdev;
0120 struct regmap *regmap;
0121 struct regmap_irq_chip_data *regmap_irqc;
0122 int irq[CHRG_INTR_END];
0123 struct power_supply *psy_usb;
0124 struct mutex lock;
0125
0126
0127 struct {
0128 struct work_struct work;
0129 struct extcon_dev *cable;
0130 struct notifier_block id_nb;
0131 bool id_short;
0132 } otg;
0133
0134
0135 struct {
0136 struct extcon_dev *edev;
0137 struct notifier_block nb;
0138 struct work_struct work;
0139 } cable;
0140
0141 int cc;
0142 int cv;
0143 int max_cc;
0144 int max_cv;
0145
0146 unsigned long last_updated;
0147 unsigned int input_status;
0148 unsigned int op_mode;
0149 unsigned int backend_control;
0150 bool valid;
0151 };
0152
0153 static inline int axp288_charger_set_cc(struct axp288_chrg_info *info, int cc)
0154 {
0155 u8 reg_val;
0156 int ret;
0157
0158 if (cc < CHRG_CCCV_CC_OFFSET)
0159 cc = CHRG_CCCV_CC_OFFSET;
0160 else if (cc > info->max_cc)
0161 cc = info->max_cc;
0162
0163 reg_val = (cc - CHRG_CCCV_CC_OFFSET) / CHRG_CCCV_CC_LSB_RES;
0164 cc = (reg_val * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;
0165 reg_val = reg_val << CHRG_CCCV_CC_BIT_POS;
0166
0167 ret = regmap_update_bits(info->regmap,
0168 AXP20X_CHRG_CTRL1,
0169 CHRG_CCCV_CC_MASK, reg_val);
0170 if (ret >= 0)
0171 info->cc = cc;
0172
0173 return ret;
0174 }
0175
0176 static inline int axp288_charger_set_cv(struct axp288_chrg_info *info, int cv)
0177 {
0178 u8 reg_val;
0179 int ret;
0180
0181 if (cv <= CV_4100MV) {
0182 reg_val = CHRG_CCCV_CV_4100MV;
0183 cv = CV_4100MV;
0184 } else if (cv <= CV_4150MV) {
0185 reg_val = CHRG_CCCV_CV_4150MV;
0186 cv = CV_4150MV;
0187 } else if (cv <= CV_4200MV) {
0188 reg_val = CHRG_CCCV_CV_4200MV;
0189 cv = CV_4200MV;
0190 } else {
0191 reg_val = CHRG_CCCV_CV_4350MV;
0192 cv = CV_4350MV;
0193 }
0194
0195 reg_val = reg_val << CHRG_CCCV_CV_BIT_POS;
0196
0197 ret = regmap_update_bits(info->regmap,
0198 AXP20X_CHRG_CTRL1,
0199 CHRG_CCCV_CV_MASK, reg_val);
0200
0201 if (ret >= 0)
0202 info->cv = cv;
0203
0204 return ret;
0205 }
0206
0207 static int axp288_charger_get_vbus_inlmt(struct axp288_chrg_info *info)
0208 {
0209 unsigned int val;
0210
0211 val = info->backend_control;
0212
0213 val >>= CHRG_VBUS_ILIM_BIT_POS;
0214 switch (val) {
0215 case CHRG_VBUS_ILIM_100MA:
0216 return 100000;
0217 case CHRG_VBUS_ILIM_500MA:
0218 return 500000;
0219 case CHRG_VBUS_ILIM_900MA:
0220 return 900000;
0221 case CHRG_VBUS_ILIM_1500MA:
0222 return 1500000;
0223 case CHRG_VBUS_ILIM_2000MA:
0224 return 2000000;
0225 case CHRG_VBUS_ILIM_2500MA:
0226 return 2500000;
0227 case CHRG_VBUS_ILIM_3000MA:
0228 return 3000000;
0229 case CHRG_VBUS_ILIM_3500MA:
0230 return 3500000;
0231 default:
0232
0233 return 4000000;
0234 }
0235 }
0236
0237 static inline int axp288_charger_set_vbus_inlmt(struct axp288_chrg_info *info,
0238 int inlmt)
0239 {
0240 int ret;
0241 u8 reg_val;
0242
0243 if (inlmt >= 4000000)
0244 reg_val = CHRG_VBUS_ILIM_4000MA << CHRG_VBUS_ILIM_BIT_POS;
0245 else if (inlmt >= 3500000)
0246 reg_val = CHRG_VBUS_ILIM_3500MA << CHRG_VBUS_ILIM_BIT_POS;
0247 else if (inlmt >= 3000000)
0248 reg_val = CHRG_VBUS_ILIM_3000MA << CHRG_VBUS_ILIM_BIT_POS;
0249 else if (inlmt >= 2500000)
0250 reg_val = CHRG_VBUS_ILIM_2500MA << CHRG_VBUS_ILIM_BIT_POS;
0251 else if (inlmt >= 2000000)
0252 reg_val = CHRG_VBUS_ILIM_2000MA << CHRG_VBUS_ILIM_BIT_POS;
0253 else if (inlmt >= 1500000)
0254 reg_val = CHRG_VBUS_ILIM_1500MA << CHRG_VBUS_ILIM_BIT_POS;
0255 else if (inlmt >= 900000)
0256 reg_val = CHRG_VBUS_ILIM_900MA << CHRG_VBUS_ILIM_BIT_POS;
0257 else if (inlmt >= 500000)
0258 reg_val = CHRG_VBUS_ILIM_500MA << CHRG_VBUS_ILIM_BIT_POS;
0259 else
0260 reg_val = CHRG_VBUS_ILIM_100MA << CHRG_VBUS_ILIM_BIT_POS;
0261
0262 ret = regmap_update_bits(info->regmap, AXP20X_CHRG_BAK_CTRL,
0263 CHRG_VBUS_ILIM_MASK, reg_val);
0264 if (ret < 0)
0265 dev_err(&info->pdev->dev, "charger BAK control %d\n", ret);
0266
0267 return ret;
0268 }
0269
0270 static int axp288_charger_vbus_path_select(struct axp288_chrg_info *info,
0271 bool enable)
0272 {
0273 int ret;
0274
0275 if (enable)
0276 ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,
0277 VBUS_ISPOUT_VBUS_PATH_DIS, 0);
0278 else
0279 ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,
0280 VBUS_ISPOUT_VBUS_PATH_DIS, VBUS_ISPOUT_VBUS_PATH_DIS);
0281
0282 if (ret < 0)
0283 dev_err(&info->pdev->dev, "axp288 vbus path select %d\n", ret);
0284
0285 return ret;
0286 }
0287
0288 static int axp288_charger_enable_charger(struct axp288_chrg_info *info,
0289 bool enable)
0290 {
0291 int ret;
0292
0293 if (enable)
0294 ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,
0295 CHRG_CCCV_CHG_EN, CHRG_CCCV_CHG_EN);
0296 else
0297 ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,
0298 CHRG_CCCV_CHG_EN, 0);
0299 if (ret < 0)
0300 dev_err(&info->pdev->dev, "axp288 enable charger %d\n", ret);
0301
0302 return ret;
0303 }
0304
0305 static int axp288_get_charger_health(struct axp288_chrg_info *info)
0306 {
0307 if (!(info->input_status & PS_STAT_VBUS_PRESENT))
0308 return POWER_SUPPLY_HEALTH_UNKNOWN;
0309
0310 if (!(info->input_status & PS_STAT_VBUS_VALID))
0311 return POWER_SUPPLY_HEALTH_DEAD;
0312 else if (info->op_mode & CHRG_STAT_PMIC_OTP)
0313 return POWER_SUPPLY_HEALTH_OVERHEAT;
0314 else if (info->op_mode & CHRG_STAT_BAT_SAFE_MODE)
0315 return POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
0316 else
0317 return POWER_SUPPLY_HEALTH_GOOD;
0318 }
0319
0320 static int axp288_charger_usb_set_property(struct power_supply *psy,
0321 enum power_supply_property psp,
0322 const union power_supply_propval *val)
0323 {
0324 struct axp288_chrg_info *info = power_supply_get_drvdata(psy);
0325 int ret = 0;
0326 int scaled_val;
0327
0328 mutex_lock(&info->lock);
0329 switch (psp) {
0330 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
0331 scaled_val = min(val->intval, info->max_cc);
0332 scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000);
0333 ret = axp288_charger_set_cc(info, scaled_val);
0334 if (ret < 0) {
0335 dev_warn(&info->pdev->dev, "set charge current failed\n");
0336 goto out;
0337 }
0338 break;
0339 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
0340 scaled_val = min(val->intval, info->max_cv);
0341 scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000);
0342 ret = axp288_charger_set_cv(info, scaled_val);
0343 if (ret < 0) {
0344 dev_warn(&info->pdev->dev, "set charge voltage failed\n");
0345 goto out;
0346 }
0347 break;
0348 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
0349 ret = axp288_charger_set_vbus_inlmt(info, val->intval);
0350 if (ret < 0) {
0351 dev_warn(&info->pdev->dev, "set input current limit failed\n");
0352 goto out;
0353 }
0354 info->valid = false;
0355 break;
0356 default:
0357 ret = -EINVAL;
0358 }
0359
0360 out:
0361 mutex_unlock(&info->lock);
0362 return ret;
0363 }
0364
0365 static int axp288_charger_reg_readb(struct axp288_chrg_info *info, int reg, unsigned int *ret_val)
0366 {
0367 int ret;
0368
0369 ret = regmap_read(info->regmap, reg, ret_val);
0370 if (ret < 0) {
0371 dev_err(&info->pdev->dev, "Error %d on reading value from register 0x%04x\n",
0372 ret,
0373 reg);
0374 return ret;
0375 }
0376 return 0;
0377 }
0378
0379 static int axp288_charger_usb_update_property(struct axp288_chrg_info *info)
0380 {
0381 int ret = 0;
0382
0383 if (info->valid && time_before(jiffies, info->last_updated + AXP288_REG_UPDATE_INTERVAL))
0384 return 0;
0385
0386 dev_dbg(&info->pdev->dev, "Charger updating register values...\n");
0387
0388 ret = iosf_mbi_block_punit_i2c_access();
0389 if (ret < 0)
0390 return ret;
0391
0392 ret = axp288_charger_reg_readb(info, AXP20X_PWR_INPUT_STATUS, &info->input_status);
0393 if (ret < 0)
0394 goto out;
0395
0396 ret = axp288_charger_reg_readb(info, AXP20X_PWR_OP_MODE, &info->op_mode);
0397 if (ret < 0)
0398 goto out;
0399
0400 ret = axp288_charger_reg_readb(info, AXP20X_CHRG_BAK_CTRL, &info->backend_control);
0401 if (ret < 0)
0402 goto out;
0403
0404 info->last_updated = jiffies;
0405 info->valid = true;
0406 out:
0407 iosf_mbi_unblock_punit_i2c_access();
0408 return ret;
0409 }
0410
0411 static int axp288_charger_usb_get_property(struct power_supply *psy,
0412 enum power_supply_property psp,
0413 union power_supply_propval *val)
0414 {
0415 struct axp288_chrg_info *info = power_supply_get_drvdata(psy);
0416 int ret;
0417
0418 mutex_lock(&info->lock);
0419 ret = axp288_charger_usb_update_property(info);
0420 if (ret < 0)
0421 goto out;
0422
0423 switch (psp) {
0424 case POWER_SUPPLY_PROP_PRESENT:
0425
0426 if (info->otg.id_short) {
0427 val->intval = 0;
0428 break;
0429 }
0430 val->intval = (info->input_status & PS_STAT_VBUS_PRESENT) ? 1 : 0;
0431 break;
0432 case POWER_SUPPLY_PROP_ONLINE:
0433
0434 if (info->otg.id_short) {
0435 val->intval = 0;
0436 break;
0437 }
0438 val->intval = (info->input_status & PS_STAT_VBUS_VALID) ? 1 : 0;
0439 break;
0440 case POWER_SUPPLY_PROP_HEALTH:
0441 val->intval = axp288_get_charger_health(info);
0442 break;
0443 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
0444 val->intval = info->cc * 1000;
0445 break;
0446 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
0447 val->intval = info->max_cc * 1000;
0448 break;
0449 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
0450 val->intval = info->cv * 1000;
0451 break;
0452 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
0453 val->intval = info->max_cv * 1000;
0454 break;
0455 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
0456 val->intval = axp288_charger_get_vbus_inlmt(info);
0457 break;
0458 default:
0459 ret = -EINVAL;
0460 }
0461
0462 out:
0463 mutex_unlock(&info->lock);
0464 return ret;
0465 }
0466
0467 static int axp288_charger_property_is_writeable(struct power_supply *psy,
0468 enum power_supply_property psp)
0469 {
0470 int ret;
0471
0472 switch (psp) {
0473 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
0474 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
0475 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
0476 ret = 1;
0477 break;
0478 default:
0479 ret = 0;
0480 }
0481
0482 return ret;
0483 }
0484
0485 static enum power_supply_property axp288_usb_props[] = {
0486 POWER_SUPPLY_PROP_PRESENT,
0487 POWER_SUPPLY_PROP_ONLINE,
0488 POWER_SUPPLY_PROP_TYPE,
0489 POWER_SUPPLY_PROP_HEALTH,
0490 POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
0491 POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
0492 POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
0493 POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
0494 POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
0495 };
0496
0497 static const struct power_supply_desc axp288_charger_desc = {
0498 .name = "axp288_charger",
0499 .type = POWER_SUPPLY_TYPE_USB,
0500 .properties = axp288_usb_props,
0501 .num_properties = ARRAY_SIZE(axp288_usb_props),
0502 .get_property = axp288_charger_usb_get_property,
0503 .set_property = axp288_charger_usb_set_property,
0504 .property_is_writeable = axp288_charger_property_is_writeable,
0505 };
0506
0507 static irqreturn_t axp288_charger_irq_thread_handler(int irq, void *dev)
0508 {
0509 struct axp288_chrg_info *info = dev;
0510 int i;
0511
0512 for (i = 0; i < CHRG_INTR_END; i++) {
0513 if (info->irq[i] == irq)
0514 break;
0515 }
0516
0517 if (i >= CHRG_INTR_END) {
0518 dev_warn(&info->pdev->dev, "spurious interrupt!!\n");
0519 return IRQ_NONE;
0520 }
0521
0522 switch (i) {
0523 case VBUS_OV_IRQ:
0524 dev_dbg(&info->pdev->dev, "VBUS Over Voltage INTR\n");
0525 break;
0526 case CHARGE_DONE_IRQ:
0527 dev_dbg(&info->pdev->dev, "Charging Done INTR\n");
0528 break;
0529 case CHARGE_CHARGING_IRQ:
0530 dev_dbg(&info->pdev->dev, "Start Charging IRQ\n");
0531 break;
0532 case BAT_SAFE_QUIT_IRQ:
0533 dev_dbg(&info->pdev->dev,
0534 "Quit Safe Mode(restart timer) Charging IRQ\n");
0535 break;
0536 case BAT_SAFE_ENTER_IRQ:
0537 dev_dbg(&info->pdev->dev,
0538 "Enter Safe Mode(timer expire) Charging IRQ\n");
0539 break;
0540 case QCBTU_IRQ:
0541 dev_dbg(&info->pdev->dev,
0542 "Quit Battery Under Temperature(CHRG) INTR\n");
0543 break;
0544 case CBTU_IRQ:
0545 dev_dbg(&info->pdev->dev,
0546 "Hit Battery Under Temperature(CHRG) INTR\n");
0547 break;
0548 case QCBTO_IRQ:
0549 dev_dbg(&info->pdev->dev,
0550 "Quit Battery Over Temperature(CHRG) INTR\n");
0551 break;
0552 case CBTO_IRQ:
0553 dev_dbg(&info->pdev->dev,
0554 "Hit Battery Over Temperature(CHRG) INTR\n");
0555 break;
0556 default:
0557 dev_warn(&info->pdev->dev, "Spurious Interrupt!!!\n");
0558 goto out;
0559 }
0560 mutex_lock(&info->lock);
0561 info->valid = false;
0562 mutex_unlock(&info->lock);
0563 power_supply_changed(info->psy_usb);
0564 out:
0565 return IRQ_HANDLED;
0566 }
0567
0568
0569
0570
0571
0572
0573
0574
0575
0576
0577
0578
0579
0580
0581
0582
0583
0584
0585
0586
0587
0588
0589
0590
0591
0592
0593
0594
0595
0596
0597
0598
0599 static const struct dmi_system_id axp288_hp_x2_dmi_ids[] = {
0600 {
0601 .matches = {
0602 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
0603 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"),
0604 DMI_EXACT_MATCH(DMI_BOARD_NAME, "815D"),
0605 },
0606 },
0607 {
0608 .matches = {
0609 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "HP"),
0610 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"),
0611 DMI_EXACT_MATCH(DMI_BOARD_NAME, "813E"),
0612 },
0613 },
0614 {}
0615 };
0616
0617 static void axp288_charger_extcon_evt_worker(struct work_struct *work)
0618 {
0619 struct axp288_chrg_info *info =
0620 container_of(work, struct axp288_chrg_info, cable.work);
0621 int ret, current_limit;
0622 struct extcon_dev *edev = info->cable.edev;
0623 unsigned int val;
0624
0625 ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
0626 if (ret < 0) {
0627 dev_err(&info->pdev->dev, "Error reading status (%d)\n", ret);
0628 return;
0629 }
0630
0631
0632 if (!(val & PS_STAT_VBUS_VALID)) {
0633 dev_dbg(&info->pdev->dev, "USB charger disconnected\n");
0634 axp288_charger_enable_charger(info, false);
0635 mutex_lock(&info->lock);
0636 info->valid = false;
0637 mutex_unlock(&info->lock);
0638 power_supply_changed(info->psy_usb);
0639 return;
0640 }
0641
0642
0643 if (dmi_check_system(axp288_hp_x2_dmi_ids)) {
0644
0645 dev_dbg(&info->pdev->dev, "HP X2 with Type-C, setting inlmt to 3A\n");
0646 current_limit = 3000000;
0647 } else if (extcon_get_state(edev, EXTCON_CHG_USB_SDP) > 0) {
0648 dev_dbg(&info->pdev->dev, "USB SDP charger is connected\n");
0649 current_limit = 500000;
0650 } else if (extcon_get_state(edev, EXTCON_CHG_USB_CDP) > 0) {
0651 dev_dbg(&info->pdev->dev, "USB CDP charger is connected\n");
0652 current_limit = 1500000;
0653 } else if (extcon_get_state(edev, EXTCON_CHG_USB_DCP) > 0) {
0654 dev_dbg(&info->pdev->dev, "USB DCP charger is connected\n");
0655 current_limit = 2000000;
0656 } else {
0657
0658 return;
0659 }
0660
0661
0662 ret = axp288_charger_set_vbus_inlmt(info, current_limit);
0663 if (ret == 0)
0664 axp288_charger_enable_charger(info, true);
0665 else
0666 dev_err(&info->pdev->dev,
0667 "error setting current limit (%d)\n", ret);
0668
0669 mutex_lock(&info->lock);
0670 info->valid = false;
0671 mutex_unlock(&info->lock);
0672 power_supply_changed(info->psy_usb);
0673 }
0674
0675 static int axp288_charger_handle_cable_evt(struct notifier_block *nb,
0676 unsigned long event, void *param)
0677 {
0678 struct axp288_chrg_info *info =
0679 container_of(nb, struct axp288_chrg_info, cable.nb);
0680 schedule_work(&info->cable.work);
0681 return NOTIFY_OK;
0682 }
0683
0684 static void axp288_charger_otg_evt_worker(struct work_struct *work)
0685 {
0686 struct axp288_chrg_info *info =
0687 container_of(work, struct axp288_chrg_info, otg.work);
0688 struct extcon_dev *edev = info->otg.cable;
0689 int ret, usb_host = extcon_get_state(edev, EXTCON_USB_HOST);
0690
0691 dev_dbg(&info->pdev->dev, "external connector USB-Host is %s\n",
0692 usb_host ? "attached" : "detached");
0693
0694
0695
0696
0697
0698 info->otg.id_short = usb_host;
0699
0700
0701 ret = axp288_charger_vbus_path_select(info, !info->otg.id_short);
0702 if (ret < 0)
0703 dev_warn(&info->pdev->dev, "vbus path disable failed\n");
0704 }
0705
0706 static int axp288_charger_handle_otg_evt(struct notifier_block *nb,
0707 unsigned long event, void *param)
0708 {
0709 struct axp288_chrg_info *info =
0710 container_of(nb, struct axp288_chrg_info, otg.id_nb);
0711
0712 schedule_work(&info->otg.work);
0713
0714 return NOTIFY_OK;
0715 }
0716
0717 static int charger_init_hw_regs(struct axp288_chrg_info *info)
0718 {
0719 int ret, cc, cv;
0720 unsigned int val;
0721
0722
0723 ret = regmap_write(info->regmap, AXP20X_V_LTF_CHRG, CHRG_VLTFC_0C);
0724 if (ret < 0) {
0725 dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
0726 AXP20X_V_LTF_CHRG, ret);
0727 return ret;
0728 }
0729
0730 ret = regmap_write(info->regmap, AXP20X_V_HTF_CHRG, CHRG_VHTFC_45C);
0731 if (ret < 0) {
0732 dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
0733 AXP20X_V_HTF_CHRG, ret);
0734 return ret;
0735 }
0736
0737
0738 ret = regmap_update_bits(info->regmap,
0739 AXP20X_CHRG_CTRL2,
0740 CNTL2_CHG_OUT_TURNON, CNTL2_CHG_OUT_TURNON);
0741 if (ret < 0) {
0742 dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
0743 AXP20X_CHRG_CTRL2, ret);
0744 return ret;
0745 }
0746
0747
0748 ret = regmap_update_bits(info->regmap,
0749 AXP20X_CHRG_CTRL1,
0750 CHRG_CCCV_ITERM_20P, 0);
0751 if (ret < 0) {
0752 dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
0753 AXP20X_CHRG_CTRL1, ret);
0754 return ret;
0755 }
0756
0757
0758 ret = regmap_update_bits(info->regmap,
0759 AXP20X_CC_CTRL,
0760 FG_CNTL_OCV_ADJ_EN, 0);
0761 if (ret < 0) {
0762 dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
0763 AXP20X_CC_CTRL, ret);
0764 return ret;
0765 }
0766
0767 if (dmi_check_system(axp288_hp_x2_dmi_ids)) {
0768
0769 ret = axp288_charger_vbus_path_select(info, true);
0770 if (ret < 0)
0771 return ret;
0772 } else {
0773
0774 val = VBUS_ISPOUT_VHOLD_SET_4400MV << VBUS_ISPOUT_VHOLD_SET_BIT_POS;
0775 ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,
0776 VBUS_ISPOUT_VHOLD_SET_MASK, val);
0777 if (ret < 0) {
0778 dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
0779 AXP20X_VBUS_IPSOUT_MGMT, ret);
0780 return ret;
0781 }
0782 }
0783
0784
0785 ret = regmap_read(info->regmap, AXP20X_CHRG_CTRL1, &val);
0786 if (ret < 0) {
0787 dev_err(&info->pdev->dev, "register(%x) read error(%d)\n",
0788 AXP20X_CHRG_CTRL1, ret);
0789 return ret;
0790 }
0791
0792
0793 cv = (val & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS;
0794 switch (cv) {
0795 case CHRG_CCCV_CV_4100MV:
0796 info->cv = CV_4100MV;
0797 break;
0798 case CHRG_CCCV_CV_4150MV:
0799 info->cv = CV_4150MV;
0800 break;
0801 case CHRG_CCCV_CV_4200MV:
0802 info->cv = CV_4200MV;
0803 break;
0804 case CHRG_CCCV_CV_4350MV:
0805 info->cv = CV_4350MV;
0806 break;
0807 }
0808
0809
0810 cc = (val & CHRG_CCCV_CC_MASK) >> CHRG_CCCV_CC_BIT_POS;
0811 cc = (cc * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;
0812 info->cc = cc;
0813
0814
0815
0816
0817
0818 info->max_cv = info->cv;
0819 info->max_cc = info->cc;
0820
0821 return 0;
0822 }
0823
0824 static void axp288_charger_cancel_work(void *data)
0825 {
0826 struct axp288_chrg_info *info = data;
0827
0828 cancel_work_sync(&info->otg.work);
0829 cancel_work_sync(&info->cable.work);
0830 }
0831
0832 static int axp288_charger_probe(struct platform_device *pdev)
0833 {
0834 int ret, i, pirq;
0835 struct axp288_chrg_info *info;
0836 struct device *dev = &pdev->dev;
0837 struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
0838 struct power_supply_config charger_cfg = {};
0839 unsigned int val;
0840
0841
0842
0843
0844
0845 if (!acpi_quirk_skip_acpi_ac_and_battery())
0846 return -ENODEV;
0847
0848
0849
0850
0851
0852 ret = regmap_read(axp20x->regmap, AXP20X_CC_CTRL, &val);
0853 if (ret < 0)
0854 return ret;
0855 if (val == 0)
0856 return -ENODEV;
0857
0858 info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
0859 if (!info)
0860 return -ENOMEM;
0861
0862 mutex_init(&info->lock);
0863 info->pdev = pdev;
0864 info->regmap = axp20x->regmap;
0865 info->regmap_irqc = axp20x->regmap_irqc;
0866
0867 info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME);
0868 if (IS_ERR(info->cable.edev)) {
0869 dev_err_probe(dev, PTR_ERR(info->cable.edev),
0870 "extcon_get_extcon_dev(%s) failed\n",
0871 AXP288_EXTCON_DEV_NAME);
0872 return PTR_ERR(info->cable.edev);
0873 }
0874
0875 if (acpi_dev_present(USB_HOST_EXTCON_HID, NULL, -1)) {
0876 info->otg.cable = extcon_get_extcon_dev(USB_HOST_EXTCON_NAME);
0877 if (IS_ERR(info->otg.cable)) {
0878 dev_err_probe(dev, PTR_ERR(info->otg.cable),
0879 "extcon_get_extcon_dev(%s) failed\n",
0880 USB_HOST_EXTCON_NAME);
0881 return PTR_ERR(info->otg.cable);
0882 }
0883 dev_info(dev, "Using " USB_HOST_EXTCON_HID " extcon for usb-id\n");
0884 }
0885
0886 platform_set_drvdata(pdev, info);
0887
0888 ret = charger_init_hw_regs(info);
0889 if (ret)
0890 return ret;
0891
0892
0893 charger_cfg.drv_data = info;
0894 info->psy_usb = devm_power_supply_register(dev, &axp288_charger_desc,
0895 &charger_cfg);
0896 if (IS_ERR(info->psy_usb)) {
0897 ret = PTR_ERR(info->psy_usb);
0898 dev_err(dev, "failed to register power supply: %d\n", ret);
0899 return ret;
0900 }
0901
0902
0903 ret = devm_add_action(dev, axp288_charger_cancel_work, info);
0904 if (ret)
0905 return ret;
0906
0907
0908 INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker);
0909 info->cable.nb.notifier_call = axp288_charger_handle_cable_evt;
0910 ret = devm_extcon_register_notifier_all(dev, info->cable.edev,
0911 &info->cable.nb);
0912 if (ret) {
0913 dev_err(dev, "failed to register cable extcon notifier\n");
0914 return ret;
0915 }
0916 schedule_work(&info->cable.work);
0917
0918
0919 INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker);
0920 info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt;
0921 if (info->otg.cable) {
0922 ret = devm_extcon_register_notifier(dev, info->otg.cable,
0923 EXTCON_USB_HOST, &info->otg.id_nb);
0924 if (ret) {
0925 dev_err(dev, "failed to register EXTCON_USB_HOST notifier\n");
0926 return ret;
0927 }
0928 schedule_work(&info->otg.work);
0929 }
0930
0931
0932 for (i = 0; i < CHRG_INTR_END; i++) {
0933 pirq = platform_get_irq(info->pdev, i);
0934 if (pirq < 0)
0935 return pirq;
0936
0937 info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq);
0938 if (info->irq[i] < 0) {
0939 dev_warn(&info->pdev->dev,
0940 "failed to get virtual interrupt=%d\n", pirq);
0941 return info->irq[i];
0942 }
0943 ret = devm_request_threaded_irq(&info->pdev->dev, info->irq[i],
0944 NULL, axp288_charger_irq_thread_handler,
0945 IRQF_ONESHOT, info->pdev->name, info);
0946 if (ret) {
0947 dev_err(dev, "failed to request interrupt=%d\n",
0948 info->irq[i]);
0949 return ret;
0950 }
0951 }
0952
0953 return 0;
0954 }
0955
0956 static const struct platform_device_id axp288_charger_id_table[] = {
0957 { .name = "axp288_charger" },
0958 {},
0959 };
0960 MODULE_DEVICE_TABLE(platform, axp288_charger_id_table);
0961
0962 static struct platform_driver axp288_charger_driver = {
0963 .probe = axp288_charger_probe,
0964 .id_table = axp288_charger_id_table,
0965 .driver = {
0966 .name = "axp288_charger",
0967 },
0968 };
0969
0970 module_platform_driver(axp288_charger_driver);
0971
0972 MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");
0973 MODULE_DESCRIPTION("X-power AXP288 Charger Driver");
0974 MODULE_LICENSE("GPL v2");