// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2014, Sony Mobile Communications Inc.
*
* This driver is for the multi-block Switch-Mode Battery Charger and Boost
* (SMBB) hardware, found in Qualcomm PM8941 PMICs. The charger is an
* integrated, single-cell lithium-ion battery charger.
*
* Sub-components:
* - Charger core
* - Buck
* - DC charge-path
* - USB charge-path
* - Battery interface
* - Boost (not implemented)
* - Misc
* - HF-Buck
*/
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/extcon-provider.h>
#include <linux/regulator/driver.h>
#define SMBB_CHG_VMAX 0x040
#define SMBB_CHG_VSAFE 0x041
#define SMBB_CHG_CFG 0x043
#define SMBB_CHG_IMAX 0x044
#define SMBB_CHG_ISAFE 0x045
#define SMBB_CHG_VIN_MIN 0x047
#define SMBB_CHG_CTRL 0x049
#define CTRL_EN BIT(7)
#define SMBB_CHG_VBAT_WEAK 0x052
#define SMBB_CHG_IBAT_TERM_CHG 0x05b
#define IBAT_TERM_CHG_IEOC BIT(7)
#define IBAT_TERM_CHG_IEOC_BMS BIT(7)
#define IBAT_TERM_CHG_IEOC_CHG 0
#define SMBB_CHG_VBAT_DET 0x05d
#define SMBB_CHG_TCHG_MAX_EN 0x060
#define TCHG_MAX_EN BIT(7)
#define SMBB_CHG_WDOG_TIME 0x062
#define SMBB_CHG_WDOG_EN 0x065
#define WDOG_EN BIT(7)
#define SMBB_BUCK_REG_MODE 0x174
#define BUCK_REG_MODE BIT(0)
#define BUCK_REG_MODE_VBAT BIT(0)
#define BUCK_REG_MODE_VSYS 0
#define SMBB_BAT_PRES_STATUS 0x208
#define PRES_STATUS_BAT_PRES BIT(7)
#define SMBB_BAT_TEMP_STATUS 0x209
#define TEMP_STATUS_OK BIT(7)
#define TEMP_STATUS_HOT BIT(6)
#define SMBB_BAT_BTC_CTRL 0x249
#define BTC_CTRL_COMP_EN BIT(7)
#define BTC_CTRL_COLD_EXT BIT(1)
#define BTC_CTRL_HOT_EXT_N BIT(0)
#define SMBB_USB_IMAX 0x344
#define SMBB_USB_OTG_CTL 0x348
#define OTG_CTL_EN BIT(0)
#define SMBB_USB_ENUM_TIMER_STOP 0x34e
#define ENUM_TIMER_STOP BIT(0)
#define SMBB_USB_SEC_ACCESS 0x3d0
#define SEC_ACCESS_MAGIC 0xa5
#define SMBB_USB_REV_BST 0x3ed
#define REV_BST_CHG_GONE BIT(7)
#define SMBB_DC_IMAX 0x444
#define SMBB_MISC_REV2 0x601
#define SMBB_MISC_BOOT_DONE 0x642
#define BOOT_DONE BIT(7)
#define STATUS_USBIN_VALID BIT(0) /* USB connection is valid */
#define STATUS_DCIN_VALID BIT(1) /* DC connection is valid */
#define STATUS_BAT_HOT BIT(2) /* Battery temp 1=Hot, 0=Cold */
#define STATUS_BAT_OK BIT(3) /* Battery temp OK */
#define STATUS_BAT_PRESENT BIT(4) /* Battery is present */
#define STATUS_CHG_DONE BIT(5) /* Charge cycle is complete */
#define STATUS_CHG_TRKL BIT(6) /* Trickle charging */
#define STATUS_CHG_FAST BIT(7) /* Fast charging */
#define STATUS_CHG_GONE BIT(8) /* No charger is connected */
enum smbb_attr {
ATTR_BAT_ISAFE,
ATTR_BAT_IMAX,
ATTR_USBIN_IMAX,
ATTR_DCIN_IMAX,
ATTR_BAT_VSAFE,
ATTR_BAT_VMAX,
ATTR_BAT_VMIN,
ATTR_CHG_VDET,
ATTR_VIN_MIN,
_ATTR_CNT,
};
struct smbb_charger {
unsigned int revision;
unsigned int addr;
struct device *dev;
struct extcon_dev *edev;
bool dc_disabled;
bool jeita_ext_temp;
unsigned long status;
struct mutex statlock;
unsigned int attr[_ATTR_CNT];
struct power_supply *usb_psy;
struct power_supply *dc_psy;
struct power_supply *bat_psy;
struct regmap *regmap;
struct regulator_desc otg_rdesc;
struct regulator_dev *otg_reg;
};
static const unsigned int smbb_usb_extcon_cable[] = {
EXTCON_USB,
EXTCON_NONE,
};
static int smbb_vbat_weak_fn(unsigned int index)
{
return 2100000 + index * 100000;
}
static int smbb_vin_fn(unsigned int index)
{
if (index > 42)
return 5600000 + (index - 43) * 200000;
return 3400000 + index * 50000;
}
static int smbb_vmax_fn(unsigned int index)
{
return 3240000 + index * 10000;
}
static int smbb_vbat_det_fn(unsigned int index)
{
return 3240000 + index * 20000;
}
static int smbb_imax_fn(unsigned int index)
{
if (index < 2)
return 100000 + index * 50000;
return index * 100000;
}
static int smbb_bat_imax_fn(unsigned int index)
{
return index * 50000;
}
static unsigned int smbb_hw_lookup(unsigned int val, int (*fn)(unsigned int))
{
unsigned int widx;
unsigned int sel;
for (widx = sel = 0; (*fn)(widx) <= val; ++widx)
sel = widx;
return sel;
}
static const struct smbb_charger_attr {
const char *name;
unsigned int reg;
unsigned int safe_reg;
unsigned int max;
unsigned int min;
unsigned int fail_ok;
int (*hw_fn)(unsigned int);
} smbb_charger_attrs[] = {
[ATTR_BAT_ISAFE] = {
.name = "qcom,fast-charge-safe-current",
.reg = SMBB_CHG_ISAFE,
.max = 3000000,
.min = 200000,
.hw_fn = smbb_bat_imax_fn,
.fail_ok = 1,
},
[ATTR_BAT_IMAX] = {
.name = "qcom,fast-charge-current-limit",
.reg = SMBB_CHG_IMAX,
.safe_reg = SMBB_CHG_ISAFE,
.max = 30000