1 // SPDX-License-Identifier: GPL-2.0
4 // fuel-gauge systems for lithium-ion (Li+) batteries
6 // Copyright (C) 2009 Samsung Electronics
7 // Minkyu Kang <mk7.kang@samsung.com>
9 #include <linux/module.h>
10 #include <linux/init.h>
11 #include <linux/platform_device.h>
12 #include <linux/mutex.h>
13 #include <linux/err.h>
14 #include <linux/i2c.h>
15 #include <linux/delay.h>
16 #include <linux/interrupt.h>
17 #include <linux/power_supply.h>
18 #include <linux/max17040_battery.h>
19 #include <linux/slab.h>
21 #define MAX17040_VCELL 0x02
22 #define MAX17040_SOC 0x04
23 #define MAX17040_MODE 0x06
24 #define MAX17040_VER 0x08
25 #define MAX17040_RCOMP 0x0C
26 #define MAX17040_CMD 0xFE
29 #define MAX17040_DELAY 1000
30 #define MAX17040_BATTERY_FULL 95
32 #define MAX17040_ATHD_MASK 0xFFC0
33 #define MAX17040_ATHD_DEFAULT_POWER_UP 4
35 struct max17040_chip
{
36 struct i2c_client
*client
;
37 struct delayed_work work
;
38 struct power_supply
*battery
;
39 struct max17040_platform_data
*pdata
;
41 /* State Of Connect */
45 /* battery capacity */
49 /* Low alert threshold from 32% to 1% of the State of Charge */
53 static int max17040_get_property(struct power_supply
*psy
,
54 enum power_supply_property psp
,
55 union power_supply_propval
*val
)
57 struct max17040_chip
*chip
= power_supply_get_drvdata(psy
);
60 case POWER_SUPPLY_PROP_STATUS
:
61 val
->intval
= chip
->status
;
63 case POWER_SUPPLY_PROP_ONLINE
:
64 val
->intval
= chip
->online
;
66 case POWER_SUPPLY_PROP_VOLTAGE_NOW
:
67 val
->intval
= chip
->vcell
;
69 case POWER_SUPPLY_PROP_CAPACITY
:
70 val
->intval
= chip
->soc
;
78 static int max17040_write_reg(struct i2c_client
*client
, int reg
, u16 value
)
82 ret
= i2c_smbus_write_word_swapped(client
, reg
, value
);
85 dev_err(&client
->dev
, "%s: err %d\n", __func__
, ret
);
90 static int max17040_read_reg(struct i2c_client
*client
, int reg
)
94 ret
= i2c_smbus_read_word_swapped(client
, reg
);
97 dev_err(&client
->dev
, "%s: err %d\n", __func__
, ret
);
102 static void max17040_reset(struct i2c_client
*client
)
104 max17040_write_reg(client
, MAX17040_CMD
, 0x0054);
107 static int max17040_set_low_soc_alert(struct i2c_client
*client
, u32 level
)
113 data
= max17040_read_reg(client
, MAX17040_RCOMP
);
114 /* clear the alrt bit and set LSb 5 bits */
115 data
&= MAX17040_ATHD_MASK
;
117 ret
= max17040_write_reg(client
, MAX17040_RCOMP
, data
);
122 static void max17040_get_vcell(struct i2c_client
*client
)
124 struct max17040_chip
*chip
= i2c_get_clientdata(client
);
127 vcell
= max17040_read_reg(client
, MAX17040_VCELL
);
132 static void max17040_get_soc(struct i2c_client
*client
)
134 struct max17040_chip
*chip
= i2c_get_clientdata(client
);
137 soc
= max17040_read_reg(client
, MAX17040_SOC
);
139 chip
->soc
= (soc
>> 8);
142 static void max17040_get_version(struct i2c_client
*client
)
146 version
= max17040_read_reg(client
, MAX17040_VER
);
148 dev_info(&client
->dev
, "MAX17040 Fuel-Gauge Ver 0x%x\n", version
);
151 static void max17040_get_online(struct i2c_client
*client
)
153 struct max17040_chip
*chip
= i2c_get_clientdata(client
);
155 if (chip
->pdata
&& chip
->pdata
->battery_online
)
156 chip
->online
= chip
->pdata
->battery_online();
161 static void max17040_get_status(struct i2c_client
*client
)
163 struct max17040_chip
*chip
= i2c_get_clientdata(client
);
165 if (!chip
->pdata
|| !chip
->pdata
->charger_online
166 || !chip
->pdata
->charger_enable
) {
167 chip
->status
= POWER_SUPPLY_STATUS_UNKNOWN
;
171 if (chip
->pdata
->charger_online()) {
172 if (chip
->pdata
->charger_enable())
173 chip
->status
= POWER_SUPPLY_STATUS_CHARGING
;
175 chip
->status
= POWER_SUPPLY_STATUS_NOT_CHARGING
;
177 chip
->status
= POWER_SUPPLY_STATUS_DISCHARGING
;
180 if (chip
->soc
> MAX17040_BATTERY_FULL
)
181 chip
->status
= POWER_SUPPLY_STATUS_FULL
;
184 static int max17040_get_of_data(struct max17040_chip
*chip
)
186 struct device
*dev
= &chip
->client
->dev
;
188 chip
->low_soc_alert
= MAX17040_ATHD_DEFAULT_POWER_UP
;
189 device_property_read_u32(dev
,
190 "maxim,alert-low-soc-level",
191 &chip
->low_soc_alert
);
193 if (chip
->low_soc_alert
<= 0 || chip
->low_soc_alert
>= 33)
199 static void max17040_check_changes(struct i2c_client
*client
)
201 max17040_get_vcell(client
);
202 max17040_get_soc(client
);
203 max17040_get_online(client
);
204 max17040_get_status(client
);
207 static void max17040_work(struct work_struct
*work
)
209 struct max17040_chip
*chip
;
210 int last_soc
, last_status
;
212 chip
= container_of(work
, struct max17040_chip
, work
.work
);
214 /* store SOC and status to check changes */
215 last_soc
= chip
->soc
;
216 last_status
= chip
->status
;
217 max17040_check_changes(chip
->client
);
219 /* check changes and send uevent */
220 if (last_soc
!= chip
->soc
|| last_status
!= chip
->status
)
221 power_supply_changed(chip
->battery
);
223 queue_delayed_work(system_power_efficient_wq
, &chip
->work
,
227 static irqreturn_t
max17040_thread_handler(int id
, void *dev
)
229 struct max17040_chip
*chip
= dev
;
230 struct i2c_client
*client
= chip
->client
;
232 dev_warn(&client
->dev
, "IRQ: Alert battery low level");
234 max17040_check_changes(chip
->client
);
237 power_supply_changed(chip
->battery
);
239 /* reset alert bit */
240 max17040_set_low_soc_alert(client
, chip
->low_soc_alert
);
245 static int max17040_enable_alert_irq(struct max17040_chip
*chip
)
247 struct i2c_client
*client
= chip
->client
;
251 flags
= IRQF_TRIGGER_FALLING
| IRQF_ONESHOT
;
252 ret
= devm_request_threaded_irq(&client
->dev
, client
->irq
, NULL
,
253 max17040_thread_handler
, flags
,
254 chip
->battery
->desc
->name
, chip
);
259 static enum power_supply_property max17040_battery_props
[] = {
260 POWER_SUPPLY_PROP_STATUS
,
261 POWER_SUPPLY_PROP_ONLINE
,
262 POWER_SUPPLY_PROP_VOLTAGE_NOW
,
263 POWER_SUPPLY_PROP_CAPACITY
,
266 static const struct power_supply_desc max17040_battery_desc
= {
268 .type
= POWER_SUPPLY_TYPE_BATTERY
,
269 .get_property
= max17040_get_property
,
270 .properties
= max17040_battery_props
,
271 .num_properties
= ARRAY_SIZE(max17040_battery_props
),
274 static int max17040_probe(struct i2c_client
*client
,
275 const struct i2c_device_id
*id
)
277 struct i2c_adapter
*adapter
= client
->adapter
;
278 struct power_supply_config psy_cfg
= {};
279 struct max17040_chip
*chip
;
282 if (!i2c_check_functionality(adapter
, I2C_FUNC_SMBUS_BYTE
))
285 chip
= devm_kzalloc(&client
->dev
, sizeof(*chip
), GFP_KERNEL
);
289 chip
->client
= client
;
290 chip
->pdata
= client
->dev
.platform_data
;
291 ret
= max17040_get_of_data(chip
);
293 dev_err(&client
->dev
,
294 "failed: low SOC alert OF data out of bounds\n");
298 i2c_set_clientdata(client
, chip
);
299 psy_cfg
.drv_data
= chip
;
301 chip
->battery
= power_supply_register(&client
->dev
,
302 &max17040_battery_desc
, &psy_cfg
);
303 if (IS_ERR(chip
->battery
)) {
304 dev_err(&client
->dev
, "failed: power supply register\n");
305 return PTR_ERR(chip
->battery
);
308 max17040_reset(client
);
309 max17040_get_version(client
);
311 /* check interrupt */
312 if (client
->irq
&& of_device_is_compatible(client
->dev
.of_node
,
313 "maxim,max77836-battery")) {
314 ret
= max17040_set_low_soc_alert(client
, chip
->low_soc_alert
);
316 dev_err(&client
->dev
,
317 "Failed to set low SOC alert: err %d\n", ret
);
321 ret
= max17040_enable_alert_irq(chip
);
324 dev_warn(&client
->dev
,
325 "Failed to get IRQ err %d\n", ret
);
329 INIT_DEFERRABLE_WORK(&chip
->work
, max17040_work
);
330 queue_delayed_work(system_power_efficient_wq
, &chip
->work
,
336 static int max17040_remove(struct i2c_client
*client
)
338 struct max17040_chip
*chip
= i2c_get_clientdata(client
);
340 power_supply_unregister(chip
->battery
);
341 cancel_delayed_work(&chip
->work
);
345 #ifdef CONFIG_PM_SLEEP
347 static int max17040_suspend(struct device
*dev
)
349 struct i2c_client
*client
= to_i2c_client(dev
);
350 struct max17040_chip
*chip
= i2c_get_clientdata(client
);
352 cancel_delayed_work(&chip
->work
);
354 if (client
->irq
&& device_may_wakeup(dev
))
355 enable_irq_wake(client
->irq
);
360 static int max17040_resume(struct device
*dev
)
362 struct i2c_client
*client
= to_i2c_client(dev
);
363 struct max17040_chip
*chip
= i2c_get_clientdata(client
);
365 queue_delayed_work(system_power_efficient_wq
, &chip
->work
,
368 if (client
->irq
&& device_may_wakeup(dev
))
369 disable_irq_wake(client
->irq
);
374 static SIMPLE_DEV_PM_OPS(max17040_pm_ops
, max17040_suspend
, max17040_resume
);
375 #define MAX17040_PM_OPS (&max17040_pm_ops)
379 #define MAX17040_PM_OPS NULL
381 #endif /* CONFIG_PM_SLEEP */
383 static const struct i2c_device_id max17040_id
[] = {
385 { "max77836-battery" },
388 MODULE_DEVICE_TABLE(i2c
, max17040_id
);
390 static const struct of_device_id max17040_of_match
[] = {
391 { .compatible
= "maxim,max17040" },
392 { .compatible
= "maxim,max77836-battery" },
395 MODULE_DEVICE_TABLE(of
, max17040_of_match
);
397 static struct i2c_driver max17040_i2c_driver
= {
400 .of_match_table
= max17040_of_match
,
401 .pm
= MAX17040_PM_OPS
,
403 .probe
= max17040_probe
,
404 .remove
= max17040_remove
,
405 .id_table
= max17040_id
,
407 module_i2c_driver(max17040_i2c_driver
);
409 MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>");
410 MODULE_DESCRIPTION("MAX17040 Fuel Gauge");
411 MODULE_LICENSE("GPL");