aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWenyou Yang <wenyou.yang@atmel.com>2016-02-15 03:22:03 -0500
committerSebastian Reichel <sre@kernel.org>2016-02-24 12:31:24 -0500
commit5c0e09e03fb5bc25ecb6a90c2bf89e0abc9f920f (patch)
treec9b5168496c000ea36389f1a572862d5e2fe473f
parentc54e9a2ac2186cd6f38cf502502d6a8ed380bbd6 (diff)
power: act8945a: add charger driver for ACT8945A
This patch adds new driver for Active-semi ACT8945A ActivePath charger (part of ACT8945A MFD driver) providing power supply class information to userspace. The driver can be configured through DT (such as, total timer, precondition timer and input over-voltage threshold). Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com> Reviewed-by: Krzysztof Kozlowski <k.kozlowski@samsung.com> [ add "|| COMPILE_TEST" to MFD_ACT8945A dependency ] Signed-off-by: Sebastian Reichel <sre@kernel.org>
-rw-r--r--drivers/power/Kconfig7
-rw-r--r--drivers/power/Makefile1
-rw-r--r--drivers/power/act8945a_charger.c359
3 files changed, 367 insertions, 0 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 1ddd13cc0c07..421770ddafa3 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -75,6 +75,13 @@ config BATTERY_88PM860X
75 help 75 help
76 Say Y here to enable battery monitor for Marvell 88PM860x chip. 76 Say Y here to enable battery monitor for Marvell 88PM860x chip.
77 77
78config BATTERY_ACT8945A
79 tristate "Active-semi ACT8945A charger driver"
80 depends on MFD_ACT8945A || COMPILE_TEST
81 help
82 Say Y here to enable support for power supply provided by
83 Active-semi ActivePath ACT8945A charger.
84
78config BATTERY_DS2760 85config BATTERY_DS2760
79 tristate "DS2760 battery driver (HP iPAQ & others)" 86 tristate "DS2760 battery driver (HP iPAQ & others)"
80 depends on W1 && W1_SLAVE_DS2760 87 depends on W1 && W1_SLAVE_DS2760
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 0e4eab55f8d7..e46b75d448a5 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_WM8350_POWER) += wm8350_power.o
17obj-$(CONFIG_TEST_POWER) += test_power.o 17obj-$(CONFIG_TEST_POWER) += test_power.o
18 18
19obj-$(CONFIG_BATTERY_88PM860X) += 88pm860x_battery.o 19obj-$(CONFIG_BATTERY_88PM860X) += 88pm860x_battery.o
20obj-$(CONFIG_BATTERY_ACT8945A) += act8945a_charger.o
20obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o 21obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
21obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o 22obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o
22obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o 23obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o
diff --git a/drivers/power/act8945a_charger.c b/drivers/power/act8945a_charger.c
new file mode 100644
index 000000000000..b5c00e45741e
--- /dev/null
+++ b/drivers/power/act8945a_charger.c
@@ -0,0 +1,359 @@
1/*
2 * Power supply driver for the Active-semi ACT8945A PMIC
3 *
4 * Copyright (C) 2015 Atmel Corporation
5 *
6 * Author: Wenyou Yang <wenyou.yang@atmel.com>
7 *
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.
11 *
12 */
13#include <linux/module.h>
14#include <linux/of.h>
15#include <linux/of_gpio.h>
16#include <linux/platform_device.h>
17#include <linux/power_supply.h>
18#include <linux/regmap.h>
19
20static const char *act8945a_charger_model = "ACT8945A";
21static const char *act8945a_charger_manufacturer = "Active-semi";
22
23/**
24 * ACT8945A Charger Register Map
25 */
26
27/* 0x70: Reserved */
28#define ACT8945A_APCH_CFG 0x71
29#define ACT8945A_APCH_STATUS 0x78
30#define ACT8945A_APCH_CTRL 0x79
31#define ACT8945A_APCH_STATE 0x7A
32
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)
50
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)
59
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)
68
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
76
77struct act8945a_charger {
78 struct regmap *regmap;
79 bool battery_temperature;
80};
81
82static int act8945a_get_charger_state(struct regmap *regmap, int *val)
83{
84 int ret;
85 unsigned int status, state;
86
87 ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);
88 if (ret < 0)
89 return ret;
90
91 ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);
92 if (ret < 0)
93 return ret;
94
95 state &= APCH_STATE_CSTATE;
96 state >>= APCH_STATE_CSTATE_SHIFT;
97
98 if (state == APCH_STATE_CSTATE_EOC) {
99 if (status & APCH_STATUS_CHGDAT)
100 *val = POWER_SUPPLY_STATUS_FULL;
101 else
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;
106 } else {
107 *val = POWER_SUPPLY_STATUS_NOT_CHARGING;
108 }
109
110 return 0;
111}
112
113static int act8945a_get_charge_type(struct regmap *regmap, int *val)
114{
115 int ret;
116 unsigned int state;
117
118 ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);
119 if (ret < 0)
120 return ret;
121
122 state &= APCH_STATE_CSTATE;
123 state >>= APCH_STATE_CSTATE_SHIFT;
124
125 switch (state) {
126 case APCH_STATE_CSTATE_PRE:
127 *val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
128 break;
129 case APCH_STATE_CSTATE_FAST:
130 *val = POWER_SUPPLY_CHARGE_TYPE_FAST;
131 break;
132 case APCH_STATE_CSTATE_EOC:
133 case APCH_STATE_CSTATE_DISABLED:
134 default:
135 *val = POWER_SUPPLY_CHARGE_TYPE_NONE;
136 }
137
138 return 0;
139}
140
141static int act8945a_get_battery_health(struct act8945a_charger *charger,
142 struct regmap *regmap, int *val)
143{
144 int ret;
145 unsigned int status;
146
147 ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);
148 if (ret < 0)
149 return ret;
150
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;
157 else
158 *val = POWER_SUPPLY_HEALTH_GOOD;
159
160 return 0;
161}
162
163static 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
170};
171
172static int act8945a_charger_get_property(struct power_supply *psy,
173 enum power_supply_property prop,
174 union power_supply_propval *val)
175{
176 struct act8945a_charger *charger = power_supply_get_drvdata(psy);
177 struct regmap *regmap = charger->regmap;
178 int ret = 0;
179
180 switch (prop) {
181 case POWER_SUPPLY_PROP_STATUS:
182 ret = act8945a_get_charger_state(regmap, &val->intval);
183 break;
184 case POWER_SUPPLY_PROP_CHARGE_TYPE:
185 ret = act8945a_get_charge_type(regmap, &val->intval);
186 break;
187 case POWER_SUPPLY_PROP_TECHNOLOGY:
188 val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
189 break;
190 case POWER_SUPPLY_PROP_HEALTH:
191 ret = act8945a_get_battery_health(charger,
192 regmap, &val->intval);
193 break;
194 case POWER_SUPPLY_PROP_MODEL_NAME:
195 val->strval = act8945a_charger_model;
196 break;
197 case POWER_SUPPLY_PROP_MANUFACTURER:
198 val->strval = act8945a_charger_manufacturer;
199 break;
200 default:
201 return -EINVAL;
202 }
203
204 return ret;
205}
206
207static 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),
213};
214
215#define DEFAULT_TOTAL_TIME_OUT 3
216#define DEFAULT_PRE_TIME_OUT 40
217#define DEFAULT_INPUT_OVP_THRESHOLD 6600
218
219static int act8945a_charger_config(struct device *dev,
220 struct act8945a_charger *charger)
221{
222 struct device_node *np = dev->of_node;
223 enum of_gpio_flags flags;
224 struct regmap *regmap = charger->regmap;
225
226 u32 total_time_out;
227 u32 pre_time_out;
228 u32 input_voltage_threshold;
229 int chglev_pin;
230
231 unsigned int value = 0;
232
233 if (!np) {
234 dev_err(dev, "no charger of node\n");
235 return -EINVAL;
236 }
237
238 charger->battery_temperature = of_property_read_bool(np,
239 "active-semi,check-battery-temperature");
240
241 chglev_pin = of_get_named_gpio_flags(np,
242 "active-semi,chglev-gpios", 0, &flags);
243
244 if (gpio_is_valid(chglev_pin)) {
245 gpio_set_value(chglev_pin,
246 ((flags == OF_GPIO_ACTIVE_LOW) ? 0 : 1));
247 }
248
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;
253
254 if (of_property_read_u32(np,
255 "active-semi,precondition-timeout",
256 &pre_time_out))
257 pre_time_out = DEFAULT_PRE_TIME_OUT;
258
259 if (of_property_read_u32(np, "active-semi,total-timeout",
260 &total_time_out))
261 total_time_out = DEFAULT_TOTAL_TIME_OUT;
262
263 switch (input_voltage_threshold) {
264 case 8000:
265 value |= APCH_CFG_OVPSET_8V;
266 break;
267 case 7500:
268 value |= APCH_CFG_OVPSET_7V5;
269 break;
270 case 7000:
271 value |= APCH_CFG_OVPSET_7V;
272 break;
273 case 6600:
274 default:
275 value |= APCH_CFG_OVPSET_6V6;
276 break;
277 }
278
279 switch (pre_time_out) {
280 case 60:
281 value |= APCH_CFG_PRETIMO_60_MIN;
282 break;
283 case 80:
284 value |= APCH_CFG_PRETIMO_80_MIN;
285 break;
286 case 0:
287 value |= APCH_CFG_PRETIMO_DISABLED;
288 break;
289 case 40:
290 default:
291 value |= APCH_CFG_PRETIMO_40_MIN;
292 break;
293 }
294
295 switch (total_time_out) {
296 case 4:
297 value |= APCH_CFG_TOTTIMO_4_HOUR;
298 break;
299 case 5:
300 value |= APCH_CFG_TOTTIMO_5_HOUR;
301 break;
302 case 0:
303 value |= APCH_CFG_TOTTIMO_DISABLED;
304 break;
305 case 3:
306 default:
307 value |= APCH_CFG_TOTTIMO_3_HOUR;
308 break;
309 }
310
311 return regmap_write(regmap, ACT8945A_APCH_CFG, value);
312}
313
314static int act8945a_charger_probe(struct platform_device *pdev)
315{
316 struct act8945a_charger *charger;
317 struct power_supply *psy;
318 struct power_supply_config psy_cfg = {};
319 int ret;
320
321 charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
322 if (!charger)
323 return -ENOMEM;
324
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");
328 return -EINVAL;
329 }
330
331 ret = act8945a_charger_config(pdev->dev.parent, charger);
332 if (ret)
333 return ret;
334
335 psy_cfg.of_node = pdev->dev.parent->of_node;
336 psy_cfg.drv_data = charger;
337
338 psy = devm_power_supply_register(&pdev->dev,
339 &act8945a_charger_desc,
340 &psy_cfg);
341 if (IS_ERR(psy)) {
342 dev_err(&pdev->dev, "failed to register power supply\n");
343 return PTR_ERR(psy);
344 }
345
346 return 0;
347}
348
349static struct platform_driver act8945a_charger_driver = {
350 .driver = {
351 .name = "act8945a-charger",
352 },
353 .probe = act8945a_charger_probe,
354};
355module_platform_driver(act8945a_charger_driver);
356
357MODULE_DESCRIPTION("Active-semi ACT8945A ActivePath charger driver");
358MODULE_AUTHOR("Wenyou Yang <wenyou.yang@atmel.com>");
359MODULE_LICENSE("GPL");