18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * TWL4030/TPS65950 BCI (Battery Charger Interface) driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2010 Gražvydas Ignotas <notasas@gmail.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * based on twl4030_bci_battery.c by TI
88c2ecf20Sopenharmony_ci * Copyright (C) 2008 Texas Instruments, Inc.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/err.h>
158c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
168c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
178c2ecf20Sopenharmony_ci#include <linux/mfd/twl.h>
188c2ecf20Sopenharmony_ci#include <linux/power_supply.h>
198c2ecf20Sopenharmony_ci#include <linux/notifier.h>
208c2ecf20Sopenharmony_ci#include <linux/usb/otg.h>
218c2ecf20Sopenharmony_ci#include <linux/iio/consumer.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define TWL4030_BCIMDEN		0x00
248c2ecf20Sopenharmony_ci#define TWL4030_BCIMDKEY	0x01
258c2ecf20Sopenharmony_ci#define TWL4030_BCIMSTATEC	0x02
268c2ecf20Sopenharmony_ci#define TWL4030_BCIICHG		0x08
278c2ecf20Sopenharmony_ci#define TWL4030_BCIVAC		0x0a
288c2ecf20Sopenharmony_ci#define TWL4030_BCIVBUS		0x0c
298c2ecf20Sopenharmony_ci#define TWL4030_BCIMFSTS3	0x0F
308c2ecf20Sopenharmony_ci#define TWL4030_BCIMFSTS4	0x10
318c2ecf20Sopenharmony_ci#define TWL4030_BCICTL1		0x23
328c2ecf20Sopenharmony_ci#define TWL4030_BB_CFG		0x12
338c2ecf20Sopenharmony_ci#define TWL4030_BCIIREF1	0x27
348c2ecf20Sopenharmony_ci#define TWL4030_BCIIREF2	0x28
358c2ecf20Sopenharmony_ci#define TWL4030_BCIMFKEY	0x11
368c2ecf20Sopenharmony_ci#define TWL4030_BCIMFEN3	0x14
378c2ecf20Sopenharmony_ci#define TWL4030_BCIMFTH8	0x1d
388c2ecf20Sopenharmony_ci#define TWL4030_BCIMFTH9	0x1e
398c2ecf20Sopenharmony_ci#define TWL4030_BCIWDKEY	0x21
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define TWL4030_BCIMFSTS1	0x01
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define TWL4030_BCIAUTOWEN	BIT(5)
448c2ecf20Sopenharmony_ci#define TWL4030_CONFIG_DONE	BIT(4)
458c2ecf20Sopenharmony_ci#define TWL4030_CVENAC		BIT(2)
468c2ecf20Sopenharmony_ci#define TWL4030_BCIAUTOUSB	BIT(1)
478c2ecf20Sopenharmony_ci#define TWL4030_BCIAUTOAC	BIT(0)
488c2ecf20Sopenharmony_ci#define TWL4030_CGAIN		BIT(5)
498c2ecf20Sopenharmony_ci#define TWL4030_USBFASTMCHG	BIT(2)
508c2ecf20Sopenharmony_ci#define TWL4030_STS_VBUS	BIT(7)
518c2ecf20Sopenharmony_ci#define TWL4030_STS_USB_ID	BIT(2)
528c2ecf20Sopenharmony_ci#define TWL4030_BBCHEN		BIT(4)
538c2ecf20Sopenharmony_ci#define TWL4030_BBSEL_MASK	0x0c
548c2ecf20Sopenharmony_ci#define TWL4030_BBSEL_2V5	0x00
558c2ecf20Sopenharmony_ci#define TWL4030_BBSEL_3V0	0x04
568c2ecf20Sopenharmony_ci#define TWL4030_BBSEL_3V1	0x08
578c2ecf20Sopenharmony_ci#define TWL4030_BBSEL_3V2	0x0c
588c2ecf20Sopenharmony_ci#define TWL4030_BBISEL_MASK	0x03
598c2ecf20Sopenharmony_ci#define TWL4030_BBISEL_25uA	0x00
608c2ecf20Sopenharmony_ci#define TWL4030_BBISEL_150uA	0x01
618c2ecf20Sopenharmony_ci#define TWL4030_BBISEL_500uA	0x02
628c2ecf20Sopenharmony_ci#define TWL4030_BBISEL_1000uA	0x03
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#define TWL4030_BATSTSPCHG	BIT(2)
658c2ecf20Sopenharmony_ci#define TWL4030_BATSTSMCHG	BIT(6)
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci/* BCI interrupts */
688c2ecf20Sopenharmony_ci#define TWL4030_WOVF		BIT(0) /* Watchdog overflow */
698c2ecf20Sopenharmony_ci#define TWL4030_TMOVF		BIT(1) /* Timer overflow */
708c2ecf20Sopenharmony_ci#define TWL4030_ICHGHIGH	BIT(2) /* Battery charge current high */
718c2ecf20Sopenharmony_ci#define TWL4030_ICHGLOW		BIT(3) /* Battery cc. low / FSM state change */
728c2ecf20Sopenharmony_ci#define TWL4030_ICHGEOC		BIT(4) /* Battery current end-of-charge */
738c2ecf20Sopenharmony_ci#define TWL4030_TBATOR2		BIT(5) /* Battery temperature out of range 2 */
748c2ecf20Sopenharmony_ci#define TWL4030_TBATOR1		BIT(6) /* Battery temperature out of range 1 */
758c2ecf20Sopenharmony_ci#define TWL4030_BATSTS		BIT(7) /* Battery status */
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci#define TWL4030_VBATLVL		BIT(0) /* VBAT level */
788c2ecf20Sopenharmony_ci#define TWL4030_VBATOV		BIT(1) /* VBAT overvoltage */
798c2ecf20Sopenharmony_ci#define TWL4030_VBUSOV		BIT(2) /* VBUS overvoltage */
808c2ecf20Sopenharmony_ci#define TWL4030_ACCHGOV		BIT(3) /* Ac charger overvoltage */
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci#define TWL4030_MSTATEC_USB		BIT(4)
838c2ecf20Sopenharmony_ci#define TWL4030_MSTATEC_AC		BIT(5)
848c2ecf20Sopenharmony_ci#define TWL4030_MSTATEC_MASK		0x0f
858c2ecf20Sopenharmony_ci#define TWL4030_MSTATEC_QUICK1		0x02
868c2ecf20Sopenharmony_ci#define TWL4030_MSTATEC_QUICK7		0x07
878c2ecf20Sopenharmony_ci#define TWL4030_MSTATEC_COMPLETE1	0x0b
888c2ecf20Sopenharmony_ci#define TWL4030_MSTATEC_COMPLETE4	0x0e
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci/*
918c2ecf20Sopenharmony_ci * If AC (Accessory Charger) voltage exceeds 4.5V (MADC 11)
928c2ecf20Sopenharmony_ci * then AC is available.
938c2ecf20Sopenharmony_ci */
948c2ecf20Sopenharmony_cistatic inline int ac_available(struct iio_channel *channel_vac)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	int val, err;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if (!channel_vac)
998c2ecf20Sopenharmony_ci		return 0;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	err = iio_read_channel_processed(channel_vac, &val);
1028c2ecf20Sopenharmony_ci	if (err < 0)
1038c2ecf20Sopenharmony_ci		return 0;
1048c2ecf20Sopenharmony_ci	return val > 4500;
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic bool allow_usb;
1088c2ecf20Sopenharmony_cimodule_param(allow_usb, bool, 0644);
1098c2ecf20Sopenharmony_ciMODULE_PARM_DESC(allow_usb, "Allow USB charge drawing default current");
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistruct twl4030_bci {
1128c2ecf20Sopenharmony_ci	struct device		*dev;
1138c2ecf20Sopenharmony_ci	struct power_supply	*ac;
1148c2ecf20Sopenharmony_ci	struct power_supply	*usb;
1158c2ecf20Sopenharmony_ci	struct usb_phy		*transceiver;
1168c2ecf20Sopenharmony_ci	struct notifier_block	usb_nb;
1178c2ecf20Sopenharmony_ci	struct work_struct	work;
1188c2ecf20Sopenharmony_ci	int			irq_chg;
1198c2ecf20Sopenharmony_ci	int			irq_bci;
1208c2ecf20Sopenharmony_ci	int			usb_enabled;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	/*
1238c2ecf20Sopenharmony_ci	 * ichg_* and *_cur values in uA. If any are 'large', we set
1248c2ecf20Sopenharmony_ci	 * CGAIN to '1' which doubles the range for half the
1258c2ecf20Sopenharmony_ci	 * precision.
1268c2ecf20Sopenharmony_ci	 */
1278c2ecf20Sopenharmony_ci	unsigned int		ichg_eoc, ichg_lo, ichg_hi;
1288c2ecf20Sopenharmony_ci	unsigned int		usb_cur, ac_cur;
1298c2ecf20Sopenharmony_ci	struct iio_channel	*channel_vac;
1308c2ecf20Sopenharmony_ci	bool			ac_is_active;
1318c2ecf20Sopenharmony_ci	int			usb_mode, ac_mode; /* charging mode requested */
1328c2ecf20Sopenharmony_ci#define	CHARGE_OFF	0
1338c2ecf20Sopenharmony_ci#define	CHARGE_AUTO	1
1348c2ecf20Sopenharmony_ci#define	CHARGE_LINEAR	2
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	/* When setting the USB current we slowly increase the
1378c2ecf20Sopenharmony_ci	 * requested current until target is reached or the voltage
1388c2ecf20Sopenharmony_ci	 * drops below 4.75V.  In the latter case we step back one
1398c2ecf20Sopenharmony_ci	 * step.
1408c2ecf20Sopenharmony_ci	 */
1418c2ecf20Sopenharmony_ci	unsigned int		usb_cur_target;
1428c2ecf20Sopenharmony_ci	struct delayed_work	current_worker;
1438c2ecf20Sopenharmony_ci#define	USB_CUR_STEP	20000	/* 20mA at a time */
1448c2ecf20Sopenharmony_ci#define	USB_MIN_VOLT	4750000	/* 4.75V */
1458c2ecf20Sopenharmony_ci#define	USB_CUR_DELAY	msecs_to_jiffies(100)
1468c2ecf20Sopenharmony_ci#define	USB_MAX_CURRENT	1700000 /* TWL4030 caps at 1.7A */
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	unsigned long		event;
1498c2ecf20Sopenharmony_ci};
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci/* strings for 'usb_mode' values */
1528c2ecf20Sopenharmony_cistatic const char *modes[] = { "off", "auto", "continuous" };
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci/*
1558c2ecf20Sopenharmony_ci * clear and set bits on an given register on a given module
1568c2ecf20Sopenharmony_ci */
1578c2ecf20Sopenharmony_cistatic int twl4030_clear_set(u8 mod_no, u8 clear, u8 set, u8 reg)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	u8 val = 0;
1608c2ecf20Sopenharmony_ci	int ret;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	ret = twl_i2c_read_u8(mod_no, &val, reg);
1638c2ecf20Sopenharmony_ci	if (ret)
1648c2ecf20Sopenharmony_ci		return ret;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	val &= ~clear;
1678c2ecf20Sopenharmony_ci	val |= set;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	return twl_i2c_write_u8(mod_no, val, reg);
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic int twl4030_bci_read(u8 reg, u8 *val)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	return twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, val, reg);
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic int twl4030_clear_set_boot_bci(u8 clear, u8 set)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	return twl4030_clear_set(TWL_MODULE_PM_MASTER, clear,
1808c2ecf20Sopenharmony_ci			TWL4030_CONFIG_DONE | TWL4030_BCIAUTOWEN | set,
1818c2ecf20Sopenharmony_ci			TWL4030_PM_MASTER_BOOT_BCI);
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic int twl4030bci_read_adc_val(u8 reg)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	int ret, temp;
1878c2ecf20Sopenharmony_ci	u8 val;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	/* read MSB */
1908c2ecf20Sopenharmony_ci	ret = twl4030_bci_read(reg + 1, &val);
1918c2ecf20Sopenharmony_ci	if (ret)
1928c2ecf20Sopenharmony_ci		return ret;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	temp = (int)(val & 0x03) << 8;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	/* read LSB */
1978c2ecf20Sopenharmony_ci	ret = twl4030_bci_read(reg, &val);
1988c2ecf20Sopenharmony_ci	if (ret)
1998c2ecf20Sopenharmony_ci		return ret;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	return temp | val;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci/*
2058c2ecf20Sopenharmony_ci * TI provided formulas:
2068c2ecf20Sopenharmony_ci * CGAIN == 0: ICHG = (BCIICHG * 1.7) / (2^10 - 1) - 0.85
2078c2ecf20Sopenharmony_ci * CGAIN == 1: ICHG = (BCIICHG * 3.4) / (2^10 - 1) - 1.7
2088c2ecf20Sopenharmony_ci * Here we use integer approximation of:
2098c2ecf20Sopenharmony_ci * CGAIN == 0: val * 1.6618 - 0.85 * 1000
2108c2ecf20Sopenharmony_ci * CGAIN == 1: (val * 1.6618 - 0.85 * 1000) * 2
2118c2ecf20Sopenharmony_ci */
2128c2ecf20Sopenharmony_ci/*
2138c2ecf20Sopenharmony_ci * convert twl register value for currents into uA
2148c2ecf20Sopenharmony_ci */
2158c2ecf20Sopenharmony_cistatic int regval2ua(int regval, bool cgain)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	if (cgain)
2188c2ecf20Sopenharmony_ci		return (regval * 16618 - 8500 * 1000) / 5;
2198c2ecf20Sopenharmony_ci	else
2208c2ecf20Sopenharmony_ci		return (regval * 16618 - 8500 * 1000) / 10;
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci/*
2248c2ecf20Sopenharmony_ci * convert uA currents into twl register value
2258c2ecf20Sopenharmony_ci */
2268c2ecf20Sopenharmony_cistatic int ua2regval(int ua, bool cgain)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	int ret;
2298c2ecf20Sopenharmony_ci	if (cgain)
2308c2ecf20Sopenharmony_ci		ua /= 2;
2318c2ecf20Sopenharmony_ci	ret = (ua * 10 + 8500 * 1000) / 16618;
2328c2ecf20Sopenharmony_ci	/* rounding problems */
2338c2ecf20Sopenharmony_ci	if (ret < 512)
2348c2ecf20Sopenharmony_ci		ret = 512;
2358c2ecf20Sopenharmony_ci	return ret;
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic int twl4030_charger_update_current(struct twl4030_bci *bci)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	int status;
2418c2ecf20Sopenharmony_ci	int cur;
2428c2ecf20Sopenharmony_ci	unsigned reg, cur_reg;
2438c2ecf20Sopenharmony_ci	u8 bcictl1, oldreg, fullreg;
2448c2ecf20Sopenharmony_ci	bool cgain = false;
2458c2ecf20Sopenharmony_ci	u8 boot_bci;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	/*
2488c2ecf20Sopenharmony_ci	 * If AC (Accessory Charger) voltage exceeds 4.5V (MADC 11)
2498c2ecf20Sopenharmony_ci	 * and AC is enabled, set current for 'ac'
2508c2ecf20Sopenharmony_ci	 */
2518c2ecf20Sopenharmony_ci	if (ac_available(bci->channel_vac)) {
2528c2ecf20Sopenharmony_ci		cur = bci->ac_cur;
2538c2ecf20Sopenharmony_ci		bci->ac_is_active = true;
2548c2ecf20Sopenharmony_ci	} else {
2558c2ecf20Sopenharmony_ci		cur = bci->usb_cur;
2568c2ecf20Sopenharmony_ci		bci->ac_is_active = false;
2578c2ecf20Sopenharmony_ci		if (cur > bci->usb_cur_target) {
2588c2ecf20Sopenharmony_ci			cur = bci->usb_cur_target;
2598c2ecf20Sopenharmony_ci			bci->usb_cur = cur;
2608c2ecf20Sopenharmony_ci		}
2618c2ecf20Sopenharmony_ci		if (cur < bci->usb_cur_target)
2628c2ecf20Sopenharmony_ci			schedule_delayed_work(&bci->current_worker, USB_CUR_DELAY);
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	/* First, check thresholds and see if cgain is needed */
2668c2ecf20Sopenharmony_ci	if (bci->ichg_eoc >= 200000)
2678c2ecf20Sopenharmony_ci		cgain = true;
2688c2ecf20Sopenharmony_ci	if (bci->ichg_lo >= 400000)
2698c2ecf20Sopenharmony_ci		cgain = true;
2708c2ecf20Sopenharmony_ci	if (bci->ichg_hi >= 820000)
2718c2ecf20Sopenharmony_ci		cgain = true;
2728c2ecf20Sopenharmony_ci	if (cur > 852000)
2738c2ecf20Sopenharmony_ci		cgain = true;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	status = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
2768c2ecf20Sopenharmony_ci	if (status < 0)
2778c2ecf20Sopenharmony_ci		return status;
2788c2ecf20Sopenharmony_ci	if (twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &boot_bci,
2798c2ecf20Sopenharmony_ci			    TWL4030_PM_MASTER_BOOT_BCI) < 0)
2808c2ecf20Sopenharmony_ci		boot_bci = 0;
2818c2ecf20Sopenharmony_ci	boot_bci &= 7;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if ((!!cgain) != !!(bcictl1 & TWL4030_CGAIN))
2848c2ecf20Sopenharmony_ci		/* Need to turn for charging while we change the
2858c2ecf20Sopenharmony_ci		 * CGAIN bit.  Leave it off while everything is
2868c2ecf20Sopenharmony_ci		 * updated.
2878c2ecf20Sopenharmony_ci		 */
2888c2ecf20Sopenharmony_ci		twl4030_clear_set_boot_bci(boot_bci, 0);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	/*
2918c2ecf20Sopenharmony_ci	 * For ichg_eoc, the hardware only supports reg values matching
2928c2ecf20Sopenharmony_ci	 * 100XXXX000, and requires the XXXX be stored in the high nibble
2938c2ecf20Sopenharmony_ci	 * of TWL4030_BCIMFTH8.
2948c2ecf20Sopenharmony_ci	 */
2958c2ecf20Sopenharmony_ci	reg = ua2regval(bci->ichg_eoc, cgain);
2968c2ecf20Sopenharmony_ci	if (reg > 0x278)
2978c2ecf20Sopenharmony_ci		reg = 0x278;
2988c2ecf20Sopenharmony_ci	if (reg < 0x200)
2998c2ecf20Sopenharmony_ci		reg = 0x200;
3008c2ecf20Sopenharmony_ci	reg = (reg >> 3) & 0xf;
3018c2ecf20Sopenharmony_ci	fullreg = reg << 4;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	/*
3048c2ecf20Sopenharmony_ci	 * For ichg_lo, reg value must match 10XXXX0000.
3058c2ecf20Sopenharmony_ci	 * XXXX is stored in low nibble of TWL4030_BCIMFTH8.
3068c2ecf20Sopenharmony_ci	 */
3078c2ecf20Sopenharmony_ci	reg = ua2regval(bci->ichg_lo, cgain);
3088c2ecf20Sopenharmony_ci	if (reg > 0x2F0)
3098c2ecf20Sopenharmony_ci		reg = 0x2F0;
3108c2ecf20Sopenharmony_ci	if (reg < 0x200)
3118c2ecf20Sopenharmony_ci		reg = 0x200;
3128c2ecf20Sopenharmony_ci	reg = (reg >> 4) & 0xf;
3138c2ecf20Sopenharmony_ci	fullreg |= reg;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	/* ichg_eoc and ichg_lo live in same register */
3168c2ecf20Sopenharmony_ci	status = twl4030_bci_read(TWL4030_BCIMFTH8, &oldreg);
3178c2ecf20Sopenharmony_ci	if (status < 0)
3188c2ecf20Sopenharmony_ci		return status;
3198c2ecf20Sopenharmony_ci	if (oldreg != fullreg) {
3208c2ecf20Sopenharmony_ci		status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xF4,
3218c2ecf20Sopenharmony_ci					  TWL4030_BCIMFKEY);
3228c2ecf20Sopenharmony_ci		if (status < 0)
3238c2ecf20Sopenharmony_ci			return status;
3248c2ecf20Sopenharmony_ci		twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
3258c2ecf20Sopenharmony_ci				 fullreg, TWL4030_BCIMFTH8);
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	/* ichg_hi threshold must be 1XXXX01100 (I think) */
3298c2ecf20Sopenharmony_ci	reg = ua2regval(bci->ichg_hi, cgain);
3308c2ecf20Sopenharmony_ci	if (reg > 0x3E0)
3318c2ecf20Sopenharmony_ci		reg = 0x3E0;
3328c2ecf20Sopenharmony_ci	if (reg < 0x200)
3338c2ecf20Sopenharmony_ci		reg = 0x200;
3348c2ecf20Sopenharmony_ci	fullreg = (reg >> 5) & 0xF;
3358c2ecf20Sopenharmony_ci	fullreg <<= 4;
3368c2ecf20Sopenharmony_ci	status = twl4030_bci_read(TWL4030_BCIMFTH9, &oldreg);
3378c2ecf20Sopenharmony_ci	if (status < 0)
3388c2ecf20Sopenharmony_ci		return status;
3398c2ecf20Sopenharmony_ci	if ((oldreg & 0xF0) != fullreg) {
3408c2ecf20Sopenharmony_ci		fullreg |= (oldreg & 0x0F);
3418c2ecf20Sopenharmony_ci		status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7,
3428c2ecf20Sopenharmony_ci					  TWL4030_BCIMFKEY);
3438c2ecf20Sopenharmony_ci		if (status < 0)
3448c2ecf20Sopenharmony_ci			return status;
3458c2ecf20Sopenharmony_ci		twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
3468c2ecf20Sopenharmony_ci				 fullreg, TWL4030_BCIMFTH9);
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	/*
3508c2ecf20Sopenharmony_ci	 * And finally, set the current.  This is stored in
3518c2ecf20Sopenharmony_ci	 * two registers.
3528c2ecf20Sopenharmony_ci	 */
3538c2ecf20Sopenharmony_ci	reg = ua2regval(cur, cgain);
3548c2ecf20Sopenharmony_ci	/* we have only 10 bits */
3558c2ecf20Sopenharmony_ci	if (reg > 0x3ff)
3568c2ecf20Sopenharmony_ci		reg = 0x3ff;
3578c2ecf20Sopenharmony_ci	status = twl4030_bci_read(TWL4030_BCIIREF1, &oldreg);
3588c2ecf20Sopenharmony_ci	if (status < 0)
3598c2ecf20Sopenharmony_ci		return status;
3608c2ecf20Sopenharmony_ci	cur_reg = oldreg;
3618c2ecf20Sopenharmony_ci	status = twl4030_bci_read(TWL4030_BCIIREF2, &oldreg);
3628c2ecf20Sopenharmony_ci	if (status < 0)
3638c2ecf20Sopenharmony_ci		return status;
3648c2ecf20Sopenharmony_ci	cur_reg |= oldreg << 8;
3658c2ecf20Sopenharmony_ci	if (reg != oldreg) {
3668c2ecf20Sopenharmony_ci		/* disable write protection for one write access for
3678c2ecf20Sopenharmony_ci		 * BCIIREF */
3688c2ecf20Sopenharmony_ci		status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7,
3698c2ecf20Sopenharmony_ci					  TWL4030_BCIMFKEY);
3708c2ecf20Sopenharmony_ci		if (status < 0)
3718c2ecf20Sopenharmony_ci			return status;
3728c2ecf20Sopenharmony_ci		status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
3738c2ecf20Sopenharmony_ci					  (reg & 0x100) ? 3 : 2,
3748c2ecf20Sopenharmony_ci					  TWL4030_BCIIREF2);
3758c2ecf20Sopenharmony_ci		if (status < 0)
3768c2ecf20Sopenharmony_ci			return status;
3778c2ecf20Sopenharmony_ci		/* disable write protection for one write access for
3788c2ecf20Sopenharmony_ci		 * BCIIREF */
3798c2ecf20Sopenharmony_ci		status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xE7,
3808c2ecf20Sopenharmony_ci					  TWL4030_BCIMFKEY);
3818c2ecf20Sopenharmony_ci		if (status < 0)
3828c2ecf20Sopenharmony_ci			return status;
3838c2ecf20Sopenharmony_ci		status = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
3848c2ecf20Sopenharmony_ci					  reg & 0xff,
3858c2ecf20Sopenharmony_ci					  TWL4030_BCIIREF1);
3868c2ecf20Sopenharmony_ci	}
3878c2ecf20Sopenharmony_ci	if ((!!cgain) != !!(bcictl1 & TWL4030_CGAIN)) {
3888c2ecf20Sopenharmony_ci		/* Flip CGAIN and re-enable charging */
3898c2ecf20Sopenharmony_ci		bcictl1 ^= TWL4030_CGAIN;
3908c2ecf20Sopenharmony_ci		twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
3918c2ecf20Sopenharmony_ci				 bcictl1, TWL4030_BCICTL1);
3928c2ecf20Sopenharmony_ci		twl4030_clear_set_boot_bci(0, boot_bci);
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci	return 0;
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_cistatic int twl4030_charger_get_current(void);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_cistatic void twl4030_current_worker(struct work_struct *data)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	int v, curr;
4028c2ecf20Sopenharmony_ci	int res;
4038c2ecf20Sopenharmony_ci	struct twl4030_bci *bci = container_of(data, struct twl4030_bci,
4048c2ecf20Sopenharmony_ci					       current_worker.work);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	res = twl4030bci_read_adc_val(TWL4030_BCIVBUS);
4078c2ecf20Sopenharmony_ci	if (res < 0)
4088c2ecf20Sopenharmony_ci		v = 0;
4098c2ecf20Sopenharmony_ci	else
4108c2ecf20Sopenharmony_ci		/* BCIVBUS uses ADCIN8, 7/1023 V/step */
4118c2ecf20Sopenharmony_ci		v = res * 6843;
4128c2ecf20Sopenharmony_ci	curr = twl4030_charger_get_current();
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	dev_dbg(bci->dev, "v=%d cur=%d limit=%d target=%d\n", v, curr,
4158c2ecf20Sopenharmony_ci		bci->usb_cur, bci->usb_cur_target);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	if (v < USB_MIN_VOLT) {
4188c2ecf20Sopenharmony_ci		/* Back up and stop adjusting. */
4198c2ecf20Sopenharmony_ci		if (bci->usb_cur >= USB_CUR_STEP)
4208c2ecf20Sopenharmony_ci			bci->usb_cur -= USB_CUR_STEP;
4218c2ecf20Sopenharmony_ci		bci->usb_cur_target = bci->usb_cur;
4228c2ecf20Sopenharmony_ci	} else if (bci->usb_cur >= bci->usb_cur_target ||
4238c2ecf20Sopenharmony_ci		   bci->usb_cur + USB_CUR_STEP > USB_MAX_CURRENT) {
4248c2ecf20Sopenharmony_ci		/* Reached target and voltage is OK - stop */
4258c2ecf20Sopenharmony_ci		return;
4268c2ecf20Sopenharmony_ci	} else {
4278c2ecf20Sopenharmony_ci		bci->usb_cur += USB_CUR_STEP;
4288c2ecf20Sopenharmony_ci		schedule_delayed_work(&bci->current_worker, USB_CUR_DELAY);
4298c2ecf20Sopenharmony_ci	}
4308c2ecf20Sopenharmony_ci	twl4030_charger_update_current(bci);
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci/*
4348c2ecf20Sopenharmony_ci * Enable/Disable USB Charge functionality.
4358c2ecf20Sopenharmony_ci */
4368c2ecf20Sopenharmony_cistatic int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	int ret;
4398c2ecf20Sopenharmony_ci	u32 reg;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	if (bci->usb_mode == CHARGE_OFF)
4428c2ecf20Sopenharmony_ci		enable = false;
4438c2ecf20Sopenharmony_ci	if (enable && !IS_ERR_OR_NULL(bci->transceiver)) {
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci		twl4030_charger_update_current(bci);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci		/* Need to keep phy powered */
4488c2ecf20Sopenharmony_ci		if (!bci->usb_enabled) {
4498c2ecf20Sopenharmony_ci			pm_runtime_get_sync(bci->transceiver->dev);
4508c2ecf20Sopenharmony_ci			bci->usb_enabled = 1;
4518c2ecf20Sopenharmony_ci		}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci		if (bci->usb_mode == CHARGE_AUTO) {
4548c2ecf20Sopenharmony_ci			/* Enable interrupts now. */
4558c2ecf20Sopenharmony_ci			reg = ~(u32)(TWL4030_ICHGLOW | TWL4030_ICHGEOC |
4568c2ecf20Sopenharmony_ci					TWL4030_TBATOR2 | TWL4030_TBATOR1 |
4578c2ecf20Sopenharmony_ci					TWL4030_BATSTS);
4588c2ecf20Sopenharmony_ci			ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg,
4598c2ecf20Sopenharmony_ci				       TWL4030_INTERRUPTS_BCIIMR1A);
4608c2ecf20Sopenharmony_ci			if (ret < 0) {
4618c2ecf20Sopenharmony_ci				dev_err(bci->dev,
4628c2ecf20Sopenharmony_ci					"failed to unmask interrupts: %d\n",
4638c2ecf20Sopenharmony_ci					ret);
4648c2ecf20Sopenharmony_ci				return ret;
4658c2ecf20Sopenharmony_ci			}
4668c2ecf20Sopenharmony_ci			/* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */
4678c2ecf20Sopenharmony_ci			ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOUSB);
4688c2ecf20Sopenharmony_ci		}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci		/* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */
4718c2ecf20Sopenharmony_ci		ret = twl4030_clear_set(TWL_MODULE_MAIN_CHARGE, 0,
4728c2ecf20Sopenharmony_ci			TWL4030_USBFASTMCHG, TWL4030_BCIMFSTS4);
4738c2ecf20Sopenharmony_ci		if (bci->usb_mode == CHARGE_LINEAR) {
4748c2ecf20Sopenharmony_ci			/* Enable interrupts now. */
4758c2ecf20Sopenharmony_ci			reg = ~(u32)(TWL4030_ICHGLOW | TWL4030_TBATOR2 |
4768c2ecf20Sopenharmony_ci					TWL4030_TBATOR1 | TWL4030_BATSTS);
4778c2ecf20Sopenharmony_ci			ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg,
4788c2ecf20Sopenharmony_ci				       TWL4030_INTERRUPTS_BCIIMR1A);
4798c2ecf20Sopenharmony_ci			if (ret < 0) {
4808c2ecf20Sopenharmony_ci				dev_err(bci->dev,
4818c2ecf20Sopenharmony_ci					"failed to unmask interrupts: %d\n",
4828c2ecf20Sopenharmony_ci					ret);
4838c2ecf20Sopenharmony_ci				return ret;
4848c2ecf20Sopenharmony_ci			}
4858c2ecf20Sopenharmony_ci			twl4030_clear_set_boot_bci(TWL4030_BCIAUTOAC|TWL4030_CVENAC, 0);
4868c2ecf20Sopenharmony_ci			/* Watch dog key: WOVF acknowledge */
4878c2ecf20Sopenharmony_ci			ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x33,
4888c2ecf20Sopenharmony_ci					       TWL4030_BCIWDKEY);
4898c2ecf20Sopenharmony_ci			/* 0x24 + EKEY6: off mode */
4908c2ecf20Sopenharmony_ci			ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x2a,
4918c2ecf20Sopenharmony_ci					       TWL4030_BCIMDKEY);
4928c2ecf20Sopenharmony_ci			/* EKEY2: Linear charge: USB path */
4938c2ecf20Sopenharmony_ci			ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x26,
4948c2ecf20Sopenharmony_ci					       TWL4030_BCIMDKEY);
4958c2ecf20Sopenharmony_ci			/* WDKEY5: stop watchdog count */
4968c2ecf20Sopenharmony_ci			ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xf3,
4978c2ecf20Sopenharmony_ci					       TWL4030_BCIWDKEY);
4988c2ecf20Sopenharmony_ci			/* enable MFEN3 access */
4998c2ecf20Sopenharmony_ci			ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x9c,
5008c2ecf20Sopenharmony_ci					       TWL4030_BCIMFKEY);
5018c2ecf20Sopenharmony_ci			 /* ICHGEOCEN - end-of-charge monitor (current < 80mA)
5028c2ecf20Sopenharmony_ci			  *                      (charging continues)
5038c2ecf20Sopenharmony_ci			  * ICHGLOWEN - current level monitor (charge continues)
5048c2ecf20Sopenharmony_ci			  * don't monitor over-current or heat save
5058c2ecf20Sopenharmony_ci			  */
5068c2ecf20Sopenharmony_ci			ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0xf0,
5078c2ecf20Sopenharmony_ci					       TWL4030_BCIMFEN3);
5088c2ecf20Sopenharmony_ci		}
5098c2ecf20Sopenharmony_ci	} else {
5108c2ecf20Sopenharmony_ci		ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, 0);
5118c2ecf20Sopenharmony_ci		ret |= twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x2a,
5128c2ecf20Sopenharmony_ci					TWL4030_BCIMDKEY);
5138c2ecf20Sopenharmony_ci		if (bci->usb_enabled) {
5148c2ecf20Sopenharmony_ci			pm_runtime_mark_last_busy(bci->transceiver->dev);
5158c2ecf20Sopenharmony_ci			pm_runtime_put_autosuspend(bci->transceiver->dev);
5168c2ecf20Sopenharmony_ci			bci->usb_enabled = 0;
5178c2ecf20Sopenharmony_ci		}
5188c2ecf20Sopenharmony_ci		bci->usb_cur = 0;
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	return ret;
5228c2ecf20Sopenharmony_ci}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci/*
5258c2ecf20Sopenharmony_ci * Enable/Disable AC Charge funtionality.
5268c2ecf20Sopenharmony_ci */
5278c2ecf20Sopenharmony_cistatic int twl4030_charger_enable_ac(struct twl4030_bci *bci, bool enable)
5288c2ecf20Sopenharmony_ci{
5298c2ecf20Sopenharmony_ci	int ret;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	if (bci->ac_mode == CHARGE_OFF)
5328c2ecf20Sopenharmony_ci		enable = false;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	if (enable)
5358c2ecf20Sopenharmony_ci		ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOAC);
5368c2ecf20Sopenharmony_ci	else
5378c2ecf20Sopenharmony_ci		ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOAC, 0);
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	return ret;
5408c2ecf20Sopenharmony_ci}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci/*
5438c2ecf20Sopenharmony_ci * Enable/Disable charging of Backup Battery.
5448c2ecf20Sopenharmony_ci */
5458c2ecf20Sopenharmony_cistatic int twl4030_charger_enable_backup(int uvolt, int uamp)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	int ret;
5488c2ecf20Sopenharmony_ci	u8 flags;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	if (uvolt < 2500000 ||
5518c2ecf20Sopenharmony_ci	    uamp < 25) {
5528c2ecf20Sopenharmony_ci		/* disable charging of backup battery */
5538c2ecf20Sopenharmony_ci		ret = twl4030_clear_set(TWL_MODULE_PM_RECEIVER,
5548c2ecf20Sopenharmony_ci					TWL4030_BBCHEN, 0, TWL4030_BB_CFG);
5558c2ecf20Sopenharmony_ci		return ret;
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	flags = TWL4030_BBCHEN;
5598c2ecf20Sopenharmony_ci	if (uvolt >= 3200000)
5608c2ecf20Sopenharmony_ci		flags |= TWL4030_BBSEL_3V2;
5618c2ecf20Sopenharmony_ci	else if (uvolt >= 3100000)
5628c2ecf20Sopenharmony_ci		flags |= TWL4030_BBSEL_3V1;
5638c2ecf20Sopenharmony_ci	else if (uvolt >= 3000000)
5648c2ecf20Sopenharmony_ci		flags |= TWL4030_BBSEL_3V0;
5658c2ecf20Sopenharmony_ci	else
5668c2ecf20Sopenharmony_ci		flags |= TWL4030_BBSEL_2V5;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	if (uamp >= 1000)
5698c2ecf20Sopenharmony_ci		flags |= TWL4030_BBISEL_1000uA;
5708c2ecf20Sopenharmony_ci	else if (uamp >= 500)
5718c2ecf20Sopenharmony_ci		flags |= TWL4030_BBISEL_500uA;
5728c2ecf20Sopenharmony_ci	else if (uamp >= 150)
5738c2ecf20Sopenharmony_ci		flags |= TWL4030_BBISEL_150uA;
5748c2ecf20Sopenharmony_ci	else
5758c2ecf20Sopenharmony_ci		flags |= TWL4030_BBISEL_25uA;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	ret = twl4030_clear_set(TWL_MODULE_PM_RECEIVER,
5788c2ecf20Sopenharmony_ci				TWL4030_BBSEL_MASK | TWL4030_BBISEL_MASK,
5798c2ecf20Sopenharmony_ci				flags,
5808c2ecf20Sopenharmony_ci				TWL4030_BB_CFG);
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	return ret;
5838c2ecf20Sopenharmony_ci}
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci/*
5868c2ecf20Sopenharmony_ci * TWL4030 CHG_PRES (AC charger presence) events
5878c2ecf20Sopenharmony_ci */
5888c2ecf20Sopenharmony_cistatic irqreturn_t twl4030_charger_interrupt(int irq, void *arg)
5898c2ecf20Sopenharmony_ci{
5908c2ecf20Sopenharmony_ci	struct twl4030_bci *bci = arg;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	dev_dbg(bci->dev, "CHG_PRES irq\n");
5938c2ecf20Sopenharmony_ci	/* reset current on each 'plug' event */
5948c2ecf20Sopenharmony_ci	bci->ac_cur = 500000;
5958c2ecf20Sopenharmony_ci	twl4030_charger_update_current(bci);
5968c2ecf20Sopenharmony_ci	power_supply_changed(bci->ac);
5978c2ecf20Sopenharmony_ci	power_supply_changed(bci->usb);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
6008c2ecf20Sopenharmony_ci}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci/*
6038c2ecf20Sopenharmony_ci * TWL4030 BCI monitoring events
6048c2ecf20Sopenharmony_ci */
6058c2ecf20Sopenharmony_cistatic irqreturn_t twl4030_bci_interrupt(int irq, void *arg)
6068c2ecf20Sopenharmony_ci{
6078c2ecf20Sopenharmony_ci	struct twl4030_bci *bci = arg;
6088c2ecf20Sopenharmony_ci	u8 irqs1, irqs2;
6098c2ecf20Sopenharmony_ci	int ret;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	ret = twl_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &irqs1,
6128c2ecf20Sopenharmony_ci			      TWL4030_INTERRUPTS_BCIISR1A);
6138c2ecf20Sopenharmony_ci	if (ret < 0)
6148c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	ret = twl_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &irqs2,
6178c2ecf20Sopenharmony_ci			      TWL4030_INTERRUPTS_BCIISR2A);
6188c2ecf20Sopenharmony_ci	if (ret < 0)
6198c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	dev_dbg(bci->dev, "BCI irq %02x %02x\n", irqs2, irqs1);
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	if (irqs1 & (TWL4030_ICHGLOW | TWL4030_ICHGEOC)) {
6248c2ecf20Sopenharmony_ci		/* charger state change, inform the core */
6258c2ecf20Sopenharmony_ci		power_supply_changed(bci->ac);
6268c2ecf20Sopenharmony_ci		power_supply_changed(bci->usb);
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci	twl4030_charger_update_current(bci);
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	/* various monitoring events, for now we just log them here */
6318c2ecf20Sopenharmony_ci	if (irqs1 & (TWL4030_TBATOR2 | TWL4030_TBATOR1))
6328c2ecf20Sopenharmony_ci		dev_warn(bci->dev, "battery temperature out of range\n");
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	if (irqs1 & TWL4030_BATSTS)
6358c2ecf20Sopenharmony_ci		dev_crit(bci->dev, "battery disconnected\n");
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	if (irqs2 & TWL4030_VBATOV)
6388c2ecf20Sopenharmony_ci		dev_crit(bci->dev, "VBAT overvoltage\n");
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	if (irqs2 & TWL4030_VBUSOV)
6418c2ecf20Sopenharmony_ci		dev_crit(bci->dev, "VBUS overvoltage\n");
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	if (irqs2 & TWL4030_ACCHGOV)
6448c2ecf20Sopenharmony_ci		dev_crit(bci->dev, "Ac charger overvoltage\n");
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
6478c2ecf20Sopenharmony_ci}
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_cistatic void twl4030_bci_usb_work(struct work_struct *data)
6508c2ecf20Sopenharmony_ci{
6518c2ecf20Sopenharmony_ci	struct twl4030_bci *bci = container_of(data, struct twl4030_bci, work);
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	switch (bci->event) {
6548c2ecf20Sopenharmony_ci	case USB_EVENT_VBUS:
6558c2ecf20Sopenharmony_ci	case USB_EVENT_CHARGER:
6568c2ecf20Sopenharmony_ci		twl4030_charger_enable_usb(bci, true);
6578c2ecf20Sopenharmony_ci		break;
6588c2ecf20Sopenharmony_ci	case USB_EVENT_NONE:
6598c2ecf20Sopenharmony_ci		twl4030_charger_enable_usb(bci, false);
6608c2ecf20Sopenharmony_ci		break;
6618c2ecf20Sopenharmony_ci	}
6628c2ecf20Sopenharmony_ci}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_cistatic int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val,
6658c2ecf20Sopenharmony_ci			       void *priv)
6668c2ecf20Sopenharmony_ci{
6678c2ecf20Sopenharmony_ci	struct twl4030_bci *bci = container_of(nb, struct twl4030_bci, usb_nb);
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	dev_dbg(bci->dev, "OTG notify %lu\n", val);
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	/* reset current on each 'plug' event */
6728c2ecf20Sopenharmony_ci	if (allow_usb)
6738c2ecf20Sopenharmony_ci		bci->usb_cur_target = 500000;
6748c2ecf20Sopenharmony_ci	else
6758c2ecf20Sopenharmony_ci		bci->usb_cur_target = 100000;
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	bci->event = val;
6788c2ecf20Sopenharmony_ci	schedule_work(&bci->work);
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	return NOTIFY_OK;
6818c2ecf20Sopenharmony_ci}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci/*
6848c2ecf20Sopenharmony_ci * sysfs charger enabled store
6858c2ecf20Sopenharmony_ci */
6868c2ecf20Sopenharmony_cistatic ssize_t
6878c2ecf20Sopenharmony_citwl4030_bci_mode_store(struct device *dev, struct device_attribute *attr,
6888c2ecf20Sopenharmony_ci			  const char *buf, size_t n)
6898c2ecf20Sopenharmony_ci{
6908c2ecf20Sopenharmony_ci	struct twl4030_bci *bci = dev_get_drvdata(dev->parent);
6918c2ecf20Sopenharmony_ci	int mode;
6928c2ecf20Sopenharmony_ci	int status;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	mode = sysfs_match_string(modes, buf);
6958c2ecf20Sopenharmony_ci	if (mode < 0)
6968c2ecf20Sopenharmony_ci		return mode;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	if (dev == &bci->ac->dev) {
6998c2ecf20Sopenharmony_ci		if (mode == 2)
7008c2ecf20Sopenharmony_ci			return -EINVAL;
7018c2ecf20Sopenharmony_ci		twl4030_charger_enable_ac(bci, false);
7028c2ecf20Sopenharmony_ci		bci->ac_mode = mode;
7038c2ecf20Sopenharmony_ci		status = twl4030_charger_enable_ac(bci, true);
7048c2ecf20Sopenharmony_ci	} else {
7058c2ecf20Sopenharmony_ci		twl4030_charger_enable_usb(bci, false);
7068c2ecf20Sopenharmony_ci		bci->usb_mode = mode;
7078c2ecf20Sopenharmony_ci		status = twl4030_charger_enable_usb(bci, true);
7088c2ecf20Sopenharmony_ci	}
7098c2ecf20Sopenharmony_ci	return (status == 0) ? n : status;
7108c2ecf20Sopenharmony_ci}
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci/*
7138c2ecf20Sopenharmony_ci * sysfs charger enabled show
7148c2ecf20Sopenharmony_ci */
7158c2ecf20Sopenharmony_cistatic ssize_t
7168c2ecf20Sopenharmony_citwl4030_bci_mode_show(struct device *dev,
7178c2ecf20Sopenharmony_ci			     struct device_attribute *attr, char *buf)
7188c2ecf20Sopenharmony_ci{
7198c2ecf20Sopenharmony_ci	struct twl4030_bci *bci = dev_get_drvdata(dev->parent);
7208c2ecf20Sopenharmony_ci	int len = 0;
7218c2ecf20Sopenharmony_ci	int i;
7228c2ecf20Sopenharmony_ci	int mode = bci->usb_mode;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	if (dev == &bci->ac->dev)
7258c2ecf20Sopenharmony_ci		mode = bci->ac_mode;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(modes); i++)
7288c2ecf20Sopenharmony_ci		if (mode == i)
7298c2ecf20Sopenharmony_ci			len += scnprintf(buf+len, PAGE_SIZE-len,
7308c2ecf20Sopenharmony_ci					"[%s] ", modes[i]);
7318c2ecf20Sopenharmony_ci		else
7328c2ecf20Sopenharmony_ci			len += scnprintf(buf+len, PAGE_SIZE-len,
7338c2ecf20Sopenharmony_ci					"%s ", modes[i]);
7348c2ecf20Sopenharmony_ci	buf[len-1] = '\n';
7358c2ecf20Sopenharmony_ci	return len;
7368c2ecf20Sopenharmony_ci}
7378c2ecf20Sopenharmony_cistatic DEVICE_ATTR(mode, 0644, twl4030_bci_mode_show,
7388c2ecf20Sopenharmony_ci		   twl4030_bci_mode_store);
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_cistatic int twl4030_charger_get_current(void)
7418c2ecf20Sopenharmony_ci{
7428c2ecf20Sopenharmony_ci	int curr;
7438c2ecf20Sopenharmony_ci	int ret;
7448c2ecf20Sopenharmony_ci	u8 bcictl1;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	curr = twl4030bci_read_adc_val(TWL4030_BCIICHG);
7478c2ecf20Sopenharmony_ci	if (curr < 0)
7488c2ecf20Sopenharmony_ci		return curr;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	ret = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
7518c2ecf20Sopenharmony_ci	if (ret)
7528c2ecf20Sopenharmony_ci		return ret;
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	return regval2ua(curr, bcictl1 & TWL4030_CGAIN);
7558c2ecf20Sopenharmony_ci}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci/*
7588c2ecf20Sopenharmony_ci * Returns the main charge FSM state
7598c2ecf20Sopenharmony_ci * Or < 0 on failure.
7608c2ecf20Sopenharmony_ci */
7618c2ecf20Sopenharmony_cistatic int twl4030bci_state(struct twl4030_bci *bci)
7628c2ecf20Sopenharmony_ci{
7638c2ecf20Sopenharmony_ci	int ret;
7648c2ecf20Sopenharmony_ci	u8 state;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	ret = twl4030_bci_read(TWL4030_BCIMSTATEC, &state);
7678c2ecf20Sopenharmony_ci	if (ret) {
7688c2ecf20Sopenharmony_ci		dev_err(bci->dev, "error reading BCIMSTATEC\n");
7698c2ecf20Sopenharmony_ci		return ret;
7708c2ecf20Sopenharmony_ci	}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	dev_dbg(bci->dev, "state: %02x\n", state);
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	return state;
7758c2ecf20Sopenharmony_ci}
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_cistatic int twl4030_bci_state_to_status(int state)
7788c2ecf20Sopenharmony_ci{
7798c2ecf20Sopenharmony_ci	state &= TWL4030_MSTATEC_MASK;
7808c2ecf20Sopenharmony_ci	if (TWL4030_MSTATEC_QUICK1 <= state && state <= TWL4030_MSTATEC_QUICK7)
7818c2ecf20Sopenharmony_ci		return POWER_SUPPLY_STATUS_CHARGING;
7828c2ecf20Sopenharmony_ci	else if (TWL4030_MSTATEC_COMPLETE1 <= state &&
7838c2ecf20Sopenharmony_ci					state <= TWL4030_MSTATEC_COMPLETE4)
7848c2ecf20Sopenharmony_ci		return POWER_SUPPLY_STATUS_FULL;
7858c2ecf20Sopenharmony_ci	else
7868c2ecf20Sopenharmony_ci		return POWER_SUPPLY_STATUS_NOT_CHARGING;
7878c2ecf20Sopenharmony_ci}
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_cistatic int twl4030_bci_get_property(struct power_supply *psy,
7908c2ecf20Sopenharmony_ci				    enum power_supply_property psp,
7918c2ecf20Sopenharmony_ci				    union power_supply_propval *val)
7928c2ecf20Sopenharmony_ci{
7938c2ecf20Sopenharmony_ci	struct twl4030_bci *bci = dev_get_drvdata(psy->dev.parent);
7948c2ecf20Sopenharmony_ci	int is_charging;
7958c2ecf20Sopenharmony_ci	int state;
7968c2ecf20Sopenharmony_ci	int ret;
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	state = twl4030bci_state(bci);
7998c2ecf20Sopenharmony_ci	if (state < 0)
8008c2ecf20Sopenharmony_ci		return state;
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	if (psy->desc->type == POWER_SUPPLY_TYPE_USB)
8038c2ecf20Sopenharmony_ci		is_charging = state & TWL4030_MSTATEC_USB;
8048c2ecf20Sopenharmony_ci	else
8058c2ecf20Sopenharmony_ci		is_charging = state & TWL4030_MSTATEC_AC;
8068c2ecf20Sopenharmony_ci	if (!is_charging) {
8078c2ecf20Sopenharmony_ci		u8 s;
8088c2ecf20Sopenharmony_ci		ret = twl4030_bci_read(TWL4030_BCIMDEN, &s);
8098c2ecf20Sopenharmony_ci		if (ret < 0)
8108c2ecf20Sopenharmony_ci			return ret;
8118c2ecf20Sopenharmony_ci		if (psy->desc->type == POWER_SUPPLY_TYPE_USB)
8128c2ecf20Sopenharmony_ci			is_charging = s & 1;
8138c2ecf20Sopenharmony_ci		else
8148c2ecf20Sopenharmony_ci			is_charging = s & 2;
8158c2ecf20Sopenharmony_ci		if (is_charging)
8168c2ecf20Sopenharmony_ci			/* A little white lie */
8178c2ecf20Sopenharmony_ci			state = TWL4030_MSTATEC_QUICK1;
8188c2ecf20Sopenharmony_ci	}
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	switch (psp) {
8218c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_STATUS:
8228c2ecf20Sopenharmony_ci		if (is_charging)
8238c2ecf20Sopenharmony_ci			val->intval = twl4030_bci_state_to_status(state);
8248c2ecf20Sopenharmony_ci		else
8258c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
8268c2ecf20Sopenharmony_ci		break;
8278c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
8288c2ecf20Sopenharmony_ci		/* charging must be active for meaningful result */
8298c2ecf20Sopenharmony_ci		if (!is_charging)
8308c2ecf20Sopenharmony_ci			return -ENODATA;
8318c2ecf20Sopenharmony_ci		if (psy->desc->type == POWER_SUPPLY_TYPE_USB) {
8328c2ecf20Sopenharmony_ci			ret = twl4030bci_read_adc_val(TWL4030_BCIVBUS);
8338c2ecf20Sopenharmony_ci			if (ret < 0)
8348c2ecf20Sopenharmony_ci				return ret;
8358c2ecf20Sopenharmony_ci			/* BCIVBUS uses ADCIN8, 7/1023 V/step */
8368c2ecf20Sopenharmony_ci			val->intval = ret * 6843;
8378c2ecf20Sopenharmony_ci		} else {
8388c2ecf20Sopenharmony_ci			ret = twl4030bci_read_adc_val(TWL4030_BCIVAC);
8398c2ecf20Sopenharmony_ci			if (ret < 0)
8408c2ecf20Sopenharmony_ci				return ret;
8418c2ecf20Sopenharmony_ci			/* BCIVAC uses ADCIN11, 10/1023 V/step */
8428c2ecf20Sopenharmony_ci			val->intval = ret * 9775;
8438c2ecf20Sopenharmony_ci		}
8448c2ecf20Sopenharmony_ci		break;
8458c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_NOW:
8468c2ecf20Sopenharmony_ci		if (!is_charging)
8478c2ecf20Sopenharmony_ci			return -ENODATA;
8488c2ecf20Sopenharmony_ci		/* current measurement is shared between AC and USB */
8498c2ecf20Sopenharmony_ci		ret = twl4030_charger_get_current();
8508c2ecf20Sopenharmony_ci		if (ret < 0)
8518c2ecf20Sopenharmony_ci			return ret;
8528c2ecf20Sopenharmony_ci		val->intval = ret;
8538c2ecf20Sopenharmony_ci		break;
8548c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
8558c2ecf20Sopenharmony_ci		val->intval = is_charging &&
8568c2ecf20Sopenharmony_ci			twl4030_bci_state_to_status(state) !=
8578c2ecf20Sopenharmony_ci				POWER_SUPPLY_STATUS_NOT_CHARGING;
8588c2ecf20Sopenharmony_ci		break;
8598c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
8608c2ecf20Sopenharmony_ci		val->intval = -1;
8618c2ecf20Sopenharmony_ci		if (psy->desc->type != POWER_SUPPLY_TYPE_USB) {
8628c2ecf20Sopenharmony_ci			if (!bci->ac_is_active)
8638c2ecf20Sopenharmony_ci				val->intval = bci->ac_cur;
8648c2ecf20Sopenharmony_ci		} else {
8658c2ecf20Sopenharmony_ci			if (bci->ac_is_active)
8668c2ecf20Sopenharmony_ci				val->intval = bci->usb_cur_target;
8678c2ecf20Sopenharmony_ci		}
8688c2ecf20Sopenharmony_ci		if (val->intval < 0) {
8698c2ecf20Sopenharmony_ci			u8 bcictl1;
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci			val->intval = twl4030bci_read_adc_val(TWL4030_BCIIREF1);
8728c2ecf20Sopenharmony_ci			if (val->intval < 0)
8738c2ecf20Sopenharmony_ci				return val->intval;
8748c2ecf20Sopenharmony_ci			ret = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
8758c2ecf20Sopenharmony_ci			if (ret < 0)
8768c2ecf20Sopenharmony_ci				return ret;
8778c2ecf20Sopenharmony_ci			val->intval = regval2ua(val->intval, bcictl1 &
8788c2ecf20Sopenharmony_ci							TWL4030_CGAIN);
8798c2ecf20Sopenharmony_ci		}
8808c2ecf20Sopenharmony_ci		break;
8818c2ecf20Sopenharmony_ci	default:
8828c2ecf20Sopenharmony_ci		return -EINVAL;
8838c2ecf20Sopenharmony_ci	}
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	return 0;
8868c2ecf20Sopenharmony_ci}
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_cistatic int twl4030_bci_set_property(struct power_supply *psy,
8898c2ecf20Sopenharmony_ci				    enum power_supply_property psp,
8908c2ecf20Sopenharmony_ci				    const union power_supply_propval *val)
8918c2ecf20Sopenharmony_ci{
8928c2ecf20Sopenharmony_ci	struct twl4030_bci *bci = dev_get_drvdata(psy->dev.parent);
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	switch (psp) {
8958c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
8968c2ecf20Sopenharmony_ci		if (psy->desc->type == POWER_SUPPLY_TYPE_USB)
8978c2ecf20Sopenharmony_ci			bci->usb_cur_target = val->intval;
8988c2ecf20Sopenharmony_ci		else
8998c2ecf20Sopenharmony_ci			bci->ac_cur = val->intval;
9008c2ecf20Sopenharmony_ci		twl4030_charger_update_current(bci);
9018c2ecf20Sopenharmony_ci		break;
9028c2ecf20Sopenharmony_ci	default:
9038c2ecf20Sopenharmony_ci		return -EINVAL;
9048c2ecf20Sopenharmony_ci	}
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	return 0;
9078c2ecf20Sopenharmony_ci}
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_cistatic int twl4030_bci_property_is_writeable(struct power_supply *psy,
9108c2ecf20Sopenharmony_ci				      enum power_supply_property psp)
9118c2ecf20Sopenharmony_ci{
9128c2ecf20Sopenharmony_ci	switch (psp) {
9138c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
9148c2ecf20Sopenharmony_ci		return true;
9158c2ecf20Sopenharmony_ci	default:
9168c2ecf20Sopenharmony_ci		return false;
9178c2ecf20Sopenharmony_ci	}
9188c2ecf20Sopenharmony_ci}
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_cistatic enum power_supply_property twl4030_charger_props[] = {
9218c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_STATUS,
9228c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
9238c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_NOW,
9248c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_NOW,
9258c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
9268c2ecf20Sopenharmony_ci};
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
9298c2ecf20Sopenharmony_cistatic const struct twl4030_bci_platform_data *
9308c2ecf20Sopenharmony_citwl4030_bci_parse_dt(struct device *dev)
9318c2ecf20Sopenharmony_ci{
9328c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
9338c2ecf20Sopenharmony_ci	struct twl4030_bci_platform_data *pdata;
9348c2ecf20Sopenharmony_ci	u32 num;
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	if (!np)
9378c2ecf20Sopenharmony_ci		return NULL;
9388c2ecf20Sopenharmony_ci	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
9398c2ecf20Sopenharmony_ci	if (!pdata)
9408c2ecf20Sopenharmony_ci		return pdata;
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	if (of_property_read_u32(np, "ti,bb-uvolt", &num) == 0)
9438c2ecf20Sopenharmony_ci		pdata->bb_uvolt = num;
9448c2ecf20Sopenharmony_ci	if (of_property_read_u32(np, "ti,bb-uamp", &num) == 0)
9458c2ecf20Sopenharmony_ci		pdata->bb_uamp = num;
9468c2ecf20Sopenharmony_ci	return pdata;
9478c2ecf20Sopenharmony_ci}
9488c2ecf20Sopenharmony_ci#else
9498c2ecf20Sopenharmony_cistatic inline const struct twl4030_bci_platform_data *
9508c2ecf20Sopenharmony_citwl4030_bci_parse_dt(struct device *dev)
9518c2ecf20Sopenharmony_ci{
9528c2ecf20Sopenharmony_ci	return NULL;
9538c2ecf20Sopenharmony_ci}
9548c2ecf20Sopenharmony_ci#endif
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_cistatic const struct power_supply_desc twl4030_bci_ac_desc = {
9578c2ecf20Sopenharmony_ci	.name		= "twl4030_ac",
9588c2ecf20Sopenharmony_ci	.type		= POWER_SUPPLY_TYPE_MAINS,
9598c2ecf20Sopenharmony_ci	.properties	= twl4030_charger_props,
9608c2ecf20Sopenharmony_ci	.num_properties	= ARRAY_SIZE(twl4030_charger_props),
9618c2ecf20Sopenharmony_ci	.get_property	= twl4030_bci_get_property,
9628c2ecf20Sopenharmony_ci	.set_property	= twl4030_bci_set_property,
9638c2ecf20Sopenharmony_ci	.property_is_writeable	= twl4030_bci_property_is_writeable,
9648c2ecf20Sopenharmony_ci};
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_cistatic const struct power_supply_desc twl4030_bci_usb_desc = {
9678c2ecf20Sopenharmony_ci	.name		= "twl4030_usb",
9688c2ecf20Sopenharmony_ci	.type		= POWER_SUPPLY_TYPE_USB,
9698c2ecf20Sopenharmony_ci	.properties	= twl4030_charger_props,
9708c2ecf20Sopenharmony_ci	.num_properties	= ARRAY_SIZE(twl4030_charger_props),
9718c2ecf20Sopenharmony_ci	.get_property	= twl4030_bci_get_property,
9728c2ecf20Sopenharmony_ci	.set_property	= twl4030_bci_set_property,
9738c2ecf20Sopenharmony_ci	.property_is_writeable	= twl4030_bci_property_is_writeable,
9748c2ecf20Sopenharmony_ci};
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_cistatic int twl4030_bci_probe(struct platform_device *pdev)
9778c2ecf20Sopenharmony_ci{
9788c2ecf20Sopenharmony_ci	struct twl4030_bci *bci;
9798c2ecf20Sopenharmony_ci	const struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data;
9808c2ecf20Sopenharmony_ci	int ret;
9818c2ecf20Sopenharmony_ci	u32 reg;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	bci = devm_kzalloc(&pdev->dev, sizeof(*bci), GFP_KERNEL);
9848c2ecf20Sopenharmony_ci	if (bci == NULL)
9858c2ecf20Sopenharmony_ci		return -ENOMEM;
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	if (!pdata)
9888c2ecf20Sopenharmony_ci		pdata = twl4030_bci_parse_dt(&pdev->dev);
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	bci->ichg_eoc = 80100; /* Stop charging when current drops to here */
9918c2ecf20Sopenharmony_ci	bci->ichg_lo = 241000; /* Low threshold */
9928c2ecf20Sopenharmony_ci	bci->ichg_hi = 500000; /* High threshold */
9938c2ecf20Sopenharmony_ci	bci->ac_cur = 500000; /* 500mA */
9948c2ecf20Sopenharmony_ci	if (allow_usb)
9958c2ecf20Sopenharmony_ci		bci->usb_cur_target = 500000;  /* 500mA */
9968c2ecf20Sopenharmony_ci	else
9978c2ecf20Sopenharmony_ci		bci->usb_cur_target = 100000;  /* 100mA */
9988c2ecf20Sopenharmony_ci	bci->usb_mode = CHARGE_AUTO;
9998c2ecf20Sopenharmony_ci	bci->ac_mode = CHARGE_AUTO;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	bci->dev = &pdev->dev;
10028c2ecf20Sopenharmony_ci	bci->irq_chg = platform_get_irq(pdev, 0);
10038c2ecf20Sopenharmony_ci	bci->irq_bci = platform_get_irq(pdev, 1);
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, bci);
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	INIT_WORK(&bci->work, twl4030_bci_usb_work);
10088c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&bci->current_worker, twl4030_current_worker);
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	bci->channel_vac = devm_iio_channel_get(&pdev->dev, "vac");
10118c2ecf20Sopenharmony_ci	if (IS_ERR(bci->channel_vac)) {
10128c2ecf20Sopenharmony_ci		ret = PTR_ERR(bci->channel_vac);
10138c2ecf20Sopenharmony_ci		if (ret == -EPROBE_DEFER)
10148c2ecf20Sopenharmony_ci			return ret;	/* iio not ready */
10158c2ecf20Sopenharmony_ci		dev_warn(&pdev->dev, "could not request vac iio channel (%d)",
10168c2ecf20Sopenharmony_ci			ret);
10178c2ecf20Sopenharmony_ci		bci->channel_vac = NULL;
10188c2ecf20Sopenharmony_ci	}
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	if (bci->dev->of_node) {
10218c2ecf20Sopenharmony_ci		struct device_node *phynode;
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci		phynode = of_get_compatible_child(bci->dev->of_node->parent,
10248c2ecf20Sopenharmony_ci						  "ti,twl4030-usb");
10258c2ecf20Sopenharmony_ci		if (phynode) {
10268c2ecf20Sopenharmony_ci			bci->usb_nb.notifier_call = twl4030_bci_usb_ncb;
10278c2ecf20Sopenharmony_ci			bci->transceiver = devm_usb_get_phy_by_node(
10288c2ecf20Sopenharmony_ci				bci->dev, phynode, &bci->usb_nb);
10298c2ecf20Sopenharmony_ci			of_node_put(phynode);
10308c2ecf20Sopenharmony_ci			if (IS_ERR(bci->transceiver)) {
10318c2ecf20Sopenharmony_ci				ret = PTR_ERR(bci->transceiver);
10328c2ecf20Sopenharmony_ci				if (ret == -EPROBE_DEFER)
10338c2ecf20Sopenharmony_ci					return ret;	/* phy not ready */
10348c2ecf20Sopenharmony_ci				dev_warn(&pdev->dev, "could not request transceiver (%d)",
10358c2ecf20Sopenharmony_ci					ret);
10368c2ecf20Sopenharmony_ci				bci->transceiver = NULL;
10378c2ecf20Sopenharmony_ci			}
10388c2ecf20Sopenharmony_ci		}
10398c2ecf20Sopenharmony_ci	}
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	bci->ac = devm_power_supply_register(&pdev->dev, &twl4030_bci_ac_desc,
10428c2ecf20Sopenharmony_ci					     NULL);
10438c2ecf20Sopenharmony_ci	if (IS_ERR(bci->ac)) {
10448c2ecf20Sopenharmony_ci		ret = PTR_ERR(bci->ac);
10458c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to register ac: %d\n", ret);
10468c2ecf20Sopenharmony_ci		return ret;
10478c2ecf20Sopenharmony_ci	}
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	bci->usb = devm_power_supply_register(&pdev->dev, &twl4030_bci_usb_desc,
10508c2ecf20Sopenharmony_ci					      NULL);
10518c2ecf20Sopenharmony_ci	if (IS_ERR(bci->usb)) {
10528c2ecf20Sopenharmony_ci		ret = PTR_ERR(bci->usb);
10538c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to register usb: %d\n", ret);
10548c2ecf20Sopenharmony_ci		return ret;
10558c2ecf20Sopenharmony_ci	}
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	ret = devm_request_threaded_irq(&pdev->dev, bci->irq_chg, NULL,
10588c2ecf20Sopenharmony_ci			twl4030_charger_interrupt, IRQF_ONESHOT, pdev->name,
10598c2ecf20Sopenharmony_ci			bci);
10608c2ecf20Sopenharmony_ci	if (ret < 0) {
10618c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "could not request irq %d, status %d\n",
10628c2ecf20Sopenharmony_ci			bci->irq_chg, ret);
10638c2ecf20Sopenharmony_ci		return ret;
10648c2ecf20Sopenharmony_ci	}
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	ret = devm_request_threaded_irq(&pdev->dev, bci->irq_bci, NULL,
10678c2ecf20Sopenharmony_ci			twl4030_bci_interrupt, IRQF_ONESHOT, pdev->name, bci);
10688c2ecf20Sopenharmony_ci	if (ret < 0) {
10698c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "could not request irq %d, status %d\n",
10708c2ecf20Sopenharmony_ci			bci->irq_bci, ret);
10718c2ecf20Sopenharmony_ci		return ret;
10728c2ecf20Sopenharmony_ci	}
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	/* Enable interrupts now. */
10758c2ecf20Sopenharmony_ci	reg = ~(u32)(TWL4030_ICHGLOW | TWL4030_ICHGEOC | TWL4030_TBATOR2 |
10768c2ecf20Sopenharmony_ci		TWL4030_TBATOR1 | TWL4030_BATSTS);
10778c2ecf20Sopenharmony_ci	ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg,
10788c2ecf20Sopenharmony_ci			       TWL4030_INTERRUPTS_BCIIMR1A);
10798c2ecf20Sopenharmony_ci	if (ret < 0) {
10808c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
10818c2ecf20Sopenharmony_ci		return ret;
10828c2ecf20Sopenharmony_ci	}
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	reg = ~(u32)(TWL4030_VBATOV | TWL4030_VBUSOV | TWL4030_ACCHGOV);
10858c2ecf20Sopenharmony_ci	ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg,
10868c2ecf20Sopenharmony_ci			       TWL4030_INTERRUPTS_BCIIMR2A);
10878c2ecf20Sopenharmony_ci	if (ret < 0)
10888c2ecf20Sopenharmony_ci		dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	twl4030_charger_update_current(bci);
10918c2ecf20Sopenharmony_ci	if (device_create_file(&bci->usb->dev, &dev_attr_mode))
10928c2ecf20Sopenharmony_ci		dev_warn(&pdev->dev, "could not create sysfs file\n");
10938c2ecf20Sopenharmony_ci	if (device_create_file(&bci->ac->dev, &dev_attr_mode))
10948c2ecf20Sopenharmony_ci		dev_warn(&pdev->dev, "could not create sysfs file\n");
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	twl4030_charger_enable_ac(bci, true);
10978c2ecf20Sopenharmony_ci	if (!IS_ERR_OR_NULL(bci->transceiver))
10988c2ecf20Sopenharmony_ci		twl4030_bci_usb_ncb(&bci->usb_nb,
10998c2ecf20Sopenharmony_ci				    bci->transceiver->last_event,
11008c2ecf20Sopenharmony_ci				    NULL);
11018c2ecf20Sopenharmony_ci	else
11028c2ecf20Sopenharmony_ci		twl4030_charger_enable_usb(bci, false);
11038c2ecf20Sopenharmony_ci	if (pdata)
11048c2ecf20Sopenharmony_ci		twl4030_charger_enable_backup(pdata->bb_uvolt,
11058c2ecf20Sopenharmony_ci					      pdata->bb_uamp);
11068c2ecf20Sopenharmony_ci	else
11078c2ecf20Sopenharmony_ci		twl4030_charger_enable_backup(0, 0);
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci	return 0;
11108c2ecf20Sopenharmony_ci}
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_cistatic int twl4030_bci_remove(struct platform_device *pdev)
11138c2ecf20Sopenharmony_ci{
11148c2ecf20Sopenharmony_ci	struct twl4030_bci *bci = platform_get_drvdata(pdev);
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	twl4030_charger_enable_ac(bci, false);
11178c2ecf20Sopenharmony_ci	twl4030_charger_enable_usb(bci, false);
11188c2ecf20Sopenharmony_ci	twl4030_charger_enable_backup(0, 0);
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	device_remove_file(&bci->usb->dev, &dev_attr_mode);
11218c2ecf20Sopenharmony_ci	device_remove_file(&bci->ac->dev, &dev_attr_mode);
11228c2ecf20Sopenharmony_ci	/* mask interrupts */
11238c2ecf20Sopenharmony_ci	twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
11248c2ecf20Sopenharmony_ci			 TWL4030_INTERRUPTS_BCIIMR1A);
11258c2ecf20Sopenharmony_ci	twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
11268c2ecf20Sopenharmony_ci			 TWL4030_INTERRUPTS_BCIIMR2A);
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci	return 0;
11298c2ecf20Sopenharmony_ci}
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_cistatic const struct of_device_id twl_bci_of_match[] = {
11328c2ecf20Sopenharmony_ci	{.compatible = "ti,twl4030-bci", },
11338c2ecf20Sopenharmony_ci	{ }
11348c2ecf20Sopenharmony_ci};
11358c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, twl_bci_of_match);
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_cistatic struct platform_driver twl4030_bci_driver = {
11388c2ecf20Sopenharmony_ci	.probe = twl4030_bci_probe,
11398c2ecf20Sopenharmony_ci	.remove	= twl4030_bci_remove,
11408c2ecf20Sopenharmony_ci	.driver	= {
11418c2ecf20Sopenharmony_ci		.name	= "twl4030_bci",
11428c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(twl_bci_of_match),
11438c2ecf20Sopenharmony_ci	},
11448c2ecf20Sopenharmony_ci};
11458c2ecf20Sopenharmony_cimodule_platform_driver(twl4030_bci_driver);
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ciMODULE_AUTHOR("Gražvydas Ignotas");
11488c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TWL4030 Battery Charger Interface driver");
11498c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
11508c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:twl4030_bci");
1151