1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
25c0e09e0SWenyou Yang /*
35c0e09e0SWenyou Yang * Power supply driver for the Active-semi ACT8945A PMIC
45c0e09e0SWenyou Yang *
55c0e09e0SWenyou Yang * Copyright (C) 2015 Atmel Corporation
65c0e09e0SWenyou Yang *
75c0e09e0SWenyou Yang * Author: Wenyou Yang <wenyou.yang@atmel.com>
85c0e09e0SWenyou Yang */
9a09209acSWenyou Yang #include <linux/interrupt.h>
105c0e09e0SWenyou Yang #include <linux/module.h>
115c0e09e0SWenyou Yang #include <linux/of.h>
12a09209acSWenyou Yang #include <linux/of_irq.h>
135c0e09e0SWenyou Yang #include <linux/platform_device.h>
145c0e09e0SWenyou Yang #include <linux/power_supply.h>
155c0e09e0SWenyou Yang #include <linux/regmap.h>
16369eba09SWenyou Yang #include <linux/gpio/consumer.h>
175c0e09e0SWenyou Yang
185c0e09e0SWenyou Yang static const char *act8945a_charger_model = "ACT8945A";
195c0e09e0SWenyou Yang static const char *act8945a_charger_manufacturer = "Active-semi";
205c0e09e0SWenyou Yang
214667d52fSKrzysztof Kozlowski /*
225c0e09e0SWenyou Yang * ACT8945A Charger Register Map
235c0e09e0SWenyou Yang */
245c0e09e0SWenyou Yang
255c0e09e0SWenyou Yang /* 0x70: Reserved */
265c0e09e0SWenyou Yang #define ACT8945A_APCH_CFG 0x71
275c0e09e0SWenyou Yang #define ACT8945A_APCH_STATUS 0x78
285c0e09e0SWenyou Yang #define ACT8945A_APCH_CTRL 0x79
295c0e09e0SWenyou Yang #define ACT8945A_APCH_STATE 0x7A
305c0e09e0SWenyou Yang
315c0e09e0SWenyou Yang /* ACT8945A_APCH_CFG */
325c0e09e0SWenyou Yang #define APCH_CFG_OVPSET (0x3 << 0)
335c0e09e0SWenyou Yang #define APCH_CFG_OVPSET_6V6 (0x0 << 0)
345c0e09e0SWenyou Yang #define APCH_CFG_OVPSET_7V (0x1 << 0)
355c0e09e0SWenyou Yang #define APCH_CFG_OVPSET_7V5 (0x2 << 0)
365c0e09e0SWenyou Yang #define APCH_CFG_OVPSET_8V (0x3 << 0)
375c0e09e0SWenyou Yang #define APCH_CFG_PRETIMO (0x3 << 2)
385c0e09e0SWenyou Yang #define APCH_CFG_PRETIMO_40_MIN (0x0 << 2)
395c0e09e0SWenyou Yang #define APCH_CFG_PRETIMO_60_MIN (0x1 << 2)
405c0e09e0SWenyou Yang #define APCH_CFG_PRETIMO_80_MIN (0x2 << 2)
415c0e09e0SWenyou Yang #define APCH_CFG_PRETIMO_DISABLED (0x3 << 2)
425c0e09e0SWenyou Yang #define APCH_CFG_TOTTIMO (0x3 << 4)
435c0e09e0SWenyou Yang #define APCH_CFG_TOTTIMO_3_HOUR (0x0 << 4)
445c0e09e0SWenyou Yang #define APCH_CFG_TOTTIMO_4_HOUR (0x1 << 4)
455c0e09e0SWenyou Yang #define APCH_CFG_TOTTIMO_5_HOUR (0x2 << 4)
465c0e09e0SWenyou Yang #define APCH_CFG_TOTTIMO_DISABLED (0x3 << 4)
475c0e09e0SWenyou Yang #define APCH_CFG_SUSCHG (0x1 << 7)
485c0e09e0SWenyou Yang
495c0e09e0SWenyou Yang #define APCH_STATUS_CHGDAT BIT(0)
505c0e09e0SWenyou Yang #define APCH_STATUS_INDAT BIT(1)
515c0e09e0SWenyou Yang #define APCH_STATUS_TEMPDAT BIT(2)
525c0e09e0SWenyou Yang #define APCH_STATUS_TIMRDAT BIT(3)
535c0e09e0SWenyou Yang #define APCH_STATUS_CHGSTAT BIT(4)
545c0e09e0SWenyou Yang #define APCH_STATUS_INSTAT BIT(5)
555c0e09e0SWenyou Yang #define APCH_STATUS_TEMPSTAT BIT(6)
565c0e09e0SWenyou Yang #define APCH_STATUS_TIMRSTAT BIT(7)
575c0e09e0SWenyou Yang
585c0e09e0SWenyou Yang #define APCH_CTRL_CHGEOCOUT BIT(0)
595c0e09e0SWenyou Yang #define APCH_CTRL_INDIS BIT(1)
605c0e09e0SWenyou Yang #define APCH_CTRL_TEMPOUT BIT(2)
615c0e09e0SWenyou Yang #define APCH_CTRL_TIMRPRE BIT(3)
625c0e09e0SWenyou Yang #define APCH_CTRL_CHGEOCIN BIT(4)
635c0e09e0SWenyou Yang #define APCH_CTRL_INCON BIT(5)
645c0e09e0SWenyou Yang #define APCH_CTRL_TEMPIN BIT(6)
655c0e09e0SWenyou Yang #define APCH_CTRL_TIMRTOT BIT(7)
665c0e09e0SWenyou Yang
675c0e09e0SWenyou Yang #define APCH_STATE_ACINSTAT (0x1 << 1)
685c0e09e0SWenyou Yang #define APCH_STATE_CSTATE (0x3 << 4)
695c0e09e0SWenyou Yang #define APCH_STATE_CSTATE_SHIFT 4
705c0e09e0SWenyou Yang #define APCH_STATE_CSTATE_DISABLED 0x00
715c0e09e0SWenyou Yang #define APCH_STATE_CSTATE_EOC 0x01
725c0e09e0SWenyou Yang #define APCH_STATE_CSTATE_FAST 0x02
735c0e09e0SWenyou Yang #define APCH_STATE_CSTATE_PRE 0x03
745c0e09e0SWenyou Yang
755c0e09e0SWenyou Yang struct act8945a_charger {
76a09209acSWenyou Yang struct power_supply *psy;
7710ca08b0SWenyou Yang struct power_supply_desc desc;
785c0e09e0SWenyou Yang struct regmap *regmap;
79a09209acSWenyou Yang struct work_struct work;
80a09209acSWenyou Yang
81a09209acSWenyou Yang bool init_done;
82369eba09SWenyou Yang struct gpio_desc *lbo_gpio;
83528e3504SWenyou Yang struct gpio_desc *chglev_gpio;
845c0e09e0SWenyou Yang };
855c0e09e0SWenyou Yang
act8945a_get_charger_state(struct regmap * regmap,int * val)865c0e09e0SWenyou Yang static int act8945a_get_charger_state(struct regmap *regmap, int *val)
875c0e09e0SWenyou Yang {
885c0e09e0SWenyou Yang int ret;
895c0e09e0SWenyou Yang unsigned int status, state;
905c0e09e0SWenyou Yang
915c0e09e0SWenyou Yang ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);
925c0e09e0SWenyou Yang if (ret < 0)
935c0e09e0SWenyou Yang return ret;
945c0e09e0SWenyou Yang
955c0e09e0SWenyou Yang ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);
965c0e09e0SWenyou Yang if (ret < 0)
975c0e09e0SWenyou Yang return ret;
985c0e09e0SWenyou Yang
995c0e09e0SWenyou Yang state &= APCH_STATE_CSTATE;
1005c0e09e0SWenyou Yang state >>= APCH_STATE_CSTATE_SHIFT;
1015c0e09e0SWenyou Yang
1021f0ba406SWenyou Yang switch (state) {
1031f0ba406SWenyou Yang case APCH_STATE_CSTATE_PRE:
1041f0ba406SWenyou Yang case APCH_STATE_CSTATE_FAST:
1051f0ba406SWenyou Yang *val = POWER_SUPPLY_STATUS_CHARGING;
1061f0ba406SWenyou Yang break;
1071f0ba406SWenyou Yang case APCH_STATE_CSTATE_EOC:
1085c0e09e0SWenyou Yang if (status & APCH_STATUS_CHGDAT)
1095c0e09e0SWenyou Yang *val = POWER_SUPPLY_STATUS_FULL;
1105c0e09e0SWenyou Yang else
1115c0e09e0SWenyou Yang *val = POWER_SUPPLY_STATUS_CHARGING;
1121f0ba406SWenyou Yang break;
1131f0ba406SWenyou Yang case APCH_STATE_CSTATE_DISABLED:
1141f0ba406SWenyou Yang default:
1151f0ba406SWenyou Yang if (!(status & APCH_STATUS_INDAT))
1161f0ba406SWenyou Yang *val = POWER_SUPPLY_STATUS_DISCHARGING;
1171f0ba406SWenyou Yang else
1185c0e09e0SWenyou Yang *val = POWER_SUPPLY_STATUS_NOT_CHARGING;
1191f0ba406SWenyou Yang break;
1205c0e09e0SWenyou Yang }
1215c0e09e0SWenyou Yang
1225c0e09e0SWenyou Yang return 0;
1235c0e09e0SWenyou Yang }
1245c0e09e0SWenyou Yang
act8945a_get_charge_type(struct regmap * regmap,int * val)1255c0e09e0SWenyou Yang static int act8945a_get_charge_type(struct regmap *regmap, int *val)
1265c0e09e0SWenyou Yang {
1275c0e09e0SWenyou Yang int ret;
1281f0ba406SWenyou Yang unsigned int status, state;
1291f0ba406SWenyou Yang
1301f0ba406SWenyou Yang ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);
1311f0ba406SWenyou Yang if (ret < 0)
1321f0ba406SWenyou Yang return ret;
1335c0e09e0SWenyou Yang
1345c0e09e0SWenyou Yang ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);
1355c0e09e0SWenyou Yang if (ret < 0)
1365c0e09e0SWenyou Yang return ret;
1375c0e09e0SWenyou Yang
1385c0e09e0SWenyou Yang state &= APCH_STATE_CSTATE;
1395c0e09e0SWenyou Yang state >>= APCH_STATE_CSTATE_SHIFT;
1405c0e09e0SWenyou Yang
1415c0e09e0SWenyou Yang switch (state) {
1425c0e09e0SWenyou Yang case APCH_STATE_CSTATE_PRE:
1435c0e09e0SWenyou Yang *val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
1445c0e09e0SWenyou Yang break;
1455c0e09e0SWenyou Yang case APCH_STATE_CSTATE_FAST:
1465c0e09e0SWenyou Yang *val = POWER_SUPPLY_CHARGE_TYPE_FAST;
1475c0e09e0SWenyou Yang break;
1485c0e09e0SWenyou Yang case APCH_STATE_CSTATE_EOC:
1491f0ba406SWenyou Yang *val = POWER_SUPPLY_CHARGE_TYPE_NONE;
1501f0ba406SWenyou Yang break;
1515c0e09e0SWenyou Yang case APCH_STATE_CSTATE_DISABLED:
1525c0e09e0SWenyou Yang default:
1531f0ba406SWenyou Yang if (!(status & APCH_STATUS_INDAT))
1545c0e09e0SWenyou Yang *val = POWER_SUPPLY_CHARGE_TYPE_NONE;
1551f0ba406SWenyou Yang else
1561f0ba406SWenyou Yang *val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
1571f0ba406SWenyou Yang break;
1585c0e09e0SWenyou Yang }
1595c0e09e0SWenyou Yang
1605c0e09e0SWenyou Yang return 0;
1615c0e09e0SWenyou Yang }
1625c0e09e0SWenyou Yang
act8945a_get_battery_health(struct regmap * regmap,int * val)1636b021fc9SWenyou Yang static int act8945a_get_battery_health(struct regmap *regmap, int *val)
1645c0e09e0SWenyou Yang {
1655c0e09e0SWenyou Yang int ret;
1661f0ba406SWenyou Yang unsigned int status, state, config;
1675c0e09e0SWenyou Yang
1685c0e09e0SWenyou Yang ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);
1695c0e09e0SWenyou Yang if (ret < 0)
1705c0e09e0SWenyou Yang return ret;
1715c0e09e0SWenyou Yang
1721f0ba406SWenyou Yang ret = regmap_read(regmap, ACT8945A_APCH_CFG, &config);
1731f0ba406SWenyou Yang if (ret < 0)
1741f0ba406SWenyou Yang return ret;
1751f0ba406SWenyou Yang
1761f0ba406SWenyou Yang ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);
1771f0ba406SWenyou Yang if (ret < 0)
1781f0ba406SWenyou Yang return ret;
1791f0ba406SWenyou Yang
1801f0ba406SWenyou Yang state &= APCH_STATE_CSTATE;
1811f0ba406SWenyou Yang state >>= APCH_STATE_CSTATE_SHIFT;
1821f0ba406SWenyou Yang
1831f0ba406SWenyou Yang switch (state) {
1841f0ba406SWenyou Yang case APCH_STATE_CSTATE_DISABLED:
1851f0ba406SWenyou Yang if (config & APCH_CFG_SUSCHG) {
1861f0ba406SWenyou Yang *val = POWER_SUPPLY_HEALTH_UNKNOWN;
1871f0ba406SWenyou Yang } else if (status & APCH_STATUS_INDAT) {
1886b021fc9SWenyou Yang if (!(status & APCH_STATUS_TEMPDAT))
1895c0e09e0SWenyou Yang *val = POWER_SUPPLY_HEALTH_OVERHEAT;
1905c0e09e0SWenyou Yang else if (status & APCH_STATUS_TIMRDAT)
1915c0e09e0SWenyou Yang *val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
1925c0e09e0SWenyou Yang else
1931f0ba406SWenyou Yang *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
1941f0ba406SWenyou Yang } else {
1955c0e09e0SWenyou Yang *val = POWER_SUPPLY_HEALTH_GOOD;
1961f0ba406SWenyou Yang }
1971f0ba406SWenyou Yang break;
1981f0ba406SWenyou Yang case APCH_STATE_CSTATE_PRE:
1991f0ba406SWenyou Yang case APCH_STATE_CSTATE_FAST:
2001f0ba406SWenyou Yang case APCH_STATE_CSTATE_EOC:
2011f0ba406SWenyou Yang default:
2021f0ba406SWenyou Yang *val = POWER_SUPPLY_HEALTH_GOOD;
2031f0ba406SWenyou Yang break;
2041f0ba406SWenyou Yang }
2055c0e09e0SWenyou Yang
2065c0e09e0SWenyou Yang return 0;
2075c0e09e0SWenyou Yang }
2085c0e09e0SWenyou Yang
act8945a_get_capacity_level(struct act8945a_charger * charger,struct regmap * regmap,int * val)209369eba09SWenyou Yang static int act8945a_get_capacity_level(struct act8945a_charger *charger,
210369eba09SWenyou Yang struct regmap *regmap, int *val)
211369eba09SWenyou Yang {
212369eba09SWenyou Yang int ret;
213369eba09SWenyou Yang unsigned int status, state, config;
214369eba09SWenyou Yang int lbo_level = gpiod_get_value(charger->lbo_gpio);
215369eba09SWenyou Yang
216369eba09SWenyou Yang ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);
217369eba09SWenyou Yang if (ret < 0)
218369eba09SWenyou Yang return ret;
219369eba09SWenyou Yang
220369eba09SWenyou Yang ret = regmap_read(regmap, ACT8945A_APCH_CFG, &config);
221369eba09SWenyou Yang if (ret < 0)
222369eba09SWenyou Yang return ret;
223369eba09SWenyou Yang
224369eba09SWenyou Yang ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);
225369eba09SWenyou Yang if (ret < 0)
226369eba09SWenyou Yang return ret;
227369eba09SWenyou Yang
228369eba09SWenyou Yang state &= APCH_STATE_CSTATE;
229369eba09SWenyou Yang state >>= APCH_STATE_CSTATE_SHIFT;
230369eba09SWenyou Yang
231369eba09SWenyou Yang switch (state) {
232369eba09SWenyou Yang case APCH_STATE_CSTATE_PRE:
233369eba09SWenyou Yang *val = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
234369eba09SWenyou Yang break;
235369eba09SWenyou Yang case APCH_STATE_CSTATE_FAST:
236369eba09SWenyou Yang if (lbo_level)
237369eba09SWenyou Yang *val = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
238369eba09SWenyou Yang else
239369eba09SWenyou Yang *val = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
240369eba09SWenyou Yang break;
241369eba09SWenyou Yang case APCH_STATE_CSTATE_EOC:
242369eba09SWenyou Yang if (status & APCH_STATUS_CHGDAT)
243369eba09SWenyou Yang *val = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
244369eba09SWenyou Yang else
245369eba09SWenyou Yang *val = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
246369eba09SWenyou Yang break;
247369eba09SWenyou Yang case APCH_STATE_CSTATE_DISABLED:
248369eba09SWenyou Yang default:
249369eba09SWenyou Yang if (config & APCH_CFG_SUSCHG) {
250369eba09SWenyou Yang *val = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
251369eba09SWenyou Yang } else {
252369eba09SWenyou Yang *val = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
253369eba09SWenyou Yang if (!(status & APCH_STATUS_INDAT)) {
254369eba09SWenyou Yang if (!lbo_level)
255369eba09SWenyou Yang *val = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
256369eba09SWenyou Yang }
257369eba09SWenyou Yang }
258369eba09SWenyou Yang break;
259369eba09SWenyou Yang }
260369eba09SWenyou Yang
261369eba09SWenyou Yang return 0;
262369eba09SWenyou Yang }
263369eba09SWenyou Yang
264528e3504SWenyou Yang #define MAX_CURRENT_USB_HIGH 450000
265528e3504SWenyou Yang #define MAX_CURRENT_USB_LOW 90000
266528e3504SWenyou Yang #define MAX_CURRENT_USB_PRE 45000
267528e3504SWenyou Yang /*
268528e3504SWenyou Yang * Riset(K) = 2336 * (1V/Ichg(mA)) - 0.205
269528e3504SWenyou Yang * Riset = 2.43K
270528e3504SWenyou Yang */
271528e3504SWenyou Yang #define MAX_CURRENT_AC_HIGH 886527
272528e3504SWenyou Yang #define MAX_CURRENT_AC_LOW 117305
273528e3504SWenyou Yang #define MAX_CURRENT_AC_HIGH_PRE 88653
274528e3504SWenyou Yang #define MAX_CURRENT_AC_LOW_PRE 11731
275528e3504SWenyou Yang
act8945a_get_current_max(struct act8945a_charger * charger,struct regmap * regmap,int * val)276528e3504SWenyou Yang static int act8945a_get_current_max(struct act8945a_charger *charger,
277528e3504SWenyou Yang struct regmap *regmap, int *val)
278528e3504SWenyou Yang {
279528e3504SWenyou Yang int ret;
280528e3504SWenyou Yang unsigned int status, state;
281528e3504SWenyou Yang unsigned int acin_state;
282528e3504SWenyou Yang int chgin_level = gpiod_get_value(charger->chglev_gpio);
283528e3504SWenyou Yang
284528e3504SWenyou Yang ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);
285528e3504SWenyou Yang if (ret < 0)
286528e3504SWenyou Yang return ret;
287528e3504SWenyou Yang
288528e3504SWenyou Yang ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);
289528e3504SWenyou Yang if (ret < 0)
290528e3504SWenyou Yang return ret;
291528e3504SWenyou Yang
292528e3504SWenyou Yang acin_state = (state & APCH_STATE_ACINSTAT) >> 1;
293528e3504SWenyou Yang
294528e3504SWenyou Yang state &= APCH_STATE_CSTATE;
295528e3504SWenyou Yang state >>= APCH_STATE_CSTATE_SHIFT;
296528e3504SWenyou Yang
297528e3504SWenyou Yang switch (state) {
298528e3504SWenyou Yang case APCH_STATE_CSTATE_PRE:
299528e3504SWenyou Yang if (acin_state) {
300528e3504SWenyou Yang if (chgin_level)
301528e3504SWenyou Yang *val = MAX_CURRENT_AC_HIGH_PRE;
302528e3504SWenyou Yang else
303528e3504SWenyou Yang *val = MAX_CURRENT_AC_LOW_PRE;
304528e3504SWenyou Yang } else {
305528e3504SWenyou Yang *val = MAX_CURRENT_USB_PRE;
306528e3504SWenyou Yang }
307528e3504SWenyou Yang break;
308528e3504SWenyou Yang case APCH_STATE_CSTATE_FAST:
309528e3504SWenyou Yang if (acin_state) {
310528e3504SWenyou Yang if (chgin_level)
311528e3504SWenyou Yang *val = MAX_CURRENT_AC_HIGH;
312528e3504SWenyou Yang else
313528e3504SWenyou Yang *val = MAX_CURRENT_AC_LOW;
314528e3504SWenyou Yang } else {
315528e3504SWenyou Yang if (chgin_level)
316528e3504SWenyou Yang *val = MAX_CURRENT_USB_HIGH;
317528e3504SWenyou Yang else
318528e3504SWenyou Yang *val = MAX_CURRENT_USB_LOW;
319528e3504SWenyou Yang }
320528e3504SWenyou Yang break;
321528e3504SWenyou Yang case APCH_STATE_CSTATE_EOC:
322528e3504SWenyou Yang case APCH_STATE_CSTATE_DISABLED:
323528e3504SWenyou Yang default:
324528e3504SWenyou Yang *val = 0;
325528e3504SWenyou Yang break;
326528e3504SWenyou Yang }
327528e3504SWenyou Yang
328528e3504SWenyou Yang return 0;
329528e3504SWenyou Yang }
330528e3504SWenyou Yang
3315c0e09e0SWenyou Yang static enum power_supply_property act8945a_charger_props[] = {
3325c0e09e0SWenyou Yang POWER_SUPPLY_PROP_STATUS,
3335c0e09e0SWenyou Yang POWER_SUPPLY_PROP_CHARGE_TYPE,
3345c0e09e0SWenyou Yang POWER_SUPPLY_PROP_TECHNOLOGY,
3355c0e09e0SWenyou Yang POWER_SUPPLY_PROP_HEALTH,
336369eba09SWenyou Yang POWER_SUPPLY_PROP_CAPACITY_LEVEL,
337528e3504SWenyou Yang POWER_SUPPLY_PROP_CURRENT_MAX,
3385c0e09e0SWenyou Yang POWER_SUPPLY_PROP_MODEL_NAME,
3395c0e09e0SWenyou Yang POWER_SUPPLY_PROP_MANUFACTURER
3405c0e09e0SWenyou Yang };
3415c0e09e0SWenyou Yang
act8945a_charger_get_property(struct power_supply * psy,enum power_supply_property prop,union power_supply_propval * val)3425c0e09e0SWenyou Yang static int act8945a_charger_get_property(struct power_supply *psy,
3435c0e09e0SWenyou Yang enum power_supply_property prop,
3445c0e09e0SWenyou Yang union power_supply_propval *val)
3455c0e09e0SWenyou Yang {
3465c0e09e0SWenyou Yang struct act8945a_charger *charger = power_supply_get_drvdata(psy);
3475c0e09e0SWenyou Yang struct regmap *regmap = charger->regmap;
3485c0e09e0SWenyou Yang int ret = 0;
3495c0e09e0SWenyou Yang
3505c0e09e0SWenyou Yang switch (prop) {
3515c0e09e0SWenyou Yang case POWER_SUPPLY_PROP_STATUS:
3525c0e09e0SWenyou Yang ret = act8945a_get_charger_state(regmap, &val->intval);
3535c0e09e0SWenyou Yang break;
3545c0e09e0SWenyou Yang case POWER_SUPPLY_PROP_CHARGE_TYPE:
3555c0e09e0SWenyou Yang ret = act8945a_get_charge_type(regmap, &val->intval);
3565c0e09e0SWenyou Yang break;
3575c0e09e0SWenyou Yang case POWER_SUPPLY_PROP_TECHNOLOGY:
3585c0e09e0SWenyou Yang val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
3595c0e09e0SWenyou Yang break;
3605c0e09e0SWenyou Yang case POWER_SUPPLY_PROP_HEALTH:
3616b021fc9SWenyou Yang ret = act8945a_get_battery_health(regmap, &val->intval);
3625c0e09e0SWenyou Yang break;
363369eba09SWenyou Yang case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
364369eba09SWenyou Yang ret = act8945a_get_capacity_level(charger,
365369eba09SWenyou Yang regmap, &val->intval);
366369eba09SWenyou Yang break;
367528e3504SWenyou Yang case POWER_SUPPLY_PROP_CURRENT_MAX:
368528e3504SWenyou Yang ret = act8945a_get_current_max(charger,
369528e3504SWenyou Yang regmap, &val->intval);
370528e3504SWenyou Yang break;
3715c0e09e0SWenyou Yang case POWER_SUPPLY_PROP_MODEL_NAME:
3725c0e09e0SWenyou Yang val->strval = act8945a_charger_model;
3735c0e09e0SWenyou Yang break;
3745c0e09e0SWenyou Yang case POWER_SUPPLY_PROP_MANUFACTURER:
3755c0e09e0SWenyou Yang val->strval = act8945a_charger_manufacturer;
3765c0e09e0SWenyou Yang break;
3775c0e09e0SWenyou Yang default:
3785c0e09e0SWenyou Yang return -EINVAL;
3795c0e09e0SWenyou Yang }
3805c0e09e0SWenyou Yang
3815c0e09e0SWenyou Yang return ret;
3825c0e09e0SWenyou Yang }
3835c0e09e0SWenyou Yang
act8945a_enable_interrupt(struct act8945a_charger * charger)384a09209acSWenyou Yang static int act8945a_enable_interrupt(struct act8945a_charger *charger)
385a09209acSWenyou Yang {
386a09209acSWenyou Yang struct regmap *regmap = charger->regmap;
387a09209acSWenyou Yang unsigned char ctrl;
388a09209acSWenyou Yang int ret;
389a09209acSWenyou Yang
390a09209acSWenyou Yang ctrl = APCH_CTRL_CHGEOCOUT | APCH_CTRL_CHGEOCIN |
391a09209acSWenyou Yang APCH_CTRL_INDIS | APCH_CTRL_INCON |
392a09209acSWenyou Yang APCH_CTRL_TEMPOUT | APCH_CTRL_TEMPIN |
393a09209acSWenyou Yang APCH_CTRL_TIMRPRE | APCH_CTRL_TIMRTOT;
394a09209acSWenyou Yang ret = regmap_write(regmap, ACT8945A_APCH_CTRL, ctrl);
395a09209acSWenyou Yang if (ret)
396a09209acSWenyou Yang return ret;
397a09209acSWenyou Yang
398a09209acSWenyou Yang ctrl = APCH_STATUS_CHGSTAT | APCH_STATUS_INSTAT |
399a09209acSWenyou Yang APCH_STATUS_TEMPSTAT | APCH_STATUS_TIMRSTAT;
400a09209acSWenyou Yang ret = regmap_write(regmap, ACT8945A_APCH_STATUS, ctrl);
401a09209acSWenyou Yang if (ret)
402a09209acSWenyou Yang return ret;
403a09209acSWenyou Yang
404a09209acSWenyou Yang return 0;
405a09209acSWenyou Yang }
406a09209acSWenyou Yang
act8945a_set_supply_type(struct act8945a_charger * charger,unsigned int * type)40710ca08b0SWenyou Yang static unsigned int act8945a_set_supply_type(struct act8945a_charger *charger,
40810ca08b0SWenyou Yang unsigned int *type)
40910ca08b0SWenyou Yang {
41010ca08b0SWenyou Yang unsigned int status, state;
41110ca08b0SWenyou Yang int ret;
41210ca08b0SWenyou Yang
41310ca08b0SWenyou Yang ret = regmap_read(charger->regmap, ACT8945A_APCH_STATUS, &status);
41410ca08b0SWenyou Yang if (ret < 0)
41510ca08b0SWenyou Yang return ret;
41610ca08b0SWenyou Yang
41710ca08b0SWenyou Yang ret = regmap_read(charger->regmap, ACT8945A_APCH_STATE, &state);
41810ca08b0SWenyou Yang if (ret < 0)
41910ca08b0SWenyou Yang return ret;
42010ca08b0SWenyou Yang
42110ca08b0SWenyou Yang if (status & APCH_STATUS_INDAT) {
42210ca08b0SWenyou Yang if (state & APCH_STATE_ACINSTAT)
42310ca08b0SWenyou Yang *type = POWER_SUPPLY_TYPE_MAINS;
42410ca08b0SWenyou Yang else
42510ca08b0SWenyou Yang *type = POWER_SUPPLY_TYPE_USB;
42610ca08b0SWenyou Yang } else {
42710ca08b0SWenyou Yang *type = POWER_SUPPLY_TYPE_BATTERY;
42810ca08b0SWenyou Yang }
42910ca08b0SWenyou Yang
43010ca08b0SWenyou Yang return 0;
43110ca08b0SWenyou Yang }
43210ca08b0SWenyou Yang
act8945a_work(struct work_struct * work)433a09209acSWenyou Yang static void act8945a_work(struct work_struct *work)
434a09209acSWenyou Yang {
435a09209acSWenyou Yang struct act8945a_charger *charger =
436a09209acSWenyou Yang container_of(work, struct act8945a_charger, work);
437a09209acSWenyou Yang
43810ca08b0SWenyou Yang act8945a_set_supply_type(charger, &charger->desc.type);
43910ca08b0SWenyou Yang
440a09209acSWenyou Yang power_supply_changed(charger->psy);
441a09209acSWenyou Yang }
442a09209acSWenyou Yang
act8945a_status_changed(int irq,void * dev_id)443a09209acSWenyou Yang static irqreturn_t act8945a_status_changed(int irq, void *dev_id)
444a09209acSWenyou Yang {
445a09209acSWenyou Yang struct act8945a_charger *charger = dev_id;
446a09209acSWenyou Yang
447a09209acSWenyou Yang if (charger->init_done)
448a09209acSWenyou Yang schedule_work(&charger->work);
449a09209acSWenyou Yang
450a09209acSWenyou Yang return IRQ_HANDLED;
451a09209acSWenyou Yang }
452a09209acSWenyou Yang
4535c0e09e0SWenyou Yang #define DEFAULT_TOTAL_TIME_OUT 3
4545c0e09e0SWenyou Yang #define DEFAULT_PRE_TIME_OUT 40
4555c0e09e0SWenyou Yang #define DEFAULT_INPUT_OVP_THRESHOLD 6600
4565c0e09e0SWenyou Yang
act8945a_charger_config(struct device * dev,struct act8945a_charger * charger)4575c0e09e0SWenyou Yang static int act8945a_charger_config(struct device *dev,
4585c0e09e0SWenyou Yang struct act8945a_charger *charger)
4595c0e09e0SWenyou Yang {
4605c0e09e0SWenyou Yang struct device_node *np = dev->of_node;
4615c0e09e0SWenyou Yang struct regmap *regmap = charger->regmap;
4625c0e09e0SWenyou Yang
4635c0e09e0SWenyou Yang u32 total_time_out;
4645c0e09e0SWenyou Yang u32 pre_time_out;
4655c0e09e0SWenyou Yang u32 input_voltage_threshold;
466369eba09SWenyou Yang int err, ret;
4675c0e09e0SWenyou Yang
4681f0ba406SWenyou Yang unsigned int tmp;
4695c0e09e0SWenyou Yang unsigned int value = 0;
4705c0e09e0SWenyou Yang
4715c0e09e0SWenyou Yang if (!np) {
4725c0e09e0SWenyou Yang dev_err(dev, "no charger of node\n");
4735c0e09e0SWenyou Yang return -EINVAL;
4745c0e09e0SWenyou Yang }
4755c0e09e0SWenyou Yang
4761f0ba406SWenyou Yang ret = regmap_read(regmap, ACT8945A_APCH_CFG, &tmp);
4771f0ba406SWenyou Yang if (ret)
4781f0ba406SWenyou Yang return ret;
4791f0ba406SWenyou Yang
4801f0ba406SWenyou Yang if (tmp & APCH_CFG_SUSCHG) {
4811f0ba406SWenyou Yang value |= APCH_CFG_SUSCHG;
4821f0ba406SWenyou Yang dev_info(dev, "have been suspended\n");
4831f0ba406SWenyou Yang }
4841f0ba406SWenyou Yang
485369eba09SWenyou Yang charger->lbo_gpio = devm_gpiod_get_optional(dev, "active-semi,lbo",
486369eba09SWenyou Yang GPIOD_IN);
487369eba09SWenyou Yang if (IS_ERR(charger->lbo_gpio)) {
488369eba09SWenyou Yang err = PTR_ERR(charger->lbo_gpio);
489369eba09SWenyou Yang dev_err(dev, "unable to claim gpio \"lbo\": %d\n", err);
490369eba09SWenyou Yang return err;
491369eba09SWenyou Yang }
492369eba09SWenyou Yang
493369eba09SWenyou Yang ret = devm_request_irq(dev, gpiod_to_irq(charger->lbo_gpio),
494369eba09SWenyou Yang act8945a_status_changed,
495369eba09SWenyou Yang (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING),
496369eba09SWenyou Yang "act8945a_lbo_detect", charger);
497369eba09SWenyou Yang if (ret)
498369eba09SWenyou Yang dev_info(dev, "failed to request gpio \"lbo\" IRQ\n");
499369eba09SWenyou Yang
500528e3504SWenyou Yang charger->chglev_gpio = devm_gpiod_get_optional(dev,
501528e3504SWenyou Yang "active-semi,chglev",
502528e3504SWenyou Yang GPIOD_IN);
503528e3504SWenyou Yang if (IS_ERR(charger->chglev_gpio)) {
504528e3504SWenyou Yang err = PTR_ERR(charger->chglev_gpio);
505528e3504SWenyou Yang dev_err(dev, "unable to claim gpio \"chglev\": %d\n", err);
506528e3504SWenyou Yang return err;
5075c0e09e0SWenyou Yang }
5085c0e09e0SWenyou Yang
5095c0e09e0SWenyou Yang if (of_property_read_u32(np,
5105c0e09e0SWenyou Yang "active-semi,input-voltage-threshold-microvolt",
5115c0e09e0SWenyou Yang &input_voltage_threshold))
5125c0e09e0SWenyou Yang input_voltage_threshold = DEFAULT_INPUT_OVP_THRESHOLD;
5135c0e09e0SWenyou Yang
5145c0e09e0SWenyou Yang if (of_property_read_u32(np,
5155c0e09e0SWenyou Yang "active-semi,precondition-timeout",
5165c0e09e0SWenyou Yang &pre_time_out))
5175c0e09e0SWenyou Yang pre_time_out = DEFAULT_PRE_TIME_OUT;
5185c0e09e0SWenyou Yang
5195c0e09e0SWenyou Yang if (of_property_read_u32(np, "active-semi,total-timeout",
5205c0e09e0SWenyou Yang &total_time_out))
5215c0e09e0SWenyou Yang total_time_out = DEFAULT_TOTAL_TIME_OUT;
5225c0e09e0SWenyou Yang
5235c0e09e0SWenyou Yang switch (input_voltage_threshold) {
5245c0e09e0SWenyou Yang case 8000:
5255c0e09e0SWenyou Yang value |= APCH_CFG_OVPSET_8V;
5265c0e09e0SWenyou Yang break;
5275c0e09e0SWenyou Yang case 7500:
5285c0e09e0SWenyou Yang value |= APCH_CFG_OVPSET_7V5;
5295c0e09e0SWenyou Yang break;
5305c0e09e0SWenyou Yang case 7000:
5315c0e09e0SWenyou Yang value |= APCH_CFG_OVPSET_7V;
5325c0e09e0SWenyou Yang break;
5335c0e09e0SWenyou Yang case 6600:
5345c0e09e0SWenyou Yang default:
5355c0e09e0SWenyou Yang value |= APCH_CFG_OVPSET_6V6;
5365c0e09e0SWenyou Yang break;
5375c0e09e0SWenyou Yang }
5385c0e09e0SWenyou Yang
5395c0e09e0SWenyou Yang switch (pre_time_out) {
5405c0e09e0SWenyou Yang case 60:
5415c0e09e0SWenyou Yang value |= APCH_CFG_PRETIMO_60_MIN;
5425c0e09e0SWenyou Yang break;
5435c0e09e0SWenyou Yang case 80:
5445c0e09e0SWenyou Yang value |= APCH_CFG_PRETIMO_80_MIN;
5455c0e09e0SWenyou Yang break;
5465c0e09e0SWenyou Yang case 0:
5475c0e09e0SWenyou Yang value |= APCH_CFG_PRETIMO_DISABLED;
5485c0e09e0SWenyou Yang break;
5495c0e09e0SWenyou Yang case 40:
5505c0e09e0SWenyou Yang default:
5515c0e09e0SWenyou Yang value |= APCH_CFG_PRETIMO_40_MIN;
5525c0e09e0SWenyou Yang break;
5535c0e09e0SWenyou Yang }
5545c0e09e0SWenyou Yang
5555c0e09e0SWenyou Yang switch (total_time_out) {
5565c0e09e0SWenyou Yang case 4:
5575c0e09e0SWenyou Yang value |= APCH_CFG_TOTTIMO_4_HOUR;
5585c0e09e0SWenyou Yang break;
5595c0e09e0SWenyou Yang case 5:
5605c0e09e0SWenyou Yang value |= APCH_CFG_TOTTIMO_5_HOUR;
5615c0e09e0SWenyou Yang break;
5625c0e09e0SWenyou Yang case 0:
5635c0e09e0SWenyou Yang value |= APCH_CFG_TOTTIMO_DISABLED;
5645c0e09e0SWenyou Yang break;
5655c0e09e0SWenyou Yang case 3:
5665c0e09e0SWenyou Yang default:
5675c0e09e0SWenyou Yang value |= APCH_CFG_TOTTIMO_3_HOUR;
5685c0e09e0SWenyou Yang break;
5695c0e09e0SWenyou Yang }
5705c0e09e0SWenyou Yang
5715c0e09e0SWenyou Yang return regmap_write(regmap, ACT8945A_APCH_CFG, value);
5725c0e09e0SWenyou Yang }
5735c0e09e0SWenyou Yang
act8945a_charger_probe(struct platform_device * pdev)5745c0e09e0SWenyou Yang static int act8945a_charger_probe(struct platform_device *pdev)
5755c0e09e0SWenyou Yang {
5765c0e09e0SWenyou Yang struct act8945a_charger *charger;
5775c0e09e0SWenyou Yang struct power_supply_config psy_cfg = {};
578a09209acSWenyou Yang int irq, ret;
5795c0e09e0SWenyou Yang
5805c0e09e0SWenyou Yang charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
5815c0e09e0SWenyou Yang if (!charger)
5825c0e09e0SWenyou Yang return -ENOMEM;
5835c0e09e0SWenyou Yang
5845c0e09e0SWenyou Yang charger->regmap = dev_get_regmap(pdev->dev.parent, NULL);
5855c0e09e0SWenyou Yang if (!charger->regmap) {
5865c0e09e0SWenyou Yang dev_err(&pdev->dev, "Parent did not provide regmap\n");
5875c0e09e0SWenyou Yang return -EINVAL;
5885c0e09e0SWenyou Yang }
5895c0e09e0SWenyou Yang
5905da643b2SWenyou Yang ret = act8945a_charger_config(&pdev->dev, charger);
5915c0e09e0SWenyou Yang if (ret)
5925c0e09e0SWenyou Yang return ret;
5935c0e09e0SWenyou Yang
594a09209acSWenyou Yang irq = of_irq_get(pdev->dev.of_node, 0);
595648b8ebaSSergei Shtylyov if (irq <= 0) {
596a09209acSWenyou Yang dev_err(&pdev->dev, "failed to find IRQ number\n");
597648b8ebaSSergei Shtylyov return irq ?: -ENXIO;
598a09209acSWenyou Yang }
599a09209acSWenyou Yang
600a09209acSWenyou Yang ret = devm_request_irq(&pdev->dev, irq, act8945a_status_changed,
601a09209acSWenyou Yang IRQF_TRIGGER_FALLING, "act8945a_interrupt",
602a09209acSWenyou Yang charger);
603a09209acSWenyou Yang if (ret) {
604a09209acSWenyou Yang dev_err(&pdev->dev, "failed to request nIRQ pin IRQ\n");
605a09209acSWenyou Yang return ret;
606a09209acSWenyou Yang }
607a09209acSWenyou Yang
60810ca08b0SWenyou Yang charger->desc.name = "act8945a-charger";
60910ca08b0SWenyou Yang charger->desc.get_property = act8945a_charger_get_property;
61010ca08b0SWenyou Yang charger->desc.properties = act8945a_charger_props;
61110ca08b0SWenyou Yang charger->desc.num_properties = ARRAY_SIZE(act8945a_charger_props);
61210ca08b0SWenyou Yang
61310ca08b0SWenyou Yang ret = act8945a_set_supply_type(charger, &charger->desc.type);
61410ca08b0SWenyou Yang if (ret)
61510ca08b0SWenyou Yang return -EINVAL;
61610ca08b0SWenyou Yang
617*46d0c03cSSebastian Reichel psy_cfg.fwnode = dev_fwnode(&pdev->dev);
6185c0e09e0SWenyou Yang psy_cfg.drv_data = charger;
6195c0e09e0SWenyou Yang
620a09209acSWenyou Yang charger->psy = devm_power_supply_register(&pdev->dev,
62110ca08b0SWenyou Yang &charger->desc,
6225c0e09e0SWenyou Yang &psy_cfg);
623a09209acSWenyou Yang if (IS_ERR(charger->psy)) {
6245c0e09e0SWenyou Yang dev_err(&pdev->dev, "failed to register power supply\n");
625a09209acSWenyou Yang return PTR_ERR(charger->psy);
6265c0e09e0SWenyou Yang }
6275c0e09e0SWenyou Yang
628a09209acSWenyou Yang platform_set_drvdata(pdev, charger);
629a09209acSWenyou Yang
630a09209acSWenyou Yang INIT_WORK(&charger->work, act8945a_work);
631a09209acSWenyou Yang
632a09209acSWenyou Yang ret = act8945a_enable_interrupt(charger);
633a09209acSWenyou Yang if (ret)
634a09209acSWenyou Yang return -EIO;
635a09209acSWenyou Yang
636a09209acSWenyou Yang charger->init_done = true;
637a09209acSWenyou Yang
638a09209acSWenyou Yang return 0;
639a09209acSWenyou Yang }
640a09209acSWenyou Yang
act8945a_charger_remove(struct platform_device * pdev)6417a9a4966SUwe Kleine-König static void act8945a_charger_remove(struct platform_device *pdev)
642a09209acSWenyou Yang {
643a09209acSWenyou Yang struct act8945a_charger *charger = platform_get_drvdata(pdev);
644a09209acSWenyou Yang
645a09209acSWenyou Yang charger->init_done = false;
646a09209acSWenyou Yang cancel_work_sync(&charger->work);
6475c0e09e0SWenyou Yang }
6485c0e09e0SWenyou Yang
6495c0e09e0SWenyou Yang static struct platform_driver act8945a_charger_driver = {
6505c0e09e0SWenyou Yang .driver = {
6515c0e09e0SWenyou Yang .name = "act8945a-charger",
6525c0e09e0SWenyou Yang },
6535c0e09e0SWenyou Yang .probe = act8945a_charger_probe,
65483bce344SUwe Kleine-König .remove = act8945a_charger_remove,
6555c0e09e0SWenyou Yang };
6565c0e09e0SWenyou Yang module_platform_driver(act8945a_charger_driver);
6575c0e09e0SWenyou Yang
6585c0e09e0SWenyou Yang MODULE_DESCRIPTION("Active-semi ACT8945A ActivePath charger driver");
6595c0e09e0SWenyou Yang MODULE_AUTHOR("Wenyou Yang <wenyou.yang@atmel.com>");
6605c0e09e0SWenyou Yang MODULE_LICENSE("GPL");
661