1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Fuel gauge driver for Richtek RT5033 4 * 5 * Copyright (C) 2014 Samsung Electronics, Co., Ltd. 6 * Author: Beomho Seo <beomho.seo@samsung.com> 7 */ 8 9 #include <linux/i2c.h> 10 #include <linux/module.h> 11 #include <linux/platform_device.h> 12 #include <linux/power_supply.h> 13 #include <linux/regmap.h> 14 #include <linux/mfd/rt5033-private.h> 15 16 struct rt5033_battery { 17 struct i2c_client *client; 18 struct regmap *regmap; 19 struct power_supply *psy; 20 }; 21 22 static int rt5033_battery_get_status(struct i2c_client *client) 23 { 24 struct rt5033_battery *battery = i2c_get_clientdata(client); 25 union power_supply_propval val; 26 int ret; 27 28 ret = power_supply_get_property_from_supplier(battery->psy, 29 POWER_SUPPLY_PROP_STATUS, 30 &val); 31 if (ret) 32 val.intval = POWER_SUPPLY_STATUS_UNKNOWN; 33 34 return val.intval; 35 } 36 37 static int rt5033_battery_get_capacity(struct i2c_client *client) 38 { 39 struct rt5033_battery *battery = i2c_get_clientdata(client); 40 u32 msb; 41 42 regmap_read(battery->regmap, RT5033_FUEL_REG_SOC_H, &msb); 43 44 return msb; 45 } 46 47 static int rt5033_battery_get_present(struct i2c_client *client) 48 { 49 struct rt5033_battery *battery = i2c_get_clientdata(client); 50 u32 val; 51 52 regmap_read(battery->regmap, RT5033_FUEL_REG_CONFIG_L, &val); 53 54 return (val & RT5033_FUEL_BAT_PRESENT) ? true : false; 55 } 56 57 static int rt5033_battery_get_watt_prop(struct i2c_client *client, 58 enum power_supply_property psp) 59 { 60 struct rt5033_battery *battery = i2c_get_clientdata(client); 61 unsigned int regh, regl; 62 int ret; 63 u32 msb, lsb; 64 65 switch (psp) { 66 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 67 regh = RT5033_FUEL_REG_VBAT_H; 68 regl = RT5033_FUEL_REG_VBAT_L; 69 break; 70 case POWER_SUPPLY_PROP_VOLTAGE_AVG: 71 regh = RT5033_FUEL_REG_AVG_VOLT_H; 72 regl = RT5033_FUEL_REG_AVG_VOLT_L; 73 break; 74 case POWER_SUPPLY_PROP_VOLTAGE_OCV: 75 regh = RT5033_FUEL_REG_OCV_H; 76 regl = RT5033_FUEL_REG_OCV_L; 77 break; 78 default: 79 return -EINVAL; 80 } 81 82 regmap_read(battery->regmap, regh, &msb); 83 regmap_read(battery->regmap, regl, &lsb); 84 85 ret = ((msb << 4) + (lsb >> 4)) * 1250; 86 87 return ret; 88 } 89 90 static int rt5033_battery_get_property(struct power_supply *psy, 91 enum power_supply_property psp, 92 union power_supply_propval *val) 93 { 94 struct rt5033_battery *battery = power_supply_get_drvdata(psy); 95 96 switch (psp) { 97 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 98 case POWER_SUPPLY_PROP_VOLTAGE_AVG: 99 case POWER_SUPPLY_PROP_VOLTAGE_OCV: 100 val->intval = rt5033_battery_get_watt_prop(battery->client, 101 psp); 102 break; 103 case POWER_SUPPLY_PROP_PRESENT: 104 val->intval = rt5033_battery_get_present(battery->client); 105 break; 106 case POWER_SUPPLY_PROP_CAPACITY: 107 val->intval = rt5033_battery_get_capacity(battery->client); 108 break; 109 case POWER_SUPPLY_PROP_STATUS: 110 val->intval = rt5033_battery_get_status(battery->client); 111 break; 112 default: 113 return -EINVAL; 114 } 115 return 0; 116 } 117 118 static enum power_supply_property rt5033_battery_props[] = { 119 POWER_SUPPLY_PROP_VOLTAGE_NOW, 120 POWER_SUPPLY_PROP_VOLTAGE_AVG, 121 POWER_SUPPLY_PROP_VOLTAGE_OCV, 122 POWER_SUPPLY_PROP_PRESENT, 123 POWER_SUPPLY_PROP_CAPACITY, 124 POWER_SUPPLY_PROP_STATUS, 125 }; 126 127 static const struct regmap_config rt5033_battery_regmap_config = { 128 .reg_bits = 8, 129 .val_bits = 8, 130 .max_register = RT5033_FUEL_REG_END, 131 }; 132 133 static const struct power_supply_desc rt5033_battery_desc = { 134 .name = "rt5033-battery", 135 .type = POWER_SUPPLY_TYPE_BATTERY, 136 .get_property = rt5033_battery_get_property, 137 .properties = rt5033_battery_props, 138 .num_properties = ARRAY_SIZE(rt5033_battery_props), 139 }; 140 141 static int rt5033_battery_probe(struct i2c_client *client) 142 { 143 struct i2c_adapter *adapter = client->adapter; 144 struct power_supply_config psy_cfg = {}; 145 struct rt5033_battery *battery; 146 147 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) 148 return -EIO; 149 150 battery = devm_kzalloc(&client->dev, sizeof(*battery), GFP_KERNEL); 151 if (!battery) 152 return -ENOMEM; 153 154 battery->client = client; 155 battery->regmap = devm_regmap_init_i2c(client, 156 &rt5033_battery_regmap_config); 157 if (IS_ERR(battery->regmap)) { 158 dev_err(&client->dev, "Failed to initialize regmap\n"); 159 return -EINVAL; 160 } 161 162 i2c_set_clientdata(client, battery); 163 psy_cfg.of_node = client->dev.of_node; 164 psy_cfg.drv_data = battery; 165 166 battery->psy = devm_power_supply_register(&client->dev, 167 &rt5033_battery_desc, 168 &psy_cfg); 169 if (IS_ERR(battery->psy)) 170 return dev_err_probe(&client->dev, PTR_ERR(battery->psy), 171 "Failed to register power supply\n"); 172 173 return 0; 174 } 175 176 static const struct i2c_device_id rt5033_battery_id[] = { 177 { "rt5033-battery", }, 178 { } 179 }; 180 MODULE_DEVICE_TABLE(i2c, rt5033_battery_id); 181 182 static const struct of_device_id rt5033_battery_of_match[] = { 183 { .compatible = "richtek,rt5033-battery", }, 184 { } 185 }; 186 MODULE_DEVICE_TABLE(of, rt5033_battery_of_match); 187 188 static struct i2c_driver rt5033_battery_driver = { 189 .driver = { 190 .name = "rt5033-battery", 191 .of_match_table = rt5033_battery_of_match, 192 }, 193 .probe = rt5033_battery_probe, 194 .id_table = rt5033_battery_id, 195 }; 196 module_i2c_driver(rt5033_battery_driver); 197 198 MODULE_DESCRIPTION("Richtek RT5033 fuel gauge driver"); 199 MODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>"); 200 MODULE_LICENSE("GPL"); 201