162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Driver for UCS1002 Programmable USB Port Power Controller
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2019 Zodiac Inflight Innovations
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#include <linux/bits.h>
862306a36Sopenharmony_ci#include <linux/freezer.h>
962306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
1062306a36Sopenharmony_ci#include <linux/i2c.h>
1162306a36Sopenharmony_ci#include <linux/interrupt.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/kthread.h>
1462306a36Sopenharmony_ci#include <linux/device.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/of.h>
1762306a36Sopenharmony_ci#include <linux/of_irq.h>
1862306a36Sopenharmony_ci#include <linux/power_supply.h>
1962306a36Sopenharmony_ci#include <linux/regmap.h>
2062306a36Sopenharmony_ci#include <linux/regulator/driver.h>
2162306a36Sopenharmony_ci#include <linux/regulator/of_regulator.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* UCS1002 Registers */
2462306a36Sopenharmony_ci#define UCS1002_REG_CURRENT_MEASUREMENT	0x00
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/*
2762306a36Sopenharmony_ci * The Total Accumulated Charge registers store the total accumulated
2862306a36Sopenharmony_ci * charge delivered from the VS source to a portable device. The total
2962306a36Sopenharmony_ci * value is calculated using four registers, from 01h to 04h. The bit
3062306a36Sopenharmony_ci * weighting of the registers is given in mA/hrs.
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ci#define UCS1002_REG_TOTAL_ACC_CHARGE	0x01
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* Other Status Register */
3562306a36Sopenharmony_ci#define UCS1002_REG_OTHER_STATUS	0x0f
3662306a36Sopenharmony_ci#  define F_ADET_PIN			BIT(4)
3762306a36Sopenharmony_ci#  define F_CHG_ACT			BIT(3)
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/* Interrupt Status */
4062306a36Sopenharmony_ci#define UCS1002_REG_INTERRUPT_STATUS	0x10
4162306a36Sopenharmony_ci#  define F_ERR				BIT(7)
4262306a36Sopenharmony_ci#  define F_DISCHARGE_ERR		BIT(6)
4362306a36Sopenharmony_ci#  define F_RESET			BIT(5)
4462306a36Sopenharmony_ci#  define F_MIN_KEEP_OUT		BIT(4)
4562306a36Sopenharmony_ci#  define F_TSD				BIT(3)
4662306a36Sopenharmony_ci#  define F_OVER_VOLT			BIT(2)
4762306a36Sopenharmony_ci#  define F_BACK_VOLT			BIT(1)
4862306a36Sopenharmony_ci#  define F_OVER_ILIM			BIT(0)
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/* Pin Status Register */
5162306a36Sopenharmony_ci#define UCS1002_REG_PIN_STATUS		0x14
5262306a36Sopenharmony_ci#  define UCS1002_PWR_STATE_MASK	0x03
5362306a36Sopenharmony_ci#  define F_PWR_EN_PIN			BIT(6)
5462306a36Sopenharmony_ci#  define F_M2_PIN			BIT(5)
5562306a36Sopenharmony_ci#  define F_M1_PIN			BIT(4)
5662306a36Sopenharmony_ci#  define F_EM_EN_PIN			BIT(3)
5762306a36Sopenharmony_ci#  define F_SEL_PIN			BIT(2)
5862306a36Sopenharmony_ci#  define F_ACTIVE_MODE_MASK		GENMASK(5, 3)
5962306a36Sopenharmony_ci#  define F_ACTIVE_MODE_PASSTHROUGH	F_M2_PIN
6062306a36Sopenharmony_ci#  define F_ACTIVE_MODE_DEDICATED	F_EM_EN_PIN
6162306a36Sopenharmony_ci#  define F_ACTIVE_MODE_BC12_DCP	(F_M2_PIN | F_EM_EN_PIN)
6262306a36Sopenharmony_ci#  define F_ACTIVE_MODE_BC12_SDP	F_M1_PIN
6362306a36Sopenharmony_ci#  define F_ACTIVE_MODE_BC12_CDP	(F_M1_PIN | F_M2_PIN | F_EM_EN_PIN)
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/* General Configuration Register */
6662306a36Sopenharmony_ci#define UCS1002_REG_GENERAL_CFG		0x15
6762306a36Sopenharmony_ci#  define F_RATION_EN			BIT(3)
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/* Emulation Configuration Register */
7062306a36Sopenharmony_ci#define UCS1002_REG_EMU_CFG		0x16
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/* Switch Configuration Register */
7362306a36Sopenharmony_ci#define UCS1002_REG_SWITCH_CFG		0x17
7462306a36Sopenharmony_ci#  define F_PIN_IGNORE			BIT(7)
7562306a36Sopenharmony_ci#  define F_EM_EN_SET			BIT(5)
7662306a36Sopenharmony_ci#  define F_M2_SET			BIT(4)
7762306a36Sopenharmony_ci#  define F_M1_SET			BIT(3)
7862306a36Sopenharmony_ci#  define F_S0_SET			BIT(2)
7962306a36Sopenharmony_ci#  define F_PWR_EN_SET			BIT(1)
8062306a36Sopenharmony_ci#  define F_LATCH_SET			BIT(0)
8162306a36Sopenharmony_ci#  define V_SET_ACTIVE_MODE_MASK	GENMASK(5, 3)
8262306a36Sopenharmony_ci#  define V_SET_ACTIVE_MODE_PASSTHROUGH	F_M2_SET
8362306a36Sopenharmony_ci#  define V_SET_ACTIVE_MODE_DEDICATED	F_EM_EN_SET
8462306a36Sopenharmony_ci#  define V_SET_ACTIVE_MODE_BC12_DCP	(F_M2_SET | F_EM_EN_SET)
8562306a36Sopenharmony_ci#  define V_SET_ACTIVE_MODE_BC12_SDP	F_M1_SET
8662306a36Sopenharmony_ci#  define V_SET_ACTIVE_MODE_BC12_CDP	(F_M1_SET | F_M2_SET | F_EM_EN_SET)
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/* Current Limit Register */
8962306a36Sopenharmony_ci#define UCS1002_REG_ILIMIT		0x19
9062306a36Sopenharmony_ci#  define UCS1002_ILIM_SW_MASK		GENMASK(3, 0)
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/* Product ID */
9362306a36Sopenharmony_ci#define UCS1002_REG_PRODUCT_ID		0xfd
9462306a36Sopenharmony_ci#  define UCS1002_PRODUCT_ID		0x4e
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci/* Manufacture name */
9762306a36Sopenharmony_ci#define UCS1002_MANUFACTURER		"SMSC"
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistruct ucs1002_info {
10062306a36Sopenharmony_ci	struct power_supply *charger;
10162306a36Sopenharmony_ci	struct i2c_client *client;
10262306a36Sopenharmony_ci	struct regmap *regmap;
10362306a36Sopenharmony_ci	struct regulator_desc *regulator_descriptor;
10462306a36Sopenharmony_ci	struct regulator_dev *rdev;
10562306a36Sopenharmony_ci	bool present;
10662306a36Sopenharmony_ci	bool output_disable;
10762306a36Sopenharmony_ci	struct delayed_work health_poll;
10862306a36Sopenharmony_ci	int health;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci};
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic enum power_supply_property ucs1002_props[] = {
11362306a36Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
11462306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CHARGE_NOW,
11562306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_NOW,
11662306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_MAX,
11762306a36Sopenharmony_ci	POWER_SUPPLY_PROP_PRESENT, /* the presence of PED */
11862306a36Sopenharmony_ci	POWER_SUPPLY_PROP_MANUFACTURER,
11962306a36Sopenharmony_ci	POWER_SUPPLY_PROP_USB_TYPE,
12062306a36Sopenharmony_ci	POWER_SUPPLY_PROP_HEALTH,
12162306a36Sopenharmony_ci};
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic int ucs1002_get_online(struct ucs1002_info *info,
12462306a36Sopenharmony_ci			      union power_supply_propval *val)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	unsigned int reg;
12762306a36Sopenharmony_ci	int ret;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	ret = regmap_read(info->regmap, UCS1002_REG_OTHER_STATUS, &reg);
13062306a36Sopenharmony_ci	if (ret)
13162306a36Sopenharmony_ci		return ret;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	val->intval = !!(reg & F_CHG_ACT);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	return 0;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic int ucs1002_get_charge(struct ucs1002_info *info,
13962306a36Sopenharmony_ci			      union power_supply_propval *val)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	/*
14262306a36Sopenharmony_ci	 * To fit within 32 bits some values are rounded (uA/h)
14362306a36Sopenharmony_ci	 *
14462306a36Sopenharmony_ci	 * For Total Accumulated Charge Middle Low Byte register, addr
14562306a36Sopenharmony_ci	 * 03h, byte 2
14662306a36Sopenharmony_ci	 *
14762306a36Sopenharmony_ci	 *   B0: 0.01084 mA/h rounded to 11 uA/h
14862306a36Sopenharmony_ci	 *   B1: 0.02169 mA/h rounded to 22 uA/h
14962306a36Sopenharmony_ci	 *   B2: 0.04340 mA/h rounded to 43 uA/h
15062306a36Sopenharmony_ci	 *   B3: 0.08676 mA/h rounded to 87 uA/h
15162306a36Sopenharmony_ci	 *   B4: 0.17350 mA/h rounded to 173 uÁ/h
15262306a36Sopenharmony_ci	 *
15362306a36Sopenharmony_ci	 * For Total Accumulated Charge Low Byte register, addr 04h,
15462306a36Sopenharmony_ci	 * byte 3
15562306a36Sopenharmony_ci	 *
15662306a36Sopenharmony_ci	 *   B6: 0.00271 mA/h rounded to 3 uA/h
15762306a36Sopenharmony_ci	 *   B7: 0.005422 mA/h rounded to 5 uA/h
15862306a36Sopenharmony_ci	 */
15962306a36Sopenharmony_ci	static const int bit_weights_uAh[BITS_PER_TYPE(u32)] = {
16062306a36Sopenharmony_ci		/*
16162306a36Sopenharmony_ci		 * Bit corresponding to low byte (offset 0x04)
16262306a36Sopenharmony_ci		 * B0 B1 B2 B3 B4 B5 B6 B7
16362306a36Sopenharmony_ci		 */
16462306a36Sopenharmony_ci		0, 0, 0, 0, 0, 0, 3, 5,
16562306a36Sopenharmony_ci		/*
16662306a36Sopenharmony_ci		 * Bit corresponding to middle low byte (offset 0x03)
16762306a36Sopenharmony_ci		 * B0 B1 B2 B3 B4 B5 B6 B7
16862306a36Sopenharmony_ci		 */
16962306a36Sopenharmony_ci		11, 22, 43, 87, 173, 347, 694, 1388,
17062306a36Sopenharmony_ci		/*
17162306a36Sopenharmony_ci		 * Bit corresponding to middle high byte (offset 0x02)
17262306a36Sopenharmony_ci		 * B0 B1 B2 B3 B4 B5 B6 B7
17362306a36Sopenharmony_ci		 */
17462306a36Sopenharmony_ci		2776, 5552, 11105, 22210, 44420, 88840, 177700, 355400,
17562306a36Sopenharmony_ci		/*
17662306a36Sopenharmony_ci		 * Bit corresponding to high byte (offset 0x01)
17762306a36Sopenharmony_ci		 * B0 B1 B2 B3 B4 B5 B6 B7
17862306a36Sopenharmony_ci		 */
17962306a36Sopenharmony_ci		710700, 1421000, 2843000, 5685000, 11371000, 22742000,
18062306a36Sopenharmony_ci		45484000, 90968000,
18162306a36Sopenharmony_ci	};
18262306a36Sopenharmony_ci	unsigned long total_acc_charger;
18362306a36Sopenharmony_ci	unsigned int reg;
18462306a36Sopenharmony_ci	int i, ret;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	ret = regmap_bulk_read(info->regmap, UCS1002_REG_TOTAL_ACC_CHARGE,
18762306a36Sopenharmony_ci			       &reg, sizeof(u32));
18862306a36Sopenharmony_ci	if (ret)
18962306a36Sopenharmony_ci		return ret;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	total_acc_charger = be32_to_cpu(reg); /* BE as per offsets above */
19262306a36Sopenharmony_ci	val->intval = 0;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	for_each_set_bit(i, &total_acc_charger, ARRAY_SIZE(bit_weights_uAh))
19562306a36Sopenharmony_ci		val->intval += bit_weights_uAh[i];
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return 0;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic int ucs1002_get_current(struct ucs1002_info *info,
20162306a36Sopenharmony_ci			       union power_supply_propval *val)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	/*
20462306a36Sopenharmony_ci	 * The Current Measurement register stores the measured
20562306a36Sopenharmony_ci	 * current value delivered to the portable device. The range
20662306a36Sopenharmony_ci	 * is from 9.76 mA to 2.5 A.
20762306a36Sopenharmony_ci	 */
20862306a36Sopenharmony_ci	static const int bit_weights_uA[BITS_PER_TYPE(u8)] = {
20962306a36Sopenharmony_ci		9760, 19500, 39000, 78100, 156200, 312300, 624600, 1249300,
21062306a36Sopenharmony_ci	};
21162306a36Sopenharmony_ci	unsigned long current_measurement;
21262306a36Sopenharmony_ci	unsigned int reg;
21362306a36Sopenharmony_ci	int i, ret;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	ret = regmap_read(info->regmap, UCS1002_REG_CURRENT_MEASUREMENT, &reg);
21662306a36Sopenharmony_ci	if (ret)
21762306a36Sopenharmony_ci		return ret;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	current_measurement = reg;
22062306a36Sopenharmony_ci	val->intval = 0;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	for_each_set_bit(i, &current_measurement, ARRAY_SIZE(bit_weights_uA))
22362306a36Sopenharmony_ci		val->intval += bit_weights_uA[i];
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	return 0;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci/*
22962306a36Sopenharmony_ci * The Current Limit register stores the maximum current used by the
23062306a36Sopenharmony_ci * port switch. The range is from 500mA to 2.5 A.
23162306a36Sopenharmony_ci */
23262306a36Sopenharmony_cistatic const u32 ucs1002_current_limit_uA[] = {
23362306a36Sopenharmony_ci	500000, 900000, 1000000, 1200000, 1500000, 1800000, 2000000, 2500000,
23462306a36Sopenharmony_ci};
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic int ucs1002_get_max_current(struct ucs1002_info *info,
23762306a36Sopenharmony_ci				   union power_supply_propval *val)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	unsigned int reg;
24062306a36Sopenharmony_ci	int ret;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	if (info->output_disable) {
24362306a36Sopenharmony_ci		val->intval = 0;
24462306a36Sopenharmony_ci		return 0;
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	ret = regmap_read(info->regmap, UCS1002_REG_ILIMIT, &reg);
24862306a36Sopenharmony_ci	if (ret)
24962306a36Sopenharmony_ci		return ret;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	val->intval = ucs1002_current_limit_uA[reg & UCS1002_ILIM_SW_MASK];
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	return 0;
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic int ucs1002_set_max_current(struct ucs1002_info *info, u32 val)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	unsigned int reg;
25962306a36Sopenharmony_ci	int ret, idx;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	if (val == 0) {
26262306a36Sopenharmony_ci		info->output_disable = true;
26362306a36Sopenharmony_ci		regulator_disable_regmap(info->rdev);
26462306a36Sopenharmony_ci		return 0;
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(ucs1002_current_limit_uA); idx++) {
26862306a36Sopenharmony_ci		if (val == ucs1002_current_limit_uA[idx])
26962306a36Sopenharmony_ci			break;
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	if (idx == ARRAY_SIZE(ucs1002_current_limit_uA))
27362306a36Sopenharmony_ci		return -EINVAL;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	ret = regmap_write(info->regmap, UCS1002_REG_ILIMIT, idx);
27662306a36Sopenharmony_ci	if (ret)
27762306a36Sopenharmony_ci		return ret;
27862306a36Sopenharmony_ci	/*
27962306a36Sopenharmony_ci	 * Any current limit setting exceeding the one set via ILIM
28062306a36Sopenharmony_ci	 * pin will be rejected, so we read out freshly changed limit
28162306a36Sopenharmony_ci	 * to make sure that it took effect.
28262306a36Sopenharmony_ci	 */
28362306a36Sopenharmony_ci	ret = regmap_read(info->regmap, UCS1002_REG_ILIMIT, &reg);
28462306a36Sopenharmony_ci	if (ret)
28562306a36Sopenharmony_ci		return ret;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	if (reg != idx)
28862306a36Sopenharmony_ci		return -EINVAL;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	info->output_disable = false;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	if (info->rdev && info->rdev->use_count &&
29362306a36Sopenharmony_ci	    !regulator_is_enabled_regmap(info->rdev))
29462306a36Sopenharmony_ci		regulator_enable_regmap(info->rdev);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	return 0;
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic enum power_supply_usb_type ucs1002_usb_types[] = {
30062306a36Sopenharmony_ci	POWER_SUPPLY_USB_TYPE_PD,
30162306a36Sopenharmony_ci	POWER_SUPPLY_USB_TYPE_SDP,
30262306a36Sopenharmony_ci	POWER_SUPPLY_USB_TYPE_DCP,
30362306a36Sopenharmony_ci	POWER_SUPPLY_USB_TYPE_CDP,
30462306a36Sopenharmony_ci	POWER_SUPPLY_USB_TYPE_UNKNOWN,
30562306a36Sopenharmony_ci};
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic int ucs1002_set_usb_type(struct ucs1002_info *info, int val)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	unsigned int mode;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	if (val < 0 || val >= ARRAY_SIZE(ucs1002_usb_types))
31262306a36Sopenharmony_ci		return -EINVAL;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	switch (ucs1002_usb_types[val]) {
31562306a36Sopenharmony_ci	case POWER_SUPPLY_USB_TYPE_PD:
31662306a36Sopenharmony_ci		mode = V_SET_ACTIVE_MODE_DEDICATED;
31762306a36Sopenharmony_ci		break;
31862306a36Sopenharmony_ci	case POWER_SUPPLY_USB_TYPE_SDP:
31962306a36Sopenharmony_ci		mode = V_SET_ACTIVE_MODE_BC12_SDP;
32062306a36Sopenharmony_ci		break;
32162306a36Sopenharmony_ci	case POWER_SUPPLY_USB_TYPE_DCP:
32262306a36Sopenharmony_ci		mode = V_SET_ACTIVE_MODE_BC12_DCP;
32362306a36Sopenharmony_ci		break;
32462306a36Sopenharmony_ci	case POWER_SUPPLY_USB_TYPE_CDP:
32562306a36Sopenharmony_ci		mode = V_SET_ACTIVE_MODE_BC12_CDP;
32662306a36Sopenharmony_ci		break;
32762306a36Sopenharmony_ci	default:
32862306a36Sopenharmony_ci		return -EINVAL;
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	return regmap_update_bits(info->regmap, UCS1002_REG_SWITCH_CFG,
33262306a36Sopenharmony_ci				  V_SET_ACTIVE_MODE_MASK, mode);
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic int ucs1002_get_usb_type(struct ucs1002_info *info,
33662306a36Sopenharmony_ci				union power_supply_propval *val)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	enum power_supply_usb_type type;
33962306a36Sopenharmony_ci	unsigned int reg;
34062306a36Sopenharmony_ci	int ret;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	ret = regmap_read(info->regmap, UCS1002_REG_PIN_STATUS, &reg);
34362306a36Sopenharmony_ci	if (ret)
34462306a36Sopenharmony_ci		return ret;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	switch (reg & F_ACTIVE_MODE_MASK) {
34762306a36Sopenharmony_ci	default:
34862306a36Sopenharmony_ci		type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
34962306a36Sopenharmony_ci		break;
35062306a36Sopenharmony_ci	case F_ACTIVE_MODE_DEDICATED:
35162306a36Sopenharmony_ci		type = POWER_SUPPLY_USB_TYPE_PD;
35262306a36Sopenharmony_ci		break;
35362306a36Sopenharmony_ci	case F_ACTIVE_MODE_BC12_SDP:
35462306a36Sopenharmony_ci		type = POWER_SUPPLY_USB_TYPE_SDP;
35562306a36Sopenharmony_ci		break;
35662306a36Sopenharmony_ci	case F_ACTIVE_MODE_BC12_DCP:
35762306a36Sopenharmony_ci		type = POWER_SUPPLY_USB_TYPE_DCP;
35862306a36Sopenharmony_ci		break;
35962306a36Sopenharmony_ci	case F_ACTIVE_MODE_BC12_CDP:
36062306a36Sopenharmony_ci		type = POWER_SUPPLY_USB_TYPE_CDP;
36162306a36Sopenharmony_ci		break;
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	val->intval = type;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	return 0;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic int ucs1002_get_property(struct power_supply *psy,
37062306a36Sopenharmony_ci				enum power_supply_property psp,
37162306a36Sopenharmony_ci				union power_supply_propval *val)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	struct ucs1002_info *info = power_supply_get_drvdata(psy);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	switch (psp) {
37662306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
37762306a36Sopenharmony_ci		return ucs1002_get_online(info, val);
37862306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_NOW:
37962306a36Sopenharmony_ci		return ucs1002_get_charge(info, val);
38062306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_NOW:
38162306a36Sopenharmony_ci		return ucs1002_get_current(info, val);
38262306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_MAX:
38362306a36Sopenharmony_ci		return ucs1002_get_max_current(info, val);
38462306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_USB_TYPE:
38562306a36Sopenharmony_ci		return ucs1002_get_usb_type(info, val);
38662306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_HEALTH:
38762306a36Sopenharmony_ci		val->intval = info->health;
38862306a36Sopenharmony_ci		return 0;
38962306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_PRESENT:
39062306a36Sopenharmony_ci		val->intval = info->present;
39162306a36Sopenharmony_ci		return 0;
39262306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_MANUFACTURER:
39362306a36Sopenharmony_ci		val->strval = UCS1002_MANUFACTURER;
39462306a36Sopenharmony_ci		return 0;
39562306a36Sopenharmony_ci	default:
39662306a36Sopenharmony_ci		return -EINVAL;
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic int ucs1002_set_property(struct power_supply *psy,
40162306a36Sopenharmony_ci				enum power_supply_property psp,
40262306a36Sopenharmony_ci				const union power_supply_propval *val)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	struct ucs1002_info *info = power_supply_get_drvdata(psy);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	switch (psp) {
40762306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_MAX:
40862306a36Sopenharmony_ci		return ucs1002_set_max_current(info, val->intval);
40962306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_USB_TYPE:
41062306a36Sopenharmony_ci		return ucs1002_set_usb_type(info, val->intval);
41162306a36Sopenharmony_ci	default:
41262306a36Sopenharmony_ci		return -EINVAL;
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_cistatic int ucs1002_property_is_writeable(struct power_supply *psy,
41762306a36Sopenharmony_ci					 enum power_supply_property psp)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_ci	switch (psp) {
42062306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_MAX:
42162306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_USB_TYPE:
42262306a36Sopenharmony_ci		return true;
42362306a36Sopenharmony_ci	default:
42462306a36Sopenharmony_ci		return false;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic const struct power_supply_desc ucs1002_charger_desc = {
42962306a36Sopenharmony_ci	.name			= "ucs1002",
43062306a36Sopenharmony_ci	.type			= POWER_SUPPLY_TYPE_USB,
43162306a36Sopenharmony_ci	.usb_types		= ucs1002_usb_types,
43262306a36Sopenharmony_ci	.num_usb_types		= ARRAY_SIZE(ucs1002_usb_types),
43362306a36Sopenharmony_ci	.get_property		= ucs1002_get_property,
43462306a36Sopenharmony_ci	.set_property		= ucs1002_set_property,
43562306a36Sopenharmony_ci	.property_is_writeable	= ucs1002_property_is_writeable,
43662306a36Sopenharmony_ci	.properties		= ucs1002_props,
43762306a36Sopenharmony_ci	.num_properties		= ARRAY_SIZE(ucs1002_props),
43862306a36Sopenharmony_ci};
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cistatic void ucs1002_health_poll(struct work_struct *work)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	struct ucs1002_info *info = container_of(work, struct ucs1002_info,
44362306a36Sopenharmony_ci						 health_poll.work);
44462306a36Sopenharmony_ci	int ret;
44562306a36Sopenharmony_ci	u32 reg;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	ret = regmap_read(info->regmap, UCS1002_REG_INTERRUPT_STATUS, &reg);
44862306a36Sopenharmony_ci	if (ret)
44962306a36Sopenharmony_ci		return;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	/* bad health and no status change, just schedule us again in a while */
45262306a36Sopenharmony_ci	if ((reg & F_ERR) && info->health != POWER_SUPPLY_HEALTH_GOOD) {
45362306a36Sopenharmony_ci		schedule_delayed_work(&info->health_poll,
45462306a36Sopenharmony_ci				      msecs_to_jiffies(2000));
45562306a36Sopenharmony_ci		return;
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	if (reg & F_TSD)
45962306a36Sopenharmony_ci		info->health = POWER_SUPPLY_HEALTH_OVERHEAT;
46062306a36Sopenharmony_ci	else if (reg & (F_OVER_VOLT | F_BACK_VOLT))
46162306a36Sopenharmony_ci		info->health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
46262306a36Sopenharmony_ci	else if (reg & F_OVER_ILIM)
46362306a36Sopenharmony_ci		info->health = POWER_SUPPLY_HEALTH_OVERCURRENT;
46462306a36Sopenharmony_ci	else if (reg & (F_DISCHARGE_ERR | F_MIN_KEEP_OUT))
46562306a36Sopenharmony_ci		info->health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
46662306a36Sopenharmony_ci	else
46762306a36Sopenharmony_ci		info->health = POWER_SUPPLY_HEALTH_GOOD;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	sysfs_notify(&info->charger->dev.kobj, NULL, "health");
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic irqreturn_t ucs1002_charger_irq(int irq, void *data)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	int ret, regval;
47562306a36Sopenharmony_ci	bool present;
47662306a36Sopenharmony_ci	struct ucs1002_info *info = data;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	present = info->present;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	ret = regmap_read(info->regmap, UCS1002_REG_OTHER_STATUS, &regval);
48162306a36Sopenharmony_ci	if (ret)
48262306a36Sopenharmony_ci		return IRQ_HANDLED;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	/* update attached status */
48562306a36Sopenharmony_ci	info->present = regval & F_ADET_PIN;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	/* notify the change */
48862306a36Sopenharmony_ci	if (present != info->present)
48962306a36Sopenharmony_ci		power_supply_changed(info->charger);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	return IRQ_HANDLED;
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_cistatic irqreturn_t ucs1002_alert_irq(int irq, void *data)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	struct ucs1002_info *info = data;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	mod_delayed_work(system_wq, &info->health_poll, 0);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	return IRQ_HANDLED;
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_cistatic int ucs1002_regulator_enable(struct regulator_dev *rdev)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci	struct ucs1002_info *info = rdev_get_drvdata(rdev);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	/*
50862306a36Sopenharmony_ci	 * If the output is disabled due to 0 maximum current, just pretend the
50962306a36Sopenharmony_ci	 * enable did work. The regulator will be enabled as soon as we get a
51062306a36Sopenharmony_ci	 * a non-zero maximum current budget.
51162306a36Sopenharmony_ci	 */
51262306a36Sopenharmony_ci	if (info->output_disable)
51362306a36Sopenharmony_ci		return 0;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	return regulator_enable_regmap(rdev);
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_cistatic const struct regulator_ops ucs1002_regulator_ops = {
51962306a36Sopenharmony_ci	.is_enabled	= regulator_is_enabled_regmap,
52062306a36Sopenharmony_ci	.enable		= ucs1002_regulator_enable,
52162306a36Sopenharmony_ci	.disable	= regulator_disable_regmap,
52262306a36Sopenharmony_ci};
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic const struct regulator_desc ucs1002_regulator_descriptor = {
52562306a36Sopenharmony_ci	.name		= "ucs1002-vbus",
52662306a36Sopenharmony_ci	.ops		= &ucs1002_regulator_ops,
52762306a36Sopenharmony_ci	.type		= REGULATOR_VOLTAGE,
52862306a36Sopenharmony_ci	.owner		= THIS_MODULE,
52962306a36Sopenharmony_ci	.enable_reg	= UCS1002_REG_SWITCH_CFG,
53062306a36Sopenharmony_ci	.enable_mask	= F_PWR_EN_SET,
53162306a36Sopenharmony_ci	.enable_val	= F_PWR_EN_SET,
53262306a36Sopenharmony_ci	.fixed_uV	= 5000000,
53362306a36Sopenharmony_ci	.n_voltages	= 1,
53462306a36Sopenharmony_ci};
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_cistatic int ucs1002_probe(struct i2c_client *client)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	struct device *dev = &client->dev;
53962306a36Sopenharmony_ci	struct power_supply_config charger_config = {};
54062306a36Sopenharmony_ci	const struct regmap_config regmap_config = {
54162306a36Sopenharmony_ci		.reg_bits = 8,
54262306a36Sopenharmony_ci		.val_bits = 8,
54362306a36Sopenharmony_ci	};
54462306a36Sopenharmony_ci	struct regulator_config regulator_config = {};
54562306a36Sopenharmony_ci	int irq_a_det, irq_alert, ret;
54662306a36Sopenharmony_ci	struct ucs1002_info *info;
54762306a36Sopenharmony_ci	unsigned int regval;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
55062306a36Sopenharmony_ci	if (!info)
55162306a36Sopenharmony_ci		return -ENOMEM;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	info->regmap = devm_regmap_init_i2c(client, &regmap_config);
55462306a36Sopenharmony_ci	ret = PTR_ERR_OR_ZERO(info->regmap);
55562306a36Sopenharmony_ci	if (ret) {
55662306a36Sopenharmony_ci		dev_err(dev, "Regmap initialization failed: %d\n", ret);
55762306a36Sopenharmony_ci		return ret;
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	info->client = client;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	irq_a_det = of_irq_get_byname(dev->of_node, "a_det");
56362306a36Sopenharmony_ci	irq_alert = of_irq_get_byname(dev->of_node, "alert");
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	charger_config.of_node = dev->of_node;
56662306a36Sopenharmony_ci	charger_config.drv_data = info;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	ret = regmap_read(info->regmap, UCS1002_REG_PRODUCT_ID, &regval);
56962306a36Sopenharmony_ci	if (ret) {
57062306a36Sopenharmony_ci		dev_err(dev, "Failed to read product ID: %d\n", ret);
57162306a36Sopenharmony_ci		return ret;
57262306a36Sopenharmony_ci	}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	if (regval != UCS1002_PRODUCT_ID) {
57562306a36Sopenharmony_ci		dev_err(dev,
57662306a36Sopenharmony_ci			"Product ID does not match (0x%02x != 0x%02x)\n",
57762306a36Sopenharmony_ci			regval, UCS1002_PRODUCT_ID);
57862306a36Sopenharmony_ci		return -ENODEV;
57962306a36Sopenharmony_ci	}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	/* Enable charge rationing by default */
58262306a36Sopenharmony_ci	ret = regmap_update_bits(info->regmap, UCS1002_REG_GENERAL_CFG,
58362306a36Sopenharmony_ci				 F_RATION_EN, F_RATION_EN);
58462306a36Sopenharmony_ci	if (ret) {
58562306a36Sopenharmony_ci		dev_err(dev, "Failed to read general config: %d\n", ret);
58662306a36Sopenharmony_ci		return ret;
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	/*
59062306a36Sopenharmony_ci	 * Ignore the M1, M2, PWR_EN, and EM_EN pin states. Set active
59162306a36Sopenharmony_ci	 * mode selection to BC1.2 CDP.
59262306a36Sopenharmony_ci	 */
59362306a36Sopenharmony_ci	ret = regmap_update_bits(info->regmap, UCS1002_REG_SWITCH_CFG,
59462306a36Sopenharmony_ci				 V_SET_ACTIVE_MODE_MASK | F_PIN_IGNORE,
59562306a36Sopenharmony_ci				 V_SET_ACTIVE_MODE_BC12_CDP | F_PIN_IGNORE);
59662306a36Sopenharmony_ci	if (ret) {
59762306a36Sopenharmony_ci		dev_err(dev, "Failed to configure default mode: %d\n", ret);
59862306a36Sopenharmony_ci		return ret;
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci	/*
60162306a36Sopenharmony_ci	 * Be safe and set initial current limit to 500mA
60262306a36Sopenharmony_ci	 */
60362306a36Sopenharmony_ci	ret = ucs1002_set_max_current(info, 500000);
60462306a36Sopenharmony_ci	if (ret) {
60562306a36Sopenharmony_ci		dev_err(dev, "Failed to set max current default: %d\n", ret);
60662306a36Sopenharmony_ci		return ret;
60762306a36Sopenharmony_ci	}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	info->charger = devm_power_supply_register(dev, &ucs1002_charger_desc,
61062306a36Sopenharmony_ci						   &charger_config);
61162306a36Sopenharmony_ci	ret = PTR_ERR_OR_ZERO(info->charger);
61262306a36Sopenharmony_ci	if (ret) {
61362306a36Sopenharmony_ci		dev_err(dev, "Failed to register power supply: %d\n", ret);
61462306a36Sopenharmony_ci		return ret;
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	ret = regmap_read(info->regmap, UCS1002_REG_PIN_STATUS, &regval);
61862306a36Sopenharmony_ci	if (ret) {
61962306a36Sopenharmony_ci		dev_err(dev, "Failed to read pin status: %d\n", ret);
62062306a36Sopenharmony_ci		return ret;
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	info->regulator_descriptor =
62462306a36Sopenharmony_ci		devm_kmemdup(dev, &ucs1002_regulator_descriptor,
62562306a36Sopenharmony_ci			     sizeof(ucs1002_regulator_descriptor),
62662306a36Sopenharmony_ci			     GFP_KERNEL);
62762306a36Sopenharmony_ci	if (!info->regulator_descriptor)
62862306a36Sopenharmony_ci		return -ENOMEM;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	info->regulator_descriptor->enable_is_inverted = !(regval & F_SEL_PIN);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	regulator_config.dev = dev;
63362306a36Sopenharmony_ci	regulator_config.of_node = dev->of_node;
63462306a36Sopenharmony_ci	regulator_config.regmap = info->regmap;
63562306a36Sopenharmony_ci	regulator_config.driver_data = info;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	info->rdev = devm_regulator_register(dev, info->regulator_descriptor,
63862306a36Sopenharmony_ci				       &regulator_config);
63962306a36Sopenharmony_ci	ret = PTR_ERR_OR_ZERO(info->rdev);
64062306a36Sopenharmony_ci	if (ret) {
64162306a36Sopenharmony_ci		dev_err(dev, "Failed to register VBUS regulator: %d\n", ret);
64262306a36Sopenharmony_ci		return ret;
64362306a36Sopenharmony_ci	}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	info->health = POWER_SUPPLY_HEALTH_GOOD;
64662306a36Sopenharmony_ci	INIT_DELAYED_WORK(&info->health_poll, ucs1002_health_poll);
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	if (irq_a_det > 0) {
64962306a36Sopenharmony_ci		ret = devm_request_threaded_irq(dev, irq_a_det, NULL,
65062306a36Sopenharmony_ci						ucs1002_charger_irq,
65162306a36Sopenharmony_ci						IRQF_ONESHOT,
65262306a36Sopenharmony_ci						"ucs1002-a_det", info);
65362306a36Sopenharmony_ci		if (ret) {
65462306a36Sopenharmony_ci			dev_err(dev, "Failed to request A_DET threaded irq: %d\n",
65562306a36Sopenharmony_ci				ret);
65662306a36Sopenharmony_ci			return ret;
65762306a36Sopenharmony_ci		}
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	if (irq_alert > 0) {
66162306a36Sopenharmony_ci		ret = devm_request_irq(dev, irq_alert, ucs1002_alert_irq,
66262306a36Sopenharmony_ci				       0,"ucs1002-alert", info);
66362306a36Sopenharmony_ci		if (ret) {
66462306a36Sopenharmony_ci			dev_err(dev, "Failed to request ALERT threaded irq: %d\n",
66562306a36Sopenharmony_ci				ret);
66662306a36Sopenharmony_ci			return ret;
66762306a36Sopenharmony_ci		}
66862306a36Sopenharmony_ci	}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	return 0;
67162306a36Sopenharmony_ci}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_cistatic const struct of_device_id ucs1002_of_match[] = {
67462306a36Sopenharmony_ci	{ .compatible = "microchip,ucs1002", },
67562306a36Sopenharmony_ci	{ /* sentinel */ },
67662306a36Sopenharmony_ci};
67762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ucs1002_of_match);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_cistatic struct i2c_driver ucs1002_driver = {
68062306a36Sopenharmony_ci	.driver = {
68162306a36Sopenharmony_ci		   .name = "ucs1002",
68262306a36Sopenharmony_ci		   .of_match_table = ucs1002_of_match,
68362306a36Sopenharmony_ci	},
68462306a36Sopenharmony_ci	.probe = ucs1002_probe,
68562306a36Sopenharmony_ci};
68662306a36Sopenharmony_cimodule_i2c_driver(ucs1002_driver);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ciMODULE_DESCRIPTION("Microchip UCS1002 Programmable USB Port Power Controller");
68962306a36Sopenharmony_ciMODULE_AUTHOR("Enric Balletbo Serra <enric.balletbo@collabora.com>");
69062306a36Sopenharmony_ciMODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
69162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
692