brintos

brintos / linux-shallow public Read only

0
0
Text · 25.1 KiB · 4e57762 Raw
1031 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/* Copyright (c) 2014, Sony Mobile Communications Inc.3 *4 * This driver is for the multi-block Switch-Mode Battery Charger and Boost5 * (SMBB) hardware, found in Qualcomm PM8941 PMICs.  The charger is an6 * integrated, single-cell lithium-ion battery charger.7 *8 * Sub-components:9 *  - Charger core10 *  - Buck11 *  - DC charge-path12 *  - USB charge-path13 *  - Battery interface14 *  - Boost (not implemented)15 *  - Misc16 *  - HF-Buck17 */18 19#include <linux/errno.h>20#include <linux/interrupt.h>21#include <linux/kernel.h>22#include <linux/module.h>23#include <linux/mutex.h>24#include <linux/of.h>25#include <linux/platform_device.h>26#include <linux/power_supply.h>27#include <linux/regmap.h>28#include <linux/slab.h>29#include <linux/extcon-provider.h>30#include <linux/regulator/driver.h>31 32#define SMBB_CHG_VMAX		0x04033#define SMBB_CHG_VSAFE		0x04134#define SMBB_CHG_CFG		0x04335#define SMBB_CHG_IMAX		0x04436#define SMBB_CHG_ISAFE		0x04537#define SMBB_CHG_VIN_MIN	0x04738#define SMBB_CHG_CTRL		0x04939#define CTRL_EN			BIT(7)40#define SMBB_CHG_VBAT_WEAK	0x05241#define SMBB_CHG_IBAT_TERM_CHG	0x05b42#define IBAT_TERM_CHG_IEOC	BIT(7)43#define IBAT_TERM_CHG_IEOC_BMS	BIT(7)44#define IBAT_TERM_CHG_IEOC_CHG	045#define SMBB_CHG_VBAT_DET	0x05d46#define SMBB_CHG_TCHG_MAX_EN	0x06047#define TCHG_MAX_EN		BIT(7)48#define SMBB_CHG_WDOG_TIME	0x06249#define SMBB_CHG_WDOG_EN	0x06550#define WDOG_EN			BIT(7)51 52#define SMBB_BUCK_REG_MODE	0x17453#define BUCK_REG_MODE		BIT(0)54#define BUCK_REG_MODE_VBAT	BIT(0)55#define BUCK_REG_MODE_VSYS	056 57#define SMBB_BAT_PRES_STATUS	0x20858#define PRES_STATUS_BAT_PRES	BIT(7)59#define SMBB_BAT_TEMP_STATUS	0x20960#define TEMP_STATUS_OK		BIT(7)61#define TEMP_STATUS_HOT		BIT(6)62#define SMBB_BAT_BTC_CTRL	0x24963#define BTC_CTRL_COMP_EN	BIT(7)64#define BTC_CTRL_COLD_EXT	BIT(1)65#define BTC_CTRL_HOT_EXT_N	BIT(0)66 67#define SMBB_USB_IMAX		0x34468#define SMBB_USB_OTG_CTL	0x34869#define OTG_CTL_EN		BIT(0)70#define SMBB_USB_ENUM_TIMER_STOP 0x34e71#define ENUM_TIMER_STOP		BIT(0)72#define SMBB_USB_SEC_ACCESS	0x3d073#define SEC_ACCESS_MAGIC	0xa574#define SMBB_USB_REV_BST	0x3ed75#define REV_BST_CHG_GONE	BIT(7)76 77#define SMBB_DC_IMAX		0x44478 79#define SMBB_MISC_REV2		0x60180#define SMBB_MISC_BOOT_DONE	0x64281#define BOOT_DONE		BIT(7)82 83#define STATUS_USBIN_VALID	BIT(0) /* USB connection is valid */84#define STATUS_DCIN_VALID	BIT(1) /* DC connection is valid */85#define STATUS_BAT_HOT		BIT(2) /* Battery temp 1=Hot, 0=Cold */86#define STATUS_BAT_OK		BIT(3) /* Battery temp OK */87#define STATUS_BAT_PRESENT	BIT(4) /* Battery is present */88#define STATUS_CHG_DONE		BIT(5) /* Charge cycle is complete */89#define STATUS_CHG_TRKL		BIT(6) /* Trickle charging */90#define STATUS_CHG_FAST		BIT(7) /* Fast charging */91#define STATUS_CHG_GONE		BIT(8) /* No charger is connected */92 93enum smbb_attr {94	ATTR_BAT_ISAFE,95	ATTR_BAT_IMAX,96	ATTR_USBIN_IMAX,97	ATTR_DCIN_IMAX,98	ATTR_BAT_VSAFE,99	ATTR_BAT_VMAX,100	ATTR_BAT_VMIN,101	ATTR_CHG_VDET,102	ATTR_VIN_MIN,103	_ATTR_CNT,104};105 106struct smbb_charger {107	unsigned int revision;108	unsigned int addr;109	struct device *dev;110	struct extcon_dev *edev;111 112	bool dc_disabled;113	bool jeita_ext_temp;114	unsigned long status;115	struct mutex statlock;116 117	unsigned int attr[_ATTR_CNT];118 119	struct power_supply *usb_psy;120	struct power_supply *dc_psy;121	struct power_supply *bat_psy;122	struct regmap *regmap;123 124	struct regulator_desc otg_rdesc;125	struct regulator_dev *otg_reg;126};127 128static const unsigned int smbb_usb_extcon_cable[] = {129	EXTCON_USB,130	EXTCON_NONE,131};132 133static int smbb_vbat_weak_fn(unsigned int index)134{135	return 2100000 + index * 100000;136}137 138static int smbb_vin_fn(unsigned int index)139{140	if (index > 42)141		return 5600000 + (index - 43) * 200000;142	return 3400000 + index * 50000;143}144 145static int smbb_vmax_fn(unsigned int index)146{147	return 3240000 + index * 10000;148}149 150static int smbb_vbat_det_fn(unsigned int index)151{152	return 3240000 + index * 20000;153}154 155static int smbb_imax_fn(unsigned int index)156{157	if (index < 2)158		return 100000 + index * 50000;159	return index * 100000;160}161 162static int smbb_bat_imax_fn(unsigned int index)163{164	return index * 50000;165}166 167static unsigned int smbb_hw_lookup(unsigned int val, int (*fn)(unsigned int))168{169	unsigned int widx;170	unsigned int sel;171 172	for (widx = sel = 0; (*fn)(widx) <= val; ++widx)173		sel = widx;174 175	return sel;176}177 178static const struct smbb_charger_attr {179	const char *name;180	unsigned int reg;181	unsigned int safe_reg;182	unsigned int max;183	unsigned int min;184	unsigned int fail_ok;185	int (*hw_fn)(unsigned int);186} smbb_charger_attrs[] = {187	[ATTR_BAT_ISAFE] = {188		.name = "qcom,fast-charge-safe-current",189		.reg = SMBB_CHG_ISAFE,190		.max = 3000000,191		.min = 200000,192		.hw_fn = smbb_bat_imax_fn,193		.fail_ok = 1,194	},195	[ATTR_BAT_IMAX] = {196		.name = "qcom,fast-charge-current-limit",197		.reg = SMBB_CHG_IMAX,198		.safe_reg = SMBB_CHG_ISAFE,199		.max = 3000000,200		.min = 200000,201		.hw_fn = smbb_bat_imax_fn,202	},203	[ATTR_DCIN_IMAX] = {204		.name = "qcom,dc-current-limit",205		.reg = SMBB_DC_IMAX,206		.max = 2500000,207		.min = 100000,208		.hw_fn = smbb_imax_fn,209	},210	[ATTR_BAT_VSAFE] = {211		.name = "qcom,fast-charge-safe-voltage",212		.reg = SMBB_CHG_VSAFE,213		.max = 5000000,214		.min = 3240000,215		.hw_fn = smbb_vmax_fn,216		.fail_ok = 1,217	},218	[ATTR_BAT_VMAX] = {219		.name = "qcom,fast-charge-high-threshold-voltage",220		.reg = SMBB_CHG_VMAX,221		.safe_reg = SMBB_CHG_VSAFE,222		.max = 5000000,223		.min = 3240000,224		.hw_fn = smbb_vmax_fn,225	},226	[ATTR_BAT_VMIN] = {227		.name = "qcom,fast-charge-low-threshold-voltage",228		.reg = SMBB_CHG_VBAT_WEAK,229		.max = 3600000,230		.min = 2100000,231		.hw_fn = smbb_vbat_weak_fn,232	},233	[ATTR_CHG_VDET] = {234		.name = "qcom,auto-recharge-threshold-voltage",235		.reg = SMBB_CHG_VBAT_DET,236		.max = 5000000,237		.min = 3240000,238		.hw_fn = smbb_vbat_det_fn,239	},240	[ATTR_VIN_MIN] = {241		.name = "qcom,minimum-input-voltage",242		.reg = SMBB_CHG_VIN_MIN,243		.max = 9600000,244		.min = 4200000,245		.hw_fn = smbb_vin_fn,246	},247	[ATTR_USBIN_IMAX] = {248		.name = "usb-charge-current-limit",249		.reg = SMBB_USB_IMAX,250		.max = 2500000,251		.min = 100000,252		.hw_fn = smbb_imax_fn,253	},254};255 256static int smbb_charger_attr_write(struct smbb_charger *chg,257		enum smbb_attr which, unsigned int val)258{259	const struct smbb_charger_attr *prop;260	unsigned int wval;261	unsigned int out;262	int rc;263 264	prop = &smbb_charger_attrs[which];265 266	if (val > prop->max || val < prop->min) {267		dev_err(chg->dev, "value out of range for %s [%u:%u]\n",268			prop->name, prop->min, prop->max);269		return -EINVAL;270	}271 272	if (prop->safe_reg) {273		rc = regmap_read(chg->regmap,274				chg->addr + prop->safe_reg, &wval);275		if (rc) {276			dev_err(chg->dev,277				"unable to read safe value for '%s'\n",278				prop->name);279			return rc;280		}281 282		wval = prop->hw_fn(wval);283 284		if (val > wval) {285			dev_warn(chg->dev,286				"%s above safe value, clamping at %u\n",287				prop->name, wval);288			val = wval;289		}290	}291 292	wval = smbb_hw_lookup(val, prop->hw_fn);293 294	rc = regmap_write(chg->regmap, chg->addr + prop->reg, wval);295	if (rc) {296		dev_err(chg->dev, "unable to update %s", prop->name);297		return rc;298	}299	out = prop->hw_fn(wval);300	if (out != val) {301		dev_warn(chg->dev,302			"%s inaccurate, rounded to %u\n",303			prop->name, out);304	}305 306	dev_dbg(chg->dev, "%s <= %d\n", prop->name, out);307 308	chg->attr[which] = out;309 310	return 0;311}312 313static int smbb_charger_attr_read(struct smbb_charger *chg,314		enum smbb_attr which)315{316	const struct smbb_charger_attr *prop;317	unsigned int val;318	int rc;319 320	prop = &smbb_charger_attrs[which];321 322	rc = regmap_read(chg->regmap, chg->addr + prop->reg, &val);323	if (rc) {324		dev_err(chg->dev, "failed to read %s\n", prop->name);325		return rc;326	}327	val = prop->hw_fn(val);328	dev_dbg(chg->dev, "%s => %d\n", prop->name, val);329 330	chg->attr[which] = val;331 332	return 0;333}334 335static int smbb_charger_attr_parse(struct smbb_charger *chg,336		enum smbb_attr which)337{338	const struct smbb_charger_attr *prop;339	unsigned int val;340	int rc;341 342	prop = &smbb_charger_attrs[which];343 344	rc = of_property_read_u32(chg->dev->of_node, prop->name, &val);345	if (rc == 0) {346		rc = smbb_charger_attr_write(chg, which, val);347		if (!rc || !prop->fail_ok)348			return rc;349	}350	return smbb_charger_attr_read(chg, which);351}352 353static void smbb_set_line_flag(struct smbb_charger *chg, int irq, int flag)354{355	bool state;356	int ret;357 358	ret = irq_get_irqchip_state(irq, IRQCHIP_STATE_LINE_LEVEL, &state);359	if (ret < 0) {360		dev_err(chg->dev, "failed to read irq line\n");361		return;362	}363 364	mutex_lock(&chg->statlock);365	if (state)366		chg->status |= flag;367	else368		chg->status &= ~flag;369	mutex_unlock(&chg->statlock);370 371	dev_dbg(chg->dev, "status = %03lx\n", chg->status);372}373 374static irqreturn_t smbb_usb_valid_handler(int irq, void *_data)375{376	struct smbb_charger *chg = _data;377 378	smbb_set_line_flag(chg, irq, STATUS_USBIN_VALID);379	extcon_set_state_sync(chg->edev, EXTCON_USB,380				chg->status & STATUS_USBIN_VALID);381	power_supply_changed(chg->usb_psy);382 383	return IRQ_HANDLED;384}385 386static irqreturn_t smbb_dc_valid_handler(int irq, void *_data)387{388	struct smbb_charger *chg = _data;389 390	smbb_set_line_flag(chg, irq, STATUS_DCIN_VALID);391	if (!chg->dc_disabled)392		power_supply_changed(chg->dc_psy);393 394	return IRQ_HANDLED;395}396 397static irqreturn_t smbb_bat_temp_handler(int irq, void *_data)398{399	struct smbb_charger *chg = _data;400	unsigned int val;401	int rc;402 403	rc = regmap_read(chg->regmap, chg->addr + SMBB_BAT_TEMP_STATUS, &val);404	if (rc)405		return IRQ_HANDLED;406 407	mutex_lock(&chg->statlock);408	if (val & TEMP_STATUS_OK) {409		chg->status |= STATUS_BAT_OK;410	} else {411		chg->status &= ~STATUS_BAT_OK;412		if (val & TEMP_STATUS_HOT)413			chg->status |= STATUS_BAT_HOT;414	}415	mutex_unlock(&chg->statlock);416 417	power_supply_changed(chg->bat_psy);418	return IRQ_HANDLED;419}420 421static irqreturn_t smbb_bat_present_handler(int irq, void *_data)422{423	struct smbb_charger *chg = _data;424 425	smbb_set_line_flag(chg, irq, STATUS_BAT_PRESENT);426	power_supply_changed(chg->bat_psy);427 428	return IRQ_HANDLED;429}430 431static irqreturn_t smbb_chg_done_handler(int irq, void *_data)432{433	struct smbb_charger *chg = _data;434 435	smbb_set_line_flag(chg, irq, STATUS_CHG_DONE);436	power_supply_changed(chg->bat_psy);437 438	return IRQ_HANDLED;439}440 441static irqreturn_t smbb_chg_gone_handler(int irq, void *_data)442{443	struct smbb_charger *chg = _data;444 445	smbb_set_line_flag(chg, irq, STATUS_CHG_GONE);446	power_supply_changed(chg->bat_psy);447	power_supply_changed(chg->usb_psy);448	if (!chg->dc_disabled)449		power_supply_changed(chg->dc_psy);450 451	return IRQ_HANDLED;452}453 454static irqreturn_t smbb_chg_fast_handler(int irq, void *_data)455{456	struct smbb_charger *chg = _data;457 458	smbb_set_line_flag(chg, irq, STATUS_CHG_FAST);459	power_supply_changed(chg->bat_psy);460 461	return IRQ_HANDLED;462}463 464static irqreturn_t smbb_chg_trkl_handler(int irq, void *_data)465{466	struct smbb_charger *chg = _data;467 468	smbb_set_line_flag(chg, irq, STATUS_CHG_TRKL);469	power_supply_changed(chg->bat_psy);470 471	return IRQ_HANDLED;472}473 474static const struct smbb_irq {475	const char *name;476	irqreturn_t (*handler)(int, void *);477} smbb_charger_irqs[] = {478	{ "chg-done", smbb_chg_done_handler },479	{ "chg-fast", smbb_chg_fast_handler },480	{ "chg-trkl", smbb_chg_trkl_handler },481	{ "bat-temp-ok", smbb_bat_temp_handler },482	{ "bat-present", smbb_bat_present_handler },483	{ "chg-gone", smbb_chg_gone_handler },484	{ "usb-valid", smbb_usb_valid_handler },485	{ "dc-valid", smbb_dc_valid_handler },486};487 488static int smbb_usbin_get_property(struct power_supply *psy,489		enum power_supply_property psp,490		union power_supply_propval *val)491{492	struct smbb_charger *chg = power_supply_get_drvdata(psy);493	int rc = 0;494 495	switch (psp) {496	case POWER_SUPPLY_PROP_ONLINE:497		mutex_lock(&chg->statlock);498		val->intval = !(chg->status & STATUS_CHG_GONE) &&499				(chg->status & STATUS_USBIN_VALID);500		mutex_unlock(&chg->statlock);501		break;502	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:503		val->intval = chg->attr[ATTR_USBIN_IMAX];504		break;505	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:506		val->intval = 2500000;507		break;508	default:509		rc = -EINVAL;510		break;511	}512 513	return rc;514}515 516static int smbb_usbin_set_property(struct power_supply *psy,517		enum power_supply_property psp,518		const union power_supply_propval *val)519{520	struct smbb_charger *chg = power_supply_get_drvdata(psy);521	int rc;522 523	switch (psp) {524	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:525		rc = smbb_charger_attr_write(chg, ATTR_USBIN_IMAX,526				val->intval);527		break;528	default:529		rc = -EINVAL;530		break;531	}532 533	return rc;534}535 536static int smbb_dcin_get_property(struct power_supply *psy,537		enum power_supply_property psp,538		union power_supply_propval *val)539{540	struct smbb_charger *chg = power_supply_get_drvdata(psy);541	int rc = 0;542 543	switch (psp) {544	case POWER_SUPPLY_PROP_ONLINE:545		mutex_lock(&chg->statlock);546		val->intval = !(chg->status & STATUS_CHG_GONE) &&547				(chg->status & STATUS_DCIN_VALID);548		mutex_unlock(&chg->statlock);549		break;550	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:551		val->intval = chg->attr[ATTR_DCIN_IMAX];552		break;553	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:554		val->intval = 2500000;555		break;556	default:557		rc = -EINVAL;558		break;559	}560 561	return rc;562}563 564static int smbb_dcin_set_property(struct power_supply *psy,565		enum power_supply_property psp,566		const union power_supply_propval *val)567{568	struct smbb_charger *chg = power_supply_get_drvdata(psy);569	int rc;570 571	switch (psp) {572	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:573		rc = smbb_charger_attr_write(chg, ATTR_DCIN_IMAX,574				val->intval);575		break;576	default:577		rc = -EINVAL;578		break;579	}580 581	return rc;582}583 584static int smbb_charger_writable_property(struct power_supply *psy,585		enum power_supply_property psp)586{587	return psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT;588}589 590static int smbb_battery_get_property(struct power_supply *psy,591		enum power_supply_property psp,592		union power_supply_propval *val)593{594	struct smbb_charger *chg = power_supply_get_drvdata(psy);595	unsigned long status;596	int rc = 0;597 598	mutex_lock(&chg->statlock);599	status = chg->status;600	mutex_unlock(&chg->statlock);601 602	switch (psp) {603	case POWER_SUPPLY_PROP_STATUS:604		if (status & STATUS_CHG_GONE)605			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;606		else if (!(status & (STATUS_DCIN_VALID | STATUS_USBIN_VALID)))607			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;608		else if (status & STATUS_CHG_DONE)609			val->intval = POWER_SUPPLY_STATUS_FULL;610		else if (!(status & STATUS_BAT_OK))611			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;612		else if (status & (STATUS_CHG_FAST | STATUS_CHG_TRKL))613			val->intval = POWER_SUPPLY_STATUS_CHARGING;614		else /* everything is ok for charging, but we are not... */615			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;616		break;617	case POWER_SUPPLY_PROP_HEALTH:618		if (status & STATUS_BAT_OK)619			val->intval = POWER_SUPPLY_HEALTH_GOOD;620		else if (status & STATUS_BAT_HOT)621			val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;622		else623			val->intval = POWER_SUPPLY_HEALTH_COLD;624		break;625	case POWER_SUPPLY_PROP_CHARGE_TYPE:626		if (status & STATUS_CHG_FAST)627			val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;628		else if (status & STATUS_CHG_TRKL)629			val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;630		else631			val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;632		break;633	case POWER_SUPPLY_PROP_PRESENT:634		val->intval = !!(status & STATUS_BAT_PRESENT);635		break;636	case POWER_SUPPLY_PROP_CURRENT_MAX:637		val->intval = chg->attr[ATTR_BAT_IMAX];638		break;639	case POWER_SUPPLY_PROP_VOLTAGE_MAX:640		val->intval = chg->attr[ATTR_BAT_VMAX];641		break;642	case POWER_SUPPLY_PROP_TECHNOLOGY:643		/* this charger is a single-cell lithium-ion battery charger644		* only.  If you hook up some other technology, there will be645		* fireworks.646		*/647		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;648		break;649	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:650		val->intval = 3000000; /* single-cell li-ion low end */651		break;652	default:653		rc = -EINVAL;654		break;655	}656 657	return rc;658}659 660static int smbb_battery_set_property(struct power_supply *psy,661		enum power_supply_property psp,662		const union power_supply_propval *val)663{664	struct smbb_charger *chg = power_supply_get_drvdata(psy);665	int rc;666 667	switch (psp) {668	case POWER_SUPPLY_PROP_CURRENT_MAX:669		rc = smbb_charger_attr_write(chg, ATTR_BAT_IMAX, val->intval);670		break;671	case POWER_SUPPLY_PROP_VOLTAGE_MAX:672		rc = smbb_charger_attr_write(chg, ATTR_BAT_VMAX, val->intval);673		break;674	default:675		rc = -EINVAL;676		break;677	}678 679	return rc;680}681 682static int smbb_battery_writable_property(struct power_supply *psy,683		enum power_supply_property psp)684{685	switch (psp) {686	case POWER_SUPPLY_PROP_CURRENT_MAX:687	case POWER_SUPPLY_PROP_VOLTAGE_MAX:688		return 1;689	default:690		return 0;691	}692}693 694static enum power_supply_property smbb_charger_properties[] = {695	POWER_SUPPLY_PROP_ONLINE,696	POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,697	POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,698};699 700static enum power_supply_property smbb_battery_properties[] = {701	POWER_SUPPLY_PROP_STATUS,702	POWER_SUPPLY_PROP_HEALTH,703	POWER_SUPPLY_PROP_PRESENT,704	POWER_SUPPLY_PROP_CHARGE_TYPE,705	POWER_SUPPLY_PROP_CURRENT_MAX,706	POWER_SUPPLY_PROP_VOLTAGE_MAX,707	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,708	POWER_SUPPLY_PROP_TECHNOLOGY,709};710 711static const struct reg_off_mask_default {712	unsigned int offset;713	unsigned int mask;714	unsigned int value;715	unsigned int rev_mask;716} smbb_charger_setup[] = {717	/* The bootloader is supposed to set this... make sure anyway. */718	{ SMBB_MISC_BOOT_DONE, BOOT_DONE, BOOT_DONE },719 720	/* Disable software timer */721	{ SMBB_CHG_TCHG_MAX_EN, TCHG_MAX_EN, 0 },722 723	/* Clear and disable watchdog */724	{ SMBB_CHG_WDOG_TIME, 0xff, 160 },725	{ SMBB_CHG_WDOG_EN, WDOG_EN, 0 },726 727	/* Use charger based EoC detection */728	{ SMBB_CHG_IBAT_TERM_CHG, IBAT_TERM_CHG_IEOC, IBAT_TERM_CHG_IEOC_CHG },729 730	/* Disable GSM PA load adjustment.731	* The PA signal is incorrectly connected on v2.732	*/733	{ SMBB_CHG_CFG, 0xff, 0x00, BIT(3) },734 735	/* Use VBAT (not VSYS) to compensate for IR drop during fast charging */736	{ SMBB_BUCK_REG_MODE, BUCK_REG_MODE, BUCK_REG_MODE_VBAT },737 738	/* Enable battery temperature comparators */739	{ SMBB_BAT_BTC_CTRL, BTC_CTRL_COMP_EN, BTC_CTRL_COMP_EN },740 741	/* Stop USB enumeration timer */742	{ SMBB_USB_ENUM_TIMER_STOP, ENUM_TIMER_STOP, ENUM_TIMER_STOP },743 744#if 0 /* FIXME supposedly only to disable hardware ARB termination */745	{ SMBB_USB_SEC_ACCESS, SEC_ACCESS_MAGIC },746	{ SMBB_USB_REV_BST, 0xff, REV_BST_CHG_GONE },747#endif748 749	/* Stop USB enumeration timer, again */750	{ SMBB_USB_ENUM_TIMER_STOP, ENUM_TIMER_STOP, ENUM_TIMER_STOP },751 752	/* Enable charging */753	{ SMBB_CHG_CTRL, CTRL_EN, CTRL_EN },754};755 756static char *smbb_bif[] = { "smbb-bif" };757 758static const struct power_supply_desc bat_psy_desc = {759	.name = "smbb-bif",760	.type = POWER_SUPPLY_TYPE_BATTERY,761	.properties = smbb_battery_properties,762	.num_properties = ARRAY_SIZE(smbb_battery_properties),763	.get_property = smbb_battery_get_property,764	.set_property = smbb_battery_set_property,765	.property_is_writeable = smbb_battery_writable_property,766};767 768static const struct power_supply_desc usb_psy_desc = {769	.name = "smbb-usbin",770	.type = POWER_SUPPLY_TYPE_USB,771	.properties = smbb_charger_properties,772	.num_properties = ARRAY_SIZE(smbb_charger_properties),773	.get_property = smbb_usbin_get_property,774	.set_property = smbb_usbin_set_property,775	.property_is_writeable = smbb_charger_writable_property,776};777 778static const struct power_supply_desc dc_psy_desc = {779	.name = "smbb-dcin",780	.type = POWER_SUPPLY_TYPE_MAINS,781	.properties = smbb_charger_properties,782	.num_properties = ARRAY_SIZE(smbb_charger_properties),783	.get_property = smbb_dcin_get_property,784	.set_property = smbb_dcin_set_property,785	.property_is_writeable = smbb_charger_writable_property,786};787 788static int smbb_chg_otg_enable(struct regulator_dev *rdev)789{790	struct smbb_charger *chg = rdev_get_drvdata(rdev);791	int rc;792 793	rc = regmap_update_bits(chg->regmap, chg->addr + SMBB_USB_OTG_CTL,794				OTG_CTL_EN, OTG_CTL_EN);795	if (rc)796		dev_err(chg->dev, "failed to update OTG_CTL\n");797	return rc;798}799 800static int smbb_chg_otg_disable(struct regulator_dev *rdev)801{802	struct smbb_charger *chg = rdev_get_drvdata(rdev);803	int rc;804 805	rc = regmap_update_bits(chg->regmap, chg->addr + SMBB_USB_OTG_CTL,806				OTG_CTL_EN, 0);807	if (rc)808		dev_err(chg->dev, "failed to update OTG_CTL\n");809	return rc;810}811 812static int smbb_chg_otg_is_enabled(struct regulator_dev *rdev)813{814	struct smbb_charger *chg = rdev_get_drvdata(rdev);815	unsigned int value = 0;816	int rc;817 818	rc = regmap_read(chg->regmap, chg->addr + SMBB_USB_OTG_CTL, &value);819	if (rc)820		dev_err(chg->dev, "failed to read OTG_CTL\n");821 822	return !!(value & OTG_CTL_EN);823}824 825static const struct regulator_ops smbb_chg_otg_ops = {826	.enable = smbb_chg_otg_enable,827	.disable = smbb_chg_otg_disable,828	.is_enabled = smbb_chg_otg_is_enabled,829};830 831static int smbb_charger_probe(struct platform_device *pdev)832{833	struct power_supply_config bat_cfg = {};834	struct power_supply_config usb_cfg = {};835	struct power_supply_config dc_cfg = {};836	struct smbb_charger *chg;837	struct regulator_config config = { };838	int rc, i;839 840	chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL);841	if (!chg)842		return -ENOMEM;843 844	chg->dev = &pdev->dev;845	mutex_init(&chg->statlock);846 847	chg->regmap = dev_get_regmap(pdev->dev.parent, NULL);848	if (!chg->regmap) {849		dev_err(&pdev->dev, "failed to locate regmap\n");850		return -ENODEV;851	}852 853	rc = of_property_read_u32(pdev->dev.of_node, "reg", &chg->addr);854	if (rc) {855		dev_err(&pdev->dev, "missing or invalid 'reg' property\n");856		return rc;857	}858 859	rc = regmap_read(chg->regmap, chg->addr + SMBB_MISC_REV2, &chg->revision);860	if (rc) {861		dev_err(&pdev->dev, "unable to read revision\n");862		return rc;863	}864 865	chg->revision += 1;866	if (chg->revision != 1 && chg->revision != 2 && chg->revision != 3) {867		dev_err(&pdev->dev, "v%d hardware not supported\n", chg->revision);868		return -ENODEV;869	}870	dev_info(&pdev->dev, "Initializing SMBB rev %u", chg->revision);871 872	chg->dc_disabled = of_property_read_bool(pdev->dev.of_node, "qcom,disable-dc");873 874	for (i = 0; i < _ATTR_CNT; ++i) {875		rc = smbb_charger_attr_parse(chg, i);876		if (rc) {877			dev_err(&pdev->dev, "failed to parse/apply settings\n");878			return rc;879		}880	}881 882	bat_cfg.drv_data = chg;883	bat_cfg.of_node = pdev->dev.of_node;884	chg->bat_psy = devm_power_supply_register(&pdev->dev,885						  &bat_psy_desc,886						  &bat_cfg);887	if (IS_ERR(chg->bat_psy)) {888		dev_err(&pdev->dev, "failed to register battery\n");889		return PTR_ERR(chg->bat_psy);890	}891 892	usb_cfg.drv_data = chg;893	usb_cfg.supplied_to = smbb_bif;894	usb_cfg.num_supplicants = ARRAY_SIZE(smbb_bif);895	chg->usb_psy = devm_power_supply_register(&pdev->dev,896						  &usb_psy_desc,897						  &usb_cfg);898	if (IS_ERR(chg->usb_psy)) {899		dev_err(&pdev->dev, "failed to register USB power supply\n");900		return PTR_ERR(chg->usb_psy);901	}902 903	chg->edev = devm_extcon_dev_allocate(&pdev->dev, smbb_usb_extcon_cable);904	if (IS_ERR(chg->edev)) {905		dev_err(&pdev->dev, "failed to allocate extcon device\n");906		return -ENOMEM;907	}908 909	rc = devm_extcon_dev_register(&pdev->dev, chg->edev);910	if (rc < 0) {911		dev_err(&pdev->dev, "failed to register extcon device\n");912		return rc;913	}914 915	if (!chg->dc_disabled) {916		dc_cfg.drv_data = chg;917		dc_cfg.supplied_to = smbb_bif;918		dc_cfg.num_supplicants = ARRAY_SIZE(smbb_bif);919		chg->dc_psy = devm_power_supply_register(&pdev->dev,920							 &dc_psy_desc,921							 &dc_cfg);922		if (IS_ERR(chg->dc_psy)) {923			dev_err(&pdev->dev, "failed to register DC power supply\n");924			return PTR_ERR(chg->dc_psy);925		}926	}927 928	for (i = 0; i < ARRAY_SIZE(smbb_charger_irqs); ++i) {929		int irq;930 931		irq = platform_get_irq_byname(pdev, smbb_charger_irqs[i].name);932		if (irq < 0)933			return irq;934 935		smbb_charger_irqs[i].handler(irq, chg);936 937		rc = devm_request_threaded_irq(&pdev->dev, irq, NULL,938				smbb_charger_irqs[i].handler, IRQF_ONESHOT,939				smbb_charger_irqs[i].name, chg);940		if (rc) {941			dev_err(&pdev->dev, "failed to request irq '%s'\n",942				smbb_charger_irqs[i].name);943			return rc;944		}945	}946 947	/*948	 * otg regulator is used to control VBUS voltage direction949	 * when USB switches between host and gadget mode950	 */951	chg->otg_rdesc.id = -1;952	chg->otg_rdesc.name = "otg-vbus";953	chg->otg_rdesc.ops = &smbb_chg_otg_ops;954	chg->otg_rdesc.owner = THIS_MODULE;955	chg->otg_rdesc.type = REGULATOR_VOLTAGE;956	chg->otg_rdesc.supply_name = "usb-otg-in";957	chg->otg_rdesc.of_match = "otg-vbus";958 959	config.dev = &pdev->dev;960	config.driver_data = chg;961 962	chg->otg_reg = devm_regulator_register(&pdev->dev, &chg->otg_rdesc,963					       &config);964	if (IS_ERR(chg->otg_reg))965		return PTR_ERR(chg->otg_reg);966 967	chg->jeita_ext_temp = of_property_read_bool(pdev->dev.of_node,968			"qcom,jeita-extended-temp-range");969 970	/* Set temperature range to [35%:70%] or [25%:80%] accordingly */971	rc = regmap_update_bits(chg->regmap, chg->addr + SMBB_BAT_BTC_CTRL,972			BTC_CTRL_COLD_EXT | BTC_CTRL_HOT_EXT_N,973			chg->jeita_ext_temp ?974				BTC_CTRL_COLD_EXT :975				BTC_CTRL_HOT_EXT_N);976	if (rc) {977		dev_err(&pdev->dev,978			"unable to set %s temperature range\n",979			chg->jeita_ext_temp ? "JEITA extended" : "normal");980		return rc;981	}982 983	for (i = 0; i < ARRAY_SIZE(smbb_charger_setup); ++i) {984		const struct reg_off_mask_default *r = &smbb_charger_setup[i];985 986		if (r->rev_mask & BIT(chg->revision))987			continue;988 989		rc = regmap_update_bits(chg->regmap, chg->addr + r->offset,990				r->mask, r->value);991		if (rc) {992			dev_err(&pdev->dev,993				"unable to initializing charging, bailing\n");994			return rc;995		}996	}997 998	platform_set_drvdata(pdev, chg);999 1000	return 0;1001}1002 1003static void smbb_charger_remove(struct platform_device *pdev)1004{1005	struct smbb_charger *chg;1006 1007	chg = platform_get_drvdata(pdev);1008 1009	regmap_update_bits(chg->regmap, chg->addr + SMBB_CHG_CTRL, CTRL_EN, 0);1010}1011 1012static const struct of_device_id smbb_charger_id_table[] = {1013	{ .compatible = "qcom,pm8226-charger" },1014	{ .compatible = "qcom,pm8941-charger" },1015	{ }1016};1017MODULE_DEVICE_TABLE(of, smbb_charger_id_table);1018 1019static struct platform_driver smbb_charger_driver = {1020	.probe	  = smbb_charger_probe,1021	.remove_new	 = smbb_charger_remove,1022	.driver	 = {1023		.name   = "qcom-smbb",1024		.of_match_table = smbb_charger_id_table,1025	},1026};1027module_platform_driver(smbb_charger_driver);1028 1029MODULE_DESCRIPTION("Qualcomm Switch-Mode Battery Charger and Boost driver");1030MODULE_LICENSE("GPL v2");1031