1d925da5cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 28c0984e5SSebastian Reichel /* 38c0984e5SSebastian Reichel * Fuel gauge driver for Richtek RT5033 48c0984e5SSebastian Reichel * 58c0984e5SSebastian Reichel * Copyright (C) 2014 Samsung Electronics, Co., Ltd. 68c0984e5SSebastian Reichel * Author: Beomho Seo <beomho.seo@samsung.com> 78c0984e5SSebastian Reichel */ 88c0984e5SSebastian Reichel 949b43590SJakob Hauser #include <linux/i2c.h> 108c0984e5SSebastian Reichel #include <linux/module.h> 118c0984e5SSebastian Reichel #include <linux/platform_device.h> 128c0984e5SSebastian Reichel #include <linux/power_supply.h> 1349b43590SJakob Hauser #include <linux/regmap.h> 148c0984e5SSebastian Reichel #include <linux/mfd/rt5033-private.h> 1549b43590SJakob Hauser 1649b43590SJakob Hauser struct rt5033_battery { 1749b43590SJakob Hauser struct i2c_client *client; 1849b43590SJakob Hauser struct regmap *regmap; 1949b43590SJakob Hauser struct power_supply *psy; 2049b43590SJakob Hauser }; 218c0984e5SSebastian Reichel 224ed21f06SJakob Hauser static int rt5033_battery_get_status(struct i2c_client *client) 234ed21f06SJakob Hauser { 244ed21f06SJakob Hauser struct rt5033_battery *battery = i2c_get_clientdata(client); 254ed21f06SJakob Hauser union power_supply_propval val; 264ed21f06SJakob Hauser int ret; 274ed21f06SJakob Hauser 284ed21f06SJakob Hauser ret = power_supply_get_property_from_supplier(battery->psy, 294ed21f06SJakob Hauser POWER_SUPPLY_PROP_STATUS, 304ed21f06SJakob Hauser &val); 314ed21f06SJakob Hauser if (ret) 324ed21f06SJakob Hauser val.intval = POWER_SUPPLY_STATUS_UNKNOWN; 334ed21f06SJakob Hauser 344ed21f06SJakob Hauser return val.intval; 354ed21f06SJakob Hauser } 364ed21f06SJakob Hauser 378c0984e5SSebastian Reichel static int rt5033_battery_get_capacity(struct i2c_client *client) 388c0984e5SSebastian Reichel { 398c0984e5SSebastian Reichel struct rt5033_battery *battery = i2c_get_clientdata(client); 408c0984e5SSebastian Reichel u32 msb; 418c0984e5SSebastian Reichel 428c0984e5SSebastian Reichel regmap_read(battery->regmap, RT5033_FUEL_REG_SOC_H, &msb); 438c0984e5SSebastian Reichel 448c0984e5SSebastian Reichel return msb; 458c0984e5SSebastian Reichel } 468c0984e5SSebastian Reichel 478c0984e5SSebastian Reichel static int rt5033_battery_get_present(struct i2c_client *client) 488c0984e5SSebastian Reichel { 498c0984e5SSebastian Reichel struct rt5033_battery *battery = i2c_get_clientdata(client); 508c0984e5SSebastian Reichel u32 val; 518c0984e5SSebastian Reichel 528c0984e5SSebastian Reichel regmap_read(battery->regmap, RT5033_FUEL_REG_CONFIG_L, &val); 538c0984e5SSebastian Reichel 548c0984e5SSebastian Reichel return (val & RT5033_FUEL_BAT_PRESENT) ? true : false; 558c0984e5SSebastian Reichel } 568c0984e5SSebastian Reichel 578c0984e5SSebastian Reichel static int rt5033_battery_get_watt_prop(struct i2c_client *client, 588c0984e5SSebastian Reichel enum power_supply_property psp) 598c0984e5SSebastian Reichel { 608c0984e5SSebastian Reichel struct rt5033_battery *battery = i2c_get_clientdata(client); 618c0984e5SSebastian Reichel unsigned int regh, regl; 628c0984e5SSebastian Reichel int ret; 638c0984e5SSebastian Reichel u32 msb, lsb; 648c0984e5SSebastian Reichel 658c0984e5SSebastian Reichel switch (psp) { 668c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_VOLTAGE_NOW: 678c0984e5SSebastian Reichel regh = RT5033_FUEL_REG_VBAT_H; 688c0984e5SSebastian Reichel regl = RT5033_FUEL_REG_VBAT_L; 698c0984e5SSebastian Reichel break; 708c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_VOLTAGE_AVG: 718c0984e5SSebastian Reichel regh = RT5033_FUEL_REG_AVG_VOLT_H; 728c0984e5SSebastian Reichel regl = RT5033_FUEL_REG_AVG_VOLT_L; 738c0984e5SSebastian Reichel break; 748c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_VOLTAGE_OCV: 758c0984e5SSebastian Reichel regh = RT5033_FUEL_REG_OCV_H; 768c0984e5SSebastian Reichel regl = RT5033_FUEL_REG_OCV_L; 778c0984e5SSebastian Reichel break; 788c0984e5SSebastian Reichel default: 798c0984e5SSebastian Reichel return -EINVAL; 808c0984e5SSebastian Reichel } 818c0984e5SSebastian Reichel 828c0984e5SSebastian Reichel regmap_read(battery->regmap, regh, &msb); 838c0984e5SSebastian Reichel regmap_read(battery->regmap, regl, &lsb); 848c0984e5SSebastian Reichel 85bf895295SJakob Hauser ret = ((msb << 4) + (lsb >> 4)) * 1250; 868c0984e5SSebastian Reichel 878c0984e5SSebastian Reichel return ret; 888c0984e5SSebastian Reichel } 898c0984e5SSebastian Reichel 908c0984e5SSebastian Reichel static int rt5033_battery_get_property(struct power_supply *psy, 918c0984e5SSebastian Reichel enum power_supply_property psp, 928c0984e5SSebastian Reichel union power_supply_propval *val) 938c0984e5SSebastian Reichel { 948c0984e5SSebastian Reichel struct rt5033_battery *battery = power_supply_get_drvdata(psy); 958c0984e5SSebastian Reichel 968c0984e5SSebastian Reichel switch (psp) { 978c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_VOLTAGE_NOW: 988c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_VOLTAGE_AVG: 998c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_VOLTAGE_OCV: 1008c0984e5SSebastian Reichel val->intval = rt5033_battery_get_watt_prop(battery->client, 1018c0984e5SSebastian Reichel psp); 1028c0984e5SSebastian Reichel break; 1038c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_PRESENT: 1048c0984e5SSebastian Reichel val->intval = rt5033_battery_get_present(battery->client); 1058c0984e5SSebastian Reichel break; 1068c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CAPACITY: 1078c0984e5SSebastian Reichel val->intval = rt5033_battery_get_capacity(battery->client); 1088c0984e5SSebastian Reichel break; 1094ed21f06SJakob Hauser case POWER_SUPPLY_PROP_STATUS: 1104ed21f06SJakob Hauser val->intval = rt5033_battery_get_status(battery->client); 1114ed21f06SJakob Hauser break; 1128c0984e5SSebastian Reichel default: 1138c0984e5SSebastian Reichel return -EINVAL; 1148c0984e5SSebastian Reichel } 1158c0984e5SSebastian Reichel return 0; 1168c0984e5SSebastian Reichel } 1178c0984e5SSebastian Reichel 1188c0984e5SSebastian Reichel static enum power_supply_property rt5033_battery_props[] = { 1198c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 1208c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_AVG, 1218c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_OCV, 1228c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, 1238c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 1244ed21f06SJakob Hauser POWER_SUPPLY_PROP_STATUS, 1258c0984e5SSebastian Reichel }; 1268c0984e5SSebastian Reichel 1278c0984e5SSebastian Reichel static const struct regmap_config rt5033_battery_regmap_config = { 1288c0984e5SSebastian Reichel .reg_bits = 8, 1298c0984e5SSebastian Reichel .val_bits = 8, 1308c0984e5SSebastian Reichel .max_register = RT5033_FUEL_REG_END, 1318c0984e5SSebastian Reichel }; 1328c0984e5SSebastian Reichel 1338c0984e5SSebastian Reichel static const struct power_supply_desc rt5033_battery_desc = { 1348c0984e5SSebastian Reichel .name = "rt5033-battery", 1358c0984e5SSebastian Reichel .type = POWER_SUPPLY_TYPE_BATTERY, 1368c0984e5SSebastian Reichel .get_property = rt5033_battery_get_property, 1378c0984e5SSebastian Reichel .properties = rt5033_battery_props, 1388c0984e5SSebastian Reichel .num_properties = ARRAY_SIZE(rt5033_battery_props), 1398c0984e5SSebastian Reichel }; 1408c0984e5SSebastian Reichel 141f40ec8bcSUwe Kleine-König static int rt5033_battery_probe(struct i2c_client *client) 1428c0984e5SSebastian Reichel { 143df324c60SWolfram Sang struct i2c_adapter *adapter = client->adapter; 1448c0984e5SSebastian Reichel struct power_supply_config psy_cfg = {}; 1458c0984e5SSebastian Reichel struct rt5033_battery *battery; 1468c0984e5SSebastian Reichel 1478c0984e5SSebastian Reichel if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) 1488c0984e5SSebastian Reichel return -EIO; 1498c0984e5SSebastian Reichel 1508c0984e5SSebastian Reichel battery = devm_kzalloc(&client->dev, sizeof(*battery), GFP_KERNEL); 1518c0984e5SSebastian Reichel if (!battery) 152466bf931SPeng Fan return -ENOMEM; 1538c0984e5SSebastian Reichel 1548c0984e5SSebastian Reichel battery->client = client; 1558c0984e5SSebastian Reichel battery->regmap = devm_regmap_init_i2c(client, 1568c0984e5SSebastian Reichel &rt5033_battery_regmap_config); 1578c0984e5SSebastian Reichel if (IS_ERR(battery->regmap)) { 1588c0984e5SSebastian Reichel dev_err(&client->dev, "Failed to initialize regmap\n"); 1598c0984e5SSebastian Reichel return -EINVAL; 1608c0984e5SSebastian Reichel } 1618c0984e5SSebastian Reichel 162*d3911f16SNikita Travkin i2c_set_clientdata(client, battery); 1634ed21f06SJakob Hauser psy_cfg.of_node = client->dev.of_node; 1648c0984e5SSebastian Reichel psy_cfg.drv_data = battery; 1658c0984e5SSebastian Reichel 1663a93da23SAndrew Davis battery->psy = devm_power_supply_register(&client->dev, 1673a93da23SAndrew Davis &rt5033_battery_desc, 1683a93da23SAndrew Davis &psy_cfg); 1694ed21f06SJakob Hauser if (IS_ERR(battery->psy)) 1704ed21f06SJakob Hauser return dev_err_probe(&client->dev, PTR_ERR(battery->psy), 1714ed21f06SJakob Hauser "Failed to register power supply\n"); 1728c0984e5SSebastian Reichel 1738c0984e5SSebastian Reichel return 0; 1748c0984e5SSebastian Reichel } 1758c0984e5SSebastian Reichel 1768c0984e5SSebastian Reichel static const struct i2c_device_id rt5033_battery_id[] = { 1778c0984e5SSebastian Reichel { "rt5033-battery", }, 1788c0984e5SSebastian Reichel { } 1798c0984e5SSebastian Reichel }; 1808c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(i2c, rt5033_battery_id); 1818c0984e5SSebastian Reichel 182f3076cd8SStephan Gerhold static const struct of_device_id rt5033_battery_of_match[] = { 183f3076cd8SStephan Gerhold { .compatible = "richtek,rt5033-battery", }, 184f3076cd8SStephan Gerhold { } 185f3076cd8SStephan Gerhold }; 186f3076cd8SStephan Gerhold MODULE_DEVICE_TABLE(of, rt5033_battery_of_match); 187f3076cd8SStephan Gerhold 1888c0984e5SSebastian Reichel static struct i2c_driver rt5033_battery_driver = { 1898c0984e5SSebastian Reichel .driver = { 1908c0984e5SSebastian Reichel .name = "rt5033-battery", 191f3076cd8SStephan Gerhold .of_match_table = rt5033_battery_of_match, 1928c0984e5SSebastian Reichel }, 193fe20b1dcSUwe Kleine-König .probe = rt5033_battery_probe, 1948c0984e5SSebastian Reichel .id_table = rt5033_battery_id, 1958c0984e5SSebastian Reichel }; 1968c0984e5SSebastian Reichel module_i2c_driver(rt5033_battery_driver); 1978c0984e5SSebastian Reichel 1988c0984e5SSebastian Reichel MODULE_DESCRIPTION("Richtek RT5033 fuel gauge driver"); 1998c0984e5SSebastian Reichel MODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>"); 2008c0984e5SSebastian Reichel MODULE_LICENSE("GPL"); 201