18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * 1-Wire implementation for Maxim Semiconductor 38c2ecf20Sopenharmony_ci * MAX7211/MAX17215 stanalone fuel gauge chip 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017 Radioavionica Corporation 68c2ecf20Sopenharmony_ci * Author: Alex A. Mihaylov <minimumlaw@rambler.ru> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Use consistent with the GNU GPL is permitted, 98c2ecf20Sopenharmony_ci * provided that this copyright notice is 108c2ecf20Sopenharmony_ci * preserved in its entirety in all copies and derived works. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/w1.h> 178c2ecf20Sopenharmony_ci#include <linux/regmap.h> 188c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define W1_MAX1721X_FAMILY_ID 0x26 218c2ecf20Sopenharmony_ci#define DEF_DEV_NAME_MAX17211 "MAX17211" 228c2ecf20Sopenharmony_ci#define DEF_DEV_NAME_MAX17215 "MAX17215" 238c2ecf20Sopenharmony_ci#define DEF_DEV_NAME_UNKNOWN "UNKNOWN" 248c2ecf20Sopenharmony_ci#define DEF_MFG_NAME "MAXIM" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define PSY_MAX_NAME_LEN 32 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* Number of valid register addresses in W1 mode */ 298c2ecf20Sopenharmony_ci#define MAX1721X_MAX_REG_NR 0x1EF 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* Factory settings (nonvilatile registers) (W1 specific) */ 328c2ecf20Sopenharmony_ci#define MAX1721X_REG_NRSENSE 0x1CF /* RSense in 10^-5 Ohm */ 338c2ecf20Sopenharmony_ci/* Strings */ 348c2ecf20Sopenharmony_ci#define MAX1721X_REG_MFG_STR 0x1CC 358c2ecf20Sopenharmony_ci#define MAX1721X_REG_MFG_NUMB 3 368c2ecf20Sopenharmony_ci#define MAX1721X_REG_DEV_STR 0x1DB 378c2ecf20Sopenharmony_ci#define MAX1721X_REG_DEV_NUMB 5 388c2ecf20Sopenharmony_ci/* HEX Strings */ 398c2ecf20Sopenharmony_ci#define MAX1721X_REG_SER_HEX 0x1D8 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* MAX172XX Output Registers for W1 chips */ 428c2ecf20Sopenharmony_ci#define MAX172XX_REG_STATUS 0x000 /* status reg */ 438c2ecf20Sopenharmony_ci#define MAX172XX_BAT_PRESENT (1<<4) /* battery connected bit */ 448c2ecf20Sopenharmony_ci#define MAX172XX_REG_DEVNAME 0x021 /* chip config */ 458c2ecf20Sopenharmony_ci#define MAX172XX_DEV_MASK 0x000F /* chip type mask */ 468c2ecf20Sopenharmony_ci#define MAX172X1_DEV 0x0001 478c2ecf20Sopenharmony_ci#define MAX172X5_DEV 0x0005 488c2ecf20Sopenharmony_ci#define MAX172XX_REG_TEMP 0x008 /* Temperature */ 498c2ecf20Sopenharmony_ci#define MAX172XX_REG_BATT 0x0DA /* Battery voltage */ 508c2ecf20Sopenharmony_ci#define MAX172XX_REG_CURRENT 0x00A /* Actual current */ 518c2ecf20Sopenharmony_ci#define MAX172XX_REG_AVGCURRENT 0x00B /* Average current */ 528c2ecf20Sopenharmony_ci#define MAX172XX_REG_REPSOC 0x006 /* Percentage of charge */ 538c2ecf20Sopenharmony_ci#define MAX172XX_REG_DESIGNCAP 0x018 /* Design capacity */ 548c2ecf20Sopenharmony_ci#define MAX172XX_REG_REPCAP 0x005 /* Average capacity */ 558c2ecf20Sopenharmony_ci#define MAX172XX_REG_TTE 0x011 /* Time to empty */ 568c2ecf20Sopenharmony_ci#define MAX172XX_REG_TTF 0x020 /* Time to full */ 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistruct max17211_device_info { 598c2ecf20Sopenharmony_ci char name[PSY_MAX_NAME_LEN]; 608c2ecf20Sopenharmony_ci struct power_supply *bat; 618c2ecf20Sopenharmony_ci struct power_supply_desc bat_desc; 628c2ecf20Sopenharmony_ci struct device *w1_dev; 638c2ecf20Sopenharmony_ci struct regmap *regmap; 648c2ecf20Sopenharmony_ci /* battery design format */ 658c2ecf20Sopenharmony_ci unsigned int rsense; /* in tenths uOhm */ 668c2ecf20Sopenharmony_ci char DeviceName[2 * MAX1721X_REG_DEV_NUMB + 1]; 678c2ecf20Sopenharmony_ci char ManufacturerName[2 * MAX1721X_REG_MFG_NUMB + 1]; 688c2ecf20Sopenharmony_ci char SerialNumber[13]; /* see get_sn_str() later for comment */ 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* Convert regs value to power_supply units */ 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic inline int max172xx_time_to_ps(unsigned int reg) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci return reg * 5625 / 1000; /* in sec. */ 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic inline int max172xx_percent_to_ps(unsigned int reg) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci return reg / 256; /* in percent from 0 to 100 */ 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic inline int max172xx_voltage_to_ps(unsigned int reg) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci return reg * 1250; /* in uV */ 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic inline int max172xx_capacity_to_ps(unsigned int reg) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci return reg * 500; /* in uAh */ 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* 948c2ecf20Sopenharmony_ci * Current and temperature is signed values, so unsigned regs 958c2ecf20Sopenharmony_ci * value must be converted to signed type 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic inline int max172xx_temperature_to_ps(unsigned int reg) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci int val = (int16_t)(reg); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci return val * 10 / 256; /* in tenths of deg. C */ 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* 1068c2ecf20Sopenharmony_ci * Calculating current registers resolution: 1078c2ecf20Sopenharmony_ci * 1088c2ecf20Sopenharmony_ci * RSense stored in 10^-5 Ohm, so mesaurment voltage must be 1098c2ecf20Sopenharmony_ci * in 10^-11 Volts for get current in uA. 1108c2ecf20Sopenharmony_ci * 16 bit current reg fullscale +/-51.2mV is 102400 uV. 1118c2ecf20Sopenharmony_ci * So: 102400 / 65535 * 10^5 = 156252 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_cistatic inline int max172xx_current_to_voltage(unsigned int reg) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci int val = (int16_t)(reg); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return val * 156252; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic inline struct max17211_device_info * 1228c2ecf20Sopenharmony_cito_device_info(struct power_supply *psy) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci return power_supply_get_drvdata(psy); 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int max1721x_battery_get_property(struct power_supply *psy, 1288c2ecf20Sopenharmony_ci enum power_supply_property psp, 1298c2ecf20Sopenharmony_ci union power_supply_propval *val) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct max17211_device_info *info = to_device_info(psy); 1328c2ecf20Sopenharmony_ci unsigned int reg = 0; 1338c2ecf20Sopenharmony_ci int ret = 0; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci switch (psp) { 1368c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 1378c2ecf20Sopenharmony_ci /* 1388c2ecf20Sopenharmony_ci * POWER_SUPPLY_PROP_PRESENT will always readable via 1398c2ecf20Sopenharmony_ci * sysfs interface. Value return 0 if battery not 1408c2ecf20Sopenharmony_ci * present or unaccesable via W1. 1418c2ecf20Sopenharmony_ci */ 1428c2ecf20Sopenharmony_ci val->intval = 1438c2ecf20Sopenharmony_ci regmap_read(info->regmap, MAX172XX_REG_STATUS, 1448c2ecf20Sopenharmony_ci ®) ? 0 : !(reg & MAX172XX_BAT_PRESENT); 1458c2ecf20Sopenharmony_ci break; 1468c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY: 1478c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, MAX172XX_REG_REPSOC, ®); 1488c2ecf20Sopenharmony_ci val->intval = max172xx_percent_to_ps(reg); 1498c2ecf20Sopenharmony_ci break; 1508c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 1518c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, MAX172XX_REG_BATT, ®); 1528c2ecf20Sopenharmony_ci val->intval = max172xx_voltage_to_ps(reg); 1538c2ecf20Sopenharmony_ci break; 1548c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: 1558c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, MAX172XX_REG_DESIGNCAP, ®); 1568c2ecf20Sopenharmony_ci val->intval = max172xx_capacity_to_ps(reg); 1578c2ecf20Sopenharmony_ci break; 1588c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_AVG: 1598c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, MAX172XX_REG_REPCAP, ®); 1608c2ecf20Sopenharmony_ci val->intval = max172xx_capacity_to_ps(reg); 1618c2ecf20Sopenharmony_ci break; 1628c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: 1638c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, MAX172XX_REG_TTE, ®); 1648c2ecf20Sopenharmony_ci val->intval = max172xx_time_to_ps(reg); 1658c2ecf20Sopenharmony_ci break; 1668c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: 1678c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, MAX172XX_REG_TTF, ®); 1688c2ecf20Sopenharmony_ci val->intval = max172xx_time_to_ps(reg); 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TEMP: 1718c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, MAX172XX_REG_TEMP, ®); 1728c2ecf20Sopenharmony_ci val->intval = max172xx_temperature_to_ps(reg); 1738c2ecf20Sopenharmony_ci break; 1748c2ecf20Sopenharmony_ci /* We need signed current, so must cast info->rsense to signed type */ 1758c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_NOW: 1768c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, MAX172XX_REG_CURRENT, ®); 1778c2ecf20Sopenharmony_ci val->intval = 1788c2ecf20Sopenharmony_ci max172xx_current_to_voltage(reg) / (int)info->rsense; 1798c2ecf20Sopenharmony_ci break; 1808c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_AVG: 1818c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, MAX172XX_REG_AVGCURRENT, ®); 1828c2ecf20Sopenharmony_ci val->intval = 1838c2ecf20Sopenharmony_ci max172xx_current_to_voltage(reg) / (int)info->rsense; 1848c2ecf20Sopenharmony_ci break; 1858c2ecf20Sopenharmony_ci /* 1868c2ecf20Sopenharmony_ci * Strings already received and inited by probe. 1878c2ecf20Sopenharmony_ci * We do dummy read for check battery still available. 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_MODEL_NAME: 1908c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, MAX1721X_REG_DEV_STR, ®); 1918c2ecf20Sopenharmony_ci val->strval = info->DeviceName; 1928c2ecf20Sopenharmony_ci break; 1938c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_MANUFACTURER: 1948c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, MAX1721X_REG_MFG_STR, ®); 1958c2ecf20Sopenharmony_ci val->strval = info->ManufacturerName; 1968c2ecf20Sopenharmony_ci break; 1978c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_SERIAL_NUMBER: 1988c2ecf20Sopenharmony_ci ret = regmap_read(info->regmap, MAX1721X_REG_SER_HEX, ®); 1998c2ecf20Sopenharmony_ci val->strval = info->SerialNumber; 2008c2ecf20Sopenharmony_ci break; 2018c2ecf20Sopenharmony_ci default: 2028c2ecf20Sopenharmony_ci ret = -EINVAL; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return ret; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic enum power_supply_property max1721x_battery_props[] = { 2098c2ecf20Sopenharmony_ci /* int */ 2108c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 2118c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CAPACITY, 2128c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 2138c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 2148c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_AVG, 2158c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 2168c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 2178c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_TEMP, 2188c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_NOW, 2198c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_AVG, 2208c2ecf20Sopenharmony_ci /* strings */ 2218c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_MODEL_NAME, 2228c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_MANUFACTURER, 2238c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_SERIAL_NUMBER, 2248c2ecf20Sopenharmony_ci}; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic int get_string(struct max17211_device_info *info, 2278c2ecf20Sopenharmony_ci uint16_t reg, uint8_t nr, char *str) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci unsigned int val; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (!str || !(reg == MAX1721X_REG_MFG_STR || 2328c2ecf20Sopenharmony_ci reg == MAX1721X_REG_DEV_STR)) 2338c2ecf20Sopenharmony_ci return -EFAULT; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci while (nr--) { 2368c2ecf20Sopenharmony_ci if (regmap_read(info->regmap, reg++, &val)) 2378c2ecf20Sopenharmony_ci return -EFAULT; 2388c2ecf20Sopenharmony_ci *str++ = val>>8 & 0x00FF; 2398c2ecf20Sopenharmony_ci *str++ = val & 0x00FF; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci return 0; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/* Maxim say: Serial number is a hex string up to 12 hex characters */ 2458c2ecf20Sopenharmony_cistatic int get_sn_string(struct max17211_device_info *info, char *str) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci unsigned int val[3]; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (!str) 2508c2ecf20Sopenharmony_ci return -EFAULT; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (regmap_read(info->regmap, MAX1721X_REG_SER_HEX, &val[0])) 2538c2ecf20Sopenharmony_ci return -EFAULT; 2548c2ecf20Sopenharmony_ci if (regmap_read(info->regmap, MAX1721X_REG_SER_HEX + 1, &val[1])) 2558c2ecf20Sopenharmony_ci return -EFAULT; 2568c2ecf20Sopenharmony_ci if (regmap_read(info->regmap, MAX1721X_REG_SER_HEX + 2, &val[2])) 2578c2ecf20Sopenharmony_ci return -EFAULT; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci snprintf(str, 13, "%04X%04X%04X", val[0], val[1], val[2]); 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci/* 2648c2ecf20Sopenharmony_ci * MAX1721x registers description for w1-regmap 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_cistatic const struct regmap_range max1721x_allow_range[] = { 2678c2ecf20Sopenharmony_ci regmap_reg_range(0, 0xDF), /* volatile data */ 2688c2ecf20Sopenharmony_ci regmap_reg_range(0x180, 0x1DF), /* non-volatile memory */ 2698c2ecf20Sopenharmony_ci regmap_reg_range(0x1E0, 0x1EF), /* non-volatile history (unused) */ 2708c2ecf20Sopenharmony_ci}; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic const struct regmap_range max1721x_deny_range[] = { 2738c2ecf20Sopenharmony_ci /* volatile data unused registers */ 2748c2ecf20Sopenharmony_ci regmap_reg_range(0x24, 0x26), 2758c2ecf20Sopenharmony_ci regmap_reg_range(0x30, 0x31), 2768c2ecf20Sopenharmony_ci regmap_reg_range(0x33, 0x34), 2778c2ecf20Sopenharmony_ci regmap_reg_range(0x37, 0x37), 2788c2ecf20Sopenharmony_ci regmap_reg_range(0x3B, 0x3C), 2798c2ecf20Sopenharmony_ci regmap_reg_range(0x40, 0x41), 2808c2ecf20Sopenharmony_ci regmap_reg_range(0x43, 0x44), 2818c2ecf20Sopenharmony_ci regmap_reg_range(0x47, 0x49), 2828c2ecf20Sopenharmony_ci regmap_reg_range(0x4B, 0x4C), 2838c2ecf20Sopenharmony_ci regmap_reg_range(0x4E, 0xAF), 2848c2ecf20Sopenharmony_ci regmap_reg_range(0xB1, 0xB3), 2858c2ecf20Sopenharmony_ci regmap_reg_range(0xB5, 0xB7), 2868c2ecf20Sopenharmony_ci regmap_reg_range(0xBF, 0xD0), 2878c2ecf20Sopenharmony_ci regmap_reg_range(0xDB, 0xDB), 2888c2ecf20Sopenharmony_ci /* hole between volatile and non-volatile registers */ 2898c2ecf20Sopenharmony_ci regmap_reg_range(0xE0, 0x17F), 2908c2ecf20Sopenharmony_ci}; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic const struct regmap_access_table max1721x_regs = { 2938c2ecf20Sopenharmony_ci .yes_ranges = max1721x_allow_range, 2948c2ecf20Sopenharmony_ci .n_yes_ranges = ARRAY_SIZE(max1721x_allow_range), 2958c2ecf20Sopenharmony_ci .no_ranges = max1721x_deny_range, 2968c2ecf20Sopenharmony_ci .n_no_ranges = ARRAY_SIZE(max1721x_deny_range), 2978c2ecf20Sopenharmony_ci}; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci/* 3008c2ecf20Sopenharmony_ci * Model Gauge M5 Algorithm output register 3018c2ecf20Sopenharmony_ci * Volatile data (must not be cached) 3028c2ecf20Sopenharmony_ci */ 3038c2ecf20Sopenharmony_cistatic const struct regmap_range max1721x_volatile_allow[] = { 3048c2ecf20Sopenharmony_ci regmap_reg_range(0, 0xDF), 3058c2ecf20Sopenharmony_ci}; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic const struct regmap_access_table max1721x_volatile_regs = { 3088c2ecf20Sopenharmony_ci .yes_ranges = max1721x_volatile_allow, 3098c2ecf20Sopenharmony_ci .n_yes_ranges = ARRAY_SIZE(max1721x_volatile_allow), 3108c2ecf20Sopenharmony_ci}; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci/* 3138c2ecf20Sopenharmony_ci * W1-regmap config 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_cistatic const struct regmap_config max1721x_regmap_w1_config = { 3168c2ecf20Sopenharmony_ci .reg_bits = 16, 3178c2ecf20Sopenharmony_ci .val_bits = 16, 3188c2ecf20Sopenharmony_ci .rd_table = &max1721x_regs, 3198c2ecf20Sopenharmony_ci .volatile_table = &max1721x_volatile_regs, 3208c2ecf20Sopenharmony_ci .max_register = MAX1721X_MAX_REG_NR, 3218c2ecf20Sopenharmony_ci}; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic int devm_w1_max1721x_add_device(struct w1_slave *sl) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci struct power_supply_config psy_cfg = {}; 3268c2ecf20Sopenharmony_ci struct max17211_device_info *info; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci info = devm_kzalloc(&sl->dev, sizeof(*info), GFP_KERNEL); 3298c2ecf20Sopenharmony_ci if (!info) 3308c2ecf20Sopenharmony_ci return -ENOMEM; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci sl->family_data = (void *)info; 3338c2ecf20Sopenharmony_ci info->w1_dev = &sl->dev; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* 3368c2ecf20Sopenharmony_ci * power_supply class battery name translated from W1 slave device 3378c2ecf20Sopenharmony_ci * unical ID (look like 26-0123456789AB) to "max1721x-0123456789AB\0" 3388c2ecf20Sopenharmony_ci * so, 26 (device family) correcpondent to max1721x devices. 3398c2ecf20Sopenharmony_ci * Device name still unical for any numbers connected devices. 3408c2ecf20Sopenharmony_ci */ 3418c2ecf20Sopenharmony_ci snprintf(info->name, sizeof(info->name), 3428c2ecf20Sopenharmony_ci "max1721x-%012X", (unsigned int)sl->reg_num.id); 3438c2ecf20Sopenharmony_ci info->bat_desc.name = info->name; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* 3468c2ecf20Sopenharmony_ci * FixMe: battery device name exceed max len for thermal_zone device 3478c2ecf20Sopenharmony_ci * name and translation to thermal_zone must be disabled. 3488c2ecf20Sopenharmony_ci */ 3498c2ecf20Sopenharmony_ci info->bat_desc.no_thermal = true; 3508c2ecf20Sopenharmony_ci info->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY; 3518c2ecf20Sopenharmony_ci info->bat_desc.properties = max1721x_battery_props; 3528c2ecf20Sopenharmony_ci info->bat_desc.num_properties = ARRAY_SIZE(max1721x_battery_props); 3538c2ecf20Sopenharmony_ci info->bat_desc.get_property = max1721x_battery_get_property; 3548c2ecf20Sopenharmony_ci psy_cfg.drv_data = info; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci /* regmap init */ 3578c2ecf20Sopenharmony_ci info->regmap = devm_regmap_init_w1(info->w1_dev, 3588c2ecf20Sopenharmony_ci &max1721x_regmap_w1_config); 3598c2ecf20Sopenharmony_ci if (IS_ERR(info->regmap)) { 3608c2ecf20Sopenharmony_ci int err = PTR_ERR(info->regmap); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci dev_err(info->w1_dev, "Failed to allocate register map: %d\n", 3638c2ecf20Sopenharmony_ci err); 3648c2ecf20Sopenharmony_ci return err; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* rsense init */ 3688c2ecf20Sopenharmony_ci info->rsense = 0; 3698c2ecf20Sopenharmony_ci if (regmap_read(info->regmap, MAX1721X_REG_NRSENSE, &info->rsense)) { 3708c2ecf20Sopenharmony_ci dev_err(info->w1_dev, "Can't read RSense. Hardware error.\n"); 3718c2ecf20Sopenharmony_ci return -ENODEV; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (!info->rsense) { 3758c2ecf20Sopenharmony_ci dev_warn(info->w1_dev, "RSense not calibrated, set 10 mOhms!\n"); 3768c2ecf20Sopenharmony_ci info->rsense = 1000; /* in regs in 10^-5 */ 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci dev_info(info->w1_dev, "RSense: %d mOhms.\n", info->rsense / 100); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (get_string(info, MAX1721X_REG_MFG_STR, 3818c2ecf20Sopenharmony_ci MAX1721X_REG_MFG_NUMB, info->ManufacturerName)) { 3828c2ecf20Sopenharmony_ci dev_err(info->w1_dev, "Can't read manufacturer. Hardware error.\n"); 3838c2ecf20Sopenharmony_ci return -ENODEV; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (!info->ManufacturerName[0]) 3878c2ecf20Sopenharmony_ci strncpy(info->ManufacturerName, DEF_MFG_NAME, 3888c2ecf20Sopenharmony_ci 2 * MAX1721X_REG_MFG_NUMB); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (get_string(info, MAX1721X_REG_DEV_STR, 3918c2ecf20Sopenharmony_ci MAX1721X_REG_DEV_NUMB, info->DeviceName)) { 3928c2ecf20Sopenharmony_ci dev_err(info->w1_dev, "Can't read device. Hardware error.\n"); 3938c2ecf20Sopenharmony_ci return -ENODEV; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci if (!info->DeviceName[0]) { 3968c2ecf20Sopenharmony_ci unsigned int dev_name; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (regmap_read(info->regmap, 3998c2ecf20Sopenharmony_ci MAX172XX_REG_DEVNAME, &dev_name)) { 4008c2ecf20Sopenharmony_ci dev_err(info->w1_dev, "Can't read device name reg.\n"); 4018c2ecf20Sopenharmony_ci return -ENODEV; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci switch (dev_name & MAX172XX_DEV_MASK) { 4058c2ecf20Sopenharmony_ci case MAX172X1_DEV: 4068c2ecf20Sopenharmony_ci strncpy(info->DeviceName, DEF_DEV_NAME_MAX17211, 4078c2ecf20Sopenharmony_ci 2 * MAX1721X_REG_DEV_NUMB); 4088c2ecf20Sopenharmony_ci break; 4098c2ecf20Sopenharmony_ci case MAX172X5_DEV: 4108c2ecf20Sopenharmony_ci strncpy(info->DeviceName, DEF_DEV_NAME_MAX17215, 4118c2ecf20Sopenharmony_ci 2 * MAX1721X_REG_DEV_NUMB); 4128c2ecf20Sopenharmony_ci break; 4138c2ecf20Sopenharmony_ci default: 4148c2ecf20Sopenharmony_ci strncpy(info->DeviceName, DEF_DEV_NAME_UNKNOWN, 4158c2ecf20Sopenharmony_ci 2 * MAX1721X_REG_DEV_NUMB); 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (get_sn_string(info, info->SerialNumber)) { 4208c2ecf20Sopenharmony_ci dev_err(info->w1_dev, "Can't read serial. Hardware error.\n"); 4218c2ecf20Sopenharmony_ci return -ENODEV; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci info->bat = devm_power_supply_register(&sl->dev, &info->bat_desc, 4258c2ecf20Sopenharmony_ci &psy_cfg); 4268c2ecf20Sopenharmony_ci if (IS_ERR(info->bat)) { 4278c2ecf20Sopenharmony_ci dev_err(info->w1_dev, "failed to register battery\n"); 4288c2ecf20Sopenharmony_ci return PTR_ERR(info->bat); 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci return 0; 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic const struct w1_family_ops w1_max1721x_fops = { 4358c2ecf20Sopenharmony_ci .add_slave = devm_w1_max1721x_add_device, 4368c2ecf20Sopenharmony_ci}; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic struct w1_family w1_max1721x_family = { 4398c2ecf20Sopenharmony_ci .fid = W1_MAX1721X_FAMILY_ID, 4408c2ecf20Sopenharmony_ci .fops = &w1_max1721x_fops, 4418c2ecf20Sopenharmony_ci}; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cimodule_w1_family(w1_max1721x_family); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4468c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alex A. Mihaylov <minimumlaw@rambler.ru>"); 4478c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Maxim MAX17211/MAX17215 Fuel Gauage IC driver"); 4488c2ecf20Sopenharmony_ciMODULE_ALIAS("w1-family-" __stringify(W1_MAX1721X_FAMILY_ID)); 449