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, ®); 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 ®, 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, ®); 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, ¤t_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, ®); 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, ®); 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, ®); 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, ®); 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, ®val); 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, ®map_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, ®val); 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, ®val); 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 ®ulator_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