162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Motorola CPCAP PMIC battery charger driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2017 Tony Lindgren <tony@atomide.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Rewritten for Linux power framework with some parts based on
862306a36Sopenharmony_ci * earlier driver found in the Motorola Linux kernel:
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Copyright (C) 2009-2010 Motorola, Inc.
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/atomic.h>
1462306a36Sopenharmony_ci#include <linux/init.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include <linux/err.h>
1862306a36Sopenharmony_ci#include <linux/interrupt.h>
1962306a36Sopenharmony_ci#include <linux/notifier.h>
2062306a36Sopenharmony_ci#include <linux/of.h>
2162306a36Sopenharmony_ci#include <linux/of_platform.h>
2262306a36Sopenharmony_ci#include <linux/platform_device.h>
2362306a36Sopenharmony_ci#include <linux/power_supply.h>
2462306a36Sopenharmony_ci#include <linux/regmap.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
2762306a36Sopenharmony_ci#include <linux/usb/phy_companion.h>
2862306a36Sopenharmony_ci#include <linux/phy/omap_usb.h>
2962306a36Sopenharmony_ci#include <linux/usb/otg.h>
3062306a36Sopenharmony_ci#include <linux/iio/consumer.h>
3162306a36Sopenharmony_ci#include <linux/mfd/motorola-cpcap.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/*
3462306a36Sopenharmony_ci * CPCAP_REG_CRM register bits. For documentation of somewhat similar hardware,
3562306a36Sopenharmony_ci * see NXP "MC13783 Power Management and Audio Circuit Users's Guide"
3662306a36Sopenharmony_ci * MC13783UG.pdf chapter "8.5 Battery Interface Register Summary". The registers
3762306a36Sopenharmony_ci * and values for CPCAP are different, but some of the internal components seem
3862306a36Sopenharmony_ci * similar. Also see the Motorola Linux kernel cpcap-regbits.h. CPCAP_REG_CHRGR_1
3962306a36Sopenharmony_ci * bits that seem to describe the CRM register.
4062306a36Sopenharmony_ci */
4162306a36Sopenharmony_ci#define CPCAP_REG_CRM_UNUSED_641_15	BIT(15)	/* 641 = register number */
4262306a36Sopenharmony_ci#define CPCAP_REG_CRM_UNUSED_641_14	BIT(14)	/* 641 = register number */
4362306a36Sopenharmony_ci#define CPCAP_REG_CRM_CHRG_LED_EN	BIT(13)	/* Charger LED */
4462306a36Sopenharmony_ci#define CPCAP_REG_CRM_RVRSMODE		BIT(12)	/* USB VBUS output enable */
4562306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_TR1		BIT(11)	/* Trickle charge current */
4662306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_TR0		BIT(10)
4762306a36Sopenharmony_ci#define CPCAP_REG_CRM_FET_OVRD		BIT(9)	/* 0 = hardware, 1 = FET_CTRL */
4862306a36Sopenharmony_ci#define CPCAP_REG_CRM_FET_CTRL		BIT(8)	/* BPFET 1 if FET_OVRD set */
4962306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG3		BIT(7)	/* Charge voltage bits */
5062306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG2		BIT(6)
5162306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG1		BIT(5)
5262306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG0		BIT(4)
5362306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG3		BIT(3)	/* Charge current bits */
5462306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG2		BIT(2)
5562306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG1		BIT(1)
5662306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG0		BIT(0)
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/* CPCAP_REG_CRM trickle charge voltages */
5962306a36Sopenharmony_ci#define CPCAP_REG_CRM_TR(val)		(((val) & 0x3) << 10)
6062306a36Sopenharmony_ci#define CPCAP_REG_CRM_TR_0A00		CPCAP_REG_CRM_TR(0x0)
6162306a36Sopenharmony_ci#define CPCAP_REG_CRM_TR_0A24		CPCAP_REG_CRM_TR(0x1)
6262306a36Sopenharmony_ci#define CPCAP_REG_CRM_TR_0A48		CPCAP_REG_CRM_TR(0x2)
6362306a36Sopenharmony_ci#define CPCAP_REG_CRM_TR_0A72		CPCAP_REG_CRM_TR(0x4)
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/*
6662306a36Sopenharmony_ci * CPCAP_REG_CRM charge voltages based on the ADC channel 1 values.
6762306a36Sopenharmony_ci * Note that these register bits don't match MC13783UG.pdf VCHRG
6862306a36Sopenharmony_ci * register bits.
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG(val)	(((val) & 0xf) << 4)
7162306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_3V80	CPCAP_REG_CRM_VCHRG(0x0)
7262306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V10	CPCAP_REG_CRM_VCHRG(0x1)
7362306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V12	CPCAP_REG_CRM_VCHRG(0x2)
7462306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V15	CPCAP_REG_CRM_VCHRG(0x3)
7562306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V17	CPCAP_REG_CRM_VCHRG(0x4)
7662306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V20	CPCAP_REG_CRM_VCHRG(0x5)
7762306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V23	CPCAP_REG_CRM_VCHRG(0x6)
7862306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V25	CPCAP_REG_CRM_VCHRG(0x7)
7962306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V27	CPCAP_REG_CRM_VCHRG(0x8)
8062306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V30	CPCAP_REG_CRM_VCHRG(0x9)
8162306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V33	CPCAP_REG_CRM_VCHRG(0xa)
8262306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V35	CPCAP_REG_CRM_VCHRG(0xb)
8362306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V38	CPCAP_REG_CRM_VCHRG(0xc)
8462306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V40	CPCAP_REG_CRM_VCHRG(0xd)
8562306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V42	CPCAP_REG_CRM_VCHRG(0xe)
8662306a36Sopenharmony_ci#define CPCAP_REG_CRM_VCHRG_4V44	CPCAP_REG_CRM_VCHRG(0xf)
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/*
8962306a36Sopenharmony_ci * CPCAP_REG_CRM charge currents. These seem to match MC13783UG.pdf
9062306a36Sopenharmony_ci * values in "Table 8-3. Charge Path Regulator Current Limit
9162306a36Sopenharmony_ci * Characteristics" for the nominal values.
9262306a36Sopenharmony_ci *
9362306a36Sopenharmony_ci * Except 70mA and 1.596A and unlimited, these are simply 88.7mA / step.
9462306a36Sopenharmony_ci */
9562306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG(val)	(((val) & 0xf) << 0)
9662306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_0A000	CPCAP_REG_CRM_ICHRG(0x0)
9762306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_0A070	CPCAP_REG_CRM_ICHRG(0x1)
9862306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_0A177	CPCAP_REG_CRM_ICHRG(0x2)
9962306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_0A266	CPCAP_REG_CRM_ICHRG(0x3)
10062306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_0A355	CPCAP_REG_CRM_ICHRG(0x4)
10162306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_0A443	CPCAP_REG_CRM_ICHRG(0x5)
10262306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_0A532	CPCAP_REG_CRM_ICHRG(0x6)
10362306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_0A621	CPCAP_REG_CRM_ICHRG(0x7)
10462306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_0A709	CPCAP_REG_CRM_ICHRG(0x8)
10562306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_0A798	CPCAP_REG_CRM_ICHRG(0x9)
10662306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_0A886	CPCAP_REG_CRM_ICHRG(0xa)
10762306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_0A975	CPCAP_REG_CRM_ICHRG(0xb)
10862306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_1A064	CPCAP_REG_CRM_ICHRG(0xc)
10962306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_1A152	CPCAP_REG_CRM_ICHRG(0xd)
11062306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_1A596	CPCAP_REG_CRM_ICHRG(0xe)
11162306a36Sopenharmony_ci#define CPCAP_REG_CRM_ICHRG_NO_LIMIT	CPCAP_REG_CRM_ICHRG(0xf)
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci/* CPCAP_REG_VUSBC register bits needed for VBUS */
11462306a36Sopenharmony_ci#define CPCAP_BIT_VBUS_SWITCH		BIT(0)	/* VBUS boost to 5V */
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cienum {
11762306a36Sopenharmony_ci	CPCAP_CHARGER_IIO_BATTDET,
11862306a36Sopenharmony_ci	CPCAP_CHARGER_IIO_VOLTAGE,
11962306a36Sopenharmony_ci	CPCAP_CHARGER_IIO_VBUS,
12062306a36Sopenharmony_ci	CPCAP_CHARGER_IIO_CHRG_CURRENT,
12162306a36Sopenharmony_ci	CPCAP_CHARGER_IIO_BATT_CURRENT,
12262306a36Sopenharmony_ci	CPCAP_CHARGER_IIO_NR,
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistruct cpcap_charger_ddata {
12662306a36Sopenharmony_ci	struct device *dev;
12762306a36Sopenharmony_ci	struct regmap *reg;
12862306a36Sopenharmony_ci	struct list_head irq_list;
12962306a36Sopenharmony_ci	struct delayed_work detect_work;
13062306a36Sopenharmony_ci	struct delayed_work vbus_work;
13162306a36Sopenharmony_ci	struct gpio_desc *gpio[2];		/* gpio_reven0 & 1 */
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	struct iio_channel *channels[CPCAP_CHARGER_IIO_NR];
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	struct power_supply *usb;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	struct phy_companion comparator;	/* For USB VBUS */
13862306a36Sopenharmony_ci	unsigned int vbus_enabled:1;
13962306a36Sopenharmony_ci	unsigned int feeding_vbus:1;
14062306a36Sopenharmony_ci	atomic_t active;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	int status;
14362306a36Sopenharmony_ci	int voltage;
14462306a36Sopenharmony_ci	int limit_current;
14562306a36Sopenharmony_ci};
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistruct cpcap_interrupt_desc {
14862306a36Sopenharmony_ci	int irq;
14962306a36Sopenharmony_ci	struct list_head node;
15062306a36Sopenharmony_ci	const char *name;
15162306a36Sopenharmony_ci};
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistruct cpcap_charger_ints_state {
15462306a36Sopenharmony_ci	bool chrg_det;
15562306a36Sopenharmony_ci	bool rvrs_chrg;
15662306a36Sopenharmony_ci	bool vbusov;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	bool chrg_se1b;
15962306a36Sopenharmony_ci	bool rvrs_mode;
16062306a36Sopenharmony_ci	bool chrgcurr2;
16162306a36Sopenharmony_ci	bool chrgcurr1;
16262306a36Sopenharmony_ci	bool vbusvld;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	bool battdetb;
16562306a36Sopenharmony_ci};
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic enum power_supply_property cpcap_charger_props[] = {
16862306a36Sopenharmony_ci	POWER_SUPPLY_PROP_STATUS,
16962306a36Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
17062306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
17162306a36Sopenharmony_ci	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
17262306a36Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_NOW,
17362306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_NOW,
17462306a36Sopenharmony_ci};
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic int cpcap_charger_get_charge_voltage(struct cpcap_charger_ddata *ddata)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	struct iio_channel *channel;
17962306a36Sopenharmony_ci	int error, value = 0;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	channel = ddata->channels[CPCAP_CHARGER_IIO_VOLTAGE];
18262306a36Sopenharmony_ci	error = iio_read_channel_processed(channel, &value);
18362306a36Sopenharmony_ci	if (error < 0) {
18462306a36Sopenharmony_ci		dev_warn(ddata->dev, "%s failed: %i\n", __func__, error);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci		return 0;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	return value;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic int cpcap_charger_get_charge_current(struct cpcap_charger_ddata *ddata)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	struct iio_channel *channel;
19562306a36Sopenharmony_ci	int error, value = 0;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	channel = ddata->channels[CPCAP_CHARGER_IIO_CHRG_CURRENT];
19862306a36Sopenharmony_ci	error = iio_read_channel_processed(channel, &value);
19962306a36Sopenharmony_ci	if (error < 0) {
20062306a36Sopenharmony_ci		dev_warn(ddata->dev, "%s failed: %i\n", __func__, error);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci		return 0;
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	return value;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic int cpcap_charger_get_property(struct power_supply *psy,
20962306a36Sopenharmony_ci				      enum power_supply_property psp,
21062306a36Sopenharmony_ci				      union power_supply_propval *val)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	struct cpcap_charger_ddata *ddata = dev_get_drvdata(psy->dev.parent);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	switch (psp) {
21562306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_STATUS:
21662306a36Sopenharmony_ci		val->intval = ddata->status;
21762306a36Sopenharmony_ci		break;
21862306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
21962306a36Sopenharmony_ci		val->intval = ddata->limit_current;
22062306a36Sopenharmony_ci		break;
22162306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
22262306a36Sopenharmony_ci		val->intval = ddata->voltage;
22362306a36Sopenharmony_ci		break;
22462306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
22562306a36Sopenharmony_ci		if (ddata->status == POWER_SUPPLY_STATUS_CHARGING)
22662306a36Sopenharmony_ci			val->intval = cpcap_charger_get_charge_voltage(ddata) *
22762306a36Sopenharmony_ci				1000;
22862306a36Sopenharmony_ci		else
22962306a36Sopenharmony_ci			val->intval = 0;
23062306a36Sopenharmony_ci		break;
23162306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_NOW:
23262306a36Sopenharmony_ci		if (ddata->status == POWER_SUPPLY_STATUS_CHARGING)
23362306a36Sopenharmony_ci			val->intval = cpcap_charger_get_charge_current(ddata) *
23462306a36Sopenharmony_ci				1000;
23562306a36Sopenharmony_ci		else
23662306a36Sopenharmony_ci			val->intval = 0;
23762306a36Sopenharmony_ci		break;
23862306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
23962306a36Sopenharmony_ci		val->intval = ddata->status == POWER_SUPPLY_STATUS_CHARGING;
24062306a36Sopenharmony_ci		break;
24162306a36Sopenharmony_ci	default:
24262306a36Sopenharmony_ci		return -EINVAL;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	return 0;
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic int cpcap_charger_match_voltage(int voltage)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	switch (voltage) {
25162306a36Sopenharmony_ci	case 0 ... 4100000 - 1: return 3800000;
25262306a36Sopenharmony_ci	case 4100000 ... 4120000 - 1: return 4100000;
25362306a36Sopenharmony_ci	case 4120000 ... 4150000 - 1: return 4120000;
25462306a36Sopenharmony_ci	case 4150000 ... 4170000 - 1: return 4150000;
25562306a36Sopenharmony_ci	case 4170000 ... 4200000 - 1: return 4170000;
25662306a36Sopenharmony_ci	case 4200000 ... 4230000 - 1: return 4200000;
25762306a36Sopenharmony_ci	case 4230000 ... 4250000 - 1: return 4230000;
25862306a36Sopenharmony_ci	case 4250000 ... 4270000 - 1: return 4250000;
25962306a36Sopenharmony_ci	case 4270000 ... 4300000 - 1: return 4270000;
26062306a36Sopenharmony_ci	case 4300000 ... 4330000 - 1: return 4300000;
26162306a36Sopenharmony_ci	case 4330000 ... 4350000 - 1: return 4330000;
26262306a36Sopenharmony_ci	case 4350000 ... 4380000 - 1: return 4350000;
26362306a36Sopenharmony_ci	case 4380000 ... 4400000 - 1: return 4380000;
26462306a36Sopenharmony_ci	case 4400000 ... 4420000 - 1: return 4400000;
26562306a36Sopenharmony_ci	case 4420000 ... 4440000 - 1: return 4420000;
26662306a36Sopenharmony_ci	case 4440000: return 4440000;
26762306a36Sopenharmony_ci	default: return 0;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic int
27262306a36Sopenharmony_cicpcap_charger_get_bat_const_charge_voltage(struct cpcap_charger_ddata *ddata)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	union power_supply_propval prop;
27562306a36Sopenharmony_ci	struct power_supply *battery;
27662306a36Sopenharmony_ci	int voltage = ddata->voltage;
27762306a36Sopenharmony_ci	int error;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	battery = power_supply_get_by_name("battery");
28062306a36Sopenharmony_ci	if (battery) {
28162306a36Sopenharmony_ci		error = power_supply_get_property(battery,
28262306a36Sopenharmony_ci				POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
28362306a36Sopenharmony_ci				&prop);
28462306a36Sopenharmony_ci		if (!error)
28562306a36Sopenharmony_ci			voltage = prop.intval;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci		power_supply_put(battery);
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	return voltage;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic int cpcap_charger_current_to_regval(int microamp)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	int miliamp = microamp / 1000;
29662306a36Sopenharmony_ci	int res;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	if (miliamp < 0)
29962306a36Sopenharmony_ci		return -EINVAL;
30062306a36Sopenharmony_ci	if (miliamp < 70)
30162306a36Sopenharmony_ci		return CPCAP_REG_CRM_ICHRG(0x0);
30262306a36Sopenharmony_ci	if (miliamp < 177)
30362306a36Sopenharmony_ci		return CPCAP_REG_CRM_ICHRG(0x1);
30462306a36Sopenharmony_ci	if (miliamp >= 1596)
30562306a36Sopenharmony_ci		return CPCAP_REG_CRM_ICHRG(0xe);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	res = microamp / 88666;
30862306a36Sopenharmony_ci	if (res > 0xd)
30962306a36Sopenharmony_ci		res = 0xd;
31062306a36Sopenharmony_ci	return CPCAP_REG_CRM_ICHRG(res);
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic int cpcap_charger_set_property(struct power_supply *psy,
31462306a36Sopenharmony_ci				      enum power_supply_property psp,
31562306a36Sopenharmony_ci				      const union power_supply_propval *val)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	struct cpcap_charger_ddata *ddata = dev_get_drvdata(psy->dev.parent);
31862306a36Sopenharmony_ci	int voltage, batvolt;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	switch (psp) {
32162306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
32262306a36Sopenharmony_ci		if (cpcap_charger_current_to_regval(val->intval) < 0)
32362306a36Sopenharmony_ci			return -EINVAL;
32462306a36Sopenharmony_ci		ddata->limit_current = val->intval;
32562306a36Sopenharmony_ci		schedule_delayed_work(&ddata->detect_work, 0);
32662306a36Sopenharmony_ci		break;
32762306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
32862306a36Sopenharmony_ci		voltage = cpcap_charger_match_voltage(val->intval);
32962306a36Sopenharmony_ci		batvolt = cpcap_charger_get_bat_const_charge_voltage(ddata);
33062306a36Sopenharmony_ci		if (voltage > batvolt)
33162306a36Sopenharmony_ci			voltage = batvolt;
33262306a36Sopenharmony_ci		ddata->voltage = voltage;
33362306a36Sopenharmony_ci		schedule_delayed_work(&ddata->detect_work, 0);
33462306a36Sopenharmony_ci		break;
33562306a36Sopenharmony_ci	default:
33662306a36Sopenharmony_ci		return -EINVAL;
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	return 0;
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic int cpcap_charger_property_is_writeable(struct power_supply *psy,
34362306a36Sopenharmony_ci					       enum power_supply_property psp)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	switch (psp) {
34662306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
34762306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
34862306a36Sopenharmony_ci		return 1;
34962306a36Sopenharmony_ci	default:
35062306a36Sopenharmony_ci		return 0;
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic void cpcap_charger_set_cable_path(struct cpcap_charger_ddata *ddata,
35562306a36Sopenharmony_ci					 bool enabled)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	if (!ddata->gpio[0])
35862306a36Sopenharmony_ci		return;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	gpiod_set_value(ddata->gpio[0], enabled);
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic void cpcap_charger_set_inductive_path(struct cpcap_charger_ddata *ddata,
36462306a36Sopenharmony_ci					     bool enabled)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	if (!ddata->gpio[1])
36762306a36Sopenharmony_ci		return;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	gpiod_set_value(ddata->gpio[1], enabled);
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic void cpcap_charger_update_state(struct cpcap_charger_ddata *ddata,
37362306a36Sopenharmony_ci				       int state)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	const char *status;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (state > POWER_SUPPLY_STATUS_FULL) {
37862306a36Sopenharmony_ci		dev_warn(ddata->dev, "unknown state: %i\n", state);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci		return;
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	ddata->status = state;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	switch (state) {
38662306a36Sopenharmony_ci	case POWER_SUPPLY_STATUS_DISCHARGING:
38762306a36Sopenharmony_ci		status = "DISCONNECTED";
38862306a36Sopenharmony_ci		break;
38962306a36Sopenharmony_ci	case POWER_SUPPLY_STATUS_NOT_CHARGING:
39062306a36Sopenharmony_ci		status = "DETECTING";
39162306a36Sopenharmony_ci		break;
39262306a36Sopenharmony_ci	case POWER_SUPPLY_STATUS_CHARGING:
39362306a36Sopenharmony_ci		status = "CHARGING";
39462306a36Sopenharmony_ci		break;
39562306a36Sopenharmony_ci	case POWER_SUPPLY_STATUS_FULL:
39662306a36Sopenharmony_ci		status = "DONE";
39762306a36Sopenharmony_ci		break;
39862306a36Sopenharmony_ci	default:
39962306a36Sopenharmony_ci		return;
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	dev_dbg(ddata->dev, "state: %s\n", status);
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_cistatic int cpcap_charger_disable(struct cpcap_charger_ddata *ddata)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	int error;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	error = regmap_update_bits(ddata->reg, CPCAP_REG_CRM, 0x3fff,
41062306a36Sopenharmony_ci				   CPCAP_REG_CRM_FET_OVRD |
41162306a36Sopenharmony_ci				   CPCAP_REG_CRM_FET_CTRL);
41262306a36Sopenharmony_ci	if (error)
41362306a36Sopenharmony_ci		dev_err(ddata->dev, "%s failed with %i\n", __func__, error);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	return error;
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic int cpcap_charger_enable(struct cpcap_charger_ddata *ddata,
41962306a36Sopenharmony_ci				int max_voltage, int charge_current,
42062306a36Sopenharmony_ci				int trickle_current)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	int error;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	if (!max_voltage || !charge_current)
42562306a36Sopenharmony_ci		return -EINVAL;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	dev_dbg(ddata->dev, "enable: %i %i %i\n",
42862306a36Sopenharmony_ci		max_voltage, charge_current, trickle_current);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	error = regmap_update_bits(ddata->reg, CPCAP_REG_CRM, 0x3fff,
43162306a36Sopenharmony_ci				   CPCAP_REG_CRM_CHRG_LED_EN |
43262306a36Sopenharmony_ci				   trickle_current |
43362306a36Sopenharmony_ci				   CPCAP_REG_CRM_FET_OVRD |
43462306a36Sopenharmony_ci				   CPCAP_REG_CRM_FET_CTRL |
43562306a36Sopenharmony_ci				   max_voltage |
43662306a36Sopenharmony_ci				   charge_current);
43762306a36Sopenharmony_ci	if (error)
43862306a36Sopenharmony_ci		dev_err(ddata->dev, "%s failed with %i\n", __func__, error);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	return error;
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistatic bool cpcap_charger_vbus_valid(struct cpcap_charger_ddata *ddata)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	int error, value = 0;
44662306a36Sopenharmony_ci	struct iio_channel *channel =
44762306a36Sopenharmony_ci		ddata->channels[CPCAP_CHARGER_IIO_VBUS];
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	error = iio_read_channel_processed(channel, &value);
45062306a36Sopenharmony_ci	if (error >= 0)
45162306a36Sopenharmony_ci		return value > 3900;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	dev_err(ddata->dev, "error reading VBUS: %i\n", error);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	return false;
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci/* VBUS control functions for the USB PHY companion */
45962306a36Sopenharmony_cistatic void cpcap_charger_vbus_work(struct work_struct *work)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	struct cpcap_charger_ddata *ddata;
46262306a36Sopenharmony_ci	bool vbus = false;
46362306a36Sopenharmony_ci	int error;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	ddata = container_of(work, struct cpcap_charger_ddata,
46662306a36Sopenharmony_ci			     vbus_work.work);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	if (ddata->vbus_enabled) {
46962306a36Sopenharmony_ci		vbus = cpcap_charger_vbus_valid(ddata);
47062306a36Sopenharmony_ci		if (vbus) {
47162306a36Sopenharmony_ci			dev_dbg(ddata->dev, "VBUS already provided\n");
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci			return;
47462306a36Sopenharmony_ci		}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci		ddata->feeding_vbus = true;
47762306a36Sopenharmony_ci		cpcap_charger_set_cable_path(ddata, false);
47862306a36Sopenharmony_ci		cpcap_charger_set_inductive_path(ddata, false);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci		error = cpcap_charger_disable(ddata);
48162306a36Sopenharmony_ci		if (error)
48262306a36Sopenharmony_ci			goto out_err;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci		cpcap_charger_update_state(ddata,
48562306a36Sopenharmony_ci					   POWER_SUPPLY_STATUS_DISCHARGING);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci		error = regmap_update_bits(ddata->reg, CPCAP_REG_VUSBC,
48862306a36Sopenharmony_ci					   CPCAP_BIT_VBUS_SWITCH,
48962306a36Sopenharmony_ci					   CPCAP_BIT_VBUS_SWITCH);
49062306a36Sopenharmony_ci		if (error)
49162306a36Sopenharmony_ci			goto out_err;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci		error = regmap_update_bits(ddata->reg, CPCAP_REG_CRM,
49462306a36Sopenharmony_ci					   CPCAP_REG_CRM_RVRSMODE,
49562306a36Sopenharmony_ci					   CPCAP_REG_CRM_RVRSMODE);
49662306a36Sopenharmony_ci		if (error)
49762306a36Sopenharmony_ci			goto out_err;
49862306a36Sopenharmony_ci	} else {
49962306a36Sopenharmony_ci		error = regmap_update_bits(ddata->reg, CPCAP_REG_VUSBC,
50062306a36Sopenharmony_ci					   CPCAP_BIT_VBUS_SWITCH, 0);
50162306a36Sopenharmony_ci		if (error)
50262306a36Sopenharmony_ci			goto out_err;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci		error = regmap_update_bits(ddata->reg, CPCAP_REG_CRM,
50562306a36Sopenharmony_ci					   CPCAP_REG_CRM_RVRSMODE, 0);
50662306a36Sopenharmony_ci		if (error)
50762306a36Sopenharmony_ci			goto out_err;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci		cpcap_charger_set_cable_path(ddata, true);
51062306a36Sopenharmony_ci		cpcap_charger_set_inductive_path(ddata, true);
51162306a36Sopenharmony_ci		ddata->feeding_vbus = false;
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	return;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ciout_err:
51762306a36Sopenharmony_ci	cpcap_charger_update_state(ddata, POWER_SUPPLY_STATUS_UNKNOWN);
51862306a36Sopenharmony_ci	dev_err(ddata->dev, "%s could not %s vbus: %i\n", __func__,
51962306a36Sopenharmony_ci		ddata->vbus_enabled ? "enable" : "disable", error);
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_cistatic int cpcap_charger_set_vbus(struct phy_companion *comparator,
52362306a36Sopenharmony_ci				  bool enabled)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	struct cpcap_charger_ddata *ddata =
52662306a36Sopenharmony_ci		container_of(comparator, struct cpcap_charger_ddata,
52762306a36Sopenharmony_ci			     comparator);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	ddata->vbus_enabled = enabled;
53062306a36Sopenharmony_ci	schedule_delayed_work(&ddata->vbus_work, 0);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	return 0;
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci/* Charger interrupt handling functions */
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_cistatic int cpcap_charger_get_ints_state(struct cpcap_charger_ddata *ddata,
53862306a36Sopenharmony_ci					struct cpcap_charger_ints_state *s)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	int val, error;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	error = regmap_read(ddata->reg, CPCAP_REG_INTS1, &val);
54362306a36Sopenharmony_ci	if (error)
54462306a36Sopenharmony_ci		return error;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	s->chrg_det = val & BIT(13);
54762306a36Sopenharmony_ci	s->rvrs_chrg = val & BIT(12);
54862306a36Sopenharmony_ci	s->vbusov = val & BIT(11);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	error = regmap_read(ddata->reg, CPCAP_REG_INTS2, &val);
55162306a36Sopenharmony_ci	if (error)
55262306a36Sopenharmony_ci		return error;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	s->chrg_se1b = val & BIT(13);
55562306a36Sopenharmony_ci	s->rvrs_mode = val & BIT(6);
55662306a36Sopenharmony_ci	s->chrgcurr2 = val & BIT(5);
55762306a36Sopenharmony_ci	s->chrgcurr1 = val & BIT(4);
55862306a36Sopenharmony_ci	s->vbusvld = val & BIT(3);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	error = regmap_read(ddata->reg, CPCAP_REG_INTS4, &val);
56162306a36Sopenharmony_ci	if (error)
56262306a36Sopenharmony_ci		return error;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	s->battdetb = val & BIT(6);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	return 0;
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_cistatic int cpcap_charger_voltage_to_regval(int voltage)
57062306a36Sopenharmony_ci{
57162306a36Sopenharmony_ci	int offset;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	switch (voltage) {
57462306a36Sopenharmony_ci	case 0 ... 4100000 - 1:
57562306a36Sopenharmony_ci		return 0;
57662306a36Sopenharmony_ci	case 4100000 ... 4200000 - 1:
57762306a36Sopenharmony_ci		offset = 1;
57862306a36Sopenharmony_ci		break;
57962306a36Sopenharmony_ci	case 4200000 ... 4300000 - 1:
58062306a36Sopenharmony_ci		offset = 0;
58162306a36Sopenharmony_ci		break;
58262306a36Sopenharmony_ci	case 4300000 ... 4380000 - 1:
58362306a36Sopenharmony_ci		offset = -1;
58462306a36Sopenharmony_ci		break;
58562306a36Sopenharmony_ci	case 4380000 ... 4440000:
58662306a36Sopenharmony_ci		offset = -2;
58762306a36Sopenharmony_ci		break;
58862306a36Sopenharmony_ci	default:
58962306a36Sopenharmony_ci		return 0;
59062306a36Sopenharmony_ci	}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	return ((voltage - 4100000) / 20000) + offset;
59362306a36Sopenharmony_ci}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_cistatic void cpcap_charger_disconnect(struct cpcap_charger_ddata *ddata,
59662306a36Sopenharmony_ci				     int state, unsigned long delay)
59762306a36Sopenharmony_ci{
59862306a36Sopenharmony_ci	int error;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	/* Update battery state before disconnecting the charger */
60162306a36Sopenharmony_ci	switch (state) {
60262306a36Sopenharmony_ci	case POWER_SUPPLY_STATUS_DISCHARGING:
60362306a36Sopenharmony_ci	case POWER_SUPPLY_STATUS_FULL:
60462306a36Sopenharmony_ci		power_supply_changed(ddata->usb);
60562306a36Sopenharmony_ci		break;
60662306a36Sopenharmony_ci	default:
60762306a36Sopenharmony_ci		break;
60862306a36Sopenharmony_ci	}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	error = cpcap_charger_disable(ddata);
61162306a36Sopenharmony_ci	if (error) {
61262306a36Sopenharmony_ci		cpcap_charger_update_state(ddata, POWER_SUPPLY_STATUS_UNKNOWN);
61362306a36Sopenharmony_ci		return;
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	cpcap_charger_update_state(ddata, state);
61762306a36Sopenharmony_ci	power_supply_changed(ddata->usb);
61862306a36Sopenharmony_ci	schedule_delayed_work(&ddata->detect_work, delay);
61962306a36Sopenharmony_ci}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_cistatic void cpcap_usb_detect(struct work_struct *work)
62262306a36Sopenharmony_ci{
62362306a36Sopenharmony_ci	struct cpcap_charger_ddata *ddata;
62462306a36Sopenharmony_ci	struct cpcap_charger_ints_state s;
62562306a36Sopenharmony_ci	int error, new_state;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	ddata = container_of(work, struct cpcap_charger_ddata,
62862306a36Sopenharmony_ci			     detect_work.work);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	error = cpcap_charger_get_ints_state(ddata, &s);
63162306a36Sopenharmony_ci	if (error)
63262306a36Sopenharmony_ci		return;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	/* Just init the state if a charger is connected with no chrg_det set */
63562306a36Sopenharmony_ci	if (!s.chrg_det && s.chrgcurr1 && s.vbusvld) {
63662306a36Sopenharmony_ci		cpcap_charger_update_state(ddata,
63762306a36Sopenharmony_ci					   POWER_SUPPLY_STATUS_NOT_CHARGING);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci		return;
64062306a36Sopenharmony_ci	}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	/*
64362306a36Sopenharmony_ci	 * If battery voltage is higher than charge voltage, it may have been
64462306a36Sopenharmony_ci	 * charged to 4.35V by Android. Try again in 10 minutes.
64562306a36Sopenharmony_ci	 */
64662306a36Sopenharmony_ci	if (cpcap_charger_get_charge_voltage(ddata) > ddata->voltage) {
64762306a36Sopenharmony_ci		cpcap_charger_disconnect(ddata,
64862306a36Sopenharmony_ci					 POWER_SUPPLY_STATUS_NOT_CHARGING,
64962306a36Sopenharmony_ci					 HZ * 60 * 10);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci		return;
65262306a36Sopenharmony_ci	}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	/* Delay for 80ms to avoid vbus bouncing when usb cable is plugged in */
65562306a36Sopenharmony_ci	usleep_range(80000, 120000);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	/* Throttle chrgcurr2 interrupt for charger done and retry */
65862306a36Sopenharmony_ci	switch (ddata->status) {
65962306a36Sopenharmony_ci	case POWER_SUPPLY_STATUS_CHARGING:
66062306a36Sopenharmony_ci		if (s.chrgcurr2)
66162306a36Sopenharmony_ci			break;
66262306a36Sopenharmony_ci		new_state = POWER_SUPPLY_STATUS_FULL;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci		if (s.chrgcurr1 && s.vbusvld) {
66562306a36Sopenharmony_ci			cpcap_charger_disconnect(ddata, new_state, HZ * 5);
66662306a36Sopenharmony_ci			return;
66762306a36Sopenharmony_ci		}
66862306a36Sopenharmony_ci		break;
66962306a36Sopenharmony_ci	case POWER_SUPPLY_STATUS_FULL:
67062306a36Sopenharmony_ci		if (!s.chrgcurr2)
67162306a36Sopenharmony_ci			break;
67262306a36Sopenharmony_ci		if (s.vbusvld)
67362306a36Sopenharmony_ci			new_state = POWER_SUPPLY_STATUS_NOT_CHARGING;
67462306a36Sopenharmony_ci		else
67562306a36Sopenharmony_ci			new_state = POWER_SUPPLY_STATUS_DISCHARGING;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci		cpcap_charger_disconnect(ddata, new_state, HZ * 5);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci		return;
68062306a36Sopenharmony_ci	default:
68162306a36Sopenharmony_ci		break;
68262306a36Sopenharmony_ci	}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	if (!ddata->feeding_vbus && cpcap_charger_vbus_valid(ddata) &&
68562306a36Sopenharmony_ci	    s.chrgcurr1) {
68662306a36Sopenharmony_ci		int max_current;
68762306a36Sopenharmony_ci		int vchrg, ichrg;
68862306a36Sopenharmony_ci		union power_supply_propval val;
68962306a36Sopenharmony_ci		struct power_supply *battery;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci		battery = power_supply_get_by_name("battery");
69262306a36Sopenharmony_ci		if (IS_ERR_OR_NULL(battery)) {
69362306a36Sopenharmony_ci			dev_err(ddata->dev, "battery power_supply not available %li\n",
69462306a36Sopenharmony_ci					PTR_ERR(battery));
69562306a36Sopenharmony_ci			return;
69662306a36Sopenharmony_ci		}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci		error = power_supply_get_property(battery, POWER_SUPPLY_PROP_PRESENT, &val);
69962306a36Sopenharmony_ci		power_supply_put(battery);
70062306a36Sopenharmony_ci		if (error)
70162306a36Sopenharmony_ci			goto out_err;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci		if (val.intval) {
70462306a36Sopenharmony_ci			max_current = 1596000;
70562306a36Sopenharmony_ci		} else {
70662306a36Sopenharmony_ci			dev_info(ddata->dev, "battery not inserted, charging disabled\n");
70762306a36Sopenharmony_ci			max_current = 0;
70862306a36Sopenharmony_ci		}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci		if (max_current > ddata->limit_current)
71162306a36Sopenharmony_ci			max_current = ddata->limit_current;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci		ichrg = cpcap_charger_current_to_regval(max_current);
71462306a36Sopenharmony_ci		vchrg = cpcap_charger_voltage_to_regval(ddata->voltage);
71562306a36Sopenharmony_ci		error = cpcap_charger_enable(ddata,
71662306a36Sopenharmony_ci					     CPCAP_REG_CRM_VCHRG(vchrg),
71762306a36Sopenharmony_ci					     ichrg, 0);
71862306a36Sopenharmony_ci		if (error)
71962306a36Sopenharmony_ci			goto out_err;
72062306a36Sopenharmony_ci		cpcap_charger_update_state(ddata,
72162306a36Sopenharmony_ci					   POWER_SUPPLY_STATUS_CHARGING);
72262306a36Sopenharmony_ci	} else {
72362306a36Sopenharmony_ci		error = cpcap_charger_disable(ddata);
72462306a36Sopenharmony_ci		if (error)
72562306a36Sopenharmony_ci			goto out_err;
72662306a36Sopenharmony_ci		cpcap_charger_update_state(ddata,
72762306a36Sopenharmony_ci					   POWER_SUPPLY_STATUS_DISCHARGING);
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	power_supply_changed(ddata->usb);
73162306a36Sopenharmony_ci	return;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ciout_err:
73462306a36Sopenharmony_ci	cpcap_charger_update_state(ddata, POWER_SUPPLY_STATUS_UNKNOWN);
73562306a36Sopenharmony_ci	dev_err(ddata->dev, "%s failed with %i\n", __func__, error);
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_cistatic irqreturn_t cpcap_charger_irq_thread(int irq, void *data)
73962306a36Sopenharmony_ci{
74062306a36Sopenharmony_ci	struct cpcap_charger_ddata *ddata = data;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	if (!atomic_read(&ddata->active))
74362306a36Sopenharmony_ci		return IRQ_NONE;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	schedule_delayed_work(&ddata->detect_work, 0);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	return IRQ_HANDLED;
74862306a36Sopenharmony_ci}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_cistatic int cpcap_usb_init_irq(struct platform_device *pdev,
75162306a36Sopenharmony_ci			      struct cpcap_charger_ddata *ddata,
75262306a36Sopenharmony_ci			      const char *name)
75362306a36Sopenharmony_ci{
75462306a36Sopenharmony_ci	struct cpcap_interrupt_desc *d;
75562306a36Sopenharmony_ci	int irq, error;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	irq = platform_get_irq_byname(pdev, name);
75862306a36Sopenharmony_ci	if (irq < 0)
75962306a36Sopenharmony_ci		return -ENODEV;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	error = devm_request_threaded_irq(ddata->dev, irq, NULL,
76262306a36Sopenharmony_ci					  cpcap_charger_irq_thread,
76362306a36Sopenharmony_ci					  IRQF_SHARED | IRQF_ONESHOT,
76462306a36Sopenharmony_ci					  name, ddata);
76562306a36Sopenharmony_ci	if (error) {
76662306a36Sopenharmony_ci		dev_err(ddata->dev, "could not get irq %s: %i\n",
76762306a36Sopenharmony_ci			name, error);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci		return error;
77062306a36Sopenharmony_ci	}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	d = devm_kzalloc(ddata->dev, sizeof(*d), GFP_KERNEL);
77362306a36Sopenharmony_ci	if (!d)
77462306a36Sopenharmony_ci		return -ENOMEM;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	d->name = name;
77762306a36Sopenharmony_ci	d->irq = irq;
77862306a36Sopenharmony_ci	list_add(&d->node, &ddata->irq_list);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	return 0;
78162306a36Sopenharmony_ci}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_cistatic const char * const cpcap_charger_irqs[] = {
78462306a36Sopenharmony_ci	/* REG_INT_0 */
78562306a36Sopenharmony_ci	"chrg_det", "rvrs_chrg",
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	/* REG_INT1 */
78862306a36Sopenharmony_ci	"chrg_se1b", "se0conn", "rvrs_mode", "chrgcurr2", "chrgcurr1", "vbusvld",
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	/* REG_INT_3 */
79162306a36Sopenharmony_ci	"battdetb",
79262306a36Sopenharmony_ci};
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_cistatic int cpcap_usb_init_interrupts(struct platform_device *pdev,
79562306a36Sopenharmony_ci				     struct cpcap_charger_ddata *ddata)
79662306a36Sopenharmony_ci{
79762306a36Sopenharmony_ci	int i, error;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(cpcap_charger_irqs); i++) {
80062306a36Sopenharmony_ci		error = cpcap_usb_init_irq(pdev, ddata, cpcap_charger_irqs[i]);
80162306a36Sopenharmony_ci		if (error)
80262306a36Sopenharmony_ci			return error;
80362306a36Sopenharmony_ci	}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	return 0;
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_cistatic void cpcap_charger_init_optional_gpios(struct cpcap_charger_ddata *ddata)
80962306a36Sopenharmony_ci{
81062306a36Sopenharmony_ci	int i;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	for (i = 0; i < 2; i++) {
81362306a36Sopenharmony_ci		ddata->gpio[i] = devm_gpiod_get_index(ddata->dev, "mode",
81462306a36Sopenharmony_ci						      i, GPIOD_OUT_HIGH);
81562306a36Sopenharmony_ci		if (IS_ERR(ddata->gpio[i])) {
81662306a36Sopenharmony_ci			dev_info(ddata->dev, "no mode change GPIO%i: %li\n",
81762306a36Sopenharmony_ci				 i, PTR_ERR(ddata->gpio[i]));
81862306a36Sopenharmony_ci			ddata->gpio[i] = NULL;
81962306a36Sopenharmony_ci		}
82062306a36Sopenharmony_ci	}
82162306a36Sopenharmony_ci}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_cistatic int cpcap_charger_init_iio(struct cpcap_charger_ddata *ddata)
82462306a36Sopenharmony_ci{
82562306a36Sopenharmony_ci	const char * const names[CPCAP_CHARGER_IIO_NR] = {
82662306a36Sopenharmony_ci		"battdetb", "battp", "vbus", "chg_isense", "batti",
82762306a36Sopenharmony_ci	};
82862306a36Sopenharmony_ci	int error, i;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	for (i = 0; i < CPCAP_CHARGER_IIO_NR; i++) {
83162306a36Sopenharmony_ci		ddata->channels[i] = devm_iio_channel_get(ddata->dev,
83262306a36Sopenharmony_ci							  names[i]);
83362306a36Sopenharmony_ci		if (IS_ERR(ddata->channels[i])) {
83462306a36Sopenharmony_ci			error = PTR_ERR(ddata->channels[i]);
83562306a36Sopenharmony_ci			goto out_err;
83662306a36Sopenharmony_ci		}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci		if (!ddata->channels[i]->indio_dev) {
83962306a36Sopenharmony_ci			error = -ENXIO;
84062306a36Sopenharmony_ci			goto out_err;
84162306a36Sopenharmony_ci		}
84262306a36Sopenharmony_ci	}
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	return 0;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ciout_err:
84762306a36Sopenharmony_ci	if (error != -EPROBE_DEFER)
84862306a36Sopenharmony_ci		dev_err(ddata->dev, "could not initialize VBUS or ID IIO: %i\n",
84962306a36Sopenharmony_ci			error);
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	return error;
85262306a36Sopenharmony_ci}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_cistatic char *cpcap_charger_supplied_to[] = {
85562306a36Sopenharmony_ci	"battery",
85662306a36Sopenharmony_ci};
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_cistatic const struct power_supply_desc cpcap_charger_usb_desc = {
85962306a36Sopenharmony_ci	.name		= "usb",
86062306a36Sopenharmony_ci	.type		= POWER_SUPPLY_TYPE_USB,
86162306a36Sopenharmony_ci	.properties	= cpcap_charger_props,
86262306a36Sopenharmony_ci	.num_properties	= ARRAY_SIZE(cpcap_charger_props),
86362306a36Sopenharmony_ci	.get_property	= cpcap_charger_get_property,
86462306a36Sopenharmony_ci	.set_property	= cpcap_charger_set_property,
86562306a36Sopenharmony_ci	.property_is_writeable = cpcap_charger_property_is_writeable,
86662306a36Sopenharmony_ci};
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci#ifdef CONFIG_OF
86962306a36Sopenharmony_cistatic const struct of_device_id cpcap_charger_id_table[] = {
87062306a36Sopenharmony_ci	{
87162306a36Sopenharmony_ci		.compatible = "motorola,mapphone-cpcap-charger",
87262306a36Sopenharmony_ci	},
87362306a36Sopenharmony_ci	{},
87462306a36Sopenharmony_ci};
87562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, cpcap_charger_id_table);
87662306a36Sopenharmony_ci#endif
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_cistatic int cpcap_charger_probe(struct platform_device *pdev)
87962306a36Sopenharmony_ci{
88062306a36Sopenharmony_ci	struct cpcap_charger_ddata *ddata;
88162306a36Sopenharmony_ci	const struct of_device_id *of_id;
88262306a36Sopenharmony_ci	struct power_supply_config psy_cfg = {};
88362306a36Sopenharmony_ci	int error;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	of_id = of_match_device(of_match_ptr(cpcap_charger_id_table),
88662306a36Sopenharmony_ci				&pdev->dev);
88762306a36Sopenharmony_ci	if (!of_id)
88862306a36Sopenharmony_ci		return -EINVAL;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
89162306a36Sopenharmony_ci	if (!ddata)
89262306a36Sopenharmony_ci		return -ENOMEM;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	ddata->dev = &pdev->dev;
89562306a36Sopenharmony_ci	ddata->voltage = 4200000;
89662306a36Sopenharmony_ci	ddata->limit_current = 532000;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	ddata->reg = dev_get_regmap(ddata->dev->parent, NULL);
89962306a36Sopenharmony_ci	if (!ddata->reg)
90062306a36Sopenharmony_ci		return -ENODEV;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	INIT_LIST_HEAD(&ddata->irq_list);
90362306a36Sopenharmony_ci	INIT_DELAYED_WORK(&ddata->detect_work, cpcap_usb_detect);
90462306a36Sopenharmony_ci	INIT_DELAYED_WORK(&ddata->vbus_work, cpcap_charger_vbus_work);
90562306a36Sopenharmony_ci	platform_set_drvdata(pdev, ddata);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	error = cpcap_charger_init_iio(ddata);
90862306a36Sopenharmony_ci	if (error)
90962306a36Sopenharmony_ci		return error;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	atomic_set(&ddata->active, 1);
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	psy_cfg.of_node = pdev->dev.of_node;
91462306a36Sopenharmony_ci	psy_cfg.drv_data = ddata;
91562306a36Sopenharmony_ci	psy_cfg.supplied_to = cpcap_charger_supplied_to;
91662306a36Sopenharmony_ci	psy_cfg.num_supplicants = ARRAY_SIZE(cpcap_charger_supplied_to),
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	ddata->usb = devm_power_supply_register(ddata->dev,
91962306a36Sopenharmony_ci						&cpcap_charger_usb_desc,
92062306a36Sopenharmony_ci						&psy_cfg);
92162306a36Sopenharmony_ci	if (IS_ERR(ddata->usb)) {
92262306a36Sopenharmony_ci		error = PTR_ERR(ddata->usb);
92362306a36Sopenharmony_ci		dev_err(ddata->dev, "failed to register USB charger: %i\n",
92462306a36Sopenharmony_ci			error);
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci		return error;
92762306a36Sopenharmony_ci	}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	error = cpcap_usb_init_interrupts(pdev, ddata);
93062306a36Sopenharmony_ci	if (error)
93162306a36Sopenharmony_ci		return error;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	ddata->comparator.set_vbus = cpcap_charger_set_vbus;
93462306a36Sopenharmony_ci	error = omap_usb2_set_comparator(&ddata->comparator);
93562306a36Sopenharmony_ci	if (error == -ENODEV) {
93662306a36Sopenharmony_ci		dev_info(ddata->dev, "charger needs phy, deferring probe\n");
93762306a36Sopenharmony_ci		return -EPROBE_DEFER;
93862306a36Sopenharmony_ci	}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	cpcap_charger_init_optional_gpios(ddata);
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	schedule_delayed_work(&ddata->detect_work, 0);
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	return 0;
94562306a36Sopenharmony_ci}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_cistatic void cpcap_charger_shutdown(struct platform_device *pdev)
94862306a36Sopenharmony_ci{
94962306a36Sopenharmony_ci	struct cpcap_charger_ddata *ddata = platform_get_drvdata(pdev);
95062306a36Sopenharmony_ci	int error;
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	atomic_set(&ddata->active, 0);
95362306a36Sopenharmony_ci	error = omap_usb2_set_comparator(NULL);
95462306a36Sopenharmony_ci	if (error)
95562306a36Sopenharmony_ci		dev_warn(ddata->dev, "could not clear USB comparator: %i\n",
95662306a36Sopenharmony_ci			 error);
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	error = cpcap_charger_disable(ddata);
95962306a36Sopenharmony_ci	if (error) {
96062306a36Sopenharmony_ci		cpcap_charger_update_state(ddata, POWER_SUPPLY_STATUS_UNKNOWN);
96162306a36Sopenharmony_ci		dev_warn(ddata->dev, "could not clear charger: %i\n",
96262306a36Sopenharmony_ci			 error);
96362306a36Sopenharmony_ci	}
96462306a36Sopenharmony_ci	cpcap_charger_update_state(ddata, POWER_SUPPLY_STATUS_DISCHARGING);
96562306a36Sopenharmony_ci	cancel_delayed_work_sync(&ddata->vbus_work);
96662306a36Sopenharmony_ci	cancel_delayed_work_sync(&ddata->detect_work);
96762306a36Sopenharmony_ci}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_cistatic int cpcap_charger_remove(struct platform_device *pdev)
97062306a36Sopenharmony_ci{
97162306a36Sopenharmony_ci	cpcap_charger_shutdown(pdev);
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	return 0;
97462306a36Sopenharmony_ci}
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_cistatic struct platform_driver cpcap_charger_driver = {
97762306a36Sopenharmony_ci	.probe = cpcap_charger_probe,
97862306a36Sopenharmony_ci	.driver	= {
97962306a36Sopenharmony_ci		.name	= "cpcap-charger",
98062306a36Sopenharmony_ci		.of_match_table = of_match_ptr(cpcap_charger_id_table),
98162306a36Sopenharmony_ci	},
98262306a36Sopenharmony_ci	.shutdown = cpcap_charger_shutdown,
98362306a36Sopenharmony_ci	.remove	= cpcap_charger_remove,
98462306a36Sopenharmony_ci};
98562306a36Sopenharmony_cimodule_platform_driver(cpcap_charger_driver);
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ciMODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
98862306a36Sopenharmony_ciMODULE_DESCRIPTION("CPCAP Battery Charger Interface driver");
98962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
99062306a36Sopenharmony_ciMODULE_ALIAS("platform:cpcap-charger");
991