17b38ebdfSKrzysztof Kozlowski // SPDX-License-Identifier: GPL-2.0
27b38ebdfSKrzysztof Kozlowski //
37b38ebdfSKrzysztof Kozlowski // max17040_battery.c
47b38ebdfSKrzysztof Kozlowski // fuel-gauge systems for lithium-ion (Li+) batteries
57b38ebdfSKrzysztof Kozlowski //
67b38ebdfSKrzysztof Kozlowski // Copyright (C) 2009 Samsung Electronics
77b38ebdfSKrzysztof Kozlowski // Minkyu Kang <mk7.kang@samsung.com>
88c0984e5SSebastian Reichel
98c0984e5SSebastian Reichel #include <linux/module.h>
108c0984e5SSebastian Reichel #include <linux/init.h>
118c0984e5SSebastian Reichel #include <linux/platform_device.h>
128c0984e5SSebastian Reichel #include <linux/mutex.h>
138c0984e5SSebastian Reichel #include <linux/err.h>
148c0984e5SSebastian Reichel #include <linux/i2c.h>
158c0984e5SSebastian Reichel #include <linux/delay.h>
162e17ed94SMatheus Castello #include <linux/interrupt.h>
178c0984e5SSebastian Reichel #include <linux/power_supply.h>
18*2ce8284cSRob Herring #include <linux/of.h>
196455a8a8SIskren Chernev #include <linux/regmap.h>
208c0984e5SSebastian Reichel #include <linux/slab.h>
218c0984e5SSebastian Reichel
2214d60bddSLiu Xiang #define MAX17040_VCELL 0x02
2314d60bddSLiu Xiang #define MAX17040_SOC 0x04
2414d60bddSLiu Xiang #define MAX17040_MODE 0x06
2514d60bddSLiu Xiang #define MAX17040_VER 0x08
266455a8a8SIskren Chernev #define MAX17040_CONFIG 0x0C
27ee2d433cSIskren Chernev #define MAX17040_STATUS 0x1A
2814d60bddSLiu Xiang #define MAX17040_CMD 0xFE
2914d60bddSLiu Xiang
308c0984e5SSebastian Reichel
318c0984e5SSebastian Reichel #define MAX17040_DELAY 1000
328c0984e5SSebastian Reichel #define MAX17040_BATTERY_FULL 95
331779c6e3SIskren Chernev #define MAX17040_RCOMP_DEFAULT 0x9700
348c0984e5SSebastian Reichel
356455a8a8SIskren Chernev #define MAX17040_ATHD_MASK 0x3f
36ee2d433cSIskren Chernev #define MAX17040_ALSC_MASK 0x40
37cccdd0caSMatheus Castello #define MAX17040_ATHD_DEFAULT_POWER_UP 4
38ee2d433cSIskren Chernev #define MAX17040_STATUS_HD_MASK 0x1000
39ee2d433cSIskren Chernev #define MAX17040_STATUS_SC_MASK 0x2000
401779c6e3SIskren Chernev #define MAX17040_CFG_RCOMP_MASK 0xff00
41cccdd0caSMatheus Castello
424f7f8e87SIskren Chernev enum chip_id {
434f7f8e87SIskren Chernev ID_MAX17040,
444f7f8e87SIskren Chernev ID_MAX17041,
454f7f8e87SIskren Chernev ID_MAX17043,
464f7f8e87SIskren Chernev ID_MAX17044,
474f7f8e87SIskren Chernev ID_MAX17048,
484f7f8e87SIskren Chernev ID_MAX17049,
494f7f8e87SIskren Chernev ID_MAX17058,
504f7f8e87SIskren Chernev ID_MAX17059,
514f7f8e87SIskren Chernev };
524f7f8e87SIskren Chernev
534f7f8e87SIskren Chernev /* values that differ by chip_id */
544f7f8e87SIskren Chernev struct chip_data {
554f7f8e87SIskren Chernev u16 reset_val;
564f7f8e87SIskren Chernev u16 vcell_shift;
574f7f8e87SIskren Chernev u16 vcell_mul;
584f7f8e87SIskren Chernev u16 vcell_div;
594f7f8e87SIskren Chernev u8 has_low_soc_alert;
601779c6e3SIskren Chernev u8 rcomp_bytes;
61ee2d433cSIskren Chernev u8 has_soc_alert;
624f7f8e87SIskren Chernev };
634f7f8e87SIskren Chernev
644f7f8e87SIskren Chernev static struct chip_data max17040_family[] = {
654f7f8e87SIskren Chernev [ID_MAX17040] = {
664f7f8e87SIskren Chernev .reset_val = 0x0054,
674f7f8e87SIskren Chernev .vcell_shift = 4,
684f7f8e87SIskren Chernev .vcell_mul = 1250,
694f7f8e87SIskren Chernev .vcell_div = 1,
704f7f8e87SIskren Chernev .has_low_soc_alert = 0,
711779c6e3SIskren Chernev .rcomp_bytes = 2,
72ee2d433cSIskren Chernev .has_soc_alert = 0,
734f7f8e87SIskren Chernev },
744f7f8e87SIskren Chernev [ID_MAX17041] = {
754f7f8e87SIskren Chernev .reset_val = 0x0054,
764f7f8e87SIskren Chernev .vcell_shift = 4,
774f7f8e87SIskren Chernev .vcell_mul = 2500,
784f7f8e87SIskren Chernev .vcell_div = 1,
794f7f8e87SIskren Chernev .has_low_soc_alert = 0,
801779c6e3SIskren Chernev .rcomp_bytes = 2,
81ee2d433cSIskren Chernev .has_soc_alert = 0,
824f7f8e87SIskren Chernev },
834f7f8e87SIskren Chernev [ID_MAX17043] = {
844f7f8e87SIskren Chernev .reset_val = 0x0054,
854f7f8e87SIskren Chernev .vcell_shift = 4,
864f7f8e87SIskren Chernev .vcell_mul = 1250,
874f7f8e87SIskren Chernev .vcell_div = 1,
884f7f8e87SIskren Chernev .has_low_soc_alert = 1,
891779c6e3SIskren Chernev .rcomp_bytes = 1,
90ee2d433cSIskren Chernev .has_soc_alert = 0,
914f7f8e87SIskren Chernev },
924f7f8e87SIskren Chernev [ID_MAX17044] = {
934f7f8e87SIskren Chernev .reset_val = 0x0054,
944f7f8e87SIskren Chernev .vcell_shift = 4,
954f7f8e87SIskren Chernev .vcell_mul = 2500,
964f7f8e87SIskren Chernev .vcell_div = 1,
974f7f8e87SIskren Chernev .has_low_soc_alert = 1,
981779c6e3SIskren Chernev .rcomp_bytes = 1,
99ee2d433cSIskren Chernev .has_soc_alert = 0,
1004f7f8e87SIskren Chernev },
1014f7f8e87SIskren Chernev [ID_MAX17048] = {
1024f7f8e87SIskren Chernev .reset_val = 0x5400,
1034f7f8e87SIskren Chernev .vcell_shift = 0,
1044f7f8e87SIskren Chernev .vcell_mul = 625,
1054f7f8e87SIskren Chernev .vcell_div = 8,
1064f7f8e87SIskren Chernev .has_low_soc_alert = 1,
1071779c6e3SIskren Chernev .rcomp_bytes = 1,
108ee2d433cSIskren Chernev .has_soc_alert = 1,
1094f7f8e87SIskren Chernev },
1104f7f8e87SIskren Chernev [ID_MAX17049] = {
1114f7f8e87SIskren Chernev .reset_val = 0x5400,
1124f7f8e87SIskren Chernev .vcell_shift = 0,
1134f7f8e87SIskren Chernev .vcell_mul = 625,
1144f7f8e87SIskren Chernev .vcell_div = 4,
1154f7f8e87SIskren Chernev .has_low_soc_alert = 1,
1161779c6e3SIskren Chernev .rcomp_bytes = 1,
117ee2d433cSIskren Chernev .has_soc_alert = 1,
1184f7f8e87SIskren Chernev },
1194f7f8e87SIskren Chernev [ID_MAX17058] = {
1204f7f8e87SIskren Chernev .reset_val = 0x5400,
1214f7f8e87SIskren Chernev .vcell_shift = 0,
1224f7f8e87SIskren Chernev .vcell_mul = 625,
1234f7f8e87SIskren Chernev .vcell_div = 8,
1244f7f8e87SIskren Chernev .has_low_soc_alert = 1,
1251779c6e3SIskren Chernev .rcomp_bytes = 1,
126ee2d433cSIskren Chernev .has_soc_alert = 0,
1274f7f8e87SIskren Chernev },
1284f7f8e87SIskren Chernev [ID_MAX17059] = {
1294f7f8e87SIskren Chernev .reset_val = 0x5400,
1304f7f8e87SIskren Chernev .vcell_shift = 0,
1314f7f8e87SIskren Chernev .vcell_mul = 625,
1324f7f8e87SIskren Chernev .vcell_div = 4,
1334f7f8e87SIskren Chernev .has_low_soc_alert = 1,
1341779c6e3SIskren Chernev .rcomp_bytes = 1,
135ee2d433cSIskren Chernev .has_soc_alert = 0,
1364f7f8e87SIskren Chernev },
1374f7f8e87SIskren Chernev };
1384f7f8e87SIskren Chernev
1398c0984e5SSebastian Reichel struct max17040_chip {
1408c0984e5SSebastian Reichel struct i2c_client *client;
1416455a8a8SIskren Chernev struct regmap *regmap;
1428c0984e5SSebastian Reichel struct delayed_work work;
1438c0984e5SSebastian Reichel struct power_supply *battery;
1444f7f8e87SIskren Chernev struct chip_data data;
1458c0984e5SSebastian Reichel
1468c0984e5SSebastian Reichel /* battery capacity */
1478c0984e5SSebastian Reichel int soc;
148cccdd0caSMatheus Castello /* Low alert threshold from 32% to 1% of the State of Charge */
149cccdd0caSMatheus Castello u32 low_soc_alert;
1504f7f8e87SIskren Chernev /* some devices return twice the capacity */
1514f7f8e87SIskren Chernev bool quirk_double_soc;
1521779c6e3SIskren Chernev /* higher 8 bits for 17043+, 16 bits for 17040,41 */
1531779c6e3SIskren Chernev u16 rcomp;
1548c0984e5SSebastian Reichel };
1558c0984e5SSebastian Reichel
max17040_reset(struct max17040_chip * chip)1566455a8a8SIskren Chernev static int max17040_reset(struct max17040_chip *chip)
1578c0984e5SSebastian Reichel {
1584f7f8e87SIskren Chernev return regmap_write(chip->regmap, MAX17040_CMD, chip->data.reset_val);
1598c0984e5SSebastian Reichel }
1608c0984e5SSebastian Reichel
max17040_set_low_soc_alert(struct max17040_chip * chip,u32 level)1616455a8a8SIskren Chernev static int max17040_set_low_soc_alert(struct max17040_chip *chip, u32 level)
1628c0984e5SSebastian Reichel {
1634f7f8e87SIskren Chernev level = 32 - level * (chip->quirk_double_soc ? 2 : 1);
1646455a8a8SIskren Chernev return regmap_update_bits(chip->regmap, MAX17040_CONFIG,
1656455a8a8SIskren Chernev MAX17040_ATHD_MASK, level);
166cccdd0caSMatheus Castello }
167cccdd0caSMatheus Castello
max17040_set_soc_alert(struct max17040_chip * chip,bool enable)168ee2d433cSIskren Chernev static int max17040_set_soc_alert(struct max17040_chip *chip, bool enable)
169ee2d433cSIskren Chernev {
170ee2d433cSIskren Chernev return regmap_update_bits(chip->regmap, MAX17040_CONFIG,
171ee2d433cSIskren Chernev MAX17040_ALSC_MASK, enable ? MAX17040_ALSC_MASK : 0);
172ee2d433cSIskren Chernev }
173ee2d433cSIskren Chernev
max17040_set_rcomp(struct max17040_chip * chip,u16 rcomp)1741779c6e3SIskren Chernev static int max17040_set_rcomp(struct max17040_chip *chip, u16 rcomp)
1751779c6e3SIskren Chernev {
1761779c6e3SIskren Chernev u16 mask = chip->data.rcomp_bytes == 2 ?
1771779c6e3SIskren Chernev 0xffff : MAX17040_CFG_RCOMP_MASK;
1781779c6e3SIskren Chernev
1791779c6e3SIskren Chernev return regmap_update_bits(chip->regmap, MAX17040_CONFIG, mask, rcomp);
1801779c6e3SIskren Chernev }
1811779c6e3SIskren Chernev
max17040_raw_vcell_to_uvolts(struct max17040_chip * chip,u16 vcell)1824f7f8e87SIskren Chernev static int max17040_raw_vcell_to_uvolts(struct max17040_chip *chip, u16 vcell)
1834f7f8e87SIskren Chernev {
1844f7f8e87SIskren Chernev struct chip_data *d = &chip->data;
1854f7f8e87SIskren Chernev
1864f7f8e87SIskren Chernev return (vcell >> d->vcell_shift) * d->vcell_mul / d->vcell_div;
1874f7f8e87SIskren Chernev }
1884f7f8e87SIskren Chernev
1894f7f8e87SIskren Chernev
max17040_get_vcell(struct max17040_chip * chip)1906455a8a8SIskren Chernev static int max17040_get_vcell(struct max17040_chip *chip)
1918c0984e5SSebastian Reichel {
1926455a8a8SIskren Chernev u32 vcell;
1938c0984e5SSebastian Reichel
1946455a8a8SIskren Chernev regmap_read(chip->regmap, MAX17040_VCELL, &vcell);
1958c0984e5SSebastian Reichel
1964f7f8e87SIskren Chernev return max17040_raw_vcell_to_uvolts(chip, vcell);
1978c0984e5SSebastian Reichel }
1988c0984e5SSebastian Reichel
max17040_get_soc(struct max17040_chip * chip)1996455a8a8SIskren Chernev static int max17040_get_soc(struct max17040_chip *chip)
2008c0984e5SSebastian Reichel {
2016455a8a8SIskren Chernev u32 soc;
2028c0984e5SSebastian Reichel
2036455a8a8SIskren Chernev regmap_read(chip->regmap, MAX17040_SOC, &soc);
2048c0984e5SSebastian Reichel
2054f7f8e87SIskren Chernev return soc >> (chip->quirk_double_soc ? 9 : 8);
2068c0984e5SSebastian Reichel }
2078c0984e5SSebastian Reichel
max17040_get_version(struct max17040_chip * chip)2086455a8a8SIskren Chernev static int max17040_get_version(struct max17040_chip *chip)
2098c0984e5SSebastian Reichel {
2106455a8a8SIskren Chernev int ret;
2116455a8a8SIskren Chernev u32 version;
2128c0984e5SSebastian Reichel
2136455a8a8SIskren Chernev ret = regmap_read(chip->regmap, MAX17040_VER, &version);
2148c0984e5SSebastian Reichel
2156455a8a8SIskren Chernev return ret ? ret : version;
2168c0984e5SSebastian Reichel }
2178c0984e5SSebastian Reichel
max17040_get_online(struct max17040_chip * chip)2186455a8a8SIskren Chernev static int max17040_get_online(struct max17040_chip *chip)
2198c0984e5SSebastian Reichel {
220ce0ae832SKrzysztof Kozlowski return 1;
2218c0984e5SSebastian Reichel }
2228c0984e5SSebastian Reichel
max17040_get_of_data(struct max17040_chip * chip)223cccdd0caSMatheus Castello static int max17040_get_of_data(struct max17040_chip *chip)
224cccdd0caSMatheus Castello {
225cccdd0caSMatheus Castello struct device *dev = &chip->client->dev;
2261779c6e3SIskren Chernev struct chip_data *data = &max17040_family[
2277f977e64SIskren Chernev (uintptr_t) of_device_get_match_data(dev)];
2281779c6e3SIskren Chernev int rcomp_len;
2291779c6e3SIskren Chernev u8 rcomp[2];
230cccdd0caSMatheus Castello
2314f7f8e87SIskren Chernev chip->quirk_double_soc = device_property_read_bool(dev,
2324f7f8e87SIskren Chernev "maxim,double-soc");
2334f7f8e87SIskren Chernev
234cccdd0caSMatheus Castello chip->low_soc_alert = MAX17040_ATHD_DEFAULT_POWER_UP;
235cccdd0caSMatheus Castello device_property_read_u32(dev,
236cccdd0caSMatheus Castello "maxim,alert-low-soc-level",
237cccdd0caSMatheus Castello &chip->low_soc_alert);
238cccdd0caSMatheus Castello
2394f7f8e87SIskren Chernev if (chip->low_soc_alert <= 0 ||
2404f7f8e87SIskren Chernev chip->low_soc_alert > (chip->quirk_double_soc ? 16 : 32)) {
2416455a8a8SIskren Chernev dev_err(dev, "maxim,alert-low-soc-level out of bounds\n");
242cccdd0caSMatheus Castello return -EINVAL;
2436455a8a8SIskren Chernev }
244cccdd0caSMatheus Castello
2451779c6e3SIskren Chernev rcomp_len = device_property_count_u8(dev, "maxim,rcomp");
2461779c6e3SIskren Chernev chip->rcomp = MAX17040_RCOMP_DEFAULT;
2471779c6e3SIskren Chernev if (rcomp_len == data->rcomp_bytes) {
248a837f35cSKrzysztof Kozlowski if (!device_property_read_u8_array(dev, "maxim,rcomp",
249a837f35cSKrzysztof Kozlowski rcomp, rcomp_len))
250a837f35cSKrzysztof Kozlowski chip->rcomp = rcomp_len == 2 ? rcomp[0] << 8 | rcomp[1] :
2511779c6e3SIskren Chernev rcomp[0] << 8;
2521779c6e3SIskren Chernev } else if (rcomp_len > 0) {
2531779c6e3SIskren Chernev dev_err(dev, "maxim,rcomp has incorrect length\n");
2541779c6e3SIskren Chernev return -EINVAL;
2551779c6e3SIskren Chernev }
2561779c6e3SIskren Chernev
257cccdd0caSMatheus Castello return 0;
258cccdd0caSMatheus Castello }
259cccdd0caSMatheus Castello
max17040_check_changes(struct max17040_chip * chip)2606455a8a8SIskren Chernev static void max17040_check_changes(struct max17040_chip *chip)
2612e17ed94SMatheus Castello {
2626455a8a8SIskren Chernev chip->soc = max17040_get_soc(chip);
2632e17ed94SMatheus Castello }
2642e17ed94SMatheus Castello
max17040_queue_work(struct max17040_chip * chip)265e55a5061SIskren Chernev static void max17040_queue_work(struct max17040_chip *chip)
266e55a5061SIskren Chernev {
267e55a5061SIskren Chernev queue_delayed_work(system_power_efficient_wq, &chip->work,
268e55a5061SIskren Chernev MAX17040_DELAY);
269e55a5061SIskren Chernev }
270e55a5061SIskren Chernev
max17040_stop_work(void * data)271e55a5061SIskren Chernev static void max17040_stop_work(void *data)
272e55a5061SIskren Chernev {
273e55a5061SIskren Chernev struct max17040_chip *chip = data;
274e55a5061SIskren Chernev
275e55a5061SIskren Chernev cancel_delayed_work_sync(&chip->work);
276e55a5061SIskren Chernev }
277e55a5061SIskren Chernev
max17040_work(struct work_struct * work)2788c0984e5SSebastian Reichel static void max17040_work(struct work_struct *work)
2798c0984e5SSebastian Reichel {
2808c0984e5SSebastian Reichel struct max17040_chip *chip;
281c603bec3SKrzysztof Kozlowski int last_soc;
2828c0984e5SSebastian Reichel
2838c0984e5SSebastian Reichel chip = container_of(work, struct max17040_chip, work.work);
284a08990eaSMatheus Castello
285c603bec3SKrzysztof Kozlowski /* store SOC to check changes */
286a08990eaSMatheus Castello last_soc = chip->soc;
2876455a8a8SIskren Chernev max17040_check_changes(chip);
2888c0984e5SSebastian Reichel
289a08990eaSMatheus Castello /* check changes and send uevent */
290c603bec3SKrzysztof Kozlowski if (last_soc != chip->soc)
291a08990eaSMatheus Castello power_supply_changed(chip->battery);
292a08990eaSMatheus Castello
293e55a5061SIskren Chernev max17040_queue_work(chip);
2948c0984e5SSebastian Reichel }
2958c0984e5SSebastian Reichel
296ee2d433cSIskren Chernev /* Returns true if alert cause was SOC change, not low SOC */
max17040_handle_soc_alert(struct max17040_chip * chip)297ee2d433cSIskren Chernev static bool max17040_handle_soc_alert(struct max17040_chip *chip)
298ee2d433cSIskren Chernev {
299ee2d433cSIskren Chernev bool ret = true;
300ee2d433cSIskren Chernev u32 data;
301ee2d433cSIskren Chernev
302ee2d433cSIskren Chernev regmap_read(chip->regmap, MAX17040_STATUS, &data);
303ee2d433cSIskren Chernev
304ee2d433cSIskren Chernev if (data & MAX17040_STATUS_HD_MASK) {
305ee2d433cSIskren Chernev // this alert was caused by low soc
306ee2d433cSIskren Chernev ret = false;
307ee2d433cSIskren Chernev }
308ee2d433cSIskren Chernev if (data & MAX17040_STATUS_SC_MASK) {
309ee2d433cSIskren Chernev // soc change bit -- deassert to mark as handled
310ee2d433cSIskren Chernev regmap_write(chip->regmap, MAX17040_STATUS,
311ee2d433cSIskren Chernev data & ~MAX17040_STATUS_SC_MASK);
312ee2d433cSIskren Chernev }
313ee2d433cSIskren Chernev
314ee2d433cSIskren Chernev return ret;
315ee2d433cSIskren Chernev }
316ee2d433cSIskren Chernev
max17040_thread_handler(int id,void * dev)3172e17ed94SMatheus Castello static irqreturn_t max17040_thread_handler(int id, void *dev)
3182e17ed94SMatheus Castello {
3192e17ed94SMatheus Castello struct max17040_chip *chip = dev;
3202e17ed94SMatheus Castello
321ee2d433cSIskren Chernev if (!(chip->data.has_soc_alert && max17040_handle_soc_alert(chip)))
322ee2d433cSIskren Chernev dev_warn(&chip->client->dev, "IRQ: Alert battery low level\n");
3236455a8a8SIskren Chernev
3242e17ed94SMatheus Castello /* read registers */
3256455a8a8SIskren Chernev max17040_check_changes(chip);
3262e17ed94SMatheus Castello
3272e17ed94SMatheus Castello /* send uevent */
3282e17ed94SMatheus Castello power_supply_changed(chip->battery);
3292e17ed94SMatheus Castello
330cccdd0caSMatheus Castello /* reset alert bit */
3316455a8a8SIskren Chernev max17040_set_low_soc_alert(chip, chip->low_soc_alert);
332cccdd0caSMatheus Castello
3332e17ed94SMatheus Castello return IRQ_HANDLED;
3342e17ed94SMatheus Castello }
3352e17ed94SMatheus Castello
max17040_enable_alert_irq(struct max17040_chip * chip)3362e17ed94SMatheus Castello static int max17040_enable_alert_irq(struct max17040_chip *chip)
3372e17ed94SMatheus Castello {
3382e17ed94SMatheus Castello struct i2c_client *client = chip->client;
3392e17ed94SMatheus Castello int ret;
3402e17ed94SMatheus Castello
3412e17ed94SMatheus Castello ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
3428bb2314fSKrzysztof Kozlowski max17040_thread_handler, IRQF_ONESHOT,
3432e17ed94SMatheus Castello chip->battery->desc->name, chip);
3442e17ed94SMatheus Castello
3452e17ed94SMatheus Castello return ret;
3462e17ed94SMatheus Castello }
3472e17ed94SMatheus Castello
max17040_prop_writeable(struct power_supply * psy,enum power_supply_property psp)3482f38dc4dSMatheus Castello static int max17040_prop_writeable(struct power_supply *psy,
3492f38dc4dSMatheus Castello enum power_supply_property psp)
3502f38dc4dSMatheus Castello {
3512f38dc4dSMatheus Castello switch (psp) {
3522f38dc4dSMatheus Castello case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
3532f38dc4dSMatheus Castello return 1;
3542f38dc4dSMatheus Castello default:
3552f38dc4dSMatheus Castello return 0;
3562f38dc4dSMatheus Castello }
3572f38dc4dSMatheus Castello }
3582f38dc4dSMatheus Castello
max17040_set_property(struct power_supply * psy,enum power_supply_property psp,const union power_supply_propval * val)3592f38dc4dSMatheus Castello static int max17040_set_property(struct power_supply *psy,
3602f38dc4dSMatheus Castello enum power_supply_property psp,
3612f38dc4dSMatheus Castello const union power_supply_propval *val)
3622f38dc4dSMatheus Castello {
3632f38dc4dSMatheus Castello struct max17040_chip *chip = power_supply_get_drvdata(psy);
3642f38dc4dSMatheus Castello int ret;
3652f38dc4dSMatheus Castello
3662f38dc4dSMatheus Castello switch (psp) {
3672f38dc4dSMatheus Castello case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
3684f7f8e87SIskren Chernev /* alert threshold can be programmed from 1% up to 16/32% */
3694f7f8e87SIskren Chernev if ((val->intval < 1) ||
3704f7f8e87SIskren Chernev (val->intval > (chip->quirk_double_soc ? 16 : 32))) {
3712f38dc4dSMatheus Castello ret = -EINVAL;
3722f38dc4dSMatheus Castello break;
3732f38dc4dSMatheus Castello }
3746455a8a8SIskren Chernev ret = max17040_set_low_soc_alert(chip, val->intval);
3752f38dc4dSMatheus Castello chip->low_soc_alert = val->intval;
3762f38dc4dSMatheus Castello break;
3772f38dc4dSMatheus Castello default:
3782f38dc4dSMatheus Castello ret = -EINVAL;
3792f38dc4dSMatheus Castello }
3802f38dc4dSMatheus Castello
3812f38dc4dSMatheus Castello return ret;
3822f38dc4dSMatheus Castello }
3832f38dc4dSMatheus Castello
max17040_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)3846455a8a8SIskren Chernev static int max17040_get_property(struct power_supply *psy,
3856455a8a8SIskren Chernev enum power_supply_property psp,
3866455a8a8SIskren Chernev union power_supply_propval *val)
3876455a8a8SIskren Chernev {
3886455a8a8SIskren Chernev struct max17040_chip *chip = power_supply_get_drvdata(psy);
3896455a8a8SIskren Chernev
3906455a8a8SIskren Chernev switch (psp) {
3916455a8a8SIskren Chernev case POWER_SUPPLY_PROP_ONLINE:
3926455a8a8SIskren Chernev val->intval = max17040_get_online(chip);
3936455a8a8SIskren Chernev break;
3946455a8a8SIskren Chernev case POWER_SUPPLY_PROP_VOLTAGE_NOW:
3956455a8a8SIskren Chernev val->intval = max17040_get_vcell(chip);
3966455a8a8SIskren Chernev break;
3976455a8a8SIskren Chernev case POWER_SUPPLY_PROP_CAPACITY:
3986455a8a8SIskren Chernev val->intval = max17040_get_soc(chip);
3996455a8a8SIskren Chernev break;
4006455a8a8SIskren Chernev case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
4016455a8a8SIskren Chernev val->intval = chip->low_soc_alert;
4026455a8a8SIskren Chernev break;
4036455a8a8SIskren Chernev default:
4046455a8a8SIskren Chernev return -EINVAL;
4056455a8a8SIskren Chernev }
4066455a8a8SIskren Chernev return 0;
4076455a8a8SIskren Chernev }
4086455a8a8SIskren Chernev
4096455a8a8SIskren Chernev static const struct regmap_config max17040_regmap = {
4106455a8a8SIskren Chernev .reg_bits = 8,
4116455a8a8SIskren Chernev .reg_stride = 2,
4126455a8a8SIskren Chernev .val_bits = 16,
4136455a8a8SIskren Chernev .val_format_endian = REGMAP_ENDIAN_BIG,
4146455a8a8SIskren Chernev };
4156455a8a8SIskren Chernev
4168c0984e5SSebastian Reichel static enum power_supply_property max17040_battery_props[] = {
4178c0984e5SSebastian Reichel POWER_SUPPLY_PROP_ONLINE,
4188c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW,
4198c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY,
4202f38dc4dSMatheus Castello POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN,
4218c0984e5SSebastian Reichel };
4228c0984e5SSebastian Reichel
4238c0984e5SSebastian Reichel static const struct power_supply_desc max17040_battery_desc = {
4248c0984e5SSebastian Reichel .name = "battery",
4258c0984e5SSebastian Reichel .type = POWER_SUPPLY_TYPE_BATTERY,
4268c0984e5SSebastian Reichel .get_property = max17040_get_property,
4272f38dc4dSMatheus Castello .set_property = max17040_set_property,
4282f38dc4dSMatheus Castello .property_is_writeable = max17040_prop_writeable,
4298c0984e5SSebastian Reichel .properties = max17040_battery_props,
4308c0984e5SSebastian Reichel .num_properties = ARRAY_SIZE(max17040_battery_props),
4318c0984e5SSebastian Reichel };
4328c0984e5SSebastian Reichel
max17040_probe(struct i2c_client * client)433a07fca69SUwe Kleine-König static int max17040_probe(struct i2c_client *client)
4348c0984e5SSebastian Reichel {
435a07fca69SUwe Kleine-König const struct i2c_device_id *id = i2c_client_get_device_id(client);
4364e9c406dSWolfram Sang struct i2c_adapter *adapter = client->adapter;
4378c0984e5SSebastian Reichel struct power_supply_config psy_cfg = {};
4388c0984e5SSebastian Reichel struct max17040_chip *chip;
4394f7f8e87SIskren Chernev enum chip_id chip_id;
440ee2d433cSIskren Chernev bool enable_irq = false;
441cccdd0caSMatheus Castello int ret;
4428c0984e5SSebastian Reichel
4438c0984e5SSebastian Reichel if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
4448c0984e5SSebastian Reichel return -EIO;
4458c0984e5SSebastian Reichel
4468c0984e5SSebastian Reichel chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
4478c0984e5SSebastian Reichel if (!chip)
4488c0984e5SSebastian Reichel return -ENOMEM;
4498c0984e5SSebastian Reichel
4508c0984e5SSebastian Reichel chip->client = client;
4516455a8a8SIskren Chernev chip->regmap = devm_regmap_init_i2c(client, &max17040_regmap);
4521d422ecfSYang Yingliang if (IS_ERR(chip->regmap))
4531d422ecfSYang Yingliang return PTR_ERR(chip->regmap);
4544f7f8e87SIskren Chernev chip_id = (enum chip_id) id->driver_data;
4554f7f8e87SIskren Chernev if (client->dev.of_node) {
456cccdd0caSMatheus Castello ret = max17040_get_of_data(chip);
4576455a8a8SIskren Chernev if (ret)
458cccdd0caSMatheus Castello return ret;
4596dfbb2ceSKrzysztof Kozlowski chip_id = (uintptr_t)of_device_get_match_data(&client->dev);
4604f7f8e87SIskren Chernev }
4614f7f8e87SIskren Chernev chip->data = max17040_family[chip_id];
4628c0984e5SSebastian Reichel
4638c0984e5SSebastian Reichel i2c_set_clientdata(client, chip);
4648c0984e5SSebastian Reichel psy_cfg.drv_data = chip;
4658c0984e5SSebastian Reichel
466e55a5061SIskren Chernev chip->battery = devm_power_supply_register(&client->dev,
4678c0984e5SSebastian Reichel &max17040_battery_desc, &psy_cfg);
4688c0984e5SSebastian Reichel if (IS_ERR(chip->battery)) {
4698c0984e5SSebastian Reichel dev_err(&client->dev, "failed: power supply register\n");
4708c0984e5SSebastian Reichel return PTR_ERR(chip->battery);
4718c0984e5SSebastian Reichel }
4728c0984e5SSebastian Reichel
4736455a8a8SIskren Chernev ret = max17040_get_version(chip);
4746455a8a8SIskren Chernev if (ret < 0)
4756455a8a8SIskren Chernev return ret;
4766455a8a8SIskren Chernev dev_dbg(&chip->client->dev, "MAX17040 Fuel-Gauge Ver 0x%x\n", ret);
4776455a8a8SIskren Chernev
4784f7f8e87SIskren Chernev if (chip_id == ID_MAX17040 || chip_id == ID_MAX17041)
4796455a8a8SIskren Chernev max17040_reset(chip);
4808c0984e5SSebastian Reichel
4811779c6e3SIskren Chernev max17040_set_rcomp(chip, chip->rcomp);
4821779c6e3SIskren Chernev
4832e17ed94SMatheus Castello /* check interrupt */
4844f7f8e87SIskren Chernev if (client->irq && chip->data.has_low_soc_alert) {
4856455a8a8SIskren Chernev ret = max17040_set_low_soc_alert(chip, chip->low_soc_alert);
486cccdd0caSMatheus Castello if (ret) {
487cccdd0caSMatheus Castello dev_err(&client->dev,
488cccdd0caSMatheus Castello "Failed to set low SOC alert: err %d\n", ret);
489cccdd0caSMatheus Castello return ret;
490cccdd0caSMatheus Castello }
4912e17ed94SMatheus Castello
492ee2d433cSIskren Chernev enable_irq = true;
493ee2d433cSIskren Chernev }
494ee2d433cSIskren Chernev
495ee2d433cSIskren Chernev if (client->irq && chip->data.has_soc_alert) {
496ee2d433cSIskren Chernev ret = max17040_set_soc_alert(chip, 1);
497ee2d433cSIskren Chernev if (ret) {
498ee2d433cSIskren Chernev dev_err(&client->dev,
499ee2d433cSIskren Chernev "Failed to set SOC alert: err %d\n", ret);
500ee2d433cSIskren Chernev return ret;
501ee2d433cSIskren Chernev }
502ee2d433cSIskren Chernev enable_irq = true;
503ee2d433cSIskren Chernev } else {
504ee2d433cSIskren Chernev /* soc alerts negate the need for polling */
505ee2d433cSIskren Chernev INIT_DEFERRABLE_WORK(&chip->work, max17040_work);
506ee2d433cSIskren Chernev ret = devm_add_action(&client->dev, max17040_stop_work, chip);
507ee2d433cSIskren Chernev if (ret)
508ee2d433cSIskren Chernev return ret;
509ee2d433cSIskren Chernev max17040_queue_work(chip);
510ee2d433cSIskren Chernev }
511ee2d433cSIskren Chernev
512ee2d433cSIskren Chernev if (enable_irq) {
5132e17ed94SMatheus Castello ret = max17040_enable_alert_irq(chip);
5142e17ed94SMatheus Castello if (ret) {
5152e17ed94SMatheus Castello client->irq = 0;
5162e17ed94SMatheus Castello dev_warn(&client->dev,
5172e17ed94SMatheus Castello "Failed to get IRQ err %d\n", ret);
5182e17ed94SMatheus Castello }
5192e17ed94SMatheus Castello }
5202e17ed94SMatheus Castello
5218c0984e5SSebastian Reichel return 0;
5228c0984e5SSebastian Reichel }
5238c0984e5SSebastian Reichel
5248c0984e5SSebastian Reichel #ifdef CONFIG_PM_SLEEP
5258c0984e5SSebastian Reichel
max17040_suspend(struct device * dev)5268c0984e5SSebastian Reichel static int max17040_suspend(struct device *dev)
5278c0984e5SSebastian Reichel {
5288c0984e5SSebastian Reichel struct i2c_client *client = to_i2c_client(dev);
5298c0984e5SSebastian Reichel struct max17040_chip *chip = i2c_get_clientdata(client);
5308c0984e5SSebastian Reichel
531ee2d433cSIskren Chernev if (client->irq && chip->data.has_soc_alert)
532ee2d433cSIskren Chernev // disable soc alert to prevent wakeup
533ee2d433cSIskren Chernev max17040_set_soc_alert(chip, 0);
534ee2d433cSIskren Chernev else
5358c0984e5SSebastian Reichel cancel_delayed_work(&chip->work);
5362e17ed94SMatheus Castello
537e29242adSMarek Szyprowski if (client->irq && device_may_wakeup(dev))
5382e17ed94SMatheus Castello enable_irq_wake(client->irq);
5392e17ed94SMatheus Castello
5408c0984e5SSebastian Reichel return 0;
5418c0984e5SSebastian Reichel }
5428c0984e5SSebastian Reichel
max17040_resume(struct device * dev)5438c0984e5SSebastian Reichel static int max17040_resume(struct device *dev)
5448c0984e5SSebastian Reichel {
5458c0984e5SSebastian Reichel struct i2c_client *client = to_i2c_client(dev);
5468c0984e5SSebastian Reichel struct max17040_chip *chip = i2c_get_clientdata(client);
5478c0984e5SSebastian Reichel
548e29242adSMarek Szyprowski if (client->irq && device_may_wakeup(dev))
5492e17ed94SMatheus Castello disable_irq_wake(client->irq);
5502e17ed94SMatheus Castello
551ee2d433cSIskren Chernev if (client->irq && chip->data.has_soc_alert)
552ee2d433cSIskren Chernev max17040_set_soc_alert(chip, 1);
553ee2d433cSIskren Chernev else
554e55a5061SIskren Chernev max17040_queue_work(chip);
555e55a5061SIskren Chernev
5568c0984e5SSebastian Reichel return 0;
5578c0984e5SSebastian Reichel }
5588c0984e5SSebastian Reichel
5598c0984e5SSebastian Reichel static SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume);
5608c0984e5SSebastian Reichel #define MAX17040_PM_OPS (&max17040_pm_ops)
5618c0984e5SSebastian Reichel
5628c0984e5SSebastian Reichel #else
5638c0984e5SSebastian Reichel
5648c0984e5SSebastian Reichel #define MAX17040_PM_OPS NULL
5658c0984e5SSebastian Reichel
5668c0984e5SSebastian Reichel #endif /* CONFIG_PM_SLEEP */
5678c0984e5SSebastian Reichel
5688c0984e5SSebastian Reichel static const struct i2c_device_id max17040_id[] = {
5694f7f8e87SIskren Chernev { "max17040", ID_MAX17040 },
5704f7f8e87SIskren Chernev { "max17041", ID_MAX17041 },
5714f7f8e87SIskren Chernev { "max17043", ID_MAX17043 },
5724f7f8e87SIskren Chernev { "max77836-battery", ID_MAX17043 },
5734f7f8e87SIskren Chernev { "max17044", ID_MAX17044 },
5744f7f8e87SIskren Chernev { "max17048", ID_MAX17048 },
5754f7f8e87SIskren Chernev { "max17049", ID_MAX17049 },
5764f7f8e87SIskren Chernev { "max17058", ID_MAX17058 },
5774f7f8e87SIskren Chernev { "max17059", ID_MAX17059 },
5784f7f8e87SIskren Chernev { /* sentinel */ }
5798c0984e5SSebastian Reichel };
5808c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(i2c, max17040_id);
5818c0984e5SSebastian Reichel
582da28122cSJavier Martinez Canillas static const struct of_device_id max17040_of_match[] = {
5834f7f8e87SIskren Chernev { .compatible = "maxim,max17040", .data = (void *) ID_MAX17040 },
5844f7f8e87SIskren Chernev { .compatible = "maxim,max17041", .data = (void *) ID_MAX17041 },
5854f7f8e87SIskren Chernev { .compatible = "maxim,max17043", .data = (void *) ID_MAX17043 },
5864f7f8e87SIskren Chernev { .compatible = "maxim,max77836-battery", .data = (void *) ID_MAX17043 },
5874f7f8e87SIskren Chernev { .compatible = "maxim,max17044", .data = (void *) ID_MAX17044 },
5884f7f8e87SIskren Chernev { .compatible = "maxim,max17048", .data = (void *) ID_MAX17048 },
5894f7f8e87SIskren Chernev { .compatible = "maxim,max17049", .data = (void *) ID_MAX17049 },
5904f7f8e87SIskren Chernev { .compatible = "maxim,max17058", .data = (void *) ID_MAX17058 },
5914f7f8e87SIskren Chernev { .compatible = "maxim,max17059", .data = (void *) ID_MAX17059 },
5924f7f8e87SIskren Chernev { /* sentinel */ },
593da28122cSJavier Martinez Canillas };
594da28122cSJavier Martinez Canillas MODULE_DEVICE_TABLE(of, max17040_of_match);
595da28122cSJavier Martinez Canillas
5968c0984e5SSebastian Reichel static struct i2c_driver max17040_i2c_driver = {
5978c0984e5SSebastian Reichel .driver = {
5988c0984e5SSebastian Reichel .name = "max17040",
599da28122cSJavier Martinez Canillas .of_match_table = max17040_of_match,
6008c0984e5SSebastian Reichel .pm = MAX17040_PM_OPS,
6018c0984e5SSebastian Reichel },
602fe20b1dcSUwe Kleine-König .probe = max17040_probe,
6038c0984e5SSebastian Reichel .id_table = max17040_id,
6048c0984e5SSebastian Reichel };
6058c0984e5SSebastian Reichel module_i2c_driver(max17040_i2c_driver);
6068c0984e5SSebastian Reichel
6078c0984e5SSebastian Reichel MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>");
6088c0984e5SSebastian Reichel MODULE_DESCRIPTION("MAX17040 Fuel Gauge");
6098c0984e5SSebastian Reichel MODULE_LICENSE("GPL");
610