2 * Power supply driver for the Active-semi ACT8945A PMIC
4 * Copyright (C) 2015 Atmel Corporation
6 * Author: Wenyou Yang <wenyou.yang@atmel.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
13 #include <linux/module.h>
15 #include <linux/of_gpio.h>
16 #include <linux/platform_device.h>
17 #include <linux/power_supply.h>
18 #include <linux/regmap.h>
20 static const char *act8945a_charger_model
= "ACT8945A";
21 static const char *act8945a_charger_manufacturer
= "Active-semi";
24 * ACT8945A Charger Register Map
28 #define ACT8945A_APCH_CFG 0x71
29 #define ACT8945A_APCH_STATUS 0x78
30 #define ACT8945A_APCH_CTRL 0x79
31 #define ACT8945A_APCH_STATE 0x7A
33 /* ACT8945A_APCH_CFG */
34 #define APCH_CFG_OVPSET (0x3 << 0)
35 #define APCH_CFG_OVPSET_6V6 (0x0 << 0)
36 #define APCH_CFG_OVPSET_7V (0x1 << 0)
37 #define APCH_CFG_OVPSET_7V5 (0x2 << 0)
38 #define APCH_CFG_OVPSET_8V (0x3 << 0)
39 #define APCH_CFG_PRETIMO (0x3 << 2)
40 #define APCH_CFG_PRETIMO_40_MIN (0x0 << 2)
41 #define APCH_CFG_PRETIMO_60_MIN (0x1 << 2)
42 #define APCH_CFG_PRETIMO_80_MIN (0x2 << 2)
43 #define APCH_CFG_PRETIMO_DISABLED (0x3 << 2)
44 #define APCH_CFG_TOTTIMO (0x3 << 4)
45 #define APCH_CFG_TOTTIMO_3_HOUR (0x0 << 4)
46 #define APCH_CFG_TOTTIMO_4_HOUR (0x1 << 4)
47 #define APCH_CFG_TOTTIMO_5_HOUR (0x2 << 4)
48 #define APCH_CFG_TOTTIMO_DISABLED (0x3 << 4)
49 #define APCH_CFG_SUSCHG (0x1 << 7)
51 #define APCH_STATUS_CHGDAT BIT(0)
52 #define APCH_STATUS_INDAT BIT(1)
53 #define APCH_STATUS_TEMPDAT BIT(2)
54 #define APCH_STATUS_TIMRDAT BIT(3)
55 #define APCH_STATUS_CHGSTAT BIT(4)
56 #define APCH_STATUS_INSTAT BIT(5)
57 #define APCH_STATUS_TEMPSTAT BIT(6)
58 #define APCH_STATUS_TIMRSTAT BIT(7)
60 #define APCH_CTRL_CHGEOCOUT BIT(0)
61 #define APCH_CTRL_INDIS BIT(1)
62 #define APCH_CTRL_TEMPOUT BIT(2)
63 #define APCH_CTRL_TIMRPRE BIT(3)
64 #define APCH_CTRL_CHGEOCIN BIT(4)
65 #define APCH_CTRL_INCON BIT(5)
66 #define APCH_CTRL_TEMPIN BIT(6)
67 #define APCH_CTRL_TIMRTOT BIT(7)
69 #define APCH_STATE_ACINSTAT (0x1 << 1)
70 #define APCH_STATE_CSTATE (0x3 << 4)
71 #define APCH_STATE_CSTATE_SHIFT 4
72 #define APCH_STATE_CSTATE_DISABLED 0x00
73 #define APCH_STATE_CSTATE_EOC 0x01
74 #define APCH_STATE_CSTATE_FAST 0x02
75 #define APCH_STATE_CSTATE_PRE 0x03
77 struct act8945a_charger
{
78 struct regmap
*regmap
;
79 bool battery_temperature
;
82 static int act8945a_get_charger_state(struct regmap
*regmap
, int *val
)
85 unsigned int status
, state
;
87 ret
= regmap_read(regmap
, ACT8945A_APCH_STATUS
, &status
);
91 ret
= regmap_read(regmap
, ACT8945A_APCH_STATE
, &state
);
95 state
&= APCH_STATE_CSTATE
;
96 state
>>= APCH_STATE_CSTATE_SHIFT
;
98 if (state
== APCH_STATE_CSTATE_EOC
) {
99 if (status
& APCH_STATUS_CHGDAT
)
100 *val
= POWER_SUPPLY_STATUS_FULL
;
102 *val
= POWER_SUPPLY_STATUS_NOT_CHARGING
;
103 } else if ((state
== APCH_STATE_CSTATE_FAST
) ||
104 (state
== APCH_STATE_CSTATE_PRE
)) {
105 *val
= POWER_SUPPLY_STATUS_CHARGING
;
107 *val
= POWER_SUPPLY_STATUS_NOT_CHARGING
;
113 static int act8945a_get_charge_type(struct regmap
*regmap
, int *val
)
118 ret
= regmap_read(regmap
, ACT8945A_APCH_STATE
, &state
);
122 state
&= APCH_STATE_CSTATE
;
123 state
>>= APCH_STATE_CSTATE_SHIFT
;
126 case APCH_STATE_CSTATE_PRE
:
127 *val
= POWER_SUPPLY_CHARGE_TYPE_TRICKLE
;
129 case APCH_STATE_CSTATE_FAST
:
130 *val
= POWER_SUPPLY_CHARGE_TYPE_FAST
;
132 case APCH_STATE_CSTATE_EOC
:
133 case APCH_STATE_CSTATE_DISABLED
:
135 *val
= POWER_SUPPLY_CHARGE_TYPE_NONE
;
141 static int act8945a_get_battery_health(struct act8945a_charger
*charger
,
142 struct regmap
*regmap
, int *val
)
147 ret
= regmap_read(regmap
, ACT8945A_APCH_STATUS
, &status
);
151 if (charger
->battery_temperature
&& !(status
& APCH_STATUS_TEMPDAT
))
152 *val
= POWER_SUPPLY_HEALTH_OVERHEAT
;
153 else if (!(status
& APCH_STATUS_INDAT
))
154 *val
= POWER_SUPPLY_HEALTH_OVERVOLTAGE
;
155 else if (status
& APCH_STATUS_TIMRDAT
)
156 *val
= POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE
;
158 *val
= POWER_SUPPLY_HEALTH_GOOD
;
163 static enum power_supply_property act8945a_charger_props
[] = {
164 POWER_SUPPLY_PROP_STATUS
,
165 POWER_SUPPLY_PROP_CHARGE_TYPE
,
166 POWER_SUPPLY_PROP_TECHNOLOGY
,
167 POWER_SUPPLY_PROP_HEALTH
,
168 POWER_SUPPLY_PROP_MODEL_NAME
,
169 POWER_SUPPLY_PROP_MANUFACTURER
172 static int act8945a_charger_get_property(struct power_supply
*psy
,
173 enum power_supply_property prop
,
174 union power_supply_propval
*val
)
176 struct act8945a_charger
*charger
= power_supply_get_drvdata(psy
);
177 struct regmap
*regmap
= charger
->regmap
;
181 case POWER_SUPPLY_PROP_STATUS
:
182 ret
= act8945a_get_charger_state(regmap
, &val
->intval
);
184 case POWER_SUPPLY_PROP_CHARGE_TYPE
:
185 ret
= act8945a_get_charge_type(regmap
, &val
->intval
);
187 case POWER_SUPPLY_PROP_TECHNOLOGY
:
188 val
->intval
= POWER_SUPPLY_TECHNOLOGY_LION
;
190 case POWER_SUPPLY_PROP_HEALTH
:
191 ret
= act8945a_get_battery_health(charger
,
192 regmap
, &val
->intval
);
194 case POWER_SUPPLY_PROP_MODEL_NAME
:
195 val
->strval
= act8945a_charger_model
;
197 case POWER_SUPPLY_PROP_MANUFACTURER
:
198 val
->strval
= act8945a_charger_manufacturer
;
207 static const struct power_supply_desc act8945a_charger_desc
= {
208 .name
= "act8945a-charger",
209 .type
= POWER_SUPPLY_TYPE_BATTERY
,
210 .get_property
= act8945a_charger_get_property
,
211 .properties
= act8945a_charger_props
,
212 .num_properties
= ARRAY_SIZE(act8945a_charger_props
),
215 #define DEFAULT_TOTAL_TIME_OUT 3
216 #define DEFAULT_PRE_TIME_OUT 40
217 #define DEFAULT_INPUT_OVP_THRESHOLD 6600
219 static int act8945a_charger_config(struct device
*dev
,
220 struct act8945a_charger
*charger
)
222 struct device_node
*np
= dev
->of_node
;
223 enum of_gpio_flags flags
;
224 struct regmap
*regmap
= charger
->regmap
;
228 u32 input_voltage_threshold
;
231 unsigned int value
= 0;
234 dev_err(dev
, "no charger of node\n");
238 charger
->battery_temperature
= of_property_read_bool(np
,
239 "active-semi,check-battery-temperature");
241 chglev_pin
= of_get_named_gpio_flags(np
,
242 "active-semi,chglev-gpios", 0, &flags
);
244 if (gpio_is_valid(chglev_pin
)) {
245 gpio_set_value(chglev_pin
,
246 ((flags
== OF_GPIO_ACTIVE_LOW
) ? 0 : 1));
249 if (of_property_read_u32(np
,
250 "active-semi,input-voltage-threshold-microvolt",
251 &input_voltage_threshold
))
252 input_voltage_threshold
= DEFAULT_INPUT_OVP_THRESHOLD
;
254 if (of_property_read_u32(np
,
255 "active-semi,precondition-timeout",
257 pre_time_out
= DEFAULT_PRE_TIME_OUT
;
259 if (of_property_read_u32(np
, "active-semi,total-timeout",
261 total_time_out
= DEFAULT_TOTAL_TIME_OUT
;
263 switch (input_voltage_threshold
) {
265 value
|= APCH_CFG_OVPSET_8V
;
268 value
|= APCH_CFG_OVPSET_7V5
;
271 value
|= APCH_CFG_OVPSET_7V
;
275 value
|= APCH_CFG_OVPSET_6V6
;
279 switch (pre_time_out
) {
281 value
|= APCH_CFG_PRETIMO_60_MIN
;
284 value
|= APCH_CFG_PRETIMO_80_MIN
;
287 value
|= APCH_CFG_PRETIMO_DISABLED
;
291 value
|= APCH_CFG_PRETIMO_40_MIN
;
295 switch (total_time_out
) {
297 value
|= APCH_CFG_TOTTIMO_4_HOUR
;
300 value
|= APCH_CFG_TOTTIMO_5_HOUR
;
303 value
|= APCH_CFG_TOTTIMO_DISABLED
;
307 value
|= APCH_CFG_TOTTIMO_3_HOUR
;
311 return regmap_write(regmap
, ACT8945A_APCH_CFG
, value
);
314 static int act8945a_charger_probe(struct platform_device
*pdev
)
316 struct act8945a_charger
*charger
;
317 struct power_supply
*psy
;
318 struct power_supply_config psy_cfg
= {};
321 charger
= devm_kzalloc(&pdev
->dev
, sizeof(*charger
), GFP_KERNEL
);
325 charger
->regmap
= dev_get_regmap(pdev
->dev
.parent
, NULL
);
326 if (!charger
->regmap
) {
327 dev_err(&pdev
->dev
, "Parent did not provide regmap\n");
331 ret
= act8945a_charger_config(pdev
->dev
.parent
, charger
);
335 psy_cfg
.of_node
= pdev
->dev
.parent
->of_node
;
336 psy_cfg
.drv_data
= charger
;
338 psy
= devm_power_supply_register(&pdev
->dev
,
339 &act8945a_charger_desc
,
342 dev_err(&pdev
->dev
, "failed to register power supply\n");
349 static struct platform_driver act8945a_charger_driver
= {
351 .name
= "act8945a-charger",
353 .probe
= act8945a_charger_probe
,
355 module_platform_driver(act8945a_charger_driver
);
357 MODULE_DESCRIPTION("Active-semi ACT8945A ActivePath charger driver");
358 MODULE_AUTHOR("Wenyou Yang <wenyou.yang@atmel.com>");
359 MODULE_LICENSE("GPL");