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 = power_supply_register(&client->dev,
167 					     &rt5033_battery_desc, &psy_cfg);
168 	if (IS_ERR(battery->psy))
169 		return dev_err_probe(&client->dev, PTR_ERR(battery->psy),
170 				     "Failed to register power supply\n");
171 
172 	return 0;
173 }
174 
175 static void rt5033_battery_remove(struct i2c_client *client)
176 {
177 	struct rt5033_battery *battery = i2c_get_clientdata(client);
178 
179 	power_supply_unregister(battery->psy);
180 }
181 
182 static const struct i2c_device_id rt5033_battery_id[] = {
183 	{ "rt5033-battery", },
184 	{ }
185 };
186 MODULE_DEVICE_TABLE(i2c, rt5033_battery_id);
187 
188 static const struct of_device_id rt5033_battery_of_match[] = {
189 	{ .compatible = "richtek,rt5033-battery", },
190 	{ }
191 };
192 MODULE_DEVICE_TABLE(of, rt5033_battery_of_match);
193 
194 static struct i2c_driver rt5033_battery_driver = {
195 	.driver = {
196 		.name = "rt5033-battery",
197 		.of_match_table = rt5033_battery_of_match,
198 	},
199 	.probe_new = rt5033_battery_probe,
200 	.remove = rt5033_battery_remove,
201 	.id_table = rt5033_battery_id,
202 };
203 module_i2c_driver(rt5033_battery_driver);
204 
205 MODULE_DESCRIPTION("Richtek RT5033 fuel gauge driver");
206 MODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>");
207 MODULE_LICENSE("GPL");
208