986 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * axp288_charger.c - X-power AXP288 PMIC Charger driver4 *5 * Copyright (C) 2016-2017 Hans de Goede <hdegoede@redhat.com>6 * Copyright (C) 2014 Intel Corporation7 * Author: Ramakrishna Pallala <ramakrishna.pallala@intel.com>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#include <asm/iosf_mbi.h>26 27#define PS_STAT_VBUS_TRIGGER BIT(0)28#define PS_STAT_BAT_CHRG_DIR BIT(2)29#define PS_STAT_VBAT_ABOVE_VHOLD BIT(3)30#define PS_STAT_VBUS_VALID BIT(4)31#define PS_STAT_VBUS_PRESENT BIT(5)32 33#define CHRG_STAT_BAT_SAFE_MODE BIT(3)34#define CHRG_STAT_BAT_VALID BIT(4)35#define CHRG_STAT_BAT_PRESENT BIT(5)36#define CHRG_STAT_CHARGING BIT(6)37#define CHRG_STAT_PMIC_OTP BIT(7)38 39#define VBUS_ISPOUT_CUR_LIM_MASK 0x0340#define VBUS_ISPOUT_CUR_LIM_BIT_POS 041#define VBUS_ISPOUT_CUR_LIM_900MA 0x0 /* 900mA */42#define VBUS_ISPOUT_CUR_LIM_1500MA 0x1 /* 1500mA */43#define VBUS_ISPOUT_CUR_LIM_2000MA 0x2 /* 2000mA */44#define VBUS_ISPOUT_CUR_NO_LIM 0x3 /* 2500mA */45#define VBUS_ISPOUT_VHOLD_SET_MASK 0x3846#define VBUS_ISPOUT_VHOLD_SET_BIT_POS 0x347#define VBUS_ISPOUT_VHOLD_SET_OFFSET 4000 /* 4000mV */48#define VBUS_ISPOUT_VHOLD_SET_LSB_RES 100 /* 100mV */49#define VBUS_ISPOUT_VHOLD_SET_4400MV 0x4 /* 4400mV */50#define VBUS_ISPOUT_VBUS_PATH_DIS BIT(7)51 52#define CHRG_CCCV_CC_MASK 0xf /* 4 bits */53#define CHRG_CCCV_CC_BIT_POS 054#define CHRG_CCCV_CC_OFFSET 200 /* 200mA */55#define CHRG_CCCV_CC_LSB_RES 200 /* 200mA */56#define CHRG_CCCV_ITERM_20P BIT(4) /* 20% of CC */57#define CHRG_CCCV_CV_MASK 0x60 /* 2 bits */58#define CHRG_CCCV_CV_BIT_POS 559#define CHRG_CCCV_CV_4100MV 0x0 /* 4.10V */60#define CHRG_CCCV_CV_4150MV 0x1 /* 4.15V */61#define CHRG_CCCV_CV_4200MV 0x2 /* 4.20V */62#define CHRG_CCCV_CV_4350MV 0x3 /* 4.35V */63#define CHRG_CCCV_CHG_EN BIT(7)64 65#define CNTL2_CC_TIMEOUT_MASK 0x3 /* 2 bits */66#define CNTL2_CC_TIMEOUT_OFFSET 6 /* 6 Hrs */67#define CNTL2_CC_TIMEOUT_LSB_RES 2 /* 2 Hrs */68#define CNTL2_CC_TIMEOUT_12HRS 0x3 /* 12 Hrs */69#define CNTL2_CHGLED_TYPEB BIT(4)70#define CNTL2_CHG_OUT_TURNON BIT(5)71#define CNTL2_PC_TIMEOUT_MASK 0xC072#define CNTL2_PC_TIMEOUT_OFFSET 40 /* 40 mins */73#define CNTL2_PC_TIMEOUT_LSB_RES 10 /* 10 mins */74#define CNTL2_PC_TIMEOUT_70MINS 0x375 76#define CHRG_ILIM_TEMP_LOOP_EN BIT(3)77#define CHRG_VBUS_ILIM_MASK 0xf078#define CHRG_VBUS_ILIM_BIT_POS 479#define CHRG_VBUS_ILIM_100MA 0x0 /* 100mA */80#define CHRG_VBUS_ILIM_500MA 0x1 /* 500mA */81#define CHRG_VBUS_ILIM_900MA 0x2 /* 900mA */82#define CHRG_VBUS_ILIM_1500MA 0x3 /* 1500mA */83#define CHRG_VBUS_ILIM_2000MA 0x4 /* 2000mA */84#define CHRG_VBUS_ILIM_2500MA 0x5 /* 2500mA */85#define CHRG_VBUS_ILIM_3000MA 0x6 /* 3000mA */86#define CHRG_VBUS_ILIM_3500MA 0x7 /* 3500mA */87#define CHRG_VBUS_ILIM_4000MA 0x8 /* 4000mA */88 89#define CHRG_VLTFC_0C 0xA5 /* 0 DegC */90#define CHRG_VHTFC_45C 0x1F /* 45 DegC */91 92#define FG_CNTL_OCV_ADJ_EN BIT(3)93 94#define CV_4100MV 4100 /* 4100mV */95#define CV_4150MV 4150 /* 4150mV */96#define CV_4200MV 4200 /* 4200mV */97#define CV_4350MV 4350 /* 4350mV */98 99#define AXP288_REG_UPDATE_INTERVAL (60 * HZ)100 101#define AXP288_EXTCON_DEV_NAME "axp288_extcon"102#define USB_HOST_EXTCON_HID "INT3496"103#define USB_HOST_EXTCON_NAME "INT3496:00"104 105enum {106 VBUS_OV_IRQ = 0,107 CHARGE_DONE_IRQ,108 CHARGE_CHARGING_IRQ,109 BAT_SAFE_QUIT_IRQ,110 BAT_SAFE_ENTER_IRQ,111 QCBTU_IRQ,112 CBTU_IRQ,113 QCBTO_IRQ,114 CBTO_IRQ,115 CHRG_INTR_END,116};117 118struct axp288_chrg_info {119 struct platform_device *pdev;120 struct regmap *regmap;121 struct regmap_irq_chip_data *regmap_irqc;122 int irq[CHRG_INTR_END];123 struct power_supply *psy_usb;124 struct mutex lock;125 126 /* OTG/Host mode */127 struct {128 struct work_struct work;129 struct extcon_dev *cable;130 struct notifier_block id_nb;131 bool id_short;132 } otg;133 134 /* SDP/CDP/DCP USB charging cable notifications */135 struct {136 struct extcon_dev *edev;137 struct notifier_block nb;138 struct work_struct work;139 } cable;140 141 int cc;142 int cv;143 int max_cc;144 int max_cv;145 146 unsigned long last_updated;147 unsigned int input_status;148 unsigned int op_mode;149 unsigned int backend_control;150 bool valid;151};152 153static inline int axp288_charger_set_cc(struct axp288_chrg_info *info, int cc)154{155 u8 reg_val;156 int ret;157 158 if (cc < CHRG_CCCV_CC_OFFSET)159 cc = CHRG_CCCV_CC_OFFSET;160 else if (cc > info->max_cc)161 cc = info->max_cc;162 163 reg_val = (cc - CHRG_CCCV_CC_OFFSET) / CHRG_CCCV_CC_LSB_RES;164 cc = (reg_val * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;165 reg_val = reg_val << CHRG_CCCV_CC_BIT_POS;166 167 ret = regmap_update_bits(info->regmap,168 AXP20X_CHRG_CTRL1,169 CHRG_CCCV_CC_MASK, reg_val);170 if (ret >= 0)171 info->cc = cc;172 173 return ret;174}175 176static inline int axp288_charger_set_cv(struct axp288_chrg_info *info, int cv)177{178 u8 reg_val;179 int ret;180 181 if (cv >= CV_4350MV) {182 reg_val = CHRG_CCCV_CV_4350MV;183 cv = CV_4350MV;184 } else if (cv >= CV_4200MV) {185 reg_val = CHRG_CCCV_CV_4200MV;186 cv = CV_4200MV;187 } else if (cv >= CV_4150MV) {188 reg_val = CHRG_CCCV_CV_4150MV;189 cv = CV_4150MV;190 } else {191 reg_val = CHRG_CCCV_CV_4100MV;192 cv = CV_4100MV;193 }194 195 reg_val = reg_val << CHRG_CCCV_CV_BIT_POS;196 197 ret = regmap_update_bits(info->regmap,198 AXP20X_CHRG_CTRL1,199 CHRG_CCCV_CV_MASK, reg_val);200 201 if (ret >= 0)202 info->cv = cv;203 204 return ret;205}206 207static int axp288_charger_get_vbus_inlmt(struct axp288_chrg_info *info)208{209 unsigned int val;210 211 val = info->backend_control;212 213 val >>= CHRG_VBUS_ILIM_BIT_POS;214 switch (val) {215 case CHRG_VBUS_ILIM_100MA:216 return 100000;217 case CHRG_VBUS_ILIM_500MA:218 return 500000;219 case CHRG_VBUS_ILIM_900MA:220 return 900000;221 case CHRG_VBUS_ILIM_1500MA:222 return 1500000;223 case CHRG_VBUS_ILIM_2000MA:224 return 2000000;225 case CHRG_VBUS_ILIM_2500MA:226 return 2500000;227 case CHRG_VBUS_ILIM_3000MA:228 return 3000000;229 case CHRG_VBUS_ILIM_3500MA:230 return 3500000;231 default:232 /* All b1xxx values map to 4000 mA */233 return 4000000;234 }235}236 237static inline int axp288_charger_set_vbus_inlmt(struct axp288_chrg_info *info,238 int inlmt)239{240 int ret;241 u8 reg_val;242 243 if (inlmt >= 4000000)244 reg_val = CHRG_VBUS_ILIM_4000MA << CHRG_VBUS_ILIM_BIT_POS;245 else if (inlmt >= 3500000)246 reg_val = CHRG_VBUS_ILIM_3500MA << CHRG_VBUS_ILIM_BIT_POS;247 else if (inlmt >= 3000000)248 reg_val = CHRG_VBUS_ILIM_3000MA << CHRG_VBUS_ILIM_BIT_POS;249 else if (inlmt >= 2500000)250 reg_val = CHRG_VBUS_ILIM_2500MA << CHRG_VBUS_ILIM_BIT_POS;251 else if (inlmt >= 2000000)252 reg_val = CHRG_VBUS_ILIM_2000MA << CHRG_VBUS_ILIM_BIT_POS;253 else if (inlmt >= 1500000)254 reg_val = CHRG_VBUS_ILIM_1500MA << CHRG_VBUS_ILIM_BIT_POS;255 else if (inlmt >= 900000)256 reg_val = CHRG_VBUS_ILIM_900MA << CHRG_VBUS_ILIM_BIT_POS;257 else if (inlmt >= 500000)258 reg_val = CHRG_VBUS_ILIM_500MA << CHRG_VBUS_ILIM_BIT_POS;259 else260 reg_val = CHRG_VBUS_ILIM_100MA << CHRG_VBUS_ILIM_BIT_POS;261 262 ret = regmap_update_bits(info->regmap, AXP20X_CHRG_BAK_CTRL,263 CHRG_VBUS_ILIM_MASK, reg_val);264 if (ret < 0)265 dev_err(&info->pdev->dev, "charger BAK control %d\n", ret);266 267 return ret;268}269 270static int axp288_charger_vbus_path_select(struct axp288_chrg_info *info,271 bool enable)272{273 int ret;274 275 if (enable)276 ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,277 VBUS_ISPOUT_VBUS_PATH_DIS, 0);278 else279 ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,280 VBUS_ISPOUT_VBUS_PATH_DIS, VBUS_ISPOUT_VBUS_PATH_DIS);281 282 if (ret < 0)283 dev_err(&info->pdev->dev, "axp288 vbus path select %d\n", ret);284 285 return ret;286}287 288static int axp288_charger_enable_charger(struct axp288_chrg_info *info,289 bool enable)290{291 int ret;292 293 if (enable)294 ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,295 CHRG_CCCV_CHG_EN, CHRG_CCCV_CHG_EN);296 else297 ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,298 CHRG_CCCV_CHG_EN, 0);299 if (ret < 0)300 dev_err(&info->pdev->dev, "axp288 enable charger %d\n", ret);301 302 return ret;303}304 305static int axp288_get_charger_health(struct axp288_chrg_info *info)306{307 if (!(info->input_status & PS_STAT_VBUS_PRESENT))308 return POWER_SUPPLY_HEALTH_UNKNOWN;309 310 if (!(info->input_status & PS_STAT_VBUS_VALID))311 return POWER_SUPPLY_HEALTH_DEAD;312 else if (info->op_mode & CHRG_STAT_PMIC_OTP)313 return POWER_SUPPLY_HEALTH_OVERHEAT;314 else if (info->op_mode & CHRG_STAT_BAT_SAFE_MODE)315 return POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;316 else317 return POWER_SUPPLY_HEALTH_GOOD;318}319 320static int axp288_charger_usb_set_property(struct power_supply *psy,321 enum power_supply_property psp,322 const union power_supply_propval *val)323{324 struct axp288_chrg_info *info = power_supply_get_drvdata(psy);325 int ret = 0;326 int scaled_val;327 328 mutex_lock(&info->lock);329 switch (psp) {330 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:331 scaled_val = min(val->intval, info->max_cc);332 scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000);333 ret = axp288_charger_set_cc(info, scaled_val);334 if (ret < 0) {335 dev_warn(&info->pdev->dev, "set charge current failed\n");336 goto out;337 }338 break;339 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:340 scaled_val = DIV_ROUND_CLOSEST(val->intval, 1000);341 scaled_val = min(scaled_val, info->max_cv);342 ret = axp288_charger_set_cv(info, scaled_val);343 if (ret < 0) {344 dev_warn(&info->pdev->dev, "set charge voltage failed\n");345 goto out;346 }347 break;348 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:349 ret = axp288_charger_set_vbus_inlmt(info, val->intval);350 if (ret < 0) {351 dev_warn(&info->pdev->dev, "set input current limit failed\n");352 goto out;353 }354 info->valid = false;355 break;356 default:357 ret = -EINVAL;358 }359 360out:361 mutex_unlock(&info->lock);362 return ret;363}364 365static int axp288_charger_reg_readb(struct axp288_chrg_info *info, int reg, unsigned int *ret_val)366{367 int ret;368 369 ret = regmap_read(info->regmap, reg, ret_val);370 if (ret < 0) {371 dev_err(&info->pdev->dev, "Error %d on reading value from register 0x%04x\n",372 ret,373 reg);374 return ret;375 }376 return 0;377}378 379static int axp288_charger_usb_update_property(struct axp288_chrg_info *info)380{381 int ret = 0;382 383 if (info->valid && time_before(jiffies, info->last_updated + AXP288_REG_UPDATE_INTERVAL))384 return 0;385 386 dev_dbg(&info->pdev->dev, "Charger updating register values...\n");387 388 ret = iosf_mbi_block_punit_i2c_access();389 if (ret < 0)390 return ret;391 392 ret = axp288_charger_reg_readb(info, AXP20X_PWR_INPUT_STATUS, &info->input_status);393 if (ret < 0)394 goto out;395 396 ret = axp288_charger_reg_readb(info, AXP20X_PWR_OP_MODE, &info->op_mode);397 if (ret < 0)398 goto out;399 400 ret = axp288_charger_reg_readb(info, AXP20X_CHRG_BAK_CTRL, &info->backend_control);401 if (ret < 0)402 goto out;403 404 info->last_updated = jiffies;405 info->valid = true;406out:407 iosf_mbi_unblock_punit_i2c_access();408 return ret;409}410 411static int axp288_charger_usb_get_property(struct power_supply *psy,412 enum power_supply_property psp,413 union power_supply_propval *val)414{415 struct axp288_chrg_info *info = power_supply_get_drvdata(psy);416 int ret;417 418 mutex_lock(&info->lock);419 ret = axp288_charger_usb_update_property(info);420 if (ret < 0)421 goto out;422 423 switch (psp) {424 case POWER_SUPPLY_PROP_PRESENT:425 /* Check for OTG case first */426 if (info->otg.id_short) {427 val->intval = 0;428 break;429 }430 val->intval = (info->input_status & PS_STAT_VBUS_PRESENT) ? 1 : 0;431 break;432 case POWER_SUPPLY_PROP_ONLINE:433 /* Check for OTG case first */434 if (info->otg.id_short) {435 val->intval = 0;436 break;437 }438 val->intval = (info->input_status & PS_STAT_VBUS_VALID) ? 1 : 0;439 break;440 case POWER_SUPPLY_PROP_HEALTH:441 val->intval = axp288_get_charger_health(info);442 break;443 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:444 val->intval = info->cc * 1000;445 break;446 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:447 val->intval = info->max_cc * 1000;448 break;449 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:450 val->intval = info->cv * 1000;451 break;452 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:453 val->intval = info->max_cv * 1000;454 break;455 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:456 val->intval = axp288_charger_get_vbus_inlmt(info);457 break;458 default:459 ret = -EINVAL;460 }461 462out:463 mutex_unlock(&info->lock);464 return ret;465}466 467static int axp288_charger_property_is_writeable(struct power_supply *psy,468 enum power_supply_property psp)469{470 int ret;471 472 switch (psp) {473 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:474 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:475 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:476 ret = 1;477 break;478 default:479 ret = 0;480 }481 482 return ret;483}484 485static enum power_supply_property axp288_usb_props[] = {486 POWER_SUPPLY_PROP_PRESENT,487 POWER_SUPPLY_PROP_ONLINE,488 POWER_SUPPLY_PROP_TYPE,489 POWER_SUPPLY_PROP_HEALTH,490 POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,491 POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,492 POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,493 POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,494 POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,495};496 497static const struct power_supply_desc axp288_charger_desc = {498 .name = "axp288_charger",499 .type = POWER_SUPPLY_TYPE_USB,500 .properties = axp288_usb_props,501 .num_properties = ARRAY_SIZE(axp288_usb_props),502 .get_property = axp288_charger_usb_get_property,503 .set_property = axp288_charger_usb_set_property,504 .property_is_writeable = axp288_charger_property_is_writeable,505};506 507static irqreturn_t axp288_charger_irq_thread_handler(int irq, void *dev)508{509 struct axp288_chrg_info *info = dev;510 int i;511 512 for (i = 0; i < CHRG_INTR_END; i++) {513 if (info->irq[i] == irq)514 break;515 }516 517 if (i >= CHRG_INTR_END) {518 dev_warn(&info->pdev->dev, "spurious interrupt!!\n");519 return IRQ_NONE;520 }521 522 switch (i) {523 case VBUS_OV_IRQ:524 dev_dbg(&info->pdev->dev, "VBUS Over Voltage INTR\n");525 break;526 case CHARGE_DONE_IRQ:527 dev_dbg(&info->pdev->dev, "Charging Done INTR\n");528 break;529 case CHARGE_CHARGING_IRQ:530 dev_dbg(&info->pdev->dev, "Start Charging IRQ\n");531 break;532 case BAT_SAFE_QUIT_IRQ:533 dev_dbg(&info->pdev->dev,534 "Quit Safe Mode(restart timer) Charging IRQ\n");535 break;536 case BAT_SAFE_ENTER_IRQ:537 dev_dbg(&info->pdev->dev,538 "Enter Safe Mode(timer expire) Charging IRQ\n");539 break;540 case QCBTU_IRQ:541 dev_dbg(&info->pdev->dev,542 "Quit Battery Under Temperature(CHRG) INTR\n");543 break;544 case CBTU_IRQ:545 dev_dbg(&info->pdev->dev,546 "Hit Battery Under Temperature(CHRG) INTR\n");547 break;548 case QCBTO_IRQ:549 dev_dbg(&info->pdev->dev,550 "Quit Battery Over Temperature(CHRG) INTR\n");551 break;552 case CBTO_IRQ:553 dev_dbg(&info->pdev->dev,554 "Hit Battery Over Temperature(CHRG) INTR\n");555 break;556 default:557 dev_warn(&info->pdev->dev, "Spurious Interrupt!!!\n");558 goto out;559 }560 mutex_lock(&info->lock);561 info->valid = false;562 mutex_unlock(&info->lock);563 power_supply_changed(info->psy_usb);564out:565 return IRQ_HANDLED;566}567 568/*569 * The HP Pavilion x2 10 series comes in a number of variants:570 * Bay Trail SoC + AXP288 PMIC, Micro-USB, DMI_BOARD_NAME: "8021"571 * Bay Trail SoC + AXP288 PMIC, Type-C, DMI_BOARD_NAME: "815D"572 * Cherry Trail SoC + AXP288 PMIC, Type-C, DMI_BOARD_NAME: "813E"573 * Cherry Trail SoC + TI PMIC, Type-C, DMI_BOARD_NAME: "827C" or "82F4"574 *575 * The variants with the AXP288 + Type-C connector are all kinds of special:576 *577 * 1. They use a Type-C connector which the AXP288 does not support, so when578 * using a Type-C charger it is not recognized. Unlike most AXP288 devices,579 * this model actually has mostly working ACPI AC / Battery code, the ACPI code580 * "solves" this by simply setting the input_current_limit to 3A.581 * There are still some issues with the ACPI code, so we use this native driver,582 * and to solve the charging not working (500mA is not enough) issue we hardcode583 * the 3A input_current_limit like the ACPI code does.584 *585 * 2. If no charger is connected the machine boots with the vbus-path disabled.586 * Normally this is done when a 5V boost converter is active to avoid the PMIC587 * trying to charge from the 5V boost converter's output. This is done when588 * an OTG host cable is inserted and the ID pin on the micro-B receptacle is589 * pulled low and the ID pin has an ACPI event handler associated with it590 * which re-enables the vbus-path when the ID pin is pulled high when the591 * OTG host cable is removed. The Type-C connector has no ID pin, there is592 * no ID pin handler and there appears to be no 5V boost converter, so we593 * end up not charging because the vbus-path is disabled, until we unplug594 * the charger which automatically clears the vbus-path disable bit and then595 * on the second plug-in of the adapter we start charging. To solve the not596 * charging on first charger plugin we unconditionally enable the vbus-path at597 * probe on this model, which is safe since there is no 5V boost converter.598 */599static const struct dmi_system_id axp288_hp_x2_dmi_ids[] = {600 {601 .matches = {602 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),603 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"),604 DMI_EXACT_MATCH(DMI_BOARD_NAME, "815D"),605 },606 },607 {608 .matches = {609 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "HP"),610 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"),611 DMI_EXACT_MATCH(DMI_BOARD_NAME, "813E"),612 },613 },614 {} /* Terminating entry */615};616 617static void axp288_charger_extcon_evt_worker(struct work_struct *work)618{619 struct axp288_chrg_info *info =620 container_of(work, struct axp288_chrg_info, cable.work);621 int ret, current_limit;622 struct extcon_dev *edev = info->cable.edev;623 unsigned int val;624 625 ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);626 if (ret < 0) {627 dev_err(&info->pdev->dev, "Error reading status (%d)\n", ret);628 return;629 }630 631 /* Offline? Disable charging and bail */632 if (!(val & PS_STAT_VBUS_VALID)) {633 dev_dbg(&info->pdev->dev, "USB charger disconnected\n");634 axp288_charger_enable_charger(info, false);635 mutex_lock(&info->lock);636 info->valid = false;637 mutex_unlock(&info->lock);638 power_supply_changed(info->psy_usb);639 return;640 }641 642 /* Determine cable/charger type */643 if (dmi_check_system(axp288_hp_x2_dmi_ids)) {644 /* See comment above axp288_hp_x2_dmi_ids declaration */645 dev_dbg(&info->pdev->dev, "HP X2 with Type-C, setting inlmt to 3A\n");646 current_limit = 3000000;647 } else if (extcon_get_state(edev, EXTCON_CHG_USB_SDP) > 0) {648 dev_dbg(&info->pdev->dev, "USB SDP charger is connected\n");649 current_limit = 500000;650 } else if (extcon_get_state(edev, EXTCON_CHG_USB_CDP) > 0) {651 dev_dbg(&info->pdev->dev, "USB CDP charger is connected\n");652 current_limit = 1500000;653 } else if (extcon_get_state(edev, EXTCON_CHG_USB_DCP) > 0) {654 dev_dbg(&info->pdev->dev, "USB DCP charger is connected\n");655 current_limit = 2000000;656 } else {657 /* Charger type detection still in progress, bail. */658 return;659 }660 661 /* Set vbus current limit first, then enable charger */662 ret = axp288_charger_set_vbus_inlmt(info, current_limit);663 if (ret == 0)664 axp288_charger_enable_charger(info, true);665 else666 dev_err(&info->pdev->dev,667 "error setting current limit (%d)\n", ret);668 669 mutex_lock(&info->lock);670 info->valid = false;671 mutex_unlock(&info->lock);672 power_supply_changed(info->psy_usb);673}674 675static int axp288_charger_handle_cable_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, cable.nb);680 schedule_work(&info->cable.work);681 return NOTIFY_OK;682}683 684static void axp288_charger_otg_evt_worker(struct work_struct *work)685{686 struct axp288_chrg_info *info =687 container_of(work, struct axp288_chrg_info, otg.work);688 struct extcon_dev *edev = info->otg.cable;689 int ret, usb_host = extcon_get_state(edev, EXTCON_USB_HOST);690 691 dev_dbg(&info->pdev->dev, "external connector USB-Host is %s\n",692 usb_host ? "attached" : "detached");693 694 /*695 * Set usb_id_short flag to avoid running charger detection logic696 * in case usb host.697 */698 info->otg.id_short = usb_host;699 700 /* Disable VBUS path before enabling the 5V boost */701 ret = axp288_charger_vbus_path_select(info, !info->otg.id_short);702 if (ret < 0)703 dev_warn(&info->pdev->dev, "vbus path disable failed\n");704}705 706static int axp288_charger_handle_otg_evt(struct notifier_block *nb,707 unsigned long event, void *param)708{709 struct axp288_chrg_info *info =710 container_of(nb, struct axp288_chrg_info, otg.id_nb);711 712 schedule_work(&info->otg.work);713 714 return NOTIFY_OK;715}716 717static int charger_init_hw_regs(struct axp288_chrg_info *info)718{719 int ret, cc, cv;720 unsigned int val;721 722 /* Program temperature thresholds */723 ret = regmap_write(info->regmap, AXP20X_V_LTF_CHRG, CHRG_VLTFC_0C);724 if (ret < 0) {725 dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",726 AXP20X_V_LTF_CHRG, ret);727 return ret;728 }729 730 ret = regmap_write(info->regmap, AXP20X_V_HTF_CHRG, CHRG_VHTFC_45C);731 if (ret < 0) {732 dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",733 AXP20X_V_HTF_CHRG, ret);734 return ret;735 }736 737 /* Do not turn-off charger o/p after charge cycle ends */738 ret = regmap_update_bits(info->regmap,739 AXP20X_CHRG_CTRL2,740 CNTL2_CHG_OUT_TURNON, CNTL2_CHG_OUT_TURNON);741 if (ret < 0) {742 dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",743 AXP20X_CHRG_CTRL2, ret);744 return ret;745 }746 747 /* Setup ending condition for charging to be 10% of I(chrg) */748 ret = regmap_update_bits(info->regmap,749 AXP20X_CHRG_CTRL1,750 CHRG_CCCV_ITERM_20P, 0);751 if (ret < 0) {752 dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",753 AXP20X_CHRG_CTRL1, ret);754 return ret;755 }756 757 /* Disable OCV-SOC curve calibration */758 ret = regmap_update_bits(info->regmap,759 AXP20X_CC_CTRL,760 FG_CNTL_OCV_ADJ_EN, 0);761 if (ret < 0) {762 dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",763 AXP20X_CC_CTRL, ret);764 return ret;765 }766 767 if (dmi_check_system(axp288_hp_x2_dmi_ids)) {768 /* See comment above axp288_hp_x2_dmi_ids declaration */769 ret = axp288_charger_vbus_path_select(info, true);770 if (ret < 0)771 return ret;772 } else {773 /* Set Vhold to the factory default / recommended 4.4V */774 val = VBUS_ISPOUT_VHOLD_SET_4400MV << VBUS_ISPOUT_VHOLD_SET_BIT_POS;775 ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,776 VBUS_ISPOUT_VHOLD_SET_MASK, val);777 if (ret < 0) {778 dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",779 AXP20X_VBUS_IPSOUT_MGMT, ret);780 return ret;781 }782 }783 784 /* Read current charge voltage and current limit */785 ret = regmap_read(info->regmap, AXP20X_CHRG_CTRL1, &val);786 if (ret < 0) {787 dev_err(&info->pdev->dev, "register(%x) read error(%d)\n",788 AXP20X_CHRG_CTRL1, ret);789 return ret;790 }791 792 /* Determine charge voltage */793 cv = (val & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS;794 switch (cv) {795 case CHRG_CCCV_CV_4100MV:796 info->cv = CV_4100MV;797 break;798 case CHRG_CCCV_CV_4150MV:799 info->cv = CV_4150MV;800 break;801 case CHRG_CCCV_CV_4200MV:802 info->cv = CV_4200MV;803 break;804 case CHRG_CCCV_CV_4350MV:805 info->cv = CV_4350MV;806 break;807 }808 809 /* Determine charge current limit */810 cc = (val & CHRG_CCCV_CC_MASK) >> CHRG_CCCV_CC_BIT_POS;811 cc = (cc * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;812 info->cc = cc;813 814 /*815 * Do not allow the user to configure higher settings then those816 * set by the firmware817 */818 info->max_cv = info->cv;819 info->max_cc = info->cc;820 821 return 0;822}823 824static void axp288_charger_cancel_work(void *data)825{826 struct axp288_chrg_info *info = data;827 828 cancel_work_sync(&info->otg.work);829 cancel_work_sync(&info->cable.work);830}831 832static int axp288_charger_probe(struct platform_device *pdev)833{834 int ret, i, pirq;835 struct axp288_chrg_info *info;836 struct device *dev = &pdev->dev;837 struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);838 struct power_supply_config charger_cfg = {};839 const char *extcon_name = NULL;840 unsigned int val;841 842 /*843 * Normally the native AXP288 fg/charger drivers are preferred but844 * on some devices the ACPI drivers should be used instead.845 */846 if (!acpi_quirk_skip_acpi_ac_and_battery())847 return -ENODEV;848 849 /*850 * On some devices the fuelgauge and charger parts of the axp288 are851 * not used, check that the fuelgauge is enabled (CC_CTRL != 0).852 */853 ret = regmap_read(axp20x->regmap, AXP20X_CC_CTRL, &val);854 if (ret < 0)855 return ret;856 if (val == 0)857 return -ENODEV;858 859 info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);860 if (!info)861 return -ENOMEM;862 863 mutex_init(&info->lock);864 info->pdev = pdev;865 info->regmap = axp20x->regmap;866 info->regmap_irqc = axp20x->regmap_irqc;867 868 info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME);869 if (IS_ERR(info->cable.edev)) {870 dev_err_probe(dev, PTR_ERR(info->cable.edev),871 "extcon_get_extcon_dev(%s) failed\n",872 AXP288_EXTCON_DEV_NAME);873 return PTR_ERR(info->cable.edev);874 }875 876 /*877 * On devices with broken ACPI GPIO event handlers there also is no ACPI878 * "INT3496" (USB_HOST_EXTCON_HID) device. x86-android-tablets.ko879 * instantiates an "intel-int3496" extcon on these devs as a workaround.880 */881 if (acpi_quirk_skip_gpio_event_handlers())882 extcon_name = "intel-int3496";883 else if (acpi_dev_present(USB_HOST_EXTCON_HID, NULL, -1))884 extcon_name = USB_HOST_EXTCON_NAME;885 886 if (extcon_name) {887 info->otg.cable = extcon_get_extcon_dev(extcon_name);888 if (IS_ERR(info->otg.cable)) {889 dev_err_probe(dev, PTR_ERR(info->otg.cable),890 "extcon_get_extcon_dev(%s) failed\n",891 USB_HOST_EXTCON_NAME);892 return PTR_ERR(info->otg.cable);893 }894 dev_info(dev, "Using " USB_HOST_EXTCON_HID " extcon for usb-id\n");895 }896 897 platform_set_drvdata(pdev, info);898 899 ret = charger_init_hw_regs(info);900 if (ret)901 return ret;902 903 /* Register with power supply class */904 charger_cfg.drv_data = info;905 info->psy_usb = devm_power_supply_register(dev, &axp288_charger_desc,906 &charger_cfg);907 if (IS_ERR(info->psy_usb)) {908 ret = PTR_ERR(info->psy_usb);909 dev_err(dev, "failed to register power supply: %d\n", ret);910 return ret;911 }912 913 /* Cancel our work on cleanup, register this before the notifiers */914 ret = devm_add_action(dev, axp288_charger_cancel_work, info);915 if (ret)916 return ret;917 918 /* Register for extcon notification */919 INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker);920 info->cable.nb.notifier_call = axp288_charger_handle_cable_evt;921 ret = devm_extcon_register_notifier_all(dev, info->cable.edev,922 &info->cable.nb);923 if (ret) {924 dev_err(dev, "failed to register cable extcon notifier\n");925 return ret;926 }927 schedule_work(&info->cable.work);928 929 /* Register for OTG notification */930 INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker);931 info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt;932 if (info->otg.cable) {933 ret = devm_extcon_register_notifier(dev, info->otg.cable,934 EXTCON_USB_HOST, &info->otg.id_nb);935 if (ret) {936 dev_err(dev, "failed to register EXTCON_USB_HOST notifier\n");937 return ret;938 }939 schedule_work(&info->otg.work);940 }941 942 /* Register charger interrupts */943 for (i = 0; i < CHRG_INTR_END; i++) {944 pirq = platform_get_irq(info->pdev, i);945 if (pirq < 0)946 return pirq;947 948 info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq);949 if (info->irq[i] < 0) {950 dev_warn(&info->pdev->dev,951 "failed to get virtual interrupt=%d\n", pirq);952 return info->irq[i];953 }954 ret = devm_request_threaded_irq(&info->pdev->dev, info->irq[i],955 NULL, axp288_charger_irq_thread_handler,956 IRQF_ONESHOT, info->pdev->name, info);957 if (ret) {958 dev_err(dev, "failed to request interrupt=%d\n",959 info->irq[i]);960 return ret;961 }962 }963 964 return 0;965}966 967static const struct platform_device_id axp288_charger_id_table[] = {968 { .name = "axp288_charger" },969 {},970};971MODULE_DEVICE_TABLE(platform, axp288_charger_id_table);972 973static struct platform_driver axp288_charger_driver = {974 .probe = axp288_charger_probe,975 .id_table = axp288_charger_id_table,976 .driver = {977 .name = "axp288_charger",978 },979};980 981module_platform_driver(axp288_charger_driver);982 983MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");984MODULE_DESCRIPTION("X-power AXP288 Charger Driver");985MODULE_LICENSE("GPL v2");986