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