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
18static 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
28static 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
38static 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
71static 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
96static 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
104static 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
110static 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
118static 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
155static 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
164static const struct i2c_device_id rt5033_battery_id[] = {
165	{ "rt5033-battery", },
166	{ }
167};
168MODULE_DEVICE_TABLE(platform, rt5033_battery_id);
169
170static 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};
178module_i2c_driver(rt5033_battery_driver);
179
180MODULE_DESCRIPTION("Richtek RT5033 fuel gauge driver");
181MODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>");
182MODULE_LICENSE("GPL");
183