2 * Power supply driver for the Active-semi ACT8945A PMIC
4 * Copyright (C) 2015 Atmel Corporation
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");
359 MODULE_LICENSE("GPL");