18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Power supply driver for the RICOH RN5T618 power management chip family 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2020 Andreas Kemnade 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/device.h> 108c2ecf20Sopenharmony_ci#include <linux/bitops.h> 118c2ecf20Sopenharmony_ci#include <linux/errno.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/mfd/rn5t618.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 188c2ecf20Sopenharmony_ci#include <linux/regmap.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define CHG_STATE_ADP_INPUT 0x40 228c2ecf20Sopenharmony_ci#define CHG_STATE_USB_INPUT 0x80 238c2ecf20Sopenharmony_ci#define CHG_STATE_MASK 0x1f 248c2ecf20Sopenharmony_ci#define CHG_STATE_CHG_OFF 0 258c2ecf20Sopenharmony_ci#define CHG_STATE_CHG_READY_VADP 1 268c2ecf20Sopenharmony_ci#define CHG_STATE_CHG_TRICKLE 2 278c2ecf20Sopenharmony_ci#define CHG_STATE_CHG_RAPID 3 288c2ecf20Sopenharmony_ci#define CHG_STATE_CHG_COMPLETE 4 298c2ecf20Sopenharmony_ci#define CHG_STATE_SUSPEND 5 308c2ecf20Sopenharmony_ci#define CHG_STATE_VCHG_OVER_VOL 6 318c2ecf20Sopenharmony_ci#define CHG_STATE_BAT_ERROR 7 328c2ecf20Sopenharmony_ci#define CHG_STATE_NO_BAT 8 338c2ecf20Sopenharmony_ci#define CHG_STATE_BAT_OVER_VOL 9 348c2ecf20Sopenharmony_ci#define CHG_STATE_BAT_TEMP_ERR 10 358c2ecf20Sopenharmony_ci#define CHG_STATE_DIE_ERR 11 368c2ecf20Sopenharmony_ci#define CHG_STATE_DIE_SHUTDOWN 12 378c2ecf20Sopenharmony_ci#define CHG_STATE_NO_BAT2 13 388c2ecf20Sopenharmony_ci#define CHG_STATE_CHG_READY_VUSB 14 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define FG_ENABLE 1 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistruct rn5t618_power_info { 438c2ecf20Sopenharmony_ci struct rn5t618 *rn5t618; 448c2ecf20Sopenharmony_ci struct platform_device *pdev; 458c2ecf20Sopenharmony_ci struct power_supply *battery; 468c2ecf20Sopenharmony_ci struct power_supply *usb; 478c2ecf20Sopenharmony_ci struct power_supply *adp; 488c2ecf20Sopenharmony_ci int irq; 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic enum power_supply_property rn5t618_usb_props[] = { 528c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 538c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic enum power_supply_property rn5t618_adp_props[] = { 578c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 588c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic enum power_supply_property rn5t618_battery_props[] = { 638c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 648c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 658c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 668c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_NOW, 678c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CAPACITY, 688c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_TEMP, 698c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 708c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, 718c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_TECHNOLOGY, 728c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_FULL, 738c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_NOW, 748c2ecf20Sopenharmony_ci}; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int rn5t618_battery_read_doublereg(struct rn5t618_power_info *info, 778c2ecf20Sopenharmony_ci u8 reg, u16 *result) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci int ret, i; 808c2ecf20Sopenharmony_ci u8 data[2]; 818c2ecf20Sopenharmony_ci u16 old, new; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci old = 0; 848c2ecf20Sopenharmony_ci /* Prevent races when registers are changing. */ 858c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 868c2ecf20Sopenharmony_ci ret = regmap_bulk_read(info->rn5t618->regmap, 878c2ecf20Sopenharmony_ci reg, data, sizeof(data)); 888c2ecf20Sopenharmony_ci if (ret) 898c2ecf20Sopenharmony_ci return ret; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci new = data[0] << 8; 928c2ecf20Sopenharmony_ci new |= data[1]; 938c2ecf20Sopenharmony_ci if (new == old) 948c2ecf20Sopenharmony_ci break; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci old = new; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci *result = new; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return 0; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic int rn5t618_decode_status(unsigned int status) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci switch (status & CHG_STATE_MASK) { 1078c2ecf20Sopenharmony_ci case CHG_STATE_CHG_OFF: 1088c2ecf20Sopenharmony_ci case CHG_STATE_SUSPEND: 1098c2ecf20Sopenharmony_ci case CHG_STATE_VCHG_OVER_VOL: 1108c2ecf20Sopenharmony_ci case CHG_STATE_DIE_SHUTDOWN: 1118c2ecf20Sopenharmony_ci return POWER_SUPPLY_STATUS_DISCHARGING; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci case CHG_STATE_CHG_TRICKLE: 1148c2ecf20Sopenharmony_ci case CHG_STATE_CHG_RAPID: 1158c2ecf20Sopenharmony_ci return POWER_SUPPLY_STATUS_CHARGING; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci case CHG_STATE_CHG_COMPLETE: 1188c2ecf20Sopenharmony_ci return POWER_SUPPLY_STATUS_FULL; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci default: 1218c2ecf20Sopenharmony_ci return POWER_SUPPLY_STATUS_NOT_CHARGING; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int rn5t618_battery_status(struct rn5t618_power_info *info, 1268c2ecf20Sopenharmony_ci union power_supply_propval *val) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci unsigned int v; 1298c2ecf20Sopenharmony_ci int ret; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGSTATE, &v); 1328c2ecf20Sopenharmony_ci if (ret) 1338c2ecf20Sopenharmony_ci return ret; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_UNKNOWN; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (v & 0xc0) { /* USB or ADP plugged */ 1388c2ecf20Sopenharmony_ci val->intval = rn5t618_decode_status(v); 1398c2ecf20Sopenharmony_ci } else 1408c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return ret; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int rn5t618_battery_present(struct rn5t618_power_info *info, 1468c2ecf20Sopenharmony_ci union power_supply_propval *val) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci unsigned int v; 1498c2ecf20Sopenharmony_ci int ret; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGSTATE, &v); 1528c2ecf20Sopenharmony_ci if (ret) 1538c2ecf20Sopenharmony_ci return ret; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci v &= CHG_STATE_MASK; 1568c2ecf20Sopenharmony_ci if ((v == CHG_STATE_NO_BAT) || (v == CHG_STATE_NO_BAT2)) 1578c2ecf20Sopenharmony_ci val->intval = 0; 1588c2ecf20Sopenharmony_ci else 1598c2ecf20Sopenharmony_ci val->intval = 1; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci return ret; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic int rn5t618_battery_voltage_now(struct rn5t618_power_info *info, 1658c2ecf20Sopenharmony_ci union power_supply_propval *val) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci u16 res; 1688c2ecf20Sopenharmony_ci int ret; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci ret = rn5t618_battery_read_doublereg(info, RN5T618_VOLTAGE_1, &res); 1718c2ecf20Sopenharmony_ci if (ret) 1728c2ecf20Sopenharmony_ci return ret; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci val->intval = res * 2 * 2500 / 4095 * 1000; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci return 0; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic int rn5t618_battery_current_now(struct rn5t618_power_info *info, 1808c2ecf20Sopenharmony_ci union power_supply_propval *val) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci u16 res; 1838c2ecf20Sopenharmony_ci int ret; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci ret = rn5t618_battery_read_doublereg(info, RN5T618_CC_AVEREG1, &res); 1868c2ecf20Sopenharmony_ci if (ret) 1878c2ecf20Sopenharmony_ci return ret; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* current is negative when discharging */ 1908c2ecf20Sopenharmony_ci val->intval = sign_extend32(res, 13) * 1000; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return 0; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic int rn5t618_battery_capacity(struct rn5t618_power_info *info, 1968c2ecf20Sopenharmony_ci union power_supply_propval *val) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci unsigned int v; 1998c2ecf20Sopenharmony_ci int ret; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci ret = regmap_read(info->rn5t618->regmap, RN5T618_SOC, &v); 2028c2ecf20Sopenharmony_ci if (ret) 2038c2ecf20Sopenharmony_ci return ret; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci val->intval = v; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci return 0; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic int rn5t618_battery_temp(struct rn5t618_power_info *info, 2118c2ecf20Sopenharmony_ci union power_supply_propval *val) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci u16 res; 2148c2ecf20Sopenharmony_ci int ret; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci ret = rn5t618_battery_read_doublereg(info, RN5T618_TEMP_1, &res); 2178c2ecf20Sopenharmony_ci if (ret) 2188c2ecf20Sopenharmony_ci return ret; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci val->intval = sign_extend32(res, 11) * 10 / 16; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int rn5t618_battery_tte(struct rn5t618_power_info *info, 2268c2ecf20Sopenharmony_ci union power_supply_propval *val) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci u16 res; 2298c2ecf20Sopenharmony_ci int ret; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci ret = rn5t618_battery_read_doublereg(info, RN5T618_TT_EMPTY_H, &res); 2328c2ecf20Sopenharmony_ci if (ret) 2338c2ecf20Sopenharmony_ci return ret; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (res == 65535) 2368c2ecf20Sopenharmony_ci return -ENODATA; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci val->intval = res * 60; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci return 0; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic int rn5t618_battery_ttf(struct rn5t618_power_info *info, 2448c2ecf20Sopenharmony_ci union power_supply_propval *val) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci u16 res; 2478c2ecf20Sopenharmony_ci int ret; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci ret = rn5t618_battery_read_doublereg(info, RN5T618_TT_FULL_H, &res); 2508c2ecf20Sopenharmony_ci if (ret) 2518c2ecf20Sopenharmony_ci return ret; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (res == 65535) 2548c2ecf20Sopenharmony_ci return -ENODATA; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci val->intval = res * 60; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci return 0; 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic int rn5t618_battery_charge_full(struct rn5t618_power_info *info, 2628c2ecf20Sopenharmony_ci union power_supply_propval *val) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci u16 res; 2658c2ecf20Sopenharmony_ci int ret; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci ret = rn5t618_battery_read_doublereg(info, RN5T618_FA_CAP_H, &res); 2688c2ecf20Sopenharmony_ci if (ret) 2698c2ecf20Sopenharmony_ci return ret; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci val->intval = res * 1000; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return 0; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic int rn5t618_battery_charge_now(struct rn5t618_power_info *info, 2778c2ecf20Sopenharmony_ci union power_supply_propval *val) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci u16 res; 2808c2ecf20Sopenharmony_ci int ret; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci ret = rn5t618_battery_read_doublereg(info, RN5T618_RE_CAP_H, &res); 2838c2ecf20Sopenharmony_ci if (ret) 2848c2ecf20Sopenharmony_ci return ret; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci val->intval = res * 1000; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return 0; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic int rn5t618_battery_get_property(struct power_supply *psy, 2928c2ecf20Sopenharmony_ci enum power_supply_property psp, 2938c2ecf20Sopenharmony_ci union power_supply_propval *val) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci int ret = 0; 2968c2ecf20Sopenharmony_ci struct rn5t618_power_info *info = power_supply_get_drvdata(psy); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci switch (psp) { 2998c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 3008c2ecf20Sopenharmony_ci ret = rn5t618_battery_status(info, val); 3018c2ecf20Sopenharmony_ci break; 3028c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 3038c2ecf20Sopenharmony_ci ret = rn5t618_battery_present(info, val); 3048c2ecf20Sopenharmony_ci break; 3058c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 3068c2ecf20Sopenharmony_ci ret = rn5t618_battery_voltage_now(info, val); 3078c2ecf20Sopenharmony_ci break; 3088c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_NOW: 3098c2ecf20Sopenharmony_ci ret = rn5t618_battery_current_now(info, val); 3108c2ecf20Sopenharmony_ci break; 3118c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY: 3128c2ecf20Sopenharmony_ci ret = rn5t618_battery_capacity(info, val); 3138c2ecf20Sopenharmony_ci break; 3148c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TEMP: 3158c2ecf20Sopenharmony_ci ret = rn5t618_battery_temp(info, val); 3168c2ecf20Sopenharmony_ci break; 3178c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: 3188c2ecf20Sopenharmony_ci ret = rn5t618_battery_tte(info, val); 3198c2ecf20Sopenharmony_ci break; 3208c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: 3218c2ecf20Sopenharmony_ci ret = rn5t618_battery_ttf(info, val); 3228c2ecf20Sopenharmony_ci break; 3238c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TECHNOLOGY: 3248c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 3258c2ecf20Sopenharmony_ci break; 3268c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_FULL: 3278c2ecf20Sopenharmony_ci ret = rn5t618_battery_charge_full(info, val); 3288c2ecf20Sopenharmony_ci break; 3298c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_NOW: 3308c2ecf20Sopenharmony_ci ret = rn5t618_battery_charge_now(info, val); 3318c2ecf20Sopenharmony_ci break; 3328c2ecf20Sopenharmony_ci default: 3338c2ecf20Sopenharmony_ci return -EINVAL; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci return ret; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic int rn5t618_adp_get_property(struct power_supply *psy, 3408c2ecf20Sopenharmony_ci enum power_supply_property psp, 3418c2ecf20Sopenharmony_ci union power_supply_propval *val) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci struct rn5t618_power_info *info = power_supply_get_drvdata(psy); 3448c2ecf20Sopenharmony_ci unsigned int chgstate; 3458c2ecf20Sopenharmony_ci bool online; 3468c2ecf20Sopenharmony_ci int ret; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGSTATE, &chgstate); 3498c2ecf20Sopenharmony_ci if (ret) 3508c2ecf20Sopenharmony_ci return ret; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci online = !!(chgstate & CHG_STATE_ADP_INPUT); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci switch (psp) { 3558c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 3568c2ecf20Sopenharmony_ci val->intval = online; 3578c2ecf20Sopenharmony_ci break; 3588c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 3598c2ecf20Sopenharmony_ci if (!online) { 3608c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 3618c2ecf20Sopenharmony_ci break; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci val->intval = rn5t618_decode_status(chgstate); 3648c2ecf20Sopenharmony_ci if (val->intval != POWER_SUPPLY_STATUS_CHARGING) 3658c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci break; 3688c2ecf20Sopenharmony_ci default: 3698c2ecf20Sopenharmony_ci return -EINVAL; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci return 0; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic int rn5t618_usb_get_property(struct power_supply *psy, 3768c2ecf20Sopenharmony_ci enum power_supply_property psp, 3778c2ecf20Sopenharmony_ci union power_supply_propval *val) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci struct rn5t618_power_info *info = power_supply_get_drvdata(psy); 3808c2ecf20Sopenharmony_ci unsigned int chgstate; 3818c2ecf20Sopenharmony_ci bool online; 3828c2ecf20Sopenharmony_ci int ret; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGSTATE, &chgstate); 3858c2ecf20Sopenharmony_ci if (ret) 3868c2ecf20Sopenharmony_ci return ret; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci online = !!(chgstate & CHG_STATE_USB_INPUT); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci switch (psp) { 3918c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 3928c2ecf20Sopenharmony_ci val->intval = online; 3938c2ecf20Sopenharmony_ci break; 3948c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 3958c2ecf20Sopenharmony_ci if (!online) { 3968c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 3978c2ecf20Sopenharmony_ci break; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci val->intval = rn5t618_decode_status(chgstate); 4008c2ecf20Sopenharmony_ci if (val->intval != POWER_SUPPLY_STATUS_CHARGING) 4018c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci break; 4048c2ecf20Sopenharmony_ci default: 4058c2ecf20Sopenharmony_ci return -EINVAL; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic const struct power_supply_desc rn5t618_battery_desc = { 4128c2ecf20Sopenharmony_ci .name = "rn5t618-battery", 4138c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_BATTERY, 4148c2ecf20Sopenharmony_ci .properties = rn5t618_battery_props, 4158c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(rn5t618_battery_props), 4168c2ecf20Sopenharmony_ci .get_property = rn5t618_battery_get_property, 4178c2ecf20Sopenharmony_ci}; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic const struct power_supply_desc rn5t618_adp_desc = { 4208c2ecf20Sopenharmony_ci .name = "rn5t618-adp", 4218c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_MAINS, 4228c2ecf20Sopenharmony_ci .properties = rn5t618_adp_props, 4238c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(rn5t618_adp_props), 4248c2ecf20Sopenharmony_ci .get_property = rn5t618_adp_get_property, 4258c2ecf20Sopenharmony_ci}; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic const struct power_supply_desc rn5t618_usb_desc = { 4288c2ecf20Sopenharmony_ci .name = "rn5t618-usb", 4298c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_USB, 4308c2ecf20Sopenharmony_ci .properties = rn5t618_usb_props, 4318c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(rn5t618_usb_props), 4328c2ecf20Sopenharmony_ci .get_property = rn5t618_usb_get_property, 4338c2ecf20Sopenharmony_ci}; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic irqreturn_t rn5t618_charger_irq(int irq, void *data) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci struct device *dev = data; 4388c2ecf20Sopenharmony_ci struct rn5t618_power_info *info = dev_get_drvdata(dev); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci unsigned int ctrl, stat1, stat2, err; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci regmap_read(info->rn5t618->regmap, RN5T618_CHGERR_IRR, &err); 4438c2ecf20Sopenharmony_ci regmap_read(info->rn5t618->regmap, RN5T618_CHGCTRL_IRR, &ctrl); 4448c2ecf20Sopenharmony_ci regmap_read(info->rn5t618->regmap, RN5T618_CHGSTAT_IRR1, &stat1); 4458c2ecf20Sopenharmony_ci regmap_read(info->rn5t618->regmap, RN5T618_CHGSTAT_IRR2, &stat2); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci regmap_write(info->rn5t618->regmap, RN5T618_CHGERR_IRR, 0); 4488c2ecf20Sopenharmony_ci regmap_write(info->rn5t618->regmap, RN5T618_CHGCTRL_IRR, 0); 4498c2ecf20Sopenharmony_ci regmap_write(info->rn5t618->regmap, RN5T618_CHGSTAT_IRR1, 0); 4508c2ecf20Sopenharmony_ci regmap_write(info->rn5t618->regmap, RN5T618_CHGSTAT_IRR2, 0); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci dev_dbg(dev, "chgerr: %x chgctrl: %x chgstat: %x chgstat2: %x\n", 4538c2ecf20Sopenharmony_ci err, ctrl, stat1, stat2); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci power_supply_changed(info->usb); 4568c2ecf20Sopenharmony_ci power_supply_changed(info->adp); 4578c2ecf20Sopenharmony_ci power_supply_changed(info->battery); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic int rn5t618_power_probe(struct platform_device *pdev) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci int ret = 0; 4658c2ecf20Sopenharmony_ci unsigned int v; 4668c2ecf20Sopenharmony_ci struct power_supply_config psy_cfg = {}; 4678c2ecf20Sopenharmony_ci struct rn5t618_power_info *info; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); 4708c2ecf20Sopenharmony_ci if (!info) 4718c2ecf20Sopenharmony_ci return -ENOMEM; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci info->pdev = pdev; 4748c2ecf20Sopenharmony_ci info->rn5t618 = dev_get_drvdata(pdev->dev.parent); 4758c2ecf20Sopenharmony_ci info->irq = -1; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, info); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci ret = regmap_read(info->rn5t618->regmap, RN5T618_CONTROL, &v); 4808c2ecf20Sopenharmony_ci if (ret) 4818c2ecf20Sopenharmony_ci return ret; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (!(v & FG_ENABLE)) { 4848c2ecf20Sopenharmony_ci /* E.g. the vendor kernels of various Kobo and Tolino Ebook 4858c2ecf20Sopenharmony_ci * readers disable the fuel gauge on shutdown. If a kernel 4868c2ecf20Sopenharmony_ci * without fuel gauge support is booted after that, the fuel 4878c2ecf20Sopenharmony_ci * gauge will get decalibrated. 4888c2ecf20Sopenharmony_ci */ 4898c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Fuel gauge not enabled, enabling now\n"); 4908c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Expect imprecise results\n"); 4918c2ecf20Sopenharmony_ci regmap_update_bits(info->rn5t618->regmap, RN5T618_CONTROL, 4928c2ecf20Sopenharmony_ci FG_ENABLE, FG_ENABLE); 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci psy_cfg.drv_data = info; 4968c2ecf20Sopenharmony_ci info->battery = devm_power_supply_register(&pdev->dev, 4978c2ecf20Sopenharmony_ci &rn5t618_battery_desc, 4988c2ecf20Sopenharmony_ci &psy_cfg); 4998c2ecf20Sopenharmony_ci if (IS_ERR(info->battery)) { 5008c2ecf20Sopenharmony_ci ret = PTR_ERR(info->battery); 5018c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register battery: %d\n", ret); 5028c2ecf20Sopenharmony_ci return ret; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci info->adp = devm_power_supply_register(&pdev->dev, 5068c2ecf20Sopenharmony_ci &rn5t618_adp_desc, 5078c2ecf20Sopenharmony_ci &psy_cfg); 5088c2ecf20Sopenharmony_ci if (IS_ERR(info->adp)) { 5098c2ecf20Sopenharmony_ci ret = PTR_ERR(info->adp); 5108c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register adp: %d\n", ret); 5118c2ecf20Sopenharmony_ci return ret; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci info->usb = devm_power_supply_register(&pdev->dev, 5158c2ecf20Sopenharmony_ci &rn5t618_usb_desc, 5168c2ecf20Sopenharmony_ci &psy_cfg); 5178c2ecf20Sopenharmony_ci if (IS_ERR(info->usb)) { 5188c2ecf20Sopenharmony_ci ret = PTR_ERR(info->usb); 5198c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register usb: %d\n", ret); 5208c2ecf20Sopenharmony_ci return ret; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (info->rn5t618->irq_data) 5248c2ecf20Sopenharmony_ci info->irq = regmap_irq_get_virq(info->rn5t618->irq_data, 5258c2ecf20Sopenharmony_ci RN5T618_IRQ_CHG); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci if (info->irq < 0) 5288c2ecf20Sopenharmony_ci info->irq = -1; 5298c2ecf20Sopenharmony_ci else { 5308c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL, 5318c2ecf20Sopenharmony_ci rn5t618_charger_irq, 5328c2ecf20Sopenharmony_ci IRQF_ONESHOT, 5338c2ecf20Sopenharmony_ci "rn5t618_power", 5348c2ecf20Sopenharmony_ci &pdev->dev); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (ret < 0) { 5378c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "request IRQ:%d fail\n", 5388c2ecf20Sopenharmony_ci info->irq); 5398c2ecf20Sopenharmony_ci info->irq = -1; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci return 0; 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_cistatic struct platform_driver rn5t618_power_driver = { 5478c2ecf20Sopenharmony_ci .driver = { 5488c2ecf20Sopenharmony_ci .name = "rn5t618-power", 5498c2ecf20Sopenharmony_ci }, 5508c2ecf20Sopenharmony_ci .probe = rn5t618_power_probe, 5518c2ecf20Sopenharmony_ci}; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cimodule_platform_driver(rn5t618_power_driver); 5548c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:rn5t618-power"); 5558c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Power supply driver for RICOH RN5T618"); 5568c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 557