cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

qcom_smbb.c (25751B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/* Copyright (c) 2014, Sony Mobile Communications Inc.
      3 *
      4 * This driver is for the multi-block Switch-Mode Battery Charger and Boost
      5 * (SMBB) hardware, found in Qualcomm PM8941 PMICs.  The charger is an
      6 * integrated, single-cell lithium-ion battery charger.
      7 *
      8 * Sub-components:
      9 *  - Charger core
     10 *  - Buck
     11 *  - DC charge-path
     12 *  - USB charge-path
     13 *  - Battery interface
     14 *  - Boost (not implemented)
     15 *  - Misc
     16 *  - HF-Buck
     17 */
     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		0x040
     33#define SMBB_CHG_VSAFE		0x041
     34#define SMBB_CHG_CFG		0x043
     35#define SMBB_CHG_IMAX		0x044
     36#define SMBB_CHG_ISAFE		0x045
     37#define SMBB_CHG_VIN_MIN	0x047
     38#define SMBB_CHG_CTRL		0x049
     39#define CTRL_EN			BIT(7)
     40#define SMBB_CHG_VBAT_WEAK	0x052
     41#define SMBB_CHG_IBAT_TERM_CHG	0x05b
     42#define IBAT_TERM_CHG_IEOC	BIT(7)
     43#define IBAT_TERM_CHG_IEOC_BMS	BIT(7)
     44#define IBAT_TERM_CHG_IEOC_CHG	0
     45#define SMBB_CHG_VBAT_DET	0x05d
     46#define SMBB_CHG_TCHG_MAX_EN	0x060
     47#define TCHG_MAX_EN		BIT(7)
     48#define SMBB_CHG_WDOG_TIME	0x062
     49#define SMBB_CHG_WDOG_EN	0x065
     50#define WDOG_EN			BIT(7)
     51
     52#define SMBB_BUCK_REG_MODE	0x174
     53#define BUCK_REG_MODE		BIT(0)
     54#define BUCK_REG_MODE_VBAT	BIT(0)
     55#define BUCK_REG_MODE_VSYS	0
     56
     57#define SMBB_BAT_PRES_STATUS	0x208
     58#define PRES_STATUS_BAT_PRES	BIT(7)
     59#define SMBB_BAT_TEMP_STATUS	0x209
     60#define TEMP_STATUS_OK		BIT(7)
     61#define TEMP_STATUS_HOT		BIT(6)
     62#define SMBB_BAT_BTC_CTRL	0x249
     63#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		0x344
     68#define SMBB_USB_OTG_CTL	0x348
     69#define OTG_CTL_EN		BIT(0)
     70#define SMBB_USB_ENUM_TIMER_STOP 0x34e
     71#define ENUM_TIMER_STOP		BIT(0)
     72#define SMBB_USB_SEC_ACCESS	0x3d0
     73#define SEC_ACCESS_MAGIC	0xa5
     74#define SMBB_USB_REV_BST	0x3ed
     75#define REV_BST_CHG_GONE	BIT(7)
     76
     77#define SMBB_DC_IMAX		0x444
     78
     79#define SMBB_MISC_REV2		0x601
     80#define SMBB_MISC_BOOT_DONE	0x642
     81#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	else
    368		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		else
    623			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		else
    631			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 charger
    644		* only.  If you hook up some other technology, there will be
    645		* 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#endif
    748
    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 direction
    949	 * when USB switches between host and gadget mode
    950	 */
    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 int 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	return 0;
   1012}
   1013
   1014static const struct of_device_id smbb_charger_id_table[] = {
   1015	{ .compatible = "qcom,pm8226-charger" },
   1016	{ .compatible = "qcom,pm8941-charger" },
   1017	{ }
   1018};
   1019MODULE_DEVICE_TABLE(of, smbb_charger_id_table);
   1020
   1021static struct platform_driver smbb_charger_driver = {
   1022	.probe	  = smbb_charger_probe,
   1023	.remove	 = smbb_charger_remove,
   1024	.driver	 = {
   1025		.name   = "qcom-smbb",
   1026		.of_match_table = smbb_charger_id_table,
   1027	},
   1028};
   1029module_platform_driver(smbb_charger_driver);
   1030
   1031MODULE_DESCRIPTION("Qualcomm Switch-Mode Battery Charger and Boost driver");
   1032MODULE_LICENSE("GPL v2");