usb: xhci-plat: properly handle probe deferral for devm_clk_get()
[linux/fpc-iii.git] / drivers / power / act8945a_charger.c
blobb5c00e45741e01a6f57977c355a2bb0108bb3e12
1 /*
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>
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>
20 static const char *act8945a_charger_model = "ACT8945A";
21 static const char *act8945a_charger_manufacturer = "Active-semi";
23 /**
24 * ACT8945A Charger Register Map
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
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)
84 int ret;
85 unsigned int status, state;
87 ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);
88 if (ret < 0)
89 return ret;
91 ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);
92 if (ret < 0)
93 return ret;
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;
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;
110 return 0;
113 static int act8945a_get_charge_type(struct regmap *regmap, int *val)
115 int ret;
116 unsigned int state;
118 ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);
119 if (ret < 0)
120 return ret;
122 state &= APCH_STATE_CSTATE;
123 state >>= APCH_STATE_CSTATE_SHIFT;
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;
138 return 0;
141 static int act8945a_get_battery_health(struct act8945a_charger *charger,
142 struct regmap *regmap, int *val)
144 int ret;
145 unsigned int status;
147 ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);
148 if (ret < 0)
149 return ret;
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;
160 return 0;
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;
178 int ret = 0;
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;
204 return ret;
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;
226 u32 total_time_out;
227 u32 pre_time_out;
228 u32 input_voltage_threshold;
229 int chglev_pin;
231 unsigned int value = 0;
233 if (!np) {
234 dev_err(dev, "no charger of node\n");
235 return -EINVAL;
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",
256 &pre_time_out))
257 pre_time_out = DEFAULT_PRE_TIME_OUT;
259 if (of_property_read_u32(np, "active-semi,total-timeout",
260 &total_time_out))
261 total_time_out = DEFAULT_TOTAL_TIME_OUT;
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;
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;
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;
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 = {};
319 int ret;
321 charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
322 if (!charger)
323 return -ENOMEM;
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;
331 ret = act8945a_charger_config(pdev->dev.parent, charger);
332 if (ret)
333 return ret;
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,
340 &psy_cfg);
341 if (IS_ERR(psy)) {
342 dev_err(&pdev->dev, "failed to register power supply\n");
343 return PTR_ERR(psy);
346 return 0;
349 static struct platform_driver act8945a_charger_driver = {
350 .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");