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(psy: battery->psy, |
29 | psp: POWER_SUPPLY_PROP_STATUS, |
30 | val: &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(map: battery->regmap, reg: RT5033_FUEL_REG_SOC_H, val: &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(map: battery->regmap, reg: RT5033_FUEL_REG_CONFIG_L, val: &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(map: battery->regmap, reg: regh, val: &msb); |
83 | regmap_read(map: battery->regmap, reg: regl, val: &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(client: battery->client, |
101 | psp); |
102 | break; |
103 | case POWER_SUPPLY_PROP_PRESENT: |
104 | val->intval = rt5033_battery_get_present(client: battery->client); |
105 | break; |
106 | case POWER_SUPPLY_PROP_CAPACITY: |
107 | val->intval = rt5033_battery_get_capacity(client: battery->client); |
108 | break; |
109 | case POWER_SUPPLY_PROP_STATUS: |
110 | val->intval = rt5033_battery_get_status(client: 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(adap: adapter, I2C_FUNC_SMBUS_BYTE)) |
148 | return -EIO; |
149 | |
150 | battery = devm_kzalloc(dev: &client->dev, size: 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(ptr: battery->regmap)) { |
158 | dev_err(&client->dev, "Failed to initialize regmap\n" ); |
159 | return -EINVAL; |
160 | } |
161 | |
162 | i2c_set_clientdata(client, data: battery); |
163 | psy_cfg.of_node = client->dev.of_node; |
164 | psy_cfg.drv_data = battery; |
165 | |
166 | battery->psy = power_supply_register(parent: &client->dev, |
167 | desc: &rt5033_battery_desc, cfg: &psy_cfg); |
168 | if (IS_ERR(ptr: battery->psy)) |
169 | return dev_err_probe(dev: &client->dev, err: PTR_ERR(ptr: battery->psy), |
170 | fmt: "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(psy: 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 = 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 | |