18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// max77693_charger.c - Battery charger driver for the Maxim 77693 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (C) 2014 Samsung Electronics 68c2ecf20Sopenharmony_ci// Krzysztof Kozlowski <krzk@kernel.org> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 108c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 118c2ecf20Sopenharmony_ci#include <linux/regmap.h> 128c2ecf20Sopenharmony_ci#include <linux/mfd/max77693.h> 138c2ecf20Sopenharmony_ci#include <linux/mfd/max77693-common.h> 148c2ecf20Sopenharmony_ci#include <linux/mfd/max77693-private.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define MAX77693_CHARGER_NAME "max77693-charger" 178c2ecf20Sopenharmony_cistatic const char *max77693_charger_model = "MAX77693"; 188c2ecf20Sopenharmony_cistatic const char *max77693_charger_manufacturer = "Maxim Integrated"; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct max77693_charger { 218c2ecf20Sopenharmony_ci struct device *dev; 228c2ecf20Sopenharmony_ci struct max77693_dev *max77693; 238c2ecf20Sopenharmony_ci struct power_supply *charger; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci u32 constant_volt; 268c2ecf20Sopenharmony_ci u32 min_system_volt; 278c2ecf20Sopenharmony_ci u32 thermal_regulation_temp; 288c2ecf20Sopenharmony_ci u32 batttery_overcurrent; 298c2ecf20Sopenharmony_ci u32 charge_input_threshold_volt; 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int max77693_get_charger_state(struct regmap *regmap, int *val) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci int ret; 358c2ecf20Sopenharmony_ci unsigned int data; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data); 388c2ecf20Sopenharmony_ci if (ret < 0) 398c2ecf20Sopenharmony_ci return ret; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci data &= CHG_DETAILS_01_CHG_MASK; 428c2ecf20Sopenharmony_ci data >>= CHG_DETAILS_01_CHG_SHIFT; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci switch (data) { 458c2ecf20Sopenharmony_ci case MAX77693_CHARGING_PREQUALIFICATION: 468c2ecf20Sopenharmony_ci case MAX77693_CHARGING_FAST_CONST_CURRENT: 478c2ecf20Sopenharmony_ci case MAX77693_CHARGING_FAST_CONST_VOLTAGE: 488c2ecf20Sopenharmony_ci case MAX77693_CHARGING_TOP_OFF: 498c2ecf20Sopenharmony_ci /* In high temp the charging current is reduced, but still charging */ 508c2ecf20Sopenharmony_ci case MAX77693_CHARGING_HIGH_TEMP: 518c2ecf20Sopenharmony_ci *val = POWER_SUPPLY_STATUS_CHARGING; 528c2ecf20Sopenharmony_ci break; 538c2ecf20Sopenharmony_ci case MAX77693_CHARGING_DONE: 548c2ecf20Sopenharmony_ci *val = POWER_SUPPLY_STATUS_FULL; 558c2ecf20Sopenharmony_ci break; 568c2ecf20Sopenharmony_ci case MAX77693_CHARGING_TIMER_EXPIRED: 578c2ecf20Sopenharmony_ci case MAX77693_CHARGING_THERMISTOR_SUSPEND: 588c2ecf20Sopenharmony_ci *val = POWER_SUPPLY_STATUS_NOT_CHARGING; 598c2ecf20Sopenharmony_ci break; 608c2ecf20Sopenharmony_ci case MAX77693_CHARGING_OFF: 618c2ecf20Sopenharmony_ci case MAX77693_CHARGING_OVER_TEMP: 628c2ecf20Sopenharmony_ci case MAX77693_CHARGING_WATCHDOG_EXPIRED: 638c2ecf20Sopenharmony_ci *val = POWER_SUPPLY_STATUS_DISCHARGING; 648c2ecf20Sopenharmony_ci break; 658c2ecf20Sopenharmony_ci case MAX77693_CHARGING_RESERVED: 668c2ecf20Sopenharmony_ci default: 678c2ecf20Sopenharmony_ci *val = POWER_SUPPLY_STATUS_UNKNOWN; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return 0; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int max77693_get_charge_type(struct regmap *regmap, int *val) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci int ret; 768c2ecf20Sopenharmony_ci unsigned int data; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data); 798c2ecf20Sopenharmony_ci if (ret < 0) 808c2ecf20Sopenharmony_ci return ret; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci data &= CHG_DETAILS_01_CHG_MASK; 838c2ecf20Sopenharmony_ci data >>= CHG_DETAILS_01_CHG_SHIFT; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci switch (data) { 868c2ecf20Sopenharmony_ci case MAX77693_CHARGING_PREQUALIFICATION: 878c2ecf20Sopenharmony_ci /* 888c2ecf20Sopenharmony_ci * Top-off: trickle or fast? In top-off the current varies between 898c2ecf20Sopenharmony_ci * 100 and 250 mA. It is higher than prequalification current. 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_ci case MAX77693_CHARGING_TOP_OFF: 928c2ecf20Sopenharmony_ci *val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; 938c2ecf20Sopenharmony_ci break; 948c2ecf20Sopenharmony_ci case MAX77693_CHARGING_FAST_CONST_CURRENT: 958c2ecf20Sopenharmony_ci case MAX77693_CHARGING_FAST_CONST_VOLTAGE: 968c2ecf20Sopenharmony_ci /* In high temp the charging current is reduced, but still charging */ 978c2ecf20Sopenharmony_ci case MAX77693_CHARGING_HIGH_TEMP: 988c2ecf20Sopenharmony_ci *val = POWER_SUPPLY_CHARGE_TYPE_FAST; 998c2ecf20Sopenharmony_ci break; 1008c2ecf20Sopenharmony_ci case MAX77693_CHARGING_DONE: 1018c2ecf20Sopenharmony_ci case MAX77693_CHARGING_TIMER_EXPIRED: 1028c2ecf20Sopenharmony_ci case MAX77693_CHARGING_THERMISTOR_SUSPEND: 1038c2ecf20Sopenharmony_ci case MAX77693_CHARGING_OFF: 1048c2ecf20Sopenharmony_ci case MAX77693_CHARGING_OVER_TEMP: 1058c2ecf20Sopenharmony_ci case MAX77693_CHARGING_WATCHDOG_EXPIRED: 1068c2ecf20Sopenharmony_ci *val = POWER_SUPPLY_CHARGE_TYPE_NONE; 1078c2ecf20Sopenharmony_ci break; 1088c2ecf20Sopenharmony_ci case MAX77693_CHARGING_RESERVED: 1098c2ecf20Sopenharmony_ci default: 1108c2ecf20Sopenharmony_ci *val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return 0; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* 1178c2ecf20Sopenharmony_ci * Supported health statuses: 1188c2ecf20Sopenharmony_ci * - POWER_SUPPLY_HEALTH_DEAD 1198c2ecf20Sopenharmony_ci * - POWER_SUPPLY_HEALTH_GOOD 1208c2ecf20Sopenharmony_ci * - POWER_SUPPLY_HEALTH_OVERVOLTAGE 1218c2ecf20Sopenharmony_ci * - POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE 1228c2ecf20Sopenharmony_ci * - POWER_SUPPLY_HEALTH_UNKNOWN 1238c2ecf20Sopenharmony_ci * - POWER_SUPPLY_HEALTH_UNSPEC_FAILURE 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_cistatic int max77693_get_battery_health(struct regmap *regmap, int *val) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci int ret; 1288c2ecf20Sopenharmony_ci unsigned int data; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data); 1318c2ecf20Sopenharmony_ci if (ret < 0) 1328c2ecf20Sopenharmony_ci return ret; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci data &= CHG_DETAILS_01_BAT_MASK; 1358c2ecf20Sopenharmony_ci data >>= CHG_DETAILS_01_BAT_SHIFT; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci switch (data) { 1388c2ecf20Sopenharmony_ci case MAX77693_BATTERY_NOBAT: 1398c2ecf20Sopenharmony_ci *val = POWER_SUPPLY_HEALTH_DEAD; 1408c2ecf20Sopenharmony_ci break; 1418c2ecf20Sopenharmony_ci case MAX77693_BATTERY_PREQUALIFICATION: 1428c2ecf20Sopenharmony_ci case MAX77693_BATTERY_GOOD: 1438c2ecf20Sopenharmony_ci case MAX77693_BATTERY_LOWVOLTAGE: 1448c2ecf20Sopenharmony_ci *val = POWER_SUPPLY_HEALTH_GOOD; 1458c2ecf20Sopenharmony_ci break; 1468c2ecf20Sopenharmony_ci case MAX77693_BATTERY_TIMER_EXPIRED: 1478c2ecf20Sopenharmony_ci /* 1488c2ecf20Sopenharmony_ci * Took longer to charge than expected, charging suspended. 1498c2ecf20Sopenharmony_ci * Damaged battery? 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_ci *val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; 1528c2ecf20Sopenharmony_ci break; 1538c2ecf20Sopenharmony_ci case MAX77693_BATTERY_OVERVOLTAGE: 1548c2ecf20Sopenharmony_ci *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 1558c2ecf20Sopenharmony_ci break; 1568c2ecf20Sopenharmony_ci case MAX77693_BATTERY_OVERCURRENT: 1578c2ecf20Sopenharmony_ci *val = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; 1588c2ecf20Sopenharmony_ci break; 1598c2ecf20Sopenharmony_ci case MAX77693_BATTERY_RESERVED: 1608c2ecf20Sopenharmony_ci default: 1618c2ecf20Sopenharmony_ci *val = POWER_SUPPLY_HEALTH_UNKNOWN; 1628c2ecf20Sopenharmony_ci break; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci return 0; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic int max77693_get_present(struct regmap *regmap, int *val) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci unsigned int data; 1718c2ecf20Sopenharmony_ci int ret; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* 1748c2ecf20Sopenharmony_ci * Read CHG_INT_OK register. High DETBAT bit here should be 1758c2ecf20Sopenharmony_ci * equal to value 0x0 in CHG_DETAILS_01/BAT field. 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_ci ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_INT_OK, &data); 1788c2ecf20Sopenharmony_ci if (ret < 0) 1798c2ecf20Sopenharmony_ci return ret; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci *val = (data & CHG_INT_OK_DETBAT_MASK) ? 0 : 1; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci return 0; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic int max77693_get_online(struct regmap *regmap, int *val) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci unsigned int data; 1898c2ecf20Sopenharmony_ci int ret; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci ret = regmap_read(regmap, MAX77693_CHG_REG_CHG_INT_OK, &data); 1928c2ecf20Sopenharmony_ci if (ret < 0) 1938c2ecf20Sopenharmony_ci return ret; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci *val = (data & CHG_INT_OK_CHGIN_MASK) ? 1 : 0; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci return 0; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic enum power_supply_property max77693_charger_props[] = { 2018c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 2028c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_TYPE, 2038c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, 2048c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 2058c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 2068c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_MODEL_NAME, 2078c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_MANUFACTURER, 2088c2ecf20Sopenharmony_ci}; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic int max77693_charger_get_property(struct power_supply *psy, 2118c2ecf20Sopenharmony_ci enum power_supply_property psp, 2128c2ecf20Sopenharmony_ci union power_supply_propval *val) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct max77693_charger *chg = power_supply_get_drvdata(psy); 2158c2ecf20Sopenharmony_ci struct regmap *regmap = chg->max77693->regmap; 2168c2ecf20Sopenharmony_ci int ret = 0; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci switch (psp) { 2198c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 2208c2ecf20Sopenharmony_ci ret = max77693_get_charger_state(regmap, &val->intval); 2218c2ecf20Sopenharmony_ci break; 2228c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_TYPE: 2238c2ecf20Sopenharmony_ci ret = max77693_get_charge_type(regmap, &val->intval); 2248c2ecf20Sopenharmony_ci break; 2258c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 2268c2ecf20Sopenharmony_ci ret = max77693_get_battery_health(regmap, &val->intval); 2278c2ecf20Sopenharmony_ci break; 2288c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 2298c2ecf20Sopenharmony_ci ret = max77693_get_present(regmap, &val->intval); 2308c2ecf20Sopenharmony_ci break; 2318c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 2328c2ecf20Sopenharmony_ci ret = max77693_get_online(regmap, &val->intval); 2338c2ecf20Sopenharmony_ci break; 2348c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_MODEL_NAME: 2358c2ecf20Sopenharmony_ci val->strval = max77693_charger_model; 2368c2ecf20Sopenharmony_ci break; 2378c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_MANUFACTURER: 2388c2ecf20Sopenharmony_ci val->strval = max77693_charger_manufacturer; 2398c2ecf20Sopenharmony_ci break; 2408c2ecf20Sopenharmony_ci default: 2418c2ecf20Sopenharmony_ci return -EINVAL; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci return ret; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic const struct power_supply_desc max77693_charger_desc = { 2488c2ecf20Sopenharmony_ci .name = MAX77693_CHARGER_NAME, 2498c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_BATTERY, 2508c2ecf20Sopenharmony_ci .properties = max77693_charger_props, 2518c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(max77693_charger_props), 2528c2ecf20Sopenharmony_ci .get_property = max77693_charger_get_property, 2538c2ecf20Sopenharmony_ci}; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic ssize_t device_attr_store(struct device *dev, 2568c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count, 2578c2ecf20Sopenharmony_ci int (*fn)(struct max77693_charger *, unsigned long)) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci struct max77693_charger *chg = dev_get_drvdata(dev); 2608c2ecf20Sopenharmony_ci unsigned long val; 2618c2ecf20Sopenharmony_ci int ret; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci ret = kstrtoul(buf, 10, &val); 2648c2ecf20Sopenharmony_ci if (ret) 2658c2ecf20Sopenharmony_ci return ret; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci ret = fn(chg, val); 2688c2ecf20Sopenharmony_ci if (ret) 2698c2ecf20Sopenharmony_ci return ret; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci return count; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic ssize_t fast_charge_timer_show(struct device *dev, 2758c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct max77693_charger *chg = dev_get_drvdata(dev); 2788c2ecf20Sopenharmony_ci unsigned int data, val; 2798c2ecf20Sopenharmony_ci int ret; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_01, 2828c2ecf20Sopenharmony_ci &data); 2838c2ecf20Sopenharmony_ci if (ret < 0) 2848c2ecf20Sopenharmony_ci return ret; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci data &= CHG_CNFG_01_FCHGTIME_MASK; 2878c2ecf20Sopenharmony_ci data >>= CHG_CNFG_01_FCHGTIME_SHIFT; 2888c2ecf20Sopenharmony_ci switch (data) { 2898c2ecf20Sopenharmony_ci case 0x1 ... 0x7: 2908c2ecf20Sopenharmony_ci /* Starting from 4 hours, step by 2 hours */ 2918c2ecf20Sopenharmony_ci val = 4 + (data - 1) * 2; 2928c2ecf20Sopenharmony_ci break; 2938c2ecf20Sopenharmony_ci case 0x0: 2948c2ecf20Sopenharmony_ci default: 2958c2ecf20Sopenharmony_ci val = 0; 2968c2ecf20Sopenharmony_ci break; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%u\n", val); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic int max77693_set_fast_charge_timer(struct max77693_charger *chg, 3038c2ecf20Sopenharmony_ci unsigned long hours) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci unsigned int data; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci /* 3088c2ecf20Sopenharmony_ci * 0x00 - disable 3098c2ecf20Sopenharmony_ci * 0x01 - 4h 3108c2ecf20Sopenharmony_ci * 0x02 - 6h 3118c2ecf20Sopenharmony_ci * ... 3128c2ecf20Sopenharmony_ci * 0x07 - 16h 3138c2ecf20Sopenharmony_ci * Round down odd values. 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_ci switch (hours) { 3168c2ecf20Sopenharmony_ci case 4 ... 16: 3178c2ecf20Sopenharmony_ci data = (hours - 4) / 2 + 1; 3188c2ecf20Sopenharmony_ci break; 3198c2ecf20Sopenharmony_ci case 0: 3208c2ecf20Sopenharmony_ci /* Disable */ 3218c2ecf20Sopenharmony_ci data = 0; 3228c2ecf20Sopenharmony_ci break; 3238c2ecf20Sopenharmony_ci default: 3248c2ecf20Sopenharmony_ci return -EINVAL; 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci data <<= CHG_CNFG_01_FCHGTIME_SHIFT; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci return regmap_update_bits(chg->max77693->regmap, 3298c2ecf20Sopenharmony_ci MAX77693_CHG_REG_CHG_CNFG_01, 3308c2ecf20Sopenharmony_ci CHG_CNFG_01_FCHGTIME_MASK, data); 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic ssize_t fast_charge_timer_store(struct device *dev, 3348c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci return device_attr_store(dev, attr, buf, count, 3378c2ecf20Sopenharmony_ci max77693_set_fast_charge_timer); 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic ssize_t top_off_threshold_current_show(struct device *dev, 3418c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci struct max77693_charger *chg = dev_get_drvdata(dev); 3448c2ecf20Sopenharmony_ci unsigned int data, val; 3458c2ecf20Sopenharmony_ci int ret; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_03, 3488c2ecf20Sopenharmony_ci &data); 3498c2ecf20Sopenharmony_ci if (ret < 0) 3508c2ecf20Sopenharmony_ci return ret; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci data &= CHG_CNFG_03_TOITH_MASK; 3538c2ecf20Sopenharmony_ci data >>= CHG_CNFG_03_TOITH_SHIFT; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (data <= 0x04) 3568c2ecf20Sopenharmony_ci val = 100000 + data * 25000; 3578c2ecf20Sopenharmony_ci else 3588c2ecf20Sopenharmony_ci val = data * 50000; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%u\n", val); 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic int max77693_set_top_off_threshold_current(struct max77693_charger *chg, 3648c2ecf20Sopenharmony_ci unsigned long uamp) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci unsigned int data; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (uamp < 100000 || uamp > 350000) 3698c2ecf20Sopenharmony_ci return -EINVAL; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (uamp <= 200000) 3728c2ecf20Sopenharmony_ci data = (uamp - 100000) / 25000; 3738c2ecf20Sopenharmony_ci else 3748c2ecf20Sopenharmony_ci /* (200000, 350000> */ 3758c2ecf20Sopenharmony_ci data = uamp / 50000; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci data <<= CHG_CNFG_03_TOITH_SHIFT; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci return regmap_update_bits(chg->max77693->regmap, 3808c2ecf20Sopenharmony_ci MAX77693_CHG_REG_CHG_CNFG_03, 3818c2ecf20Sopenharmony_ci CHG_CNFG_03_TOITH_MASK, data); 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic ssize_t top_off_threshold_current_store(struct device *dev, 3858c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci return device_attr_store(dev, attr, buf, count, 3888c2ecf20Sopenharmony_ci max77693_set_top_off_threshold_current); 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic ssize_t top_off_timer_show(struct device *dev, 3928c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci struct max77693_charger *chg = dev_get_drvdata(dev); 3958c2ecf20Sopenharmony_ci unsigned int data, val; 3968c2ecf20Sopenharmony_ci int ret; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_03, 3998c2ecf20Sopenharmony_ci &data); 4008c2ecf20Sopenharmony_ci if (ret < 0) 4018c2ecf20Sopenharmony_ci return ret; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci data &= CHG_CNFG_03_TOTIME_MASK; 4048c2ecf20Sopenharmony_ci data >>= CHG_CNFG_03_TOTIME_SHIFT; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci val = data * 10; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%u\n", val); 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic int max77693_set_top_off_timer(struct max77693_charger *chg, 4128c2ecf20Sopenharmony_ci unsigned long minutes) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci unsigned int data; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci if (minutes > 70) 4178c2ecf20Sopenharmony_ci return -EINVAL; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci data = minutes / 10; 4208c2ecf20Sopenharmony_ci data <<= CHG_CNFG_03_TOTIME_SHIFT; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci return regmap_update_bits(chg->max77693->regmap, 4238c2ecf20Sopenharmony_ci MAX77693_CHG_REG_CHG_CNFG_03, 4248c2ecf20Sopenharmony_ci CHG_CNFG_03_TOTIME_MASK, data); 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic ssize_t top_off_timer_store(struct device *dev, 4288c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci return device_attr_store(dev, attr, buf, count, 4318c2ecf20Sopenharmony_ci max77693_set_top_off_timer); 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(fast_charge_timer); 4358c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(top_off_threshold_current); 4368c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(top_off_timer); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic int max77693_set_constant_volt(struct max77693_charger *chg, 4398c2ecf20Sopenharmony_ci unsigned int uvolt) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci unsigned int data; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci /* 4448c2ecf20Sopenharmony_ci * 0x00 - 3.650 V 4458c2ecf20Sopenharmony_ci * 0x01 - 3.675 V 4468c2ecf20Sopenharmony_ci * ... 4478c2ecf20Sopenharmony_ci * 0x1b - 4.325 V 4488c2ecf20Sopenharmony_ci * 0x1c - 4.340 V 4498c2ecf20Sopenharmony_ci * 0x1d - 4.350 V 4508c2ecf20Sopenharmony_ci * 0x1e - 4.375 V 4518c2ecf20Sopenharmony_ci * 0x1f - 4.400 V 4528c2ecf20Sopenharmony_ci */ 4538c2ecf20Sopenharmony_ci if (uvolt >= 3650000 && uvolt < 4340000) 4548c2ecf20Sopenharmony_ci data = (uvolt - 3650000) / 25000; 4558c2ecf20Sopenharmony_ci else if (uvolt >= 4340000 && uvolt < 4350000) 4568c2ecf20Sopenharmony_ci data = 0x1c; 4578c2ecf20Sopenharmony_ci else if (uvolt >= 4350000 && uvolt <= 4400000) 4588c2ecf20Sopenharmony_ci data = 0x1d + (uvolt - 4350000) / 25000; 4598c2ecf20Sopenharmony_ci else { 4608c2ecf20Sopenharmony_ci dev_err(chg->dev, "Wrong value for charging constant voltage\n"); 4618c2ecf20Sopenharmony_ci return -EINVAL; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci data <<= CHG_CNFG_04_CHGCVPRM_SHIFT; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci dev_dbg(chg->dev, "Charging constant voltage: %u (0x%x)\n", uvolt, 4678c2ecf20Sopenharmony_ci data); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci return regmap_update_bits(chg->max77693->regmap, 4708c2ecf20Sopenharmony_ci MAX77693_CHG_REG_CHG_CNFG_04, 4718c2ecf20Sopenharmony_ci CHG_CNFG_04_CHGCVPRM_MASK, data); 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic int max77693_set_min_system_volt(struct max77693_charger *chg, 4758c2ecf20Sopenharmony_ci unsigned int uvolt) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci unsigned int data; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (uvolt < 3000000 || uvolt > 3700000) { 4808c2ecf20Sopenharmony_ci dev_err(chg->dev, "Wrong value for minimum system regulation voltage\n"); 4818c2ecf20Sopenharmony_ci return -EINVAL; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci data = (uvolt - 3000000) / 100000; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci data <<= CHG_CNFG_04_MINVSYS_SHIFT; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci dev_dbg(chg->dev, "Minimum system regulation voltage: %u (0x%x)\n", 4898c2ecf20Sopenharmony_ci uvolt, data); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci return regmap_update_bits(chg->max77693->regmap, 4928c2ecf20Sopenharmony_ci MAX77693_CHG_REG_CHG_CNFG_04, 4938c2ecf20Sopenharmony_ci CHG_CNFG_04_MINVSYS_MASK, data); 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic int max77693_set_thermal_regulation_temp(struct max77693_charger *chg, 4978c2ecf20Sopenharmony_ci unsigned int cels) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci unsigned int data; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci switch (cels) { 5028c2ecf20Sopenharmony_ci case 70: 5038c2ecf20Sopenharmony_ci case 85: 5048c2ecf20Sopenharmony_ci case 100: 5058c2ecf20Sopenharmony_ci case 115: 5068c2ecf20Sopenharmony_ci data = (cels - 70) / 15; 5078c2ecf20Sopenharmony_ci break; 5088c2ecf20Sopenharmony_ci default: 5098c2ecf20Sopenharmony_ci dev_err(chg->dev, "Wrong value for thermal regulation loop temperature\n"); 5108c2ecf20Sopenharmony_ci return -EINVAL; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci data <<= CHG_CNFG_07_REGTEMP_SHIFT; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci dev_dbg(chg->dev, "Thermal regulation loop temperature: %u (0x%x)\n", 5168c2ecf20Sopenharmony_ci cels, data); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci return regmap_update_bits(chg->max77693->regmap, 5198c2ecf20Sopenharmony_ci MAX77693_CHG_REG_CHG_CNFG_07, 5208c2ecf20Sopenharmony_ci CHG_CNFG_07_REGTEMP_MASK, data); 5218c2ecf20Sopenharmony_ci} 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_cistatic int max77693_set_batttery_overcurrent(struct max77693_charger *chg, 5248c2ecf20Sopenharmony_ci unsigned int uamp) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci unsigned int data; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (uamp && (uamp < 2000000 || uamp > 3500000)) { 5298c2ecf20Sopenharmony_ci dev_err(chg->dev, "Wrong value for battery overcurrent\n"); 5308c2ecf20Sopenharmony_ci return -EINVAL; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci if (uamp) 5348c2ecf20Sopenharmony_ci data = ((uamp - 2000000) / 250000) + 1; 5358c2ecf20Sopenharmony_ci else 5368c2ecf20Sopenharmony_ci data = 0; /* disable */ 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci data <<= CHG_CNFG_12_B2SOVRC_SHIFT; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci dev_dbg(chg->dev, "Battery overcurrent: %u (0x%x)\n", uamp, data); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci return regmap_update_bits(chg->max77693->regmap, 5438c2ecf20Sopenharmony_ci MAX77693_CHG_REG_CHG_CNFG_12, 5448c2ecf20Sopenharmony_ci CHG_CNFG_12_B2SOVRC_MASK, data); 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic int max77693_set_charge_input_threshold_volt(struct max77693_charger *chg, 5488c2ecf20Sopenharmony_ci unsigned int uvolt) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci unsigned int data; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci switch (uvolt) { 5538c2ecf20Sopenharmony_ci case 4300000: 5548c2ecf20Sopenharmony_ci data = 0x0; 5558c2ecf20Sopenharmony_ci break; 5568c2ecf20Sopenharmony_ci case 4700000: 5578c2ecf20Sopenharmony_ci case 4800000: 5588c2ecf20Sopenharmony_ci case 4900000: 5598c2ecf20Sopenharmony_ci data = (uvolt - 4700000) / 100000; 5608c2ecf20Sopenharmony_ci break; 5618c2ecf20Sopenharmony_ci default: 5628c2ecf20Sopenharmony_ci dev_err(chg->dev, "Wrong value for charge input voltage regulation threshold\n"); 5638c2ecf20Sopenharmony_ci return -EINVAL; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci data <<= CHG_CNFG_12_VCHGINREG_SHIFT; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci dev_dbg(chg->dev, "Charge input voltage regulation threshold: %u (0x%x)\n", 5698c2ecf20Sopenharmony_ci uvolt, data); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci return regmap_update_bits(chg->max77693->regmap, 5728c2ecf20Sopenharmony_ci MAX77693_CHG_REG_CHG_CNFG_12, 5738c2ecf20Sopenharmony_ci CHG_CNFG_12_VCHGINREG_MASK, data); 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci/* 5778c2ecf20Sopenharmony_ci * Sets charger registers to proper and safe default values. 5788c2ecf20Sopenharmony_ci */ 5798c2ecf20Sopenharmony_cistatic int max77693_reg_init(struct max77693_charger *chg) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci int ret; 5828c2ecf20Sopenharmony_ci unsigned int data; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* Unlock charger register protection */ 5858c2ecf20Sopenharmony_ci data = (0x3 << CHG_CNFG_06_CHGPROT_SHIFT); 5868c2ecf20Sopenharmony_ci ret = regmap_update_bits(chg->max77693->regmap, 5878c2ecf20Sopenharmony_ci MAX77693_CHG_REG_CHG_CNFG_06, 5888c2ecf20Sopenharmony_ci CHG_CNFG_06_CHGPROT_MASK, data); 5898c2ecf20Sopenharmony_ci if (ret) { 5908c2ecf20Sopenharmony_ci dev_err(chg->dev, "Error unlocking registers: %d\n", ret); 5918c2ecf20Sopenharmony_ci return ret; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci ret = max77693_set_fast_charge_timer(chg, DEFAULT_FAST_CHARGE_TIMER); 5958c2ecf20Sopenharmony_ci if (ret) 5968c2ecf20Sopenharmony_ci return ret; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci ret = max77693_set_top_off_threshold_current(chg, 5998c2ecf20Sopenharmony_ci DEFAULT_TOP_OFF_THRESHOLD_CURRENT); 6008c2ecf20Sopenharmony_ci if (ret) 6018c2ecf20Sopenharmony_ci return ret; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci ret = max77693_set_top_off_timer(chg, DEFAULT_TOP_OFF_TIMER); 6048c2ecf20Sopenharmony_ci if (ret) 6058c2ecf20Sopenharmony_ci return ret; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci ret = max77693_set_constant_volt(chg, chg->constant_volt); 6088c2ecf20Sopenharmony_ci if (ret) 6098c2ecf20Sopenharmony_ci return ret; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci ret = max77693_set_min_system_volt(chg, chg->min_system_volt); 6128c2ecf20Sopenharmony_ci if (ret) 6138c2ecf20Sopenharmony_ci return ret; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci ret = max77693_set_thermal_regulation_temp(chg, 6168c2ecf20Sopenharmony_ci chg->thermal_regulation_temp); 6178c2ecf20Sopenharmony_ci if (ret) 6188c2ecf20Sopenharmony_ci return ret; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci ret = max77693_set_batttery_overcurrent(chg, chg->batttery_overcurrent); 6218c2ecf20Sopenharmony_ci if (ret) 6228c2ecf20Sopenharmony_ci return ret; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci return max77693_set_charge_input_threshold_volt(chg, 6258c2ecf20Sopenharmony_ci chg->charge_input_threshold_volt); 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 6298c2ecf20Sopenharmony_cistatic int max77693_dt_init(struct device *dev, struct max77693_charger *chg) 6308c2ecf20Sopenharmony_ci{ 6318c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci if (!np) { 6348c2ecf20Sopenharmony_ci dev_err(dev, "no charger OF node\n"); 6358c2ecf20Sopenharmony_ci return -EINVAL; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "maxim,constant-microvolt", 6398c2ecf20Sopenharmony_ci &chg->constant_volt)) 6408c2ecf20Sopenharmony_ci chg->constant_volt = DEFAULT_CONSTANT_VOLT; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "maxim,min-system-microvolt", 6438c2ecf20Sopenharmony_ci &chg->min_system_volt)) 6448c2ecf20Sopenharmony_ci chg->min_system_volt = DEFAULT_MIN_SYSTEM_VOLT; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "maxim,thermal-regulation-celsius", 6478c2ecf20Sopenharmony_ci &chg->thermal_regulation_temp)) 6488c2ecf20Sopenharmony_ci chg->thermal_regulation_temp = DEFAULT_THERMAL_REGULATION_TEMP; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "maxim,battery-overcurrent-microamp", 6518c2ecf20Sopenharmony_ci &chg->batttery_overcurrent)) 6528c2ecf20Sopenharmony_ci chg->batttery_overcurrent = DEFAULT_BATTERY_OVERCURRENT; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "maxim,charge-input-threshold-microvolt", 6558c2ecf20Sopenharmony_ci &chg->charge_input_threshold_volt)) 6568c2ecf20Sopenharmony_ci chg->charge_input_threshold_volt = 6578c2ecf20Sopenharmony_ci DEFAULT_CHARGER_INPUT_THRESHOLD_VOLT; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci return 0; 6608c2ecf20Sopenharmony_ci} 6618c2ecf20Sopenharmony_ci#else /* CONFIG_OF */ 6628c2ecf20Sopenharmony_cistatic int max77693_dt_init(struct device *dev, struct max77693_charger *chg) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci return 0; 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci#endif /* CONFIG_OF */ 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic int max77693_charger_probe(struct platform_device *pdev) 6698c2ecf20Sopenharmony_ci{ 6708c2ecf20Sopenharmony_ci struct max77693_charger *chg; 6718c2ecf20Sopenharmony_ci struct power_supply_config psy_cfg = {}; 6728c2ecf20Sopenharmony_ci struct max77693_dev *max77693 = dev_get_drvdata(pdev->dev.parent); 6738c2ecf20Sopenharmony_ci int ret; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL); 6768c2ecf20Sopenharmony_ci if (!chg) 6778c2ecf20Sopenharmony_ci return -ENOMEM; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, chg); 6808c2ecf20Sopenharmony_ci chg->dev = &pdev->dev; 6818c2ecf20Sopenharmony_ci chg->max77693 = max77693; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci ret = max77693_dt_init(&pdev->dev, chg); 6848c2ecf20Sopenharmony_ci if (ret) 6858c2ecf20Sopenharmony_ci return ret; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci ret = max77693_reg_init(chg); 6888c2ecf20Sopenharmony_ci if (ret) 6898c2ecf20Sopenharmony_ci return ret; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci psy_cfg.drv_data = chg; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci ret = device_create_file(&pdev->dev, &dev_attr_fast_charge_timer); 6948c2ecf20Sopenharmony_ci if (ret) { 6958c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed: create fast charge timer sysfs entry\n"); 6968c2ecf20Sopenharmony_ci goto err; 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci ret = device_create_file(&pdev->dev, 7008c2ecf20Sopenharmony_ci &dev_attr_top_off_threshold_current); 7018c2ecf20Sopenharmony_ci if (ret) { 7028c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed: create top off current sysfs entry\n"); 7038c2ecf20Sopenharmony_ci goto err; 7048c2ecf20Sopenharmony_ci } 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci ret = device_create_file(&pdev->dev, &dev_attr_top_off_timer); 7078c2ecf20Sopenharmony_ci if (ret) { 7088c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed: create top off timer sysfs entry\n"); 7098c2ecf20Sopenharmony_ci goto err; 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci chg->charger = power_supply_register(&pdev->dev, 7138c2ecf20Sopenharmony_ci &max77693_charger_desc, 7148c2ecf20Sopenharmony_ci &psy_cfg); 7158c2ecf20Sopenharmony_ci if (IS_ERR(chg->charger)) { 7168c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed: power supply register\n"); 7178c2ecf20Sopenharmony_ci ret = PTR_ERR(chg->charger); 7188c2ecf20Sopenharmony_ci goto err; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci return 0; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_cierr: 7248c2ecf20Sopenharmony_ci device_remove_file(&pdev->dev, &dev_attr_top_off_timer); 7258c2ecf20Sopenharmony_ci device_remove_file(&pdev->dev, &dev_attr_top_off_threshold_current); 7268c2ecf20Sopenharmony_ci device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci return ret; 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_cistatic int max77693_charger_remove(struct platform_device *pdev) 7328c2ecf20Sopenharmony_ci{ 7338c2ecf20Sopenharmony_ci struct max77693_charger *chg = platform_get_drvdata(pdev); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci device_remove_file(&pdev->dev, &dev_attr_top_off_timer); 7368c2ecf20Sopenharmony_ci device_remove_file(&pdev->dev, &dev_attr_top_off_threshold_current); 7378c2ecf20Sopenharmony_ci device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci power_supply_unregister(chg->charger); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci return 0; 7428c2ecf20Sopenharmony_ci} 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_cistatic const struct platform_device_id max77693_charger_id[] = { 7458c2ecf20Sopenharmony_ci { "max77693-charger", 0, }, 7468c2ecf20Sopenharmony_ci { } 7478c2ecf20Sopenharmony_ci}; 7488c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, max77693_charger_id); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_cistatic struct platform_driver max77693_charger_driver = { 7518c2ecf20Sopenharmony_ci .driver = { 7528c2ecf20Sopenharmony_ci .name = "max77693-charger", 7538c2ecf20Sopenharmony_ci }, 7548c2ecf20Sopenharmony_ci .probe = max77693_charger_probe, 7558c2ecf20Sopenharmony_ci .remove = max77693_charger_remove, 7568c2ecf20Sopenharmony_ci .id_table = max77693_charger_id, 7578c2ecf20Sopenharmony_ci}; 7588c2ecf20Sopenharmony_cimodule_platform_driver(max77693_charger_driver); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ciMODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>"); 7618c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Maxim 77693 charger driver"); 7628c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 763