Commit | Line | Data |
---|---|---|
d925da5c | 1 | // SPDX-License-Identifier: GPL-2.0-only |
b847dd96 BS |
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> | |
b847dd96 BS |
7 | */ |
8 | ||
49b43590 | 9 | #include <linux/i2c.h> |
b847dd96 BS |
10 | #include <linux/module.h> |
11 | #include <linux/platform_device.h> | |
12 | #include <linux/power_supply.h> | |
49b43590 | 13 | #include <linux/regmap.h> |
b847dd96 | 14 | #include <linux/mfd/rt5033-private.h> |
49b43590 JH |
15 | |
16 | struct rt5033_battery { | |
17 | struct i2c_client *client; | |
18 | struct regmap *regmap; | |
19 | struct power_supply *psy; | |
20 | }; | |
b847dd96 | 21 | |
4ed21f06 JH |
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 | ||
b847dd96 BS |
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 | ||
bf895295 | 85 | ret = ((msb << 4) + (lsb >> 4)) * 1250; |
b847dd96 BS |
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 | { | |
297d716f | 94 | struct rt5033_battery *battery = power_supply_get_drvdata(psy); |
b847dd96 BS |
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; | |
4ed21f06 JH |
109 | case POWER_SUPPLY_PROP_STATUS: |
110 | val->intval = rt5033_battery_get_status(battery->client); | |
111 | break; | |
b847dd96 BS |
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, | |
4ed21f06 | 124 | POWER_SUPPLY_PROP_STATUS, |
b847dd96 BS |
125 | }; |
126 | ||
65ce1c95 | 127 | static const struct regmap_config rt5033_battery_regmap_config = { |
b847dd96 BS |
128 | .reg_bits = 8, |
129 | .val_bits = 8, | |
130 | .max_register = RT5033_FUEL_REG_END, | |
131 | }; | |
132 | ||
297d716f KK |
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 | ||
f40ec8bc | 141 | static int rt5033_battery_probe(struct i2c_client *client) |
b847dd96 | 142 | { |
df324c60 | 143 | struct i2c_adapter *adapter = client->adapter; |
297d716f | 144 | struct power_supply_config psy_cfg = {}; |
b847dd96 | 145 | struct rt5033_battery *battery; |
b847dd96 BS |
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) | |
466bf931 | 152 | return -ENOMEM; |
b847dd96 BS |
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 | ||
d3911f16 | 162 | i2c_set_clientdata(client, battery); |
4ed21f06 | 163 | psy_cfg.of_node = client->dev.of_node; |
297d716f | 164 | psy_cfg.drv_data = battery; |
b847dd96 | 165 | |
3a93da23 AD |
166 | battery->psy = devm_power_supply_register(&client->dev, |
167 | &rt5033_battery_desc, | |
168 | &psy_cfg); | |
4ed21f06 JH |
169 | if (IS_ERR(battery->psy)) |
170 | return dev_err_probe(&client->dev, PTR_ERR(battery->psy), | |
171 | "Failed to register power supply\n"); | |
b847dd96 BS |
172 | |
173 | return 0; | |
174 | } | |
175 | ||
b847dd96 BS |
176 | static const struct i2c_device_id rt5033_battery_id[] = { |
177 | { "rt5033-battery", }, | |
178 | { } | |
179 | }; | |
63369b2b | 180 | MODULE_DEVICE_TABLE(i2c, rt5033_battery_id); |
b847dd96 | 181 | |
f3076cd8 SG |
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 | ||
b847dd96 BS |
188 | static struct i2c_driver rt5033_battery_driver = { |
189 | .driver = { | |
190 | .name = "rt5033-battery", | |
f3076cd8 | 191 | .of_match_table = rt5033_battery_of_match, |
b847dd96 | 192 | }, |
fe20b1dc | 193 | .probe = rt5033_battery_probe, |
b847dd96 BS |
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"); |