18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for UCS1002 Programmable USB Port Power Controller 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2019 Zodiac Inflight Innovations 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/bits.h> 88c2ecf20Sopenharmony_ci#include <linux/freezer.h> 98c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 108c2ecf20Sopenharmony_ci#include <linux/i2c.h> 118c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/kthread.h> 148c2ecf20Sopenharmony_ci#include <linux/device.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/of.h> 178c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 188c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 198c2ecf20Sopenharmony_ci#include <linux/regmap.h> 208c2ecf20Sopenharmony_ci#include <linux/regulator/driver.h> 218c2ecf20Sopenharmony_ci#include <linux/regulator/of_regulator.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* UCS1002 Registers */ 248c2ecf20Sopenharmony_ci#define UCS1002_REG_CURRENT_MEASUREMENT 0x00 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* 278c2ecf20Sopenharmony_ci * The Total Accumulated Charge registers store the total accumulated 288c2ecf20Sopenharmony_ci * charge delivered from the VS source to a portable device. The total 298c2ecf20Sopenharmony_ci * value is calculated using four registers, from 01h to 04h. The bit 308c2ecf20Sopenharmony_ci * weighting of the registers is given in mA/hrs. 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci#define UCS1002_REG_TOTAL_ACC_CHARGE 0x01 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* Other Status Register */ 358c2ecf20Sopenharmony_ci#define UCS1002_REG_OTHER_STATUS 0x0f 368c2ecf20Sopenharmony_ci# define F_ADET_PIN BIT(4) 378c2ecf20Sopenharmony_ci# define F_CHG_ACT BIT(3) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* Interrupt Status */ 408c2ecf20Sopenharmony_ci#define UCS1002_REG_INTERRUPT_STATUS 0x10 418c2ecf20Sopenharmony_ci# define F_ERR BIT(7) 428c2ecf20Sopenharmony_ci# define F_DISCHARGE_ERR BIT(6) 438c2ecf20Sopenharmony_ci# define F_RESET BIT(5) 448c2ecf20Sopenharmony_ci# define F_MIN_KEEP_OUT BIT(4) 458c2ecf20Sopenharmony_ci# define F_TSD BIT(3) 468c2ecf20Sopenharmony_ci# define F_OVER_VOLT BIT(2) 478c2ecf20Sopenharmony_ci# define F_BACK_VOLT BIT(1) 488c2ecf20Sopenharmony_ci# define F_OVER_ILIM BIT(0) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* Pin Status Register */ 518c2ecf20Sopenharmony_ci#define UCS1002_REG_PIN_STATUS 0x14 528c2ecf20Sopenharmony_ci# define UCS1002_PWR_STATE_MASK 0x03 538c2ecf20Sopenharmony_ci# define F_PWR_EN_PIN BIT(6) 548c2ecf20Sopenharmony_ci# define F_M2_PIN BIT(5) 558c2ecf20Sopenharmony_ci# define F_M1_PIN BIT(4) 568c2ecf20Sopenharmony_ci# define F_EM_EN_PIN BIT(3) 578c2ecf20Sopenharmony_ci# define F_SEL_PIN BIT(2) 588c2ecf20Sopenharmony_ci# define F_ACTIVE_MODE_MASK GENMASK(5, 3) 598c2ecf20Sopenharmony_ci# define F_ACTIVE_MODE_PASSTHROUGH F_M2_PIN 608c2ecf20Sopenharmony_ci# define F_ACTIVE_MODE_DEDICATED F_EM_EN_PIN 618c2ecf20Sopenharmony_ci# define F_ACTIVE_MODE_BC12_DCP (F_M2_PIN | F_EM_EN_PIN) 628c2ecf20Sopenharmony_ci# define F_ACTIVE_MODE_BC12_SDP F_M1_PIN 638c2ecf20Sopenharmony_ci# define F_ACTIVE_MODE_BC12_CDP (F_M1_PIN | F_M2_PIN | F_EM_EN_PIN) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* General Configuration Register */ 668c2ecf20Sopenharmony_ci#define UCS1002_REG_GENERAL_CFG 0x15 678c2ecf20Sopenharmony_ci# define F_RATION_EN BIT(3) 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* Emulation Configuration Register */ 708c2ecf20Sopenharmony_ci#define UCS1002_REG_EMU_CFG 0x16 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* Switch Configuration Register */ 738c2ecf20Sopenharmony_ci#define UCS1002_REG_SWITCH_CFG 0x17 748c2ecf20Sopenharmony_ci# define F_PIN_IGNORE BIT(7) 758c2ecf20Sopenharmony_ci# define F_EM_EN_SET BIT(5) 768c2ecf20Sopenharmony_ci# define F_M2_SET BIT(4) 778c2ecf20Sopenharmony_ci# define F_M1_SET BIT(3) 788c2ecf20Sopenharmony_ci# define F_S0_SET BIT(2) 798c2ecf20Sopenharmony_ci# define F_PWR_EN_SET BIT(1) 808c2ecf20Sopenharmony_ci# define F_LATCH_SET BIT(0) 818c2ecf20Sopenharmony_ci# define V_SET_ACTIVE_MODE_MASK GENMASK(5, 3) 828c2ecf20Sopenharmony_ci# define V_SET_ACTIVE_MODE_PASSTHROUGH F_M2_SET 838c2ecf20Sopenharmony_ci# define V_SET_ACTIVE_MODE_DEDICATED F_EM_EN_SET 848c2ecf20Sopenharmony_ci# define V_SET_ACTIVE_MODE_BC12_DCP (F_M2_SET | F_EM_EN_SET) 858c2ecf20Sopenharmony_ci# define V_SET_ACTIVE_MODE_BC12_SDP F_M1_SET 868c2ecf20Sopenharmony_ci# define V_SET_ACTIVE_MODE_BC12_CDP (F_M1_SET | F_M2_SET | F_EM_EN_SET) 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* Current Limit Register */ 898c2ecf20Sopenharmony_ci#define UCS1002_REG_ILIMIT 0x19 908c2ecf20Sopenharmony_ci# define UCS1002_ILIM_SW_MASK GENMASK(3, 0) 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* Product ID */ 938c2ecf20Sopenharmony_ci#define UCS1002_REG_PRODUCT_ID 0xfd 948c2ecf20Sopenharmony_ci# define UCS1002_PRODUCT_ID 0x4e 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci/* Manufacture name */ 978c2ecf20Sopenharmony_ci#define UCS1002_MANUFACTURER "SMSC" 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistruct ucs1002_info { 1008c2ecf20Sopenharmony_ci struct power_supply *charger; 1018c2ecf20Sopenharmony_ci struct i2c_client *client; 1028c2ecf20Sopenharmony_ci struct regmap *regmap; 1038c2ecf20Sopenharmony_ci struct regulator_desc *regulator_descriptor; 1048c2ecf20Sopenharmony_ci struct regulator_dev *rdev; 1058c2ecf20Sopenharmony_ci bool present; 1068c2ecf20Sopenharmony_ci bool output_disable; 1078c2ecf20Sopenharmony_ci struct delayed_work health_poll; 1088c2ecf20Sopenharmony_ci int health; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic enum power_supply_property ucs1002_props[] = { 1138c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 1148c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_NOW, 1158c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_NOW, 1168c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_MAX, 1178c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, /* the presence of PED */ 1188c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_MANUFACTURER, 1198c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_USB_TYPE, 1208c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, 1218c2ecf20Sopenharmony_ci}; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int ucs1002_get_online(struct ucs1002_info *info, 1248c2ecf20Sopenharmony_ci union power_supply_propval *val) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci unsigned int reg; 1278c2ecf20Sopenharmony_ci int ret; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, UCS1002_REG_OTHER_STATUS, ®); 1308c2ecf20Sopenharmony_ci if (ret) 1318c2ecf20Sopenharmony_ci return ret; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci val->intval = !!(reg & F_CHG_ACT); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return 0; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic int ucs1002_get_charge(struct ucs1002_info *info, 1398c2ecf20Sopenharmony_ci union power_supply_propval *val) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci /* 1428c2ecf20Sopenharmony_ci * To fit within 32 bits some values are rounded (uA/h) 1438c2ecf20Sopenharmony_ci * 1448c2ecf20Sopenharmony_ci * For Total Accumulated Charge Middle Low Byte register, addr 1458c2ecf20Sopenharmony_ci * 03h, byte 2 1468c2ecf20Sopenharmony_ci * 1478c2ecf20Sopenharmony_ci * B0: 0.01084 mA/h rounded to 11 uA/h 1488c2ecf20Sopenharmony_ci * B1: 0.02169 mA/h rounded to 22 uA/h 1498c2ecf20Sopenharmony_ci * B2: 0.04340 mA/h rounded to 43 uA/h 1508c2ecf20Sopenharmony_ci * B3: 0.08676 mA/h rounded to 87 uA/h 1518c2ecf20Sopenharmony_ci * B4: 0.17350 mA/h rounded to 173 uÁ/h 1528c2ecf20Sopenharmony_ci * 1538c2ecf20Sopenharmony_ci * For Total Accumulated Charge Low Byte register, addr 04h, 1548c2ecf20Sopenharmony_ci * byte 3 1558c2ecf20Sopenharmony_ci * 1568c2ecf20Sopenharmony_ci * B6: 0.00271 mA/h rounded to 3 uA/h 1578c2ecf20Sopenharmony_ci * B7: 0.005422 mA/h rounded to 5 uA/h 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_ci static const int bit_weights_uAh[BITS_PER_TYPE(u32)] = { 1608c2ecf20Sopenharmony_ci /* 1618c2ecf20Sopenharmony_ci * Bit corresponding to low byte (offset 0x04) 1628c2ecf20Sopenharmony_ci * B0 B1 B2 B3 B4 B5 B6 B7 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_ci 0, 0, 0, 0, 0, 0, 3, 5, 1658c2ecf20Sopenharmony_ci /* 1668c2ecf20Sopenharmony_ci * Bit corresponding to middle low byte (offset 0x03) 1678c2ecf20Sopenharmony_ci * B0 B1 B2 B3 B4 B5 B6 B7 1688c2ecf20Sopenharmony_ci */ 1698c2ecf20Sopenharmony_ci 11, 22, 43, 87, 173, 347, 694, 1388, 1708c2ecf20Sopenharmony_ci /* 1718c2ecf20Sopenharmony_ci * Bit corresponding to middle high byte (offset 0x02) 1728c2ecf20Sopenharmony_ci * B0 B1 B2 B3 B4 B5 B6 B7 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_ci 2776, 5552, 11105, 22210, 44420, 88840, 177700, 355400, 1758c2ecf20Sopenharmony_ci /* 1768c2ecf20Sopenharmony_ci * Bit corresponding to high byte (offset 0x01) 1778c2ecf20Sopenharmony_ci * B0 B1 B2 B3 B4 B5 B6 B7 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_ci 710700, 1421000, 2843000, 5685000, 11371000, 22742000, 1808c2ecf20Sopenharmony_ci 45484000, 90968000, 1818c2ecf20Sopenharmony_ci }; 1828c2ecf20Sopenharmony_ci unsigned long total_acc_charger; 1838c2ecf20Sopenharmony_ci unsigned int reg; 1848c2ecf20Sopenharmony_ci int i, ret; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci ret = regmap_bulk_read(info->regmap, UCS1002_REG_TOTAL_ACC_CHARGE, 1878c2ecf20Sopenharmony_ci ®, sizeof(u32)); 1888c2ecf20Sopenharmony_ci if (ret) 1898c2ecf20Sopenharmony_ci return ret; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci total_acc_charger = be32_to_cpu(reg); /* BE as per offsets above */ 1928c2ecf20Sopenharmony_ci val->intval = 0; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci for_each_set_bit(i, &total_acc_charger, ARRAY_SIZE(bit_weights_uAh)) 1958c2ecf20Sopenharmony_ci val->intval += bit_weights_uAh[i]; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci return 0; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic int ucs1002_get_current(struct ucs1002_info *info, 2018c2ecf20Sopenharmony_ci union power_supply_propval *val) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci /* 2048c2ecf20Sopenharmony_ci * The Current Measurement register stores the measured 2058c2ecf20Sopenharmony_ci * current value delivered to the portable device. The range 2068c2ecf20Sopenharmony_ci * is from 9.76 mA to 2.5 A. 2078c2ecf20Sopenharmony_ci */ 2088c2ecf20Sopenharmony_ci static const int bit_weights_uA[BITS_PER_TYPE(u8)] = { 2098c2ecf20Sopenharmony_ci 9760, 19500, 39000, 78100, 156200, 312300, 624600, 1249300, 2108c2ecf20Sopenharmony_ci }; 2118c2ecf20Sopenharmony_ci unsigned long current_measurement; 2128c2ecf20Sopenharmony_ci unsigned int reg; 2138c2ecf20Sopenharmony_ci int i, ret; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, UCS1002_REG_CURRENT_MEASUREMENT, ®); 2168c2ecf20Sopenharmony_ci if (ret) 2178c2ecf20Sopenharmony_ci return ret; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci current_measurement = reg; 2208c2ecf20Sopenharmony_ci val->intval = 0; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci for_each_set_bit(i, ¤t_measurement, ARRAY_SIZE(bit_weights_uA)) 2238c2ecf20Sopenharmony_ci val->intval += bit_weights_uA[i]; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return 0; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci/* 2298c2ecf20Sopenharmony_ci * The Current Limit register stores the maximum current used by the 2308c2ecf20Sopenharmony_ci * port switch. The range is from 500mA to 2.5 A. 2318c2ecf20Sopenharmony_ci */ 2328c2ecf20Sopenharmony_cistatic const u32 ucs1002_current_limit_uA[] = { 2338c2ecf20Sopenharmony_ci 500000, 900000, 1000000, 1200000, 1500000, 1800000, 2000000, 2500000, 2348c2ecf20Sopenharmony_ci}; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic int ucs1002_get_max_current(struct ucs1002_info *info, 2378c2ecf20Sopenharmony_ci union power_supply_propval *val) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci unsigned int reg; 2408c2ecf20Sopenharmony_ci int ret; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (info->output_disable) { 2438c2ecf20Sopenharmony_ci val->intval = 0; 2448c2ecf20Sopenharmony_ci return 0; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, UCS1002_REG_ILIMIT, ®); 2488c2ecf20Sopenharmony_ci if (ret) 2498c2ecf20Sopenharmony_ci return ret; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci val->intval = ucs1002_current_limit_uA[reg & UCS1002_ILIM_SW_MASK]; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci return 0; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic int ucs1002_set_max_current(struct ucs1002_info *info, u32 val) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci unsigned int reg; 2598c2ecf20Sopenharmony_ci int ret, idx; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (val == 0) { 2628c2ecf20Sopenharmony_ci info->output_disable = true; 2638c2ecf20Sopenharmony_ci regulator_disable_regmap(info->rdev); 2648c2ecf20Sopenharmony_ci return 0; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(ucs1002_current_limit_uA); idx++) { 2688c2ecf20Sopenharmony_ci if (val == ucs1002_current_limit_uA[idx]) 2698c2ecf20Sopenharmony_ci break; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (idx == ARRAY_SIZE(ucs1002_current_limit_uA)) 2738c2ecf20Sopenharmony_ci return -EINVAL; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci ret = regmap_write(info->regmap, UCS1002_REG_ILIMIT, idx); 2768c2ecf20Sopenharmony_ci if (ret) 2778c2ecf20Sopenharmony_ci return ret; 2788c2ecf20Sopenharmony_ci /* 2798c2ecf20Sopenharmony_ci * Any current limit setting exceeding the one set via ILIM 2808c2ecf20Sopenharmony_ci * pin will be rejected, so we read out freshly changed limit 2818c2ecf20Sopenharmony_ci * to make sure that it took effect. 2828c2ecf20Sopenharmony_ci */ 2838c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, UCS1002_REG_ILIMIT, ®); 2848c2ecf20Sopenharmony_ci if (ret) 2858c2ecf20Sopenharmony_ci return ret; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (reg != idx) 2888c2ecf20Sopenharmony_ci return -EINVAL; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci info->output_disable = false; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (info->rdev && info->rdev->use_count && 2938c2ecf20Sopenharmony_ci !regulator_is_enabled_regmap(info->rdev)) 2948c2ecf20Sopenharmony_ci regulator_enable_regmap(info->rdev); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci return 0; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic enum power_supply_usb_type ucs1002_usb_types[] = { 3008c2ecf20Sopenharmony_ci POWER_SUPPLY_USB_TYPE_PD, 3018c2ecf20Sopenharmony_ci POWER_SUPPLY_USB_TYPE_SDP, 3028c2ecf20Sopenharmony_ci POWER_SUPPLY_USB_TYPE_DCP, 3038c2ecf20Sopenharmony_ci POWER_SUPPLY_USB_TYPE_CDP, 3048c2ecf20Sopenharmony_ci POWER_SUPPLY_USB_TYPE_UNKNOWN, 3058c2ecf20Sopenharmony_ci}; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic int ucs1002_set_usb_type(struct ucs1002_info *info, int val) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci unsigned int mode; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (val < 0 || val >= ARRAY_SIZE(ucs1002_usb_types)) 3128c2ecf20Sopenharmony_ci return -EINVAL; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci switch (ucs1002_usb_types[val]) { 3158c2ecf20Sopenharmony_ci case POWER_SUPPLY_USB_TYPE_PD: 3168c2ecf20Sopenharmony_ci mode = V_SET_ACTIVE_MODE_DEDICATED; 3178c2ecf20Sopenharmony_ci break; 3188c2ecf20Sopenharmony_ci case POWER_SUPPLY_USB_TYPE_SDP: 3198c2ecf20Sopenharmony_ci mode = V_SET_ACTIVE_MODE_BC12_SDP; 3208c2ecf20Sopenharmony_ci break; 3218c2ecf20Sopenharmony_ci case POWER_SUPPLY_USB_TYPE_DCP: 3228c2ecf20Sopenharmony_ci mode = V_SET_ACTIVE_MODE_BC12_DCP; 3238c2ecf20Sopenharmony_ci break; 3248c2ecf20Sopenharmony_ci case POWER_SUPPLY_USB_TYPE_CDP: 3258c2ecf20Sopenharmony_ci mode = V_SET_ACTIVE_MODE_BC12_CDP; 3268c2ecf20Sopenharmony_ci break; 3278c2ecf20Sopenharmony_ci default: 3288c2ecf20Sopenharmony_ci return -EINVAL; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci return regmap_update_bits(info->regmap, UCS1002_REG_SWITCH_CFG, 3328c2ecf20Sopenharmony_ci V_SET_ACTIVE_MODE_MASK, mode); 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic int ucs1002_get_usb_type(struct ucs1002_info *info, 3368c2ecf20Sopenharmony_ci union power_supply_propval *val) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci enum power_supply_usb_type type; 3398c2ecf20Sopenharmony_ci unsigned int reg; 3408c2ecf20Sopenharmony_ci int ret; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, UCS1002_REG_PIN_STATUS, ®); 3438c2ecf20Sopenharmony_ci if (ret) 3448c2ecf20Sopenharmony_ci return ret; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci switch (reg & F_ACTIVE_MODE_MASK) { 3478c2ecf20Sopenharmony_ci default: 3488c2ecf20Sopenharmony_ci type = POWER_SUPPLY_USB_TYPE_UNKNOWN; 3498c2ecf20Sopenharmony_ci break; 3508c2ecf20Sopenharmony_ci case F_ACTIVE_MODE_DEDICATED: 3518c2ecf20Sopenharmony_ci type = POWER_SUPPLY_USB_TYPE_PD; 3528c2ecf20Sopenharmony_ci break; 3538c2ecf20Sopenharmony_ci case F_ACTIVE_MODE_BC12_SDP: 3548c2ecf20Sopenharmony_ci type = POWER_SUPPLY_USB_TYPE_SDP; 3558c2ecf20Sopenharmony_ci break; 3568c2ecf20Sopenharmony_ci case F_ACTIVE_MODE_BC12_DCP: 3578c2ecf20Sopenharmony_ci type = POWER_SUPPLY_USB_TYPE_DCP; 3588c2ecf20Sopenharmony_ci break; 3598c2ecf20Sopenharmony_ci case F_ACTIVE_MODE_BC12_CDP: 3608c2ecf20Sopenharmony_ci type = POWER_SUPPLY_USB_TYPE_CDP; 3618c2ecf20Sopenharmony_ci break; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci val->intval = type; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci return 0; 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic int ucs1002_get_property(struct power_supply *psy, 3708c2ecf20Sopenharmony_ci enum power_supply_property psp, 3718c2ecf20Sopenharmony_ci union power_supply_propval *val) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct ucs1002_info *info = power_supply_get_drvdata(psy); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci switch (psp) { 3768c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 3778c2ecf20Sopenharmony_ci return ucs1002_get_online(info, val); 3788c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_NOW: 3798c2ecf20Sopenharmony_ci return ucs1002_get_charge(info, val); 3808c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_NOW: 3818c2ecf20Sopenharmony_ci return ucs1002_get_current(info, val); 3828c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_MAX: 3838c2ecf20Sopenharmony_ci return ucs1002_get_max_current(info, val); 3848c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_USB_TYPE: 3858c2ecf20Sopenharmony_ci return ucs1002_get_usb_type(info, val); 3868c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 3878c2ecf20Sopenharmony_ci val->intval = info->health; 3888c2ecf20Sopenharmony_ci return 0; 3898c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 3908c2ecf20Sopenharmony_ci val->intval = info->present; 3918c2ecf20Sopenharmony_ci return 0; 3928c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_MANUFACTURER: 3938c2ecf20Sopenharmony_ci val->strval = UCS1002_MANUFACTURER; 3948c2ecf20Sopenharmony_ci return 0; 3958c2ecf20Sopenharmony_ci default: 3968c2ecf20Sopenharmony_ci return -EINVAL; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic int ucs1002_set_property(struct power_supply *psy, 4018c2ecf20Sopenharmony_ci enum power_supply_property psp, 4028c2ecf20Sopenharmony_ci const union power_supply_propval *val) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct ucs1002_info *info = power_supply_get_drvdata(psy); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci switch (psp) { 4078c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_MAX: 4088c2ecf20Sopenharmony_ci return ucs1002_set_max_current(info, val->intval); 4098c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_USB_TYPE: 4108c2ecf20Sopenharmony_ci return ucs1002_set_usb_type(info, val->intval); 4118c2ecf20Sopenharmony_ci default: 4128c2ecf20Sopenharmony_ci return -EINVAL; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic int ucs1002_property_is_writeable(struct power_supply *psy, 4178c2ecf20Sopenharmony_ci enum power_supply_property psp) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci switch (psp) { 4208c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_MAX: 4218c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_USB_TYPE: 4228c2ecf20Sopenharmony_ci return true; 4238c2ecf20Sopenharmony_ci default: 4248c2ecf20Sopenharmony_ci return false; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic const struct power_supply_desc ucs1002_charger_desc = { 4298c2ecf20Sopenharmony_ci .name = "ucs1002", 4308c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_USB, 4318c2ecf20Sopenharmony_ci .usb_types = ucs1002_usb_types, 4328c2ecf20Sopenharmony_ci .num_usb_types = ARRAY_SIZE(ucs1002_usb_types), 4338c2ecf20Sopenharmony_ci .get_property = ucs1002_get_property, 4348c2ecf20Sopenharmony_ci .set_property = ucs1002_set_property, 4358c2ecf20Sopenharmony_ci .property_is_writeable = ucs1002_property_is_writeable, 4368c2ecf20Sopenharmony_ci .properties = ucs1002_props, 4378c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(ucs1002_props), 4388c2ecf20Sopenharmony_ci}; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic void ucs1002_health_poll(struct work_struct *work) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci struct ucs1002_info *info = container_of(work, struct ucs1002_info, 4438c2ecf20Sopenharmony_ci health_poll.work); 4448c2ecf20Sopenharmony_ci int ret; 4458c2ecf20Sopenharmony_ci u32 reg; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, UCS1002_REG_INTERRUPT_STATUS, ®); 4488c2ecf20Sopenharmony_ci if (ret) 4498c2ecf20Sopenharmony_ci return; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* bad health and no status change, just schedule us again in a while */ 4528c2ecf20Sopenharmony_ci if ((reg & F_ERR) && info->health != POWER_SUPPLY_HEALTH_GOOD) { 4538c2ecf20Sopenharmony_ci schedule_delayed_work(&info->health_poll, 4548c2ecf20Sopenharmony_ci msecs_to_jiffies(2000)); 4558c2ecf20Sopenharmony_ci return; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (reg & F_TSD) 4598c2ecf20Sopenharmony_ci info->health = POWER_SUPPLY_HEALTH_OVERHEAT; 4608c2ecf20Sopenharmony_ci else if (reg & (F_OVER_VOLT | F_BACK_VOLT)) 4618c2ecf20Sopenharmony_ci info->health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 4628c2ecf20Sopenharmony_ci else if (reg & F_OVER_ILIM) 4638c2ecf20Sopenharmony_ci info->health = POWER_SUPPLY_HEALTH_OVERCURRENT; 4648c2ecf20Sopenharmony_ci else if (reg & (F_DISCHARGE_ERR | F_MIN_KEEP_OUT)) 4658c2ecf20Sopenharmony_ci info->health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; 4668c2ecf20Sopenharmony_ci else 4678c2ecf20Sopenharmony_ci info->health = POWER_SUPPLY_HEALTH_GOOD; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci sysfs_notify(&info->charger->dev.kobj, NULL, "health"); 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic irqreturn_t ucs1002_charger_irq(int irq, void *data) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci int ret, regval; 4758c2ecf20Sopenharmony_ci bool present; 4768c2ecf20Sopenharmony_ci struct ucs1002_info *info = data; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci present = info->present; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, UCS1002_REG_OTHER_STATUS, ®val); 4818c2ecf20Sopenharmony_ci if (ret) 4828c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* update attached status */ 4858c2ecf20Sopenharmony_ci info->present = regval & F_ADET_PIN; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* notify the change */ 4888c2ecf20Sopenharmony_ci if (present != info->present) 4898c2ecf20Sopenharmony_ci power_supply_changed(info->charger); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic irqreturn_t ucs1002_alert_irq(int irq, void *data) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci struct ucs1002_info *info = data; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci mod_delayed_work(system_wq, &info->health_poll, 0); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_cistatic int ucs1002_regulator_enable(struct regulator_dev *rdev) 5048c2ecf20Sopenharmony_ci{ 5058c2ecf20Sopenharmony_ci struct ucs1002_info *info = rdev_get_drvdata(rdev); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci /* 5088c2ecf20Sopenharmony_ci * If the output is disabled due to 0 maximum current, just pretend the 5098c2ecf20Sopenharmony_ci * enable did work. The regulator will be enabled as soon as we get a 5108c2ecf20Sopenharmony_ci * a non-zero maximum current budget. 5118c2ecf20Sopenharmony_ci */ 5128c2ecf20Sopenharmony_ci if (info->output_disable) 5138c2ecf20Sopenharmony_ci return 0; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci return regulator_enable_regmap(rdev); 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_cistatic const struct regulator_ops ucs1002_regulator_ops = { 5198c2ecf20Sopenharmony_ci .is_enabled = regulator_is_enabled_regmap, 5208c2ecf20Sopenharmony_ci .enable = ucs1002_regulator_enable, 5218c2ecf20Sopenharmony_ci .disable = regulator_disable_regmap, 5228c2ecf20Sopenharmony_ci}; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cistatic const struct regulator_desc ucs1002_regulator_descriptor = { 5258c2ecf20Sopenharmony_ci .name = "ucs1002-vbus", 5268c2ecf20Sopenharmony_ci .ops = &ucs1002_regulator_ops, 5278c2ecf20Sopenharmony_ci .type = REGULATOR_VOLTAGE, 5288c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 5298c2ecf20Sopenharmony_ci .enable_reg = UCS1002_REG_SWITCH_CFG, 5308c2ecf20Sopenharmony_ci .enable_mask = F_PWR_EN_SET, 5318c2ecf20Sopenharmony_ci .enable_val = F_PWR_EN_SET, 5328c2ecf20Sopenharmony_ci .fixed_uV = 5000000, 5338c2ecf20Sopenharmony_ci .n_voltages = 1, 5348c2ecf20Sopenharmony_ci}; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cistatic int ucs1002_probe(struct i2c_client *client, 5378c2ecf20Sopenharmony_ci const struct i2c_device_id *dev_id) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 5408c2ecf20Sopenharmony_ci struct power_supply_config charger_config = {}; 5418c2ecf20Sopenharmony_ci const struct regmap_config regmap_config = { 5428c2ecf20Sopenharmony_ci .reg_bits = 8, 5438c2ecf20Sopenharmony_ci .val_bits = 8, 5448c2ecf20Sopenharmony_ci }; 5458c2ecf20Sopenharmony_ci struct regulator_config regulator_config = {}; 5468c2ecf20Sopenharmony_ci int irq_a_det, irq_alert, ret; 5478c2ecf20Sopenharmony_ci struct ucs1002_info *info; 5488c2ecf20Sopenharmony_ci unsigned int regval; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); 5518c2ecf20Sopenharmony_ci if (!info) 5528c2ecf20Sopenharmony_ci return -ENOMEM; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci info->regmap = devm_regmap_init_i2c(client, ®map_config); 5558c2ecf20Sopenharmony_ci ret = PTR_ERR_OR_ZERO(info->regmap); 5568c2ecf20Sopenharmony_ci if (ret) { 5578c2ecf20Sopenharmony_ci dev_err(dev, "Regmap initialization failed: %d\n", ret); 5588c2ecf20Sopenharmony_ci return ret; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci info->client = client; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci irq_a_det = of_irq_get_byname(dev->of_node, "a_det"); 5648c2ecf20Sopenharmony_ci irq_alert = of_irq_get_byname(dev->of_node, "alert"); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci charger_config.of_node = dev->of_node; 5678c2ecf20Sopenharmony_ci charger_config.drv_data = info; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, UCS1002_REG_PRODUCT_ID, ®val); 5708c2ecf20Sopenharmony_ci if (ret) { 5718c2ecf20Sopenharmony_ci dev_err(dev, "Failed to read product ID: %d\n", ret); 5728c2ecf20Sopenharmony_ci return ret; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci if (regval != UCS1002_PRODUCT_ID) { 5768c2ecf20Sopenharmony_ci dev_err(dev, 5778c2ecf20Sopenharmony_ci "Product ID does not match (0x%02x != 0x%02x)\n", 5788c2ecf20Sopenharmony_ci regval, UCS1002_PRODUCT_ID); 5798c2ecf20Sopenharmony_ci return -ENODEV; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* Enable charge rationing by default */ 5838c2ecf20Sopenharmony_ci ret = regmap_update_bits(info->regmap, UCS1002_REG_GENERAL_CFG, 5848c2ecf20Sopenharmony_ci F_RATION_EN, F_RATION_EN); 5858c2ecf20Sopenharmony_ci if (ret) { 5868c2ecf20Sopenharmony_ci dev_err(dev, "Failed to read general config: %d\n", ret); 5878c2ecf20Sopenharmony_ci return ret; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci /* 5918c2ecf20Sopenharmony_ci * Ignore the M1, M2, PWR_EN, and EM_EN pin states. Set active 5928c2ecf20Sopenharmony_ci * mode selection to BC1.2 CDP. 5938c2ecf20Sopenharmony_ci */ 5948c2ecf20Sopenharmony_ci ret = regmap_update_bits(info->regmap, UCS1002_REG_SWITCH_CFG, 5958c2ecf20Sopenharmony_ci V_SET_ACTIVE_MODE_MASK | F_PIN_IGNORE, 5968c2ecf20Sopenharmony_ci V_SET_ACTIVE_MODE_BC12_CDP | F_PIN_IGNORE); 5978c2ecf20Sopenharmony_ci if (ret) { 5988c2ecf20Sopenharmony_ci dev_err(dev, "Failed to configure default mode: %d\n", ret); 5998c2ecf20Sopenharmony_ci return ret; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci /* 6028c2ecf20Sopenharmony_ci * Be safe and set initial current limit to 500mA 6038c2ecf20Sopenharmony_ci */ 6048c2ecf20Sopenharmony_ci ret = ucs1002_set_max_current(info, 500000); 6058c2ecf20Sopenharmony_ci if (ret) { 6068c2ecf20Sopenharmony_ci dev_err(dev, "Failed to set max current default: %d\n", ret); 6078c2ecf20Sopenharmony_ci return ret; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci info->charger = devm_power_supply_register(dev, &ucs1002_charger_desc, 6118c2ecf20Sopenharmony_ci &charger_config); 6128c2ecf20Sopenharmony_ci ret = PTR_ERR_OR_ZERO(info->charger); 6138c2ecf20Sopenharmony_ci if (ret) { 6148c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register power supply: %d\n", ret); 6158c2ecf20Sopenharmony_ci return ret; 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, UCS1002_REG_PIN_STATUS, ®val); 6198c2ecf20Sopenharmony_ci if (ret) { 6208c2ecf20Sopenharmony_ci dev_err(dev, "Failed to read pin status: %d\n", ret); 6218c2ecf20Sopenharmony_ci return ret; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci info->regulator_descriptor = 6258c2ecf20Sopenharmony_ci devm_kmemdup(dev, &ucs1002_regulator_descriptor, 6268c2ecf20Sopenharmony_ci sizeof(ucs1002_regulator_descriptor), 6278c2ecf20Sopenharmony_ci GFP_KERNEL); 6288c2ecf20Sopenharmony_ci if (!info->regulator_descriptor) 6298c2ecf20Sopenharmony_ci return -ENOMEM; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci info->regulator_descriptor->enable_is_inverted = !(regval & F_SEL_PIN); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci regulator_config.dev = dev; 6348c2ecf20Sopenharmony_ci regulator_config.of_node = dev->of_node; 6358c2ecf20Sopenharmony_ci regulator_config.regmap = info->regmap; 6368c2ecf20Sopenharmony_ci regulator_config.driver_data = info; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci info->rdev = devm_regulator_register(dev, info->regulator_descriptor, 6398c2ecf20Sopenharmony_ci ®ulator_config); 6408c2ecf20Sopenharmony_ci ret = PTR_ERR_OR_ZERO(info->rdev); 6418c2ecf20Sopenharmony_ci if (ret) { 6428c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register VBUS regulator: %d\n", ret); 6438c2ecf20Sopenharmony_ci return ret; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci info->health = POWER_SUPPLY_HEALTH_GOOD; 6478c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&info->health_poll, ucs1002_health_poll); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci if (irq_a_det > 0) { 6508c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(dev, irq_a_det, NULL, 6518c2ecf20Sopenharmony_ci ucs1002_charger_irq, 6528c2ecf20Sopenharmony_ci IRQF_ONESHOT, 6538c2ecf20Sopenharmony_ci "ucs1002-a_det", info); 6548c2ecf20Sopenharmony_ci if (ret) { 6558c2ecf20Sopenharmony_ci dev_err(dev, "Failed to request A_DET threaded irq: %d\n", 6568c2ecf20Sopenharmony_ci ret); 6578c2ecf20Sopenharmony_ci return ret; 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci if (irq_alert > 0) { 6628c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, irq_alert, ucs1002_alert_irq, 6638c2ecf20Sopenharmony_ci 0,"ucs1002-alert", info); 6648c2ecf20Sopenharmony_ci if (ret) { 6658c2ecf20Sopenharmony_ci dev_err(dev, "Failed to request ALERT threaded irq: %d\n", 6668c2ecf20Sopenharmony_ci ret); 6678c2ecf20Sopenharmony_ci return ret; 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci } 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci return 0; 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cistatic const struct of_device_id ucs1002_of_match[] = { 6758c2ecf20Sopenharmony_ci { .compatible = "microchip,ucs1002", }, 6768c2ecf20Sopenharmony_ci { /* sentinel */ }, 6778c2ecf20Sopenharmony_ci}; 6788c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ucs1002_of_match); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_cistatic struct i2c_driver ucs1002_driver = { 6818c2ecf20Sopenharmony_ci .driver = { 6828c2ecf20Sopenharmony_ci .name = "ucs1002", 6838c2ecf20Sopenharmony_ci .of_match_table = ucs1002_of_match, 6848c2ecf20Sopenharmony_ci }, 6858c2ecf20Sopenharmony_ci .probe = ucs1002_probe, 6868c2ecf20Sopenharmony_ci}; 6878c2ecf20Sopenharmony_cimodule_i2c_driver(ucs1002_driver); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Microchip UCS1002 Programmable USB Port Power Controller"); 6908c2ecf20Sopenharmony_ciMODULE_AUTHOR("Enric Balletbo Serra <enric.balletbo@collabora.com>"); 6918c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>"); 6928c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 693