aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRhyland Klein <rklein@nvidia.com>2013-03-12 18:08:09 -0400
committerAnton Vorontsov <anton@enomsg.org>2013-03-18 22:20:05 -0400
commit6f8da5df8c451103e0043f73a00c90676da6be9e (patch)
tree3e791e4a8c081279f8da239ef7f5a72ce0909617
parent78f7bcedf8ba70027e0f9f94ec420998a273a95c (diff)
power_supply: Add support for tps65090-charger
This patch adds support for the tps65090 charger driver. This driver is responsible for controlling the charger aspect of the tps65090 mfd. Currently, this mainly consists of turning on and off the charger, but some features of the charger can be supported through this driver including: - Enable Auto Recharge based on Battery voltage - Fast Charge Safety Timer - Maximum battery discharge current - Maximum battery adapter current - Enable External Charge - Disable charging termination based on low charger current (supported) Once the driver is accepted, later patches can add support for the features above which are not yet supported. Based on work by: Syed Rafiuddin <srafiuddin@nvidia.com> Laxman Dewangan <ldewangan@nvidia.com> Signed-off-by: Rhyland Klein <rklein@nvidia.com> Signed-off-by: Anton Vorontsov <anton@enomsg.org>
-rw-r--r--drivers/power/Kconfig7
-rw-r--r--drivers/power/Makefile1
-rw-r--r--drivers/power/tps65090-charger.c315
-rw-r--r--include/linux/mfd/tps65090.h5
4 files changed, 328 insertions, 0 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 07e1a8f8d03e..339f802b91c1 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -340,6 +340,13 @@ config CHARGER_SMB347
340 Say Y to include support for Summit Microelectronics SMB347 340 Say Y to include support for Summit Microelectronics SMB347
341 Battery Charger. 341 Battery Charger.
342 342
343config CHARGER_TPS65090
344 tristate "TPS65090 battery charger driver"
345 depends on MFD_TPS65090
346 help
347 Say Y here to enable support for battery charging with TPS65090
348 PMIC chips.
349
343config AB8500_BM 350config AB8500_BM
344 bool "AB8500 Battery Management Driver" 351 bool "AB8500 Battery Management Driver"
345 depends on AB8500_CORE && AB8500_GPADC 352 depends on AB8500_CORE && AB8500_GPADC
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index eb520ea74970..653bf6ceff30 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -52,4 +52,5 @@ obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
52obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o 52obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o
53obj-$(CONFIG_POWER_AVS) += avs/ 53obj-$(CONFIG_POWER_AVS) += avs/
54obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o 54obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o
55obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
55obj-$(CONFIG_POWER_RESET) += reset/ 56obj-$(CONFIG_POWER_RESET) += reset/
diff --git a/drivers/power/tps65090-charger.c b/drivers/power/tps65090-charger.c
new file mode 100644
index 000000000000..0c66c6656b13
--- /dev/null
+++ b/drivers/power/tps65090-charger.c
@@ -0,0 +1,315 @@
1/*
2 * Battery charger driver for TI's tps65090
3 *
4 * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
5
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18#include <linux/err.h>
19#include <linux/init.h>
20#include <linux/interrupt.h>
21#include <linux/kernel.h>
22#include <linux/module.h>
23#include <linux/slab.h>
24#include <linux/delay.h>
25#include <linux/platform_device.h>
26#include <linux/power_supply.h>
27#include <linux/mfd/tps65090.h>
28
29#define TPS65090_REG_INTR_STS 0x00
30#define TPS65090_REG_CG_CTRL0 0x04
31#define TPS65090_REG_CG_CTRL1 0x05
32#define TPS65090_REG_CG_CTRL2 0x06
33#define TPS65090_REG_CG_CTRL3 0x07
34#define TPS65090_REG_CG_CTRL4 0x08
35#define TPS65090_REG_CG_CTRL5 0x09
36#define TPS65090_REG_CG_STATUS1 0x0a
37#define TPS65090_REG_CG_STATUS2 0x0b
38
39#define TPS65090_CHARGER_ENABLE BIT(0)
40#define TPS65090_VACG BIT(1)
41#define TPS65090_NOITERM BIT(5)
42
43struct tps65090_charger {
44 struct device *dev;
45 int ac_online;
46 int prev_ac_online;
47 int irq;
48 struct power_supply ac;
49 struct tps65090_platform_data *pdata;
50};
51
52static enum power_supply_property tps65090_ac_props[] = {
53 POWER_SUPPLY_PROP_ONLINE,
54};
55
56static int tps65090_low_chrg_current(struct tps65090_charger *charger)
57{
58 int ret;
59
60 ret = tps65090_write(charger->dev->parent, TPS65090_REG_CG_CTRL5,
61 TPS65090_NOITERM);
62 if (ret < 0) {
63 dev_err(charger->dev, "%s(): error reading in register 0x%x\n",
64 __func__, TPS65090_REG_CG_CTRL5);
65 return ret;
66 }
67 return 0;
68}
69
70static int tps65090_enable_charging(struct tps65090_charger *charger,
71 uint8_t enable)
72{
73 int ret;
74 uint8_t ctrl0 = 0;
75
76 ret = tps65090_read(charger->dev->parent, TPS65090_REG_CG_CTRL0,
77 &ctrl0);
78 if (ret < 0) {
79 dev_err(charger->dev, "%s(): error reading in register 0x%x\n",
80 __func__, TPS65090_REG_CG_CTRL0);
81 return ret;
82 }
83
84 ret = tps65090_write(charger->dev->parent, TPS65090_REG_CG_CTRL0,
85 (ctrl0 | TPS65090_CHARGER_ENABLE));
86 if (ret < 0) {
87 dev_err(charger->dev, "%s(): error reading in register 0x%x\n",
88 __func__, TPS65090_REG_CG_CTRL0);
89 return ret;
90 }
91 return 0;
92}
93
94static int tps65090_config_charger(struct tps65090_charger *charger)
95{
96 int ret;
97
98 if (charger->pdata->enable_low_current_chrg) {
99 ret = tps65090_low_chrg_current(charger);
100 if (ret < 0) {
101 dev_err(charger->dev,
102 "error configuring low charge current\n");
103 return ret;
104 }
105 }
106
107 return 0;
108}
109
110static int tps65090_ac_get_property(struct power_supply *psy,
111 enum power_supply_property psp,
112 union power_supply_propval *val)
113{
114 struct tps65090_charger *charger = container_of(psy,
115 struct tps65090_charger, ac);
116
117 if (psp == POWER_SUPPLY_PROP_ONLINE) {
118 val->intval = charger->ac_online;
119 charger->prev_ac_online = charger->ac_online;
120 return 0;
121 }
122 return -EINVAL;
123}
124
125static irqreturn_t tps65090_charger_isr(int irq, void *dev_id)
126{
127 struct tps65090_charger *charger = dev_id;
128 int ret;
129 uint8_t status1 = 0;
130 uint8_t intrsts = 0;
131
132 ret = tps65090_read(charger->dev->parent, TPS65090_REG_CG_STATUS1,
133 &status1);
134 if (ret < 0) {
135 dev_err(charger->dev, "%s(): Error in reading reg 0x%x\n",
136 __func__, TPS65090_REG_CG_STATUS1);
137 return IRQ_HANDLED;
138 }
139 msleep(75);
140 ret = tps65090_read(charger->dev->parent, TPS65090_REG_INTR_STS,
141 &intrsts);
142 if (ret < 0) {
143 dev_err(charger->dev, "%s(): Error in reading reg 0x%x\n",
144 __func__, TPS65090_REG_INTR_STS);
145 return IRQ_HANDLED;
146 }
147
148 if (intrsts & TPS65090_VACG) {
149 ret = tps65090_enable_charging(charger, 1);
150 if (ret < 0)
151 return IRQ_HANDLED;
152 charger->ac_online = 1;
153 } else {
154 charger->ac_online = 0;
155 }
156
157 if (charger->prev_ac_online != charger->ac_online)
158 power_supply_changed(&charger->ac);
159
160 return IRQ_HANDLED;
161}
162
163#if defined(CONFIG_OF)
164
165#include <linux/of_device.h>
166
167static struct tps65090_platform_data *
168 tps65090_parse_dt_charger_data(struct platform_device *pdev)
169{
170 struct tps65090_platform_data *pdata;
171 struct device_node *np = pdev->dev.parent->of_node;
172 unsigned int prop;
173
174 pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
175 if (!pdata) {
176 dev_err(&pdev->dev, "Memory alloc for tps65090_pdata failed\n");
177 return NULL;
178 }
179
180 prop = of_property_read_bool(np, "ti,enable-low-current-chrg");
181 pdata->enable_low_current_chrg = prop;
182
183 pdata->irq_base = -1;
184
185 return pdata;
186
187}
188#else
189static struct tps65090_platform_data *
190 tps65090_parse_dt_charger_data(struct platform_device *pdev)
191{
192 return NULL;
193}
194#endif
195
196static int tps65090_charger_probe(struct platform_device *pdev)
197{
198 struct tps65090 *tps65090_mfd = dev_get_drvdata(pdev->dev.parent);
199 struct tps65090_charger *cdata;
200 struct tps65090_platform_data *pdata;
201 uint8_t status1 = 0;
202 int ret;
203 int irq;
204
205 pdata = dev_get_platdata(pdev->dev.parent);
206
207 if (!pdata && tps65090_mfd->dev->of_node)
208 pdata = tps65090_parse_dt_charger_data(pdev);
209
210 if (!pdata) {
211 dev_err(&pdev->dev, "%s():no platform data available\n",
212 __func__);
213 return -ENODEV;
214 }
215
216 cdata = devm_kzalloc(&pdev->dev, sizeof(*cdata), GFP_KERNEL);
217 if (!cdata) {
218 dev_err(&pdev->dev, "failed to allocate memory status\n");
219 return -ENOMEM;
220 }
221
222 dev_set_drvdata(&pdev->dev, cdata);
223
224 cdata->dev = &pdev->dev;
225 cdata->pdata = pdata;
226
227 cdata->ac.name = "tps65090-ac";
228 cdata->ac.type = POWER_SUPPLY_TYPE_MAINS;
229 cdata->ac.get_property = tps65090_ac_get_property;
230 cdata->ac.properties = tps65090_ac_props;
231 cdata->ac.num_properties = ARRAY_SIZE(tps65090_ac_props);
232 cdata->ac.supplied_to = pdata->supplied_to;
233 cdata->ac.num_supplicants = pdata->num_supplicants;
234
235 ret = power_supply_register(&pdev->dev, &cdata->ac);
236 if (ret) {
237 dev_err(&pdev->dev, "failed: power supply register\n");
238 return ret;
239 }
240
241 irq = platform_get_irq(pdev, 0);
242 if (irq <= 0) {
243 dev_warn(&pdev->dev, "Unable to get charger irq = %d\n", irq);
244 ret = irq;
245 goto fail_unregister_supply;
246 }
247
248 cdata->irq = irq;
249
250 ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
251 tps65090_charger_isr, 0, "tps65090-charger", cdata);
252 if (ret) {
253 dev_err(cdata->dev, "Unable to register irq %d err %d\n", irq,
254 ret);
255 goto fail_free_irq;
256 }
257
258 ret = tps65090_config_charger(cdata);
259 if (ret < 0) {
260 dev_err(&pdev->dev, "charger config failed, err %d\n", ret);
261 goto fail_free_irq;
262 }
263
264 /* Check for charger presence */
265 ret = tps65090_read(cdata->dev->parent, TPS65090_REG_CG_STATUS1,
266 &status1);
267 if (ret < 0) {
268 dev_err(cdata->dev, "%s(): Error in reading reg 0x%x", __func__,
269 TPS65090_REG_CG_STATUS1);
270 goto fail_free_irq;
271 }
272
273 if (status1 != 0) {
274 ret = tps65090_enable_charging(cdata, 1);
275 if (ret < 0) {
276 dev_err(cdata->dev, "error enabling charger\n");
277 goto fail_free_irq;
278 }
279 cdata->ac_online = 1;
280 power_supply_changed(&cdata->ac);
281 }
282
283 return 0;
284
285fail_free_irq:
286 devm_free_irq(cdata->dev, irq, cdata);
287fail_unregister_supply:
288 power_supply_unregister(&cdata->ac);
289
290 return ret;
291}
292
293static int tps65090_charger_remove(struct platform_device *pdev)
294{
295 struct tps65090_charger *cdata = dev_get_drvdata(&pdev->dev);
296
297 devm_free_irq(cdata->dev, cdata->irq, cdata);
298 power_supply_unregister(&cdata->ac);
299
300 return 0;
301}
302
303static struct platform_driver tps65090_charger_driver = {
304 .driver = {
305 .name = "tps65090-charger",
306 .owner = THIS_MODULE,
307 },
308 .probe = tps65090_charger_probe,
309 .remove = tps65090_charger_remove,
310};
311module_platform_driver(tps65090_charger_driver);
312
313MODULE_LICENSE("GPL v2");
314MODULE_AUTHOR("Syed Rafiuddin <srafiuddin@nvidia.com>");
315MODULE_DESCRIPTION("tps65090 battery charger driver");
diff --git a/include/linux/mfd/tps65090.h b/include/linux/mfd/tps65090.h
index 6694cf43e8b8..998628a2b08b 100644
--- a/include/linux/mfd/tps65090.h
+++ b/include/linux/mfd/tps65090.h
@@ -86,6 +86,11 @@ struct tps65090_regulator_plat_data {
86 86
87struct tps65090_platform_data { 87struct tps65090_platform_data {
88 int irq_base; 88 int irq_base;
89
90 char **supplied_to;
91 size_t num_supplicants;
92 int enable_low_current_chrg;
93
89 struct tps65090_regulator_plat_data *reg_pdata[TPS65090_REGULATOR_MAX]; 94 struct tps65090_regulator_plat_data *reg_pdata[TPS65090_REGULATOR_MAX];
90}; 95};
91 96