brintos

brintos / linux-shallow public Read only

0
0
Text · 27.3 KiB · ac05942 Raw
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