xref: /linux/drivers/power/supply/cpcap-charger.c (revision 07b43820437bd96f31f5d7f9baf4453fcb7dedbf)
11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
20c9888e3STony Lindgren /*
30c9888e3STony Lindgren  * Motorola CPCAP PMIC battery charger driver
40c9888e3STony Lindgren  *
50c9888e3STony Lindgren  * Copyright (C) 2017 Tony Lindgren <tony@atomide.com>
60c9888e3STony Lindgren  *
70c9888e3STony Lindgren  * Rewritten for Linux power framework with some parts based on
8da7dc6a7Swangjianli  * earlier driver found in the Motorola Linux kernel:
90c9888e3STony Lindgren  *
100c9888e3STony Lindgren  * Copyright (C) 2009-2010 Motorola, Inc.
110c9888e3STony Lindgren  */
120c9888e3STony Lindgren 
130c9888e3STony Lindgren #include <linux/atomic.h>
140c9888e3STony Lindgren #include <linux/init.h>
150c9888e3STony Lindgren #include <linux/module.h>
160c9888e3STony Lindgren #include <linux/slab.h>
172d678e3eSKrzysztof Kozlowski #include <linux/string_choices.h>
180c9888e3STony Lindgren #include <linux/err.h>
190c9888e3STony Lindgren #include <linux/interrupt.h>
200c9888e3STony Lindgren #include <linux/notifier.h>
21b3c3a197SRob Herring #include <linux/mod_devicetable.h>
220c9888e3STony Lindgren #include <linux/platform_device.h>
230c9888e3STony Lindgren #include <linux/power_supply.h>
240c9888e3STony Lindgren #include <linux/regmap.h>
250c9888e3STony Lindgren 
260c9888e3STony Lindgren #include <linux/gpio/consumer.h>
270c9888e3STony Lindgren #include <linux/usb/phy_companion.h>
280c9888e3STony Lindgren #include <linux/phy/omap_usb.h>
290c9888e3STony Lindgren #include <linux/usb/otg.h>
300c9888e3STony Lindgren #include <linux/iio/consumer.h>
310c9888e3STony Lindgren #include <linux/mfd/motorola-cpcap.h>
320c9888e3STony Lindgren 
331e45330bSTony Lindgren /*
341e45330bSTony Lindgren  * CPCAP_REG_CRM register bits. For documentation of somewhat similar hardware,
351e45330bSTony Lindgren  * see NXP "MC13783 Power Management and Audio Circuit Users's Guide"
361e45330bSTony Lindgren  * MC13783UG.pdf chapter "8.5 Battery Interface Register Summary". The registers
371e45330bSTony Lindgren  * and values for CPCAP are different, but some of the internal components seem
381e45330bSTony Lindgren  * similar. Also see the Motorola Linux kernel cpcap-regbits.h. CPCAP_REG_CHRGR_1
391e45330bSTony Lindgren  * bits that seem to describe the CRM register.
401e45330bSTony Lindgren  */
410c9888e3STony Lindgren #define CPCAP_REG_CRM_UNUSED_641_15	BIT(15)	/* 641 = register number */
420c9888e3STony Lindgren #define CPCAP_REG_CRM_UNUSED_641_14	BIT(14)	/* 641 = register number */
431e45330bSTony Lindgren #define CPCAP_REG_CRM_CHRG_LED_EN	BIT(13)	/* Charger LED */
441e45330bSTony Lindgren #define CPCAP_REG_CRM_RVRSMODE		BIT(12)	/* USB VBUS output enable */
451e45330bSTony Lindgren #define CPCAP_REG_CRM_ICHRG_TR1		BIT(11)	/* Trickle charge current */
460c9888e3STony Lindgren #define CPCAP_REG_CRM_ICHRG_TR0		BIT(10)
471e45330bSTony Lindgren #define CPCAP_REG_CRM_FET_OVRD		BIT(9)	/* 0 = hardware, 1 = FET_CTRL */
481e45330bSTony Lindgren #define CPCAP_REG_CRM_FET_CTRL		BIT(8)	/* BPFET 1 if FET_OVRD set */
491e45330bSTony Lindgren #define CPCAP_REG_CRM_VCHRG3		BIT(7)	/* Charge voltage bits */
500c9888e3STony Lindgren #define CPCAP_REG_CRM_VCHRG2		BIT(6)
510c9888e3STony Lindgren #define CPCAP_REG_CRM_VCHRG1		BIT(5)
520c9888e3STony Lindgren #define CPCAP_REG_CRM_VCHRG0		BIT(4)
531e45330bSTony Lindgren #define CPCAP_REG_CRM_ICHRG3		BIT(3)	/* Charge current bits */
540c9888e3STony Lindgren #define CPCAP_REG_CRM_ICHRG2		BIT(2)
550c9888e3STony Lindgren #define CPCAP_REG_CRM_ICHRG1		BIT(1)
560c9888e3STony Lindgren #define CPCAP_REG_CRM_ICHRG0		BIT(0)
570c9888e3STony Lindgren 
580c9888e3STony Lindgren /* CPCAP_REG_CRM trickle charge voltages */
590c9888e3STony Lindgren #define CPCAP_REG_CRM_TR(val)		(((val) & 0x3) << 10)
600c9888e3STony Lindgren #define CPCAP_REG_CRM_TR_0A00		CPCAP_REG_CRM_TR(0x0)
610c9888e3STony Lindgren #define CPCAP_REG_CRM_TR_0A24		CPCAP_REG_CRM_TR(0x1)
620c9888e3STony Lindgren #define CPCAP_REG_CRM_TR_0A48		CPCAP_REG_CRM_TR(0x2)
630c9888e3STony Lindgren #define CPCAP_REG_CRM_TR_0A72		CPCAP_REG_CRM_TR(0x4)
640c9888e3STony Lindgren 
6570c9fc9aSTony Lindgren /*
6670c9fc9aSTony Lindgren  * CPCAP_REG_CRM charge voltages based on the ADC channel 1 values.
6770c9fc9aSTony Lindgren  * Note that these register bits don't match MC13783UG.pdf VCHRG
6870c9fc9aSTony Lindgren  * register bits.
6970c9fc9aSTony Lindgren  */
700c9888e3STony Lindgren #define CPCAP_REG_CRM_VCHRG(val)	(((val) & 0xf) << 4)
710c9888e3STony Lindgren #define CPCAP_REG_CRM_VCHRG_3V80	CPCAP_REG_CRM_VCHRG(0x0)
720c9888e3STony Lindgren #define CPCAP_REG_CRM_VCHRG_4V10	CPCAP_REG_CRM_VCHRG(0x1)
7370c9fc9aSTony Lindgren #define CPCAP_REG_CRM_VCHRG_4V12	CPCAP_REG_CRM_VCHRG(0x2)
7470c9fc9aSTony Lindgren #define CPCAP_REG_CRM_VCHRG_4V15	CPCAP_REG_CRM_VCHRG(0x3)
7570c9fc9aSTony Lindgren #define CPCAP_REG_CRM_VCHRG_4V17	CPCAP_REG_CRM_VCHRG(0x4)
7670c9fc9aSTony Lindgren #define CPCAP_REG_CRM_VCHRG_4V20	CPCAP_REG_CRM_VCHRG(0x5)
7770c9fc9aSTony Lindgren #define CPCAP_REG_CRM_VCHRG_4V23	CPCAP_REG_CRM_VCHRG(0x6)
7870c9fc9aSTony Lindgren #define CPCAP_REG_CRM_VCHRG_4V25	CPCAP_REG_CRM_VCHRG(0x7)
7970c9fc9aSTony Lindgren #define CPCAP_REG_CRM_VCHRG_4V27	CPCAP_REG_CRM_VCHRG(0x8)
8070c9fc9aSTony Lindgren #define CPCAP_REG_CRM_VCHRG_4V30	CPCAP_REG_CRM_VCHRG(0x9)
8170c9fc9aSTony Lindgren #define CPCAP_REG_CRM_VCHRG_4V33	CPCAP_REG_CRM_VCHRG(0xa)
823ae5f066STony Lindgren #define CPCAP_REG_CRM_VCHRG_4V35	CPCAP_REG_CRM_VCHRG(0xb)
830c9888e3STony Lindgren #define CPCAP_REG_CRM_VCHRG_4V38	CPCAP_REG_CRM_VCHRG(0xc)
840c9888e3STony Lindgren #define CPCAP_REG_CRM_VCHRG_4V40	CPCAP_REG_CRM_VCHRG(0xd)
850c9888e3STony Lindgren #define CPCAP_REG_CRM_VCHRG_4V42	CPCAP_REG_CRM_VCHRG(0xe)
860c9888e3STony Lindgren #define CPCAP_REG_CRM_VCHRG_4V44	CPCAP_REG_CRM_VCHRG(0xf)
870c9888e3STony Lindgren 
881e45330bSTony Lindgren /*
891e45330bSTony Lindgren  * CPCAP_REG_CRM charge currents. These seem to match MC13783UG.pdf
901e45330bSTony Lindgren  * values in "Table 8-3. Charge Path Regulator Current Limit
911e45330bSTony Lindgren  * Characteristics" for the nominal values.
92c6fdea96SPavel Machek  *
93c6fdea96SPavel Machek  * Except 70mA and 1.596A and unlimited, these are simply 88.7mA / step.
941e45330bSTony Lindgren  */
950c9888e3STony Lindgren #define CPCAP_REG_CRM_ICHRG(val)	(((val) & 0xf) << 0)
960c9888e3STony Lindgren #define CPCAP_REG_CRM_ICHRG_0A000	CPCAP_REG_CRM_ICHRG(0x0)
970c9888e3STony Lindgren #define CPCAP_REG_CRM_ICHRG_0A070	CPCAP_REG_CRM_ICHRG(0x1)
981e45330bSTony Lindgren #define CPCAP_REG_CRM_ICHRG_0A177	CPCAP_REG_CRM_ICHRG(0x2)
991e45330bSTony Lindgren #define CPCAP_REG_CRM_ICHRG_0A266	CPCAP_REG_CRM_ICHRG(0x3)
1001e45330bSTony Lindgren #define CPCAP_REG_CRM_ICHRG_0A355	CPCAP_REG_CRM_ICHRG(0x4)
1011e45330bSTony Lindgren #define CPCAP_REG_CRM_ICHRG_0A443	CPCAP_REG_CRM_ICHRG(0x5)
1021e45330bSTony Lindgren #define CPCAP_REG_CRM_ICHRG_0A532	CPCAP_REG_CRM_ICHRG(0x6)
1031e45330bSTony Lindgren #define CPCAP_REG_CRM_ICHRG_0A621	CPCAP_REG_CRM_ICHRG(0x7)
1041e45330bSTony Lindgren #define CPCAP_REG_CRM_ICHRG_0A709	CPCAP_REG_CRM_ICHRG(0x8)
1051e45330bSTony Lindgren #define CPCAP_REG_CRM_ICHRG_0A798	CPCAP_REG_CRM_ICHRG(0x9)
1061e45330bSTony Lindgren #define CPCAP_REG_CRM_ICHRG_0A886	CPCAP_REG_CRM_ICHRG(0xa)
1071e45330bSTony Lindgren #define CPCAP_REG_CRM_ICHRG_0A975	CPCAP_REG_CRM_ICHRG(0xb)
1081e45330bSTony Lindgren #define CPCAP_REG_CRM_ICHRG_1A064	CPCAP_REG_CRM_ICHRG(0xc)
1091e45330bSTony Lindgren #define CPCAP_REG_CRM_ICHRG_1A152	CPCAP_REG_CRM_ICHRG(0xd)
1101e45330bSTony Lindgren #define CPCAP_REG_CRM_ICHRG_1A596	CPCAP_REG_CRM_ICHRG(0xe)
1110c9888e3STony Lindgren #define CPCAP_REG_CRM_ICHRG_NO_LIMIT	CPCAP_REG_CRM_ICHRG(0xf)
1120c9888e3STony Lindgren 
1137f737861STony Lindgren /* CPCAP_REG_VUSBC register bits needed for VBUS */
1147f737861STony Lindgren #define CPCAP_BIT_VBUS_SWITCH		BIT(0)	/* VBUS boost to 5V */
1157f737861STony Lindgren 
1160c9888e3STony Lindgren enum {
1170c9888e3STony Lindgren 	CPCAP_CHARGER_IIO_BATTDET,
1180c9888e3STony Lindgren 	CPCAP_CHARGER_IIO_VOLTAGE,
1190c9888e3STony Lindgren 	CPCAP_CHARGER_IIO_VBUS,
1200c9888e3STony Lindgren 	CPCAP_CHARGER_IIO_CHRG_CURRENT,
1210c9888e3STony Lindgren 	CPCAP_CHARGER_IIO_BATT_CURRENT,
1220c9888e3STony Lindgren 	CPCAP_CHARGER_IIO_NR,
1230c9888e3STony Lindgren };
1240c9888e3STony Lindgren 
1250c9888e3STony Lindgren struct cpcap_charger_ddata {
1260c9888e3STony Lindgren 	struct device *dev;
1270c9888e3STony Lindgren 	struct regmap *reg;
1280c9888e3STony Lindgren 	struct list_head irq_list;
1290c9888e3STony Lindgren 	struct delayed_work detect_work;
1300c9888e3STony Lindgren 	struct delayed_work vbus_work;
1310c9888e3STony Lindgren 	struct gpio_desc *gpio[2];		/* gpio_reven0 & 1 */
1320c9888e3STony Lindgren 
1330c9888e3STony Lindgren 	struct iio_channel *channels[CPCAP_CHARGER_IIO_NR];
1340c9888e3STony Lindgren 
1350c9888e3STony Lindgren 	struct power_supply *usb;
1360c9888e3STony Lindgren 
1370c9888e3STony Lindgren 	struct phy_companion comparator;	/* For USB VBUS */
1387f737861STony Lindgren 	unsigned int vbus_enabled:1;
1397f737861STony Lindgren 	unsigned int feeding_vbus:1;
1400c9888e3STony Lindgren 	atomic_t active;
1410c9888e3STony Lindgren 
1420c9888e3STony Lindgren 	int status;
143d4ee021cSTony Lindgren 	int voltage;
144c6fdea96SPavel Machek 	int limit_current;
1450c9888e3STony Lindgren };
1460c9888e3STony Lindgren 
1470c9888e3STony Lindgren struct cpcap_interrupt_desc {
1480c9888e3STony Lindgren 	int irq;
1490c9888e3STony Lindgren 	struct list_head node;
1500c9888e3STony Lindgren 	const char *name;
1510c9888e3STony Lindgren };
1520c9888e3STony Lindgren 
1530c9888e3STony Lindgren struct cpcap_charger_ints_state {
1540c9888e3STony Lindgren 	bool chrg_det;
1550c9888e3STony Lindgren 	bool rvrs_chrg;
1560c9888e3STony Lindgren 	bool vbusov;
1570c9888e3STony Lindgren 
1580c9888e3STony Lindgren 	bool chrg_se1b;
1590c9888e3STony Lindgren 	bool rvrs_mode;
160d4ee021cSTony Lindgren 	bool chrgcurr2;
1610c9888e3STony Lindgren 	bool chrgcurr1;
1620c9888e3STony Lindgren 	bool vbusvld;
1630c9888e3STony Lindgren 
1640c9888e3STony Lindgren 	bool battdetb;
1650c9888e3STony Lindgren };
1660c9888e3STony Lindgren 
1670c9888e3STony Lindgren static enum power_supply_property cpcap_charger_props[] = {
1680c9888e3STony Lindgren 	POWER_SUPPLY_PROP_STATUS,
1690c9888e3STony Lindgren 	POWER_SUPPLY_PROP_ONLINE,
1705688ea04STony Lindgren 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
171c6fdea96SPavel Machek 	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
1720c9888e3STony Lindgren 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
1730c9888e3STony Lindgren 	POWER_SUPPLY_PROP_CURRENT_NOW,
1740c9888e3STony Lindgren };
1750c9888e3STony Lindgren 
cpcap_charger_get_charge_voltage(struct cpcap_charger_ddata * ddata)1760c9888e3STony Lindgren static int cpcap_charger_get_charge_voltage(struct cpcap_charger_ddata *ddata)
1770c9888e3STony Lindgren {
1780c9888e3STony Lindgren 	struct iio_channel *channel;
1790c9888e3STony Lindgren 	int error, value = 0;
1800c9888e3STony Lindgren 
1810c9888e3STony Lindgren 	channel = ddata->channels[CPCAP_CHARGER_IIO_VOLTAGE];
1820c9888e3STony Lindgren 	error = iio_read_channel_processed(channel, &value);
1830c9888e3STony Lindgren 	if (error < 0) {
1840c9888e3STony Lindgren 		dev_warn(ddata->dev, "%s failed: %i\n", __func__, error);
1850c9888e3STony Lindgren 
1860c9888e3STony Lindgren 		return 0;
1870c9888e3STony Lindgren 	}
1880c9888e3STony Lindgren 
1890c9888e3STony Lindgren 	return value;
1900c9888e3STony Lindgren }
1910c9888e3STony Lindgren 
cpcap_charger_get_charge_current(struct cpcap_charger_ddata * ddata)1920c9888e3STony Lindgren static int cpcap_charger_get_charge_current(struct cpcap_charger_ddata *ddata)
1930c9888e3STony Lindgren {
1940c9888e3STony Lindgren 	struct iio_channel *channel;
1950c9888e3STony Lindgren 	int error, value = 0;
1960c9888e3STony Lindgren 
1970c9888e3STony Lindgren 	channel = ddata->channels[CPCAP_CHARGER_IIO_CHRG_CURRENT];
1980c9888e3STony Lindgren 	error = iio_read_channel_processed(channel, &value);
1990c9888e3STony Lindgren 	if (error < 0) {
2000c9888e3STony Lindgren 		dev_warn(ddata->dev, "%s failed: %i\n", __func__, error);
2010c9888e3STony Lindgren 
2020c9888e3STony Lindgren 		return 0;
2030c9888e3STony Lindgren 	}
2040c9888e3STony Lindgren 
2050c9888e3STony Lindgren 	return value;
2060c9888e3STony Lindgren }
2070c9888e3STony Lindgren 
cpcap_charger_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)2080c9888e3STony Lindgren static int cpcap_charger_get_property(struct power_supply *psy,
2090c9888e3STony Lindgren 				      enum power_supply_property psp,
2100c9888e3STony Lindgren 				      union power_supply_propval *val)
2110c9888e3STony Lindgren {
2120c9888e3STony Lindgren 	struct cpcap_charger_ddata *ddata = dev_get_drvdata(psy->dev.parent);
2130c9888e3STony Lindgren 
2140c9888e3STony Lindgren 	switch (psp) {
2150c9888e3STony Lindgren 	case POWER_SUPPLY_PROP_STATUS:
2160c9888e3STony Lindgren 		val->intval = ddata->status;
2170c9888e3STony Lindgren 		break;
218c6fdea96SPavel Machek 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
219c6fdea96SPavel Machek 		val->intval = ddata->limit_current;
220c6fdea96SPavel Machek 		break;
2215688ea04STony Lindgren 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
2225688ea04STony Lindgren 		val->intval = ddata->voltage;
2235688ea04STony Lindgren 		break;
2240c9888e3STony Lindgren 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
2250c9888e3STony Lindgren 		if (ddata->status == POWER_SUPPLY_STATUS_CHARGING)
2260c9888e3STony Lindgren 			val->intval = cpcap_charger_get_charge_voltage(ddata) *
2270c9888e3STony Lindgren 				1000;
2280c9888e3STony Lindgren 		else
2290c9888e3STony Lindgren 			val->intval = 0;
2300c9888e3STony Lindgren 		break;
2310c9888e3STony Lindgren 	case POWER_SUPPLY_PROP_CURRENT_NOW:
2320c9888e3STony Lindgren 		if (ddata->status == POWER_SUPPLY_STATUS_CHARGING)
2330c9888e3STony Lindgren 			val->intval = cpcap_charger_get_charge_current(ddata) *
2340c9888e3STony Lindgren 				1000;
2350c9888e3STony Lindgren 		else
2360c9888e3STony Lindgren 			val->intval = 0;
2370c9888e3STony Lindgren 		break;
2380c9888e3STony Lindgren 	case POWER_SUPPLY_PROP_ONLINE:
2390c9888e3STony Lindgren 		val->intval = ddata->status == POWER_SUPPLY_STATUS_CHARGING;
2400c9888e3STony Lindgren 		break;
2410c9888e3STony Lindgren 	default:
2420c9888e3STony Lindgren 		return -EINVAL;
2430c9888e3STony Lindgren 	}
2440c9888e3STony Lindgren 
2450c9888e3STony Lindgren 	return 0;
2460c9888e3STony Lindgren }
2470c9888e3STony Lindgren 
cpcap_charger_match_voltage(int voltage)2485688ea04STony Lindgren static int cpcap_charger_match_voltage(int voltage)
2495688ea04STony Lindgren {
2505688ea04STony Lindgren 	switch (voltage) {
2515688ea04STony Lindgren 	case 0 ... 4100000 - 1: return 3800000;
2525688ea04STony Lindgren 	case 4100000 ... 4120000 - 1: return 4100000;
2535688ea04STony Lindgren 	case 4120000 ... 4150000 - 1: return 4120000;
2545688ea04STony Lindgren 	case 4150000 ... 4170000 - 1: return 4150000;
2555688ea04STony Lindgren 	case 4170000 ... 4200000 - 1: return 4170000;
2565688ea04STony Lindgren 	case 4200000 ... 4230000 - 1: return 4200000;
2575688ea04STony Lindgren 	case 4230000 ... 4250000 - 1: return 4230000;
2585688ea04STony Lindgren 	case 4250000 ... 4270000 - 1: return 4250000;
2595688ea04STony Lindgren 	case 4270000 ... 4300000 - 1: return 4270000;
2605688ea04STony Lindgren 	case 4300000 ... 4330000 - 1: return 4300000;
2615688ea04STony Lindgren 	case 4330000 ... 4350000 - 1: return 4330000;
2625688ea04STony Lindgren 	case 4350000 ... 4380000 - 1: return 4350000;
2635688ea04STony Lindgren 	case 4380000 ... 4400000 - 1: return 4380000;
2645688ea04STony Lindgren 	case 4400000 ... 4420000 - 1: return 4400000;
2655688ea04STony Lindgren 	case 4420000 ... 4440000 - 1: return 4420000;
2665688ea04STony Lindgren 	case 4440000: return 4440000;
2675688ea04STony Lindgren 	default: return 0;
2685688ea04STony Lindgren 	}
2695688ea04STony Lindgren }
2705688ea04STony Lindgren 
2715688ea04STony Lindgren static int
cpcap_charger_get_bat_const_charge_voltage(struct cpcap_charger_ddata * ddata)2725688ea04STony Lindgren cpcap_charger_get_bat_const_charge_voltage(struct cpcap_charger_ddata *ddata)
2735688ea04STony Lindgren {
2745688ea04STony Lindgren 	union power_supply_propval prop;
2755688ea04STony Lindgren 	struct power_supply *battery;
2765688ea04STony Lindgren 	int voltage = ddata->voltage;
2775688ea04STony Lindgren 	int error;
2785688ea04STony Lindgren 
2795688ea04STony Lindgren 	battery = power_supply_get_by_name("battery");
2805688ea04STony Lindgren 	if (battery) {
2815688ea04STony Lindgren 		error = power_supply_get_property(battery,
2825688ea04STony Lindgren 				POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
2835688ea04STony Lindgren 				&prop);
2845688ea04STony Lindgren 		if (!error)
2855688ea04STony Lindgren 			voltage = prop.intval;
28639196cfeSColin Ian King 
2874bff91bbSTony Lindgren 		power_supply_put(battery);
28839196cfeSColin Ian King 	}
2895688ea04STony Lindgren 
2905688ea04STony Lindgren 	return voltage;
2915688ea04STony Lindgren }
2925688ea04STony Lindgren 
cpcap_charger_current_to_regval(int microamp)293c6fdea96SPavel Machek static int cpcap_charger_current_to_regval(int microamp)
294c6fdea96SPavel Machek {
295c6fdea96SPavel Machek 	int miliamp = microamp / 1000;
296c6fdea96SPavel Machek 	int res;
297c6fdea96SPavel Machek 
298c6fdea96SPavel Machek 	if (miliamp < 0)
299c6fdea96SPavel Machek 		return -EINVAL;
300c6fdea96SPavel Machek 	if (miliamp < 70)
301c6fdea96SPavel Machek 		return CPCAP_REG_CRM_ICHRG(0x0);
302c6fdea96SPavel Machek 	if (miliamp < 177)
303c6fdea96SPavel Machek 		return CPCAP_REG_CRM_ICHRG(0x1);
3048a5a0cc1SCarl Philipp Klemm 	if (miliamp >= 1596)
305c6fdea96SPavel Machek 		return CPCAP_REG_CRM_ICHRG(0xe);
306c6fdea96SPavel Machek 
307c6fdea96SPavel Machek 	res = microamp / 88666;
308c6fdea96SPavel Machek 	if (res > 0xd)
309c6fdea96SPavel Machek 		res = 0xd;
310c6fdea96SPavel Machek 	return CPCAP_REG_CRM_ICHRG(res);
311c6fdea96SPavel Machek }
312c6fdea96SPavel Machek 
cpcap_charger_set_property(struct power_supply * psy,enum power_supply_property psp,const union power_supply_propval * val)3135688ea04STony Lindgren static int cpcap_charger_set_property(struct power_supply *psy,
3145688ea04STony Lindgren 				      enum power_supply_property psp,
3155688ea04STony Lindgren 				      const union power_supply_propval *val)
3165688ea04STony Lindgren {
3175688ea04STony Lindgren 	struct cpcap_charger_ddata *ddata = dev_get_drvdata(psy->dev.parent);
3185688ea04STony Lindgren 	int voltage, batvolt;
3195688ea04STony Lindgren 
3205688ea04STony Lindgren 	switch (psp) {
321c6fdea96SPavel Machek 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
322c6fdea96SPavel Machek 		if (cpcap_charger_current_to_regval(val->intval) < 0)
323c6fdea96SPavel Machek 			return -EINVAL;
324c6fdea96SPavel Machek 		ddata->limit_current = val->intval;
325c6fdea96SPavel Machek 		schedule_delayed_work(&ddata->detect_work, 0);
326c6fdea96SPavel Machek 		break;
3275688ea04STony Lindgren 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
3285688ea04STony Lindgren 		voltage = cpcap_charger_match_voltage(val->intval);
3295688ea04STony Lindgren 		batvolt = cpcap_charger_get_bat_const_charge_voltage(ddata);
3305688ea04STony Lindgren 		if (voltage > batvolt)
3315688ea04STony Lindgren 			voltage = batvolt;
3325688ea04STony Lindgren 		ddata->voltage = voltage;
3335688ea04STony Lindgren 		schedule_delayed_work(&ddata->detect_work, 0);
3345688ea04STony Lindgren 		break;
3355688ea04STony Lindgren 	default:
3365688ea04STony Lindgren 		return -EINVAL;
3375688ea04STony Lindgren 	}
3385688ea04STony Lindgren 
3395688ea04STony Lindgren 	return 0;
3405688ea04STony Lindgren }
3415688ea04STony Lindgren 
cpcap_charger_property_is_writeable(struct power_supply * psy,enum power_supply_property psp)3425688ea04STony Lindgren static int cpcap_charger_property_is_writeable(struct power_supply *psy,
3435688ea04STony Lindgren 					       enum power_supply_property psp)
3445688ea04STony Lindgren {
3455688ea04STony Lindgren 	switch (psp) {
346c6fdea96SPavel Machek 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
3475688ea04STony Lindgren 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
3485688ea04STony Lindgren 		return 1;
3495688ea04STony Lindgren 	default:
3505688ea04STony Lindgren 		return 0;
3515688ea04STony Lindgren 	}
3525688ea04STony Lindgren }
3535688ea04STony Lindgren 
cpcap_charger_set_cable_path(struct cpcap_charger_ddata * ddata,bool enabled)3540c9888e3STony Lindgren static void cpcap_charger_set_cable_path(struct cpcap_charger_ddata *ddata,
3550c9888e3STony Lindgren 					 bool enabled)
3560c9888e3STony Lindgren {
3570c9888e3STony Lindgren 	if (!ddata->gpio[0])
3580c9888e3STony Lindgren 		return;
3590c9888e3STony Lindgren 
3600c9888e3STony Lindgren 	gpiod_set_value(ddata->gpio[0], enabled);
3610c9888e3STony Lindgren }
3620c9888e3STony Lindgren 
cpcap_charger_set_inductive_path(struct cpcap_charger_ddata * ddata,bool enabled)3630c9888e3STony Lindgren static void cpcap_charger_set_inductive_path(struct cpcap_charger_ddata *ddata,
3640c9888e3STony Lindgren 					     bool enabled)
3650c9888e3STony Lindgren {
3660c9888e3STony Lindgren 	if (!ddata->gpio[1])
3670c9888e3STony Lindgren 		return;
3680c9888e3STony Lindgren 
3690c9888e3STony Lindgren 	gpiod_set_value(ddata->gpio[1], enabled);
3700c9888e3STony Lindgren }
3710c9888e3STony Lindgren 
cpcap_charger_update_state(struct cpcap_charger_ddata * ddata,int state)3725a214892STony Lindgren static void cpcap_charger_update_state(struct cpcap_charger_ddata *ddata,
3735a214892STony Lindgren 				       int state)
3745a214892STony Lindgren {
3755a214892STony Lindgren 	const char *status;
3765a214892STony Lindgren 
3775a214892STony Lindgren 	if (state > POWER_SUPPLY_STATUS_FULL) {
3785a214892STony Lindgren 		dev_warn(ddata->dev, "unknown state: %i\n", state);
3795a214892STony Lindgren 
3805a214892STony Lindgren 		return;
3815a214892STony Lindgren 	}
3825a214892STony Lindgren 
3835a214892STony Lindgren 	ddata->status = state;
3845a214892STony Lindgren 
3855a214892STony Lindgren 	switch (state) {
3865a214892STony Lindgren 	case POWER_SUPPLY_STATUS_DISCHARGING:
3875a214892STony Lindgren 		status = "DISCONNECTED";
3885a214892STony Lindgren 		break;
3895a214892STony Lindgren 	case POWER_SUPPLY_STATUS_NOT_CHARGING:
3905a214892STony Lindgren 		status = "DETECTING";
3915a214892STony Lindgren 		break;
3925a214892STony Lindgren 	case POWER_SUPPLY_STATUS_CHARGING:
3935a214892STony Lindgren 		status = "CHARGING";
3945a214892STony Lindgren 		break;
3955a214892STony Lindgren 	case POWER_SUPPLY_STATUS_FULL:
3965a214892STony Lindgren 		status = "DONE";
3975a214892STony Lindgren 		break;
3985a214892STony Lindgren 	default:
3995a214892STony Lindgren 		return;
4005a214892STony Lindgren 	}
4015a214892STony Lindgren 
4025a214892STony Lindgren 	dev_dbg(ddata->dev, "state: %s\n", status);
4035a214892STony Lindgren }
4045a214892STony Lindgren 
cpcap_charger_disable(struct cpcap_charger_ddata * ddata)4056ddcec58STony Lindgren static int cpcap_charger_disable(struct cpcap_charger_ddata *ddata)
4066ddcec58STony Lindgren {
4076ddcec58STony Lindgren 	int error;
4086ddcec58STony Lindgren 
4096ddcec58STony Lindgren 	error = regmap_update_bits(ddata->reg, CPCAP_REG_CRM, 0x3fff,
4106ddcec58STony Lindgren 				   CPCAP_REG_CRM_FET_OVRD |
4116ddcec58STony Lindgren 				   CPCAP_REG_CRM_FET_CTRL);
4126ddcec58STony Lindgren 	if (error)
4136ddcec58STony Lindgren 		dev_err(ddata->dev, "%s failed with %i\n", __func__, error);
4146ddcec58STony Lindgren 
4156ddcec58STony Lindgren 	return error;
4166ddcec58STony Lindgren }
4176ddcec58STony Lindgren 
cpcap_charger_enable(struct cpcap_charger_ddata * ddata,int max_voltage,int charge_current,int trickle_current)4186ddcec58STony Lindgren static int cpcap_charger_enable(struct cpcap_charger_ddata *ddata,
4190c9888e3STony Lindgren 				int max_voltage, int charge_current,
4200c9888e3STony Lindgren 				int trickle_current)
4210c9888e3STony Lindgren {
4220c9888e3STony Lindgren 	int error;
4230c9888e3STony Lindgren 
4246ddcec58STony Lindgren 	if (!max_voltage || !charge_current)
4256ddcec58STony Lindgren 		return -EINVAL;
4260c9888e3STony Lindgren 
4276ddcec58STony Lindgren 	dev_dbg(ddata->dev, "enable: %i %i %i\n",
4286ddcec58STony Lindgren 		max_voltage, charge_current, trickle_current);
4290c9888e3STony Lindgren 
4300c9888e3STony Lindgren 	error = regmap_update_bits(ddata->reg, CPCAP_REG_CRM, 0x3fff,
4310c9888e3STony Lindgren 				   CPCAP_REG_CRM_CHRG_LED_EN |
4320c9888e3STony Lindgren 				   trickle_current |
4330c9888e3STony Lindgren 				   CPCAP_REG_CRM_FET_OVRD |
4340c9888e3STony Lindgren 				   CPCAP_REG_CRM_FET_CTRL |
4350c9888e3STony Lindgren 				   max_voltage |
4360c9888e3STony Lindgren 				   charge_current);
4376ddcec58STony Lindgren 	if (error)
4380c9888e3STony Lindgren 		dev_err(ddata->dev, "%s failed with %i\n", __func__, error);
4390c9888e3STony Lindgren 
4400c9888e3STony Lindgren 	return error;
4410c9888e3STony Lindgren }
4420c9888e3STony Lindgren 
cpcap_charger_vbus_valid(struct cpcap_charger_ddata * ddata)4430c9888e3STony Lindgren static bool cpcap_charger_vbus_valid(struct cpcap_charger_ddata *ddata)
4440c9888e3STony Lindgren {
4450c9888e3STony Lindgren 	int error, value = 0;
4460c9888e3STony Lindgren 	struct iio_channel *channel =
4470c9888e3STony Lindgren 		ddata->channels[CPCAP_CHARGER_IIO_VBUS];
4480c9888e3STony Lindgren 
4490c9888e3STony Lindgren 	error = iio_read_channel_processed(channel, &value);
4500c9888e3STony Lindgren 	if (error >= 0)
451816aacd5SYang Li 		return value > 3900;
4520c9888e3STony Lindgren 
4530c9888e3STony Lindgren 	dev_err(ddata->dev, "error reading VBUS: %i\n", error);
4540c9888e3STony Lindgren 
4550c9888e3STony Lindgren 	return false;
4560c9888e3STony Lindgren }
4570c9888e3STony Lindgren 
4580c9888e3STony Lindgren /* VBUS control functions for the USB PHY companion */
cpcap_charger_vbus_work(struct work_struct * work)4590c9888e3STony Lindgren static void cpcap_charger_vbus_work(struct work_struct *work)
4600c9888e3STony Lindgren {
4610c9888e3STony Lindgren 	struct cpcap_charger_ddata *ddata;
4620c9888e3STony Lindgren 	bool vbus = false;
4630c9888e3STony Lindgren 	int error;
4640c9888e3STony Lindgren 
4650c9888e3STony Lindgren 	ddata = container_of(work, struct cpcap_charger_ddata,
4660c9888e3STony Lindgren 			     vbus_work.work);
4670c9888e3STony Lindgren 
4680c9888e3STony Lindgren 	if (ddata->vbus_enabled) {
4690c9888e3STony Lindgren 		vbus = cpcap_charger_vbus_valid(ddata);
4700c9888e3STony Lindgren 		if (vbus) {
471e015964aSTony Lindgren 			dev_dbg(ddata->dev, "VBUS already provided\n");
4720c9888e3STony Lindgren 
4730c9888e3STony Lindgren 			return;
4740c9888e3STony Lindgren 		}
4750c9888e3STony Lindgren 
4767f737861STony Lindgren 		ddata->feeding_vbus = true;
4770c9888e3STony Lindgren 		cpcap_charger_set_cable_path(ddata, false);
4780c9888e3STony Lindgren 		cpcap_charger_set_inductive_path(ddata, false);
4790c9888e3STony Lindgren 
4806ddcec58STony Lindgren 		error = cpcap_charger_disable(ddata);
4810c9888e3STony Lindgren 		if (error)
4820c9888e3STony Lindgren 			goto out_err;
4830c9888e3STony Lindgren 
4846ddcec58STony Lindgren 		cpcap_charger_update_state(ddata,
4856ddcec58STony Lindgren 					   POWER_SUPPLY_STATUS_DISCHARGING);
4866ddcec58STony Lindgren 
4877f737861STony Lindgren 		error = regmap_update_bits(ddata->reg, CPCAP_REG_VUSBC,
4887f737861STony Lindgren 					   CPCAP_BIT_VBUS_SWITCH,
4897f737861STony Lindgren 					   CPCAP_BIT_VBUS_SWITCH);
4907f737861STony Lindgren 		if (error)
4917f737861STony Lindgren 			goto out_err;
4927f737861STony Lindgren 
4930c9888e3STony Lindgren 		error = regmap_update_bits(ddata->reg, CPCAP_REG_CRM,
4940c9888e3STony Lindgren 					   CPCAP_REG_CRM_RVRSMODE,
4950c9888e3STony Lindgren 					   CPCAP_REG_CRM_RVRSMODE);
4960c9888e3STony Lindgren 		if (error)
4970c9888e3STony Lindgren 			goto out_err;
4980c9888e3STony Lindgren 	} else {
4997f737861STony Lindgren 		error = regmap_update_bits(ddata->reg, CPCAP_REG_VUSBC,
5007f737861STony Lindgren 					   CPCAP_BIT_VBUS_SWITCH, 0);
5017f737861STony Lindgren 		if (error)
5027f737861STony Lindgren 			goto out_err;
5037f737861STony Lindgren 
5040c9888e3STony Lindgren 		error = regmap_update_bits(ddata->reg, CPCAP_REG_CRM,
5050c9888e3STony Lindgren 					   CPCAP_REG_CRM_RVRSMODE, 0);
5060c9888e3STony Lindgren 		if (error)
5070c9888e3STony Lindgren 			goto out_err;
5080c9888e3STony Lindgren 
5090c9888e3STony Lindgren 		cpcap_charger_set_cable_path(ddata, true);
5100c9888e3STony Lindgren 		cpcap_charger_set_inductive_path(ddata, true);
5117f737861STony Lindgren 		ddata->feeding_vbus = false;
5120c9888e3STony Lindgren 	}
5130c9888e3STony Lindgren 
5140c9888e3STony Lindgren 	return;
5150c9888e3STony Lindgren 
5160c9888e3STony Lindgren out_err:
5176ddcec58STony Lindgren 	cpcap_charger_update_state(ddata, POWER_SUPPLY_STATUS_UNKNOWN);
5180c9888e3STony Lindgren 	dev_err(ddata->dev, "%s could not %s vbus: %i\n", __func__,
5192d678e3eSKrzysztof Kozlowski 		str_enable_disable(ddata->vbus_enabled), error);
5200c9888e3STony Lindgren }
5210c9888e3STony Lindgren 
cpcap_charger_set_vbus(struct phy_companion * comparator,bool enabled)5220c9888e3STony Lindgren static int cpcap_charger_set_vbus(struct phy_companion *comparator,
5230c9888e3STony Lindgren 				  bool enabled)
5240c9888e3STony Lindgren {
5250c9888e3STony Lindgren 	struct cpcap_charger_ddata *ddata =
5260c9888e3STony Lindgren 		container_of(comparator, struct cpcap_charger_ddata,
5270c9888e3STony Lindgren 			     comparator);
5280c9888e3STony Lindgren 
5290c9888e3STony Lindgren 	ddata->vbus_enabled = enabled;
5300c9888e3STony Lindgren 	schedule_delayed_work(&ddata->vbus_work, 0);
5310c9888e3STony Lindgren 
5320c9888e3STony Lindgren 	return 0;
5330c9888e3STony Lindgren }
5340c9888e3STony Lindgren 
5350c9888e3STony Lindgren /* Charger interrupt handling functions */
5360c9888e3STony Lindgren 
cpcap_charger_get_ints_state(struct cpcap_charger_ddata * ddata,struct cpcap_charger_ints_state * s)5370c9888e3STony Lindgren static int cpcap_charger_get_ints_state(struct cpcap_charger_ddata *ddata,
5380c9888e3STony Lindgren 					struct cpcap_charger_ints_state *s)
5390c9888e3STony Lindgren {
5400c9888e3STony Lindgren 	int val, error;
5410c9888e3STony Lindgren 
5420c9888e3STony Lindgren 	error = regmap_read(ddata->reg, CPCAP_REG_INTS1, &val);
5430c9888e3STony Lindgren 	if (error)
5440c9888e3STony Lindgren 		return error;
5450c9888e3STony Lindgren 
5460c9888e3STony Lindgren 	s->chrg_det = val & BIT(13);
5470c9888e3STony Lindgren 	s->rvrs_chrg = val & BIT(12);
5480c9888e3STony Lindgren 	s->vbusov = val & BIT(11);
5490c9888e3STony Lindgren 
5500c9888e3STony Lindgren 	error = regmap_read(ddata->reg, CPCAP_REG_INTS2, &val);
5510c9888e3STony Lindgren 	if (error)
5520c9888e3STony Lindgren 		return error;
5530c9888e3STony Lindgren 
5540c9888e3STony Lindgren 	s->chrg_se1b = val & BIT(13);
5550c9888e3STony Lindgren 	s->rvrs_mode = val & BIT(6);
556d4ee021cSTony Lindgren 	s->chrgcurr2 = val & BIT(5);
5570c9888e3STony Lindgren 	s->chrgcurr1 = val & BIT(4);
5580c9888e3STony Lindgren 	s->vbusvld = val & BIT(3);
5590c9888e3STony Lindgren 
5600c9888e3STony Lindgren 	error = regmap_read(ddata->reg, CPCAP_REG_INTS4, &val);
5610c9888e3STony Lindgren 	if (error)
5620c9888e3STony Lindgren 		return error;
5630c9888e3STony Lindgren 
5640c9888e3STony Lindgren 	s->battdetb = val & BIT(6);
5650c9888e3STony Lindgren 
5660c9888e3STony Lindgren 	return 0;
5670c9888e3STony Lindgren }
5680c9888e3STony Lindgren 
cpcap_charger_voltage_to_regval(int voltage)569e3da2ce0Skbuild test robot static int cpcap_charger_voltage_to_regval(int voltage)
570d4ee021cSTony Lindgren {
571d4ee021cSTony Lindgren 	int offset;
572d4ee021cSTony Lindgren 
573d4ee021cSTony Lindgren 	switch (voltage) {
574d4ee021cSTony Lindgren 	case 0 ... 4100000 - 1:
575d4ee021cSTony Lindgren 		return 0;
576d4ee021cSTony Lindgren 	case 4100000 ... 4200000 - 1:
577d4ee021cSTony Lindgren 		offset = 1;
578d4ee021cSTony Lindgren 		break;
579d4ee021cSTony Lindgren 	case 4200000 ... 4300000 - 1:
580d4ee021cSTony Lindgren 		offset = 0;
581d4ee021cSTony Lindgren 		break;
582d4ee021cSTony Lindgren 	case 4300000 ... 4380000 - 1:
583d4ee021cSTony Lindgren 		offset = -1;
584d4ee021cSTony Lindgren 		break;
585d4ee021cSTony Lindgren 	case 4380000 ... 4440000:
586d4ee021cSTony Lindgren 		offset = -2;
587d4ee021cSTony Lindgren 		break;
588d4ee021cSTony Lindgren 	default:
589d4ee021cSTony Lindgren 		return 0;
590d4ee021cSTony Lindgren 	}
591d4ee021cSTony Lindgren 
592d4ee021cSTony Lindgren 	return ((voltage - 4100000) / 20000) + offset;
593d4ee021cSTony Lindgren }
594d4ee021cSTony Lindgren 
cpcap_charger_disconnect(struct cpcap_charger_ddata * ddata,int state,unsigned long delay)595d4ee021cSTony Lindgren static void cpcap_charger_disconnect(struct cpcap_charger_ddata *ddata,
596d4ee021cSTony Lindgren 				     int state, unsigned long delay)
597d4ee021cSTony Lindgren {
598d4ee021cSTony Lindgren 	int error;
599d4ee021cSTony Lindgren 
6002071236bSTony Lindgren 	/* Update battery state before disconnecting the charger */
6012071236bSTony Lindgren 	switch (state) {
6022071236bSTony Lindgren 	case POWER_SUPPLY_STATUS_DISCHARGING:
6032071236bSTony Lindgren 	case POWER_SUPPLY_STATUS_FULL:
6042071236bSTony Lindgren 		power_supply_changed(ddata->usb);
6052071236bSTony Lindgren 		break;
6062071236bSTony Lindgren 	default:
6072071236bSTony Lindgren 		break;
6082071236bSTony Lindgren 	}
6092071236bSTony Lindgren 
6106ddcec58STony Lindgren 	error = cpcap_charger_disable(ddata);
6116ddcec58STony Lindgren 	if (error) {
6126ddcec58STony Lindgren 		cpcap_charger_update_state(ddata, POWER_SUPPLY_STATUS_UNKNOWN);
613d4ee021cSTony Lindgren 		return;
6146ddcec58STony Lindgren 	}
615d4ee021cSTony Lindgren 
616d4ee021cSTony Lindgren 	cpcap_charger_update_state(ddata, state);
617d4ee021cSTony Lindgren 	power_supply_changed(ddata->usb);
618d4ee021cSTony Lindgren 	schedule_delayed_work(&ddata->detect_work, delay);
619d4ee021cSTony Lindgren }
620d4ee021cSTony Lindgren 
cpcap_usb_detect(struct work_struct * work)6210c9888e3STony Lindgren static void cpcap_usb_detect(struct work_struct *work)
6220c9888e3STony Lindgren {
6230c9888e3STony Lindgren 	struct cpcap_charger_ddata *ddata;
6240c9888e3STony Lindgren 	struct cpcap_charger_ints_state s;
6252071236bSTony Lindgren 	int error, new_state;
6260c9888e3STony Lindgren 
6270c9888e3STony Lindgren 	ddata = container_of(work, struct cpcap_charger_ddata,
6280c9888e3STony Lindgren 			     detect_work.work);
6290c9888e3STony Lindgren 
6300c9888e3STony Lindgren 	error = cpcap_charger_get_ints_state(ddata, &s);
6310c9888e3STony Lindgren 	if (error)
6320c9888e3STony Lindgren 		return;
6330c9888e3STony Lindgren 
634d4ee021cSTony Lindgren 	/* Just init the state if a charger is connected with no chrg_det set */
635d4ee021cSTony Lindgren 	if (!s.chrg_det && s.chrgcurr1 && s.vbusvld) {
63641ac23f5STony Lindgren 		cpcap_charger_update_state(ddata,
63741ac23f5STony Lindgren 					   POWER_SUPPLY_STATUS_NOT_CHARGING);
638d4ee021cSTony Lindgren 
639d4ee021cSTony Lindgren 		return;
640d4ee021cSTony Lindgren 	}
641d4ee021cSTony Lindgren 
642d4ee021cSTony Lindgren 	/*
643d4ee021cSTony Lindgren 	 * If battery voltage is higher than charge voltage, it may have been
644d4ee021cSTony Lindgren 	 * charged to 4.35V by Android. Try again in 10 minutes.
645d4ee021cSTony Lindgren 	 */
646d4ee021cSTony Lindgren 	if (cpcap_charger_get_charge_voltage(ddata) > ddata->voltage) {
64741ac23f5STony Lindgren 		cpcap_charger_disconnect(ddata,
64841ac23f5STony Lindgren 					 POWER_SUPPLY_STATUS_NOT_CHARGING,
649d4ee021cSTony Lindgren 					 HZ * 60 * 10);
650d4ee021cSTony Lindgren 
651d4ee021cSTony Lindgren 		return;
652d4ee021cSTony Lindgren 	}
653d4ee021cSTony Lindgren 
654751faedfSCarl Philipp Klemm 	/* Delay for 80ms to avoid vbus bouncing when usb cable is plugged in */
655751faedfSCarl Philipp Klemm 	usleep_range(80000, 120000);
656751faedfSCarl Philipp Klemm 
657d4ee021cSTony Lindgren 	/* Throttle chrgcurr2 interrupt for charger done and retry */
6585a214892STony Lindgren 	switch (ddata->status) {
65941ac23f5STony Lindgren 	case POWER_SUPPLY_STATUS_CHARGING:
660d4ee021cSTony Lindgren 		if (s.chrgcurr2)
661d4ee021cSTony Lindgren 			break;
6622071236bSTony Lindgren 		new_state = POWER_SUPPLY_STATUS_FULL;
6632071236bSTony Lindgren 
664d4ee021cSTony Lindgren 		if (s.chrgcurr1 && s.vbusvld) {
6652071236bSTony Lindgren 			cpcap_charger_disconnect(ddata, new_state, HZ * 5);
666d4ee021cSTony Lindgren 			return;
667d4ee021cSTony Lindgren 		}
668d4ee021cSTony Lindgren 		break;
66941ac23f5STony Lindgren 	case POWER_SUPPLY_STATUS_FULL:
670d4ee021cSTony Lindgren 		if (!s.chrgcurr2)
671d4ee021cSTony Lindgren 			break;
6722071236bSTony Lindgren 		if (s.vbusvld)
6732071236bSTony Lindgren 			new_state = POWER_SUPPLY_STATUS_NOT_CHARGING;
6742071236bSTony Lindgren 		else
6752071236bSTony Lindgren 			new_state = POWER_SUPPLY_STATUS_DISCHARGING;
6762071236bSTony Lindgren 
6772071236bSTony Lindgren 		cpcap_charger_disconnect(ddata, new_state, HZ * 5);
6782071236bSTony Lindgren 
679d4ee021cSTony Lindgren 		return;
680d4ee021cSTony Lindgren 	default:
681d4ee021cSTony Lindgren 		break;
682d4ee021cSTony Lindgren 	}
683d4ee021cSTony Lindgren 
6847f737861STony Lindgren 	if (!ddata->feeding_vbus && cpcap_charger_vbus_valid(ddata) &&
6857f737861STony Lindgren 	    s.chrgcurr1) {
686eab4e6d9SCarl Philipp Klemm 		int max_current;
687c6fdea96SPavel Machek 		int vchrg, ichrg;
688eab4e6d9SCarl Philipp Klemm 		union power_supply_propval val;
689eab4e6d9SCarl Philipp Klemm 		struct power_supply *battery;
6900c9888e3STony Lindgren 
691eab4e6d9SCarl Philipp Klemm 		battery = power_supply_get_by_name("battery");
692*d9fa3aaeSCharles Han 		if (!battery) {
693*d9fa3aaeSCharles Han 			dev_err(ddata->dev, "battery power_supply not available\n");
694eab4e6d9SCarl Philipp Klemm 			return;
695eab4e6d9SCarl Philipp Klemm 		}
696eab4e6d9SCarl Philipp Klemm 
697eab4e6d9SCarl Philipp Klemm 		error = power_supply_get_property(battery, POWER_SUPPLY_PROP_PRESENT, &val);
698eab4e6d9SCarl Philipp Klemm 		power_supply_put(battery);
699eab4e6d9SCarl Philipp Klemm 		if (error)
700eab4e6d9SCarl Philipp Klemm 			goto out_err;
701eab4e6d9SCarl Philipp Klemm 
702eab4e6d9SCarl Philipp Klemm 		if (val.intval) {
703c6fdea96SPavel Machek 			max_current = 1596000;
704eab4e6d9SCarl Philipp Klemm 		} else {
705eab4e6d9SCarl Philipp Klemm 			dev_info(ddata->dev, "battery not inserted, charging disabled\n");
706eab4e6d9SCarl Philipp Klemm 			max_current = 0;
707eab4e6d9SCarl Philipp Klemm 		}
7080c9888e3STony Lindgren 
709c6fdea96SPavel Machek 		if (max_current > ddata->limit_current)
710c6fdea96SPavel Machek 			max_current = ddata->limit_current;
711c6fdea96SPavel Machek 
712c6fdea96SPavel Machek 		ichrg = cpcap_charger_current_to_regval(max_current);
713d4ee021cSTony Lindgren 		vchrg = cpcap_charger_voltage_to_regval(ddata->voltage);
7146ddcec58STony Lindgren 		error = cpcap_charger_enable(ddata,
715d4ee021cSTony Lindgren 					     CPCAP_REG_CRM_VCHRG(vchrg),
716c6fdea96SPavel Machek 					     ichrg, 0);
7170c9888e3STony Lindgren 		if (error)
7180c9888e3STony Lindgren 			goto out_err;
71941ac23f5STony Lindgren 		cpcap_charger_update_state(ddata,
72041ac23f5STony Lindgren 					   POWER_SUPPLY_STATUS_CHARGING);
7210c9888e3STony Lindgren 	} else {
7226ddcec58STony Lindgren 		error = cpcap_charger_disable(ddata);
7230c9888e3STony Lindgren 		if (error)
7240c9888e3STony Lindgren 			goto out_err;
72541ac23f5STony Lindgren 		cpcap_charger_update_state(ddata,
72641ac23f5STony Lindgren 					   POWER_SUPPLY_STATUS_DISCHARGING);
7270c9888e3STony Lindgren 	}
7280c9888e3STony Lindgren 
729fd10606fSPavel Machek 	power_supply_changed(ddata->usb);
7300c9888e3STony Lindgren 	return;
7310c9888e3STony Lindgren 
7320c9888e3STony Lindgren out_err:
7336ddcec58STony Lindgren 	cpcap_charger_update_state(ddata, POWER_SUPPLY_STATUS_UNKNOWN);
7340c9888e3STony Lindgren 	dev_err(ddata->dev, "%s failed with %i\n", __func__, error);
7350c9888e3STony Lindgren }
7360c9888e3STony Lindgren 
cpcap_charger_irq_thread(int irq,void * data)7370c9888e3STony Lindgren static irqreturn_t cpcap_charger_irq_thread(int irq, void *data)
7380c9888e3STony Lindgren {
7390c9888e3STony Lindgren 	struct cpcap_charger_ddata *ddata = data;
7400c9888e3STony Lindgren 
7410c9888e3STony Lindgren 	if (!atomic_read(&ddata->active))
7420c9888e3STony Lindgren 		return IRQ_NONE;
7430c9888e3STony Lindgren 
7440c9888e3STony Lindgren 	schedule_delayed_work(&ddata->detect_work, 0);
7450c9888e3STony Lindgren 
7460c9888e3STony Lindgren 	return IRQ_HANDLED;
7470c9888e3STony Lindgren }
7480c9888e3STony Lindgren 
cpcap_usb_init_irq(struct platform_device * pdev,struct cpcap_charger_ddata * ddata,const char * name)7490c9888e3STony Lindgren static int cpcap_usb_init_irq(struct platform_device *pdev,
7500c9888e3STony Lindgren 			      struct cpcap_charger_ddata *ddata,
7510c9888e3STony Lindgren 			      const char *name)
7520c9888e3STony Lindgren {
7530c9888e3STony Lindgren 	struct cpcap_interrupt_desc *d;
7540c9888e3STony Lindgren 	int irq, error;
7550c9888e3STony Lindgren 
7560c9888e3STony Lindgren 	irq = platform_get_irq_byname(pdev, name);
757838c8afaSPan Bian 	if (irq < 0)
7580c9888e3STony Lindgren 		return -ENODEV;
7590c9888e3STony Lindgren 
7600c9888e3STony Lindgren 	error = devm_request_threaded_irq(ddata->dev, irq, NULL,
7610c9888e3STony Lindgren 					  cpcap_charger_irq_thread,
762e62333e2STony Lindgren 					  IRQF_SHARED | IRQF_ONESHOT,
7630c9888e3STony Lindgren 					  name, ddata);
7640c9888e3STony Lindgren 	if (error) {
7650c9888e3STony Lindgren 		dev_err(ddata->dev, "could not get irq %s: %i\n",
7660c9888e3STony Lindgren 			name, error);
7670c9888e3STony Lindgren 
7680c9888e3STony Lindgren 		return error;
7690c9888e3STony Lindgren 	}
7700c9888e3STony Lindgren 
7710c9888e3STony Lindgren 	d = devm_kzalloc(ddata->dev, sizeof(*d), GFP_KERNEL);
7720c9888e3STony Lindgren 	if (!d)
7730c9888e3STony Lindgren 		return -ENOMEM;
7740c9888e3STony Lindgren 
7750c9888e3STony Lindgren 	d->name = name;
7760c9888e3STony Lindgren 	d->irq = irq;
7770c9888e3STony Lindgren 	list_add(&d->node, &ddata->irq_list);
7780c9888e3STony Lindgren 
7790c9888e3STony Lindgren 	return 0;
7800c9888e3STony Lindgren }
7810c9888e3STony Lindgren 
7820c9888e3STony Lindgren static const char * const cpcap_charger_irqs[] = {
7830c9888e3STony Lindgren 	/* REG_INT_0 */
7840c9888e3STony Lindgren 	"chrg_det", "rvrs_chrg",
7850c9888e3STony Lindgren 
7860c9888e3STony Lindgren 	/* REG_INT1 */
787d4ee021cSTony Lindgren 	"chrg_se1b", "se0conn", "rvrs_mode", "chrgcurr2", "chrgcurr1", "vbusvld",
7880c9888e3STony Lindgren 
7890c9888e3STony Lindgren 	/* REG_INT_3 */
7900c9888e3STony Lindgren 	"battdetb",
7910c9888e3STony Lindgren };
7920c9888e3STony Lindgren 
cpcap_usb_init_interrupts(struct platform_device * pdev,struct cpcap_charger_ddata * ddata)7930c9888e3STony Lindgren static int cpcap_usb_init_interrupts(struct platform_device *pdev,
7940c9888e3STony Lindgren 				     struct cpcap_charger_ddata *ddata)
7950c9888e3STony Lindgren {
7960c9888e3STony Lindgren 	int i, error;
7970c9888e3STony Lindgren 
7980c9888e3STony Lindgren 	for (i = 0; i < ARRAY_SIZE(cpcap_charger_irqs); i++) {
7990c9888e3STony Lindgren 		error = cpcap_usb_init_irq(pdev, ddata, cpcap_charger_irqs[i]);
8000c9888e3STony Lindgren 		if (error)
8010c9888e3STony Lindgren 			return error;
8020c9888e3STony Lindgren 	}
8030c9888e3STony Lindgren 
8040c9888e3STony Lindgren 	return 0;
8050c9888e3STony Lindgren }
8060c9888e3STony Lindgren 
cpcap_charger_init_optional_gpios(struct cpcap_charger_ddata * ddata)8070c9888e3STony Lindgren static void cpcap_charger_init_optional_gpios(struct cpcap_charger_ddata *ddata)
8080c9888e3STony Lindgren {
8090c9888e3STony Lindgren 	int i;
8100c9888e3STony Lindgren 
8110c9888e3STony Lindgren 	for (i = 0; i < 2; i++) {
8120c9888e3STony Lindgren 		ddata->gpio[i] = devm_gpiod_get_index(ddata->dev, "mode",
8130c9888e3STony Lindgren 						      i, GPIOD_OUT_HIGH);
8140c9888e3STony Lindgren 		if (IS_ERR(ddata->gpio[i])) {
8150c9888e3STony Lindgren 			dev_info(ddata->dev, "no mode change GPIO%i: %li\n",
8160c9888e3STony Lindgren 				 i, PTR_ERR(ddata->gpio[i]));
8170c9888e3STony Lindgren 			ddata->gpio[i] = NULL;
8180c9888e3STony Lindgren 		}
8190c9888e3STony Lindgren 	}
8200c9888e3STony Lindgren }
8210c9888e3STony Lindgren 
cpcap_charger_init_iio(struct cpcap_charger_ddata * ddata)8220c9888e3STony Lindgren static int cpcap_charger_init_iio(struct cpcap_charger_ddata *ddata)
8230c9888e3STony Lindgren {
8240c9888e3STony Lindgren 	const char * const names[CPCAP_CHARGER_IIO_NR] = {
8250c9888e3STony Lindgren 		"battdetb", "battp", "vbus", "chg_isense", "batti",
8260c9888e3STony Lindgren 	};
8270c9888e3STony Lindgren 	int error, i;
8280c9888e3STony Lindgren 
8290c9888e3STony Lindgren 	for (i = 0; i < CPCAP_CHARGER_IIO_NR; i++) {
8300c9888e3STony Lindgren 		ddata->channels[i] = devm_iio_channel_get(ddata->dev,
8310c9888e3STony Lindgren 							  names[i]);
8320c9888e3STony Lindgren 		if (IS_ERR(ddata->channels[i])) {
8330c9888e3STony Lindgren 			error = PTR_ERR(ddata->channels[i]);
8340c9888e3STony Lindgren 			goto out_err;
8350c9888e3STony Lindgren 		}
8360c9888e3STony Lindgren 
8370c9888e3STony Lindgren 		if (!ddata->channels[i]->indio_dev) {
8380c9888e3STony Lindgren 			error = -ENXIO;
8390c9888e3STony Lindgren 			goto out_err;
8400c9888e3STony Lindgren 		}
8410c9888e3STony Lindgren 	}
8420c9888e3STony Lindgren 
8430c9888e3STony Lindgren 	return 0;
8440c9888e3STony Lindgren 
8450c9888e3STony Lindgren out_err:
8467d90fcc1STony Lindgren 	if (error != -EPROBE_DEFER)
8470c9888e3STony Lindgren 		dev_err(ddata->dev, "could not initialize VBUS or ID IIO: %i\n",
8480c9888e3STony Lindgren 			error);
8490c9888e3STony Lindgren 
8500c9888e3STony Lindgren 	return error;
8510c9888e3STony Lindgren }
8520c9888e3STony Lindgren 
8532071236bSTony Lindgren static char *cpcap_charger_supplied_to[] = {
8542071236bSTony Lindgren 	"battery",
8552071236bSTony Lindgren };
8562071236bSTony Lindgren 
8570c9888e3STony Lindgren static const struct power_supply_desc cpcap_charger_usb_desc = {
8584f700a52STony Lindgren 	.name		= "usb",
8590c9888e3STony Lindgren 	.type		= POWER_SUPPLY_TYPE_USB,
8600c9888e3STony Lindgren 	.properties	= cpcap_charger_props,
8610c9888e3STony Lindgren 	.num_properties	= ARRAY_SIZE(cpcap_charger_props),
8620c9888e3STony Lindgren 	.get_property	= cpcap_charger_get_property,
8635688ea04STony Lindgren 	.set_property	= cpcap_charger_set_property,
8645688ea04STony Lindgren 	.property_is_writeable = cpcap_charger_property_is_writeable,
8650c9888e3STony Lindgren };
8660c9888e3STony Lindgren 
8670c9888e3STony Lindgren static const struct of_device_id cpcap_charger_id_table[] = {
8680c9888e3STony Lindgren 	{
8690c9888e3STony Lindgren 		.compatible = "motorola,mapphone-cpcap-charger",
8700c9888e3STony Lindgren 	},
8710c9888e3STony Lindgren 	{},
8720c9888e3STony Lindgren };
8730c9888e3STony Lindgren MODULE_DEVICE_TABLE(of, cpcap_charger_id_table);
8740c9888e3STony Lindgren 
cpcap_charger_probe(struct platform_device * pdev)8750c9888e3STony Lindgren static int cpcap_charger_probe(struct platform_device *pdev)
8760c9888e3STony Lindgren {
8770c9888e3STony Lindgren 	struct cpcap_charger_ddata *ddata;
878fc443138STony Lindgren 	struct power_supply_config psy_cfg = {};
8790c9888e3STony Lindgren 	int error;
8800c9888e3STony Lindgren 
8810c9888e3STony Lindgren 	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
8820c9888e3STony Lindgren 	if (!ddata)
8830c9888e3STony Lindgren 		return -ENOMEM;
8840c9888e3STony Lindgren 
8850c9888e3STony Lindgren 	ddata->dev = &pdev->dev;
886d4ee021cSTony Lindgren 	ddata->voltage = 4200000;
887c6fdea96SPavel Machek 	ddata->limit_current = 532000;
8880c9888e3STony Lindgren 
8890c9888e3STony Lindgren 	ddata->reg = dev_get_regmap(ddata->dev->parent, NULL);
8900c9888e3STony Lindgren 	if (!ddata->reg)
8910c9888e3STony Lindgren 		return -ENODEV;
8920c9888e3STony Lindgren 
8930c9888e3STony Lindgren 	INIT_LIST_HEAD(&ddata->irq_list);
8940c9888e3STony Lindgren 	INIT_DELAYED_WORK(&ddata->detect_work, cpcap_usb_detect);
8950c9888e3STony Lindgren 	INIT_DELAYED_WORK(&ddata->vbus_work, cpcap_charger_vbus_work);
8960c9888e3STony Lindgren 	platform_set_drvdata(pdev, ddata);
8970c9888e3STony Lindgren 
8980c9888e3STony Lindgren 	error = cpcap_charger_init_iio(ddata);
8990c9888e3STony Lindgren 	if (error)
9000c9888e3STony Lindgren 		return error;
9010c9888e3STony Lindgren 
9020c9888e3STony Lindgren 	atomic_set(&ddata->active, 1);
9030c9888e3STony Lindgren 
90446d0c03cSSebastian Reichel 	psy_cfg.fwnode = dev_fwnode(&pdev->dev);
905fc443138STony Lindgren 	psy_cfg.drv_data = ddata;
9062071236bSTony Lindgren 	psy_cfg.supplied_to = cpcap_charger_supplied_to;
90750f74b78SChen Ni 	psy_cfg.num_supplicants = ARRAY_SIZE(cpcap_charger_supplied_to);
908fc443138STony Lindgren 
9090c9888e3STony Lindgren 	ddata->usb = devm_power_supply_register(ddata->dev,
9100c9888e3STony Lindgren 						&cpcap_charger_usb_desc,
911fc443138STony Lindgren 						&psy_cfg);
9120c9888e3STony Lindgren 	if (IS_ERR(ddata->usb)) {
9130c9888e3STony Lindgren 		error = PTR_ERR(ddata->usb);
9140c9888e3STony Lindgren 		dev_err(ddata->dev, "failed to register USB charger: %i\n",
9150c9888e3STony Lindgren 			error);
9160c9888e3STony Lindgren 
9170c9888e3STony Lindgren 		return error;
9180c9888e3STony Lindgren 	}
9190c9888e3STony Lindgren 
9200c9888e3STony Lindgren 	error = cpcap_usb_init_interrupts(pdev, ddata);
9210c9888e3STony Lindgren 	if (error)
9220c9888e3STony Lindgren 		return error;
9230c9888e3STony Lindgren 
9240c9888e3STony Lindgren 	ddata->comparator.set_vbus = cpcap_charger_set_vbus;
9250c9888e3STony Lindgren 	error = omap_usb2_set_comparator(&ddata->comparator);
9260c9888e3STony Lindgren 	if (error == -ENODEV) {
9270c9888e3STony Lindgren 		dev_info(ddata->dev, "charger needs phy, deferring probe\n");
9280c9888e3STony Lindgren 		return -EPROBE_DEFER;
9290c9888e3STony Lindgren 	}
9300c9888e3STony Lindgren 
9310c9888e3STony Lindgren 	cpcap_charger_init_optional_gpios(ddata);
9320c9888e3STony Lindgren 
9330c9888e3STony Lindgren 	schedule_delayed_work(&ddata->detect_work, 0);
9340c9888e3STony Lindgren 
9350c9888e3STony Lindgren 	return 0;
9360c9888e3STony Lindgren }
9370c9888e3STony Lindgren 
cpcap_charger_shutdown(struct platform_device * pdev)9382828ffc2STony Lindgren static void cpcap_charger_shutdown(struct platform_device *pdev)
9390c9888e3STony Lindgren {
9400c9888e3STony Lindgren 	struct cpcap_charger_ddata *ddata = platform_get_drvdata(pdev);
9410c9888e3STony Lindgren 	int error;
9420c9888e3STony Lindgren 
9430c9888e3STony Lindgren 	atomic_set(&ddata->active, 0);
9440c9888e3STony Lindgren 	error = omap_usb2_set_comparator(NULL);
9450c9888e3STony Lindgren 	if (error)
9460c9888e3STony Lindgren 		dev_warn(ddata->dev, "could not clear USB comparator: %i\n",
9470c9888e3STony Lindgren 			 error);
9480c9888e3STony Lindgren 
9496ddcec58STony Lindgren 	error = cpcap_charger_disable(ddata);
9506ddcec58STony Lindgren 	if (error) {
9516ddcec58STony Lindgren 		cpcap_charger_update_state(ddata, POWER_SUPPLY_STATUS_UNKNOWN);
9520c9888e3STony Lindgren 		dev_warn(ddata->dev, "could not clear charger: %i\n",
9530c9888e3STony Lindgren 			 error);
9546ddcec58STony Lindgren 	}
9556ddcec58STony Lindgren 	cpcap_charger_update_state(ddata, POWER_SUPPLY_STATUS_DISCHARGING);
9560c9888e3STony Lindgren 	cancel_delayed_work_sync(&ddata->vbus_work);
9570c9888e3STony Lindgren 	cancel_delayed_work_sync(&ddata->detect_work);
9582828ffc2STony Lindgren }
9592828ffc2STony Lindgren 
cpcap_charger_remove(struct platform_device * pdev)9601abbcff9SUwe Kleine-König static void cpcap_charger_remove(struct platform_device *pdev)
9612828ffc2STony Lindgren {
9622828ffc2STony Lindgren 	cpcap_charger_shutdown(pdev);
9630c9888e3STony Lindgren }
9640c9888e3STony Lindgren 
9650c9888e3STony Lindgren static struct platform_driver cpcap_charger_driver = {
9660c9888e3STony Lindgren 	.probe = cpcap_charger_probe,
9670c9888e3STony Lindgren 	.driver	= {
9680c9888e3STony Lindgren 		.name	= "cpcap-charger",
969b3c3a197SRob Herring 		.of_match_table = cpcap_charger_id_table,
9700c9888e3STony Lindgren 	},
9712828ffc2STony Lindgren 	.shutdown = cpcap_charger_shutdown,
97283bce344SUwe Kleine-König 	.remove = cpcap_charger_remove,
9730c9888e3STony Lindgren };
9740c9888e3STony Lindgren module_platform_driver(cpcap_charger_driver);
9750c9888e3STony Lindgren 
9760c9888e3STony Lindgren MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
9770c9888e3STony Lindgren MODULE_DESCRIPTION("CPCAP Battery Charger Interface driver");
9780c9888e3STony Lindgren MODULE_LICENSE("GPL v2");
9790c9888e3STony Lindgren MODULE_ALIAS("platform:cpcap-charger");
980