blob: 2da4e54e128f810045cd24fb30750aeba80b054b [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/i2c.h>
15#include <linux/gpio.h>
16#include <linux/errno.h>
17#include <linux/delay.h>
18#include <linux/module.h>
19#include <linux/debugfs.h>
20#include <linux/workqueue.h>
21#include <linux/interrupt.h>
22#include <linux/msm-charger.h>
23#include <linux/slab.h>
24#include <linux/i2c/isl9519.h>
25#include <linux/msm_adc.h>
26
27#define CHG_CURRENT_REG 0x14
28#define MAX_SYS_VOLTAGE_REG 0x15
29#define CONTROL_REG 0x3D
30#define MIN_SYS_VOLTAGE_REG 0x3E
31#define INPUT_CURRENT_REG 0x3F
32#define MANUFACTURER_ID_REG 0xFE
33#define DEVICE_ID_REG 0xFF
34
35#define TRCKL_CHG_STATUS_BIT 0x80
36
37#define ISL9519_CHG_PERIOD ((HZ) * 150)
38
39struct isl9519q_struct {
40 struct i2c_client *client;
41 struct delayed_work charge_work;
42 int present;
43 int batt_present;
44 bool charging;
45 int chgcurrent;
46 int term_current;
47 int input_current;
48 int max_system_voltage;
49 int min_system_voltage;
50 int valid_n_gpio;
51 struct dentry *dent;
52 struct msm_hardware_charger adapter_hw_chg;
53};
54
55static int isl9519q_read_reg(struct i2c_client *client, int reg,
56 u16 *val)
57{
58 int ret;
59 struct isl9519q_struct *isl_chg;
60
61 isl_chg = i2c_get_clientdata(client);
62 ret = i2c_smbus_read_word_data(isl_chg->client, reg);
63
64 if (ret < 0) {
65 dev_err(&isl_chg->client->dev,
66 "i2c read fail: can't read from %02x: %d\n", reg, ret);
67 return -EAGAIN;
68 } else
69 *val = ret;
70
71 return 0;
72}
73
74static int isl9519q_write_reg(struct i2c_client *client, int reg,
75 u16 val)
76{
77 int ret;
78 struct isl9519q_struct *isl_chg;
79
80 isl_chg = i2c_get_clientdata(client);
81 ret = i2c_smbus_write_word_data(isl_chg->client, reg, val);
82
83 if (ret < 0) {
84 dev_err(&isl_chg->client->dev,
85 "i2c write fail: can't write %02x to %02x: %d\n",
86 val, reg, ret);
87 return -EAGAIN;
88 }
89 return 0;
90}
91
92static int isl_read_adc(int channel, int *mv_reading)
93{
94 int ret;
95 void *h;
96 struct adc_chan_result adc_chan_result;
97 struct completion conv_complete_evt;
98
99 pr_debug("%s: called for %d\n", __func__, channel);
100 ret = adc_channel_open(channel, &h);
101 if (ret) {
102 pr_err("%s: couldnt open channel %d ret=%d\n",
103 __func__, channel, ret);
104 goto out;
105 }
106 init_completion(&conv_complete_evt);
107 ret = adc_channel_request_conv(h, &conv_complete_evt);
108 if (ret) {
109 pr_err("%s: couldnt request conv channel %d ret=%d\n",
110 __func__, channel, ret);
111 goto out;
112 }
113 ret = wait_for_completion_interruptible(&conv_complete_evt);
114 if (ret) {
115 pr_err("%s: wait interrupted channel %d ret=%d\n",
116 __func__, channel, ret);
117 goto out;
118 }
119 ret = adc_channel_read_result(h, &adc_chan_result);
120 if (ret) {
121 pr_err("%s: couldnt read result channel %d ret=%d\n",
122 __func__, channel, ret);
123 goto out;
124 }
125 ret = adc_channel_close(h);
126 if (ret)
127 pr_err("%s: couldnt close channel %d ret=%d\n",
128 __func__, channel, ret);
129 if (mv_reading)
130 *mv_reading = (int)adc_chan_result.measurement;
131
132 pr_debug("%s: done for %d\n", __func__, channel);
133 return adc_chan_result.physical;
134out:
135 *mv_reading = 0;
136 pr_debug("%s: done with error for %d\n", __func__, channel);
137 return -EINVAL;
138
139}
140
141static void isl9519q_charge(struct work_struct *isl9519_work)
142{
143 u16 temp;
144 int ret;
145 struct isl9519q_struct *isl_chg;
146 int isl_charger_current;
147 int mv_reading;
148
149 isl_chg = container_of(isl9519_work, struct isl9519q_struct,
150 charge_work.work);
151
152 dev_dbg(&isl_chg->client->dev, "%s\n", __func__);
153
154 if (isl_chg->charging) {
155 isl_charger_current = isl_read_adc(CHANNEL_ADC_BATT_AMON,
156 &mv_reading);
157 dev_dbg(&isl_chg->client->dev, "%s mv_reading=%d\n",
158 __func__, mv_reading);
159 dev_dbg(&isl_chg->client->dev, "%s isl_charger_current=%d\n",
160 __func__, isl_charger_current);
161 if (isl_charger_current >= 0
162 && isl_charger_current <= isl_chg->term_current) {
163 msm_charger_notify_event(
164 &isl_chg->adapter_hw_chg,
165 CHG_DONE_EVENT);
166 }
167 isl9519q_write_reg(isl_chg->client, CHG_CURRENT_REG,
168 isl_chg->chgcurrent);
169 ret = isl9519q_read_reg(isl_chg->client, CONTROL_REG, &temp);
170 if (!ret) {
171 if (!(temp & TRCKL_CHG_STATUS_BIT))
172 msm_charger_notify_event(
173 &isl_chg->adapter_hw_chg,
174 CHG_BATT_BEGIN_FAST_CHARGING);
175 } else {
176 dev_err(&isl_chg->client->dev,
177 "%s couldnt read cntrl reg\n", __func__);
178 }
179 schedule_delayed_work(&isl_chg->charge_work,
180 ISL9519_CHG_PERIOD);
181 }
182}
183
184static int isl9519q_start_charging(struct msm_hardware_charger *hw_chg,
185 int chg_voltage, int chg_current)
186{
187 struct isl9519q_struct *isl_chg;
188 int ret = 0;
189
190 isl_chg = container_of(hw_chg, struct isl9519q_struct, adapter_hw_chg);
191 if (isl_chg->charging)
192 /* we are already charging */
193 return 0;
194
195 dev_dbg(&isl_chg->client->dev, "%s\n", __func__);
196
197 ret = isl9519q_write_reg(isl_chg->client, CHG_CURRENT_REG,
198 isl_chg->chgcurrent);
199 if (ret) {
200 dev_err(&isl_chg->client->dev,
201 "%s coulnt write to current_reg\n", __func__);
202 goto out;
203 }
204
205 dev_dbg(&isl_chg->client->dev, "%s starting timed work\n",
206 __func__);
207 schedule_delayed_work(&isl_chg->charge_work,
208 ISL9519_CHG_PERIOD);
209 isl_chg->charging = true;
210
211out:
212 return ret;
213}
214
215static int isl9519q_stop_charging(struct msm_hardware_charger *hw_chg)
216{
217 struct isl9519q_struct *isl_chg;
218 int ret = 0;
219
220 isl_chg = container_of(hw_chg, struct isl9519q_struct, adapter_hw_chg);
221 if (!(isl_chg->charging))
222 /* we arent charging */
223 return 0;
224
225 dev_dbg(&isl_chg->client->dev, "%s\n", __func__);
226
227 ret = isl9519q_write_reg(isl_chg->client, CHG_CURRENT_REG, 0);
228 if (ret) {
229 dev_err(&isl_chg->client->dev,
230 "%s coulnt write to current_reg\n", __func__);
231 goto out;
232 }
233
234 isl_chg->charging = false;
235 cancel_delayed_work(&isl_chg->charge_work);
236out:
237 return ret;
238}
239
240static int isl9519q_charging_switched(struct msm_hardware_charger *hw_chg)
241{
242 struct isl9519q_struct *isl_chg;
243
244 isl_chg = container_of(hw_chg, struct isl9519q_struct, adapter_hw_chg);
245 dev_dbg(&isl_chg->client->dev, "%s\n", __func__);
246 return 0;
247}
248
249static irqreturn_t isl_valid_handler(int irq, void *dev_id)
250{
251 int val;
252 struct isl9519q_struct *isl_chg;
253 struct i2c_client *client = dev_id;
254
255 isl_chg = i2c_get_clientdata(client);
256 val = gpio_get_value_cansleep(isl_chg->valid_n_gpio);
257 if (val < 0) {
258 dev_err(&isl_chg->client->dev,
259 "%s gpio_get_value failed for %d ret=%d\n", __func__,
260 isl_chg->valid_n_gpio, val);
261 goto err;
262 }
263 dev_dbg(&isl_chg->client->dev, "%s val=%d\n", __func__, val);
264
265 if (val) {
266 if (isl_chg->present == 1) {
267 msm_charger_notify_event(&isl_chg->adapter_hw_chg,
268 CHG_REMOVED_EVENT);
269 isl_chg->present = 0;
270 }
271 } else {
272 if (isl_chg->present == 0) {
273 msm_charger_notify_event(&isl_chg->adapter_hw_chg,
274 CHG_INSERTED_EVENT);
275 isl_chg->present = 1;
276 }
277 }
278err:
279 return IRQ_HANDLED;
280}
281
282#define MAX_VOLTAGE_REG_MASK 0x3FF0
283#define MIN_VOLTAGE_REG_MASK 0x3F00
284#define DEFAULT_MAX_VOLTAGE_REG_VALUE 0x1070
285#define DEFAULT_MIN_VOLTAGE_REG_VALUE 0x0D00
286
287static int __devinit isl9519q_probe(struct i2c_client *client,
288 const struct i2c_device_id *id)
289{
290 struct isl_platform_data *pdata;
291 struct isl9519q_struct *isl_chg;
292 int ret;
293
294 ret = 0;
295 pdata = client->dev.platform_data;
296
297 if (pdata == NULL) {
298 dev_err(&client->dev, "%s no platform data\n", __func__);
299 ret = -EINVAL;
300 goto out;
301 }
302
303 if (!i2c_check_functionality(client->adapter,
304 I2C_FUNC_SMBUS_WORD_DATA)) {
305 ret = -EIO;
306 goto out;
307 }
308
309 isl_chg = kzalloc(sizeof(*isl_chg), GFP_KERNEL);
310 if (!isl_chg) {
311 ret = -ENOMEM;
312 goto out;
313 }
314
315 INIT_DELAYED_WORK(&isl_chg->charge_work, isl9519q_charge);
316 isl_chg->client = client;
317 isl_chg->chgcurrent = pdata->chgcurrent;
318 isl_chg->term_current = pdata->term_current;
319 isl_chg->input_current = pdata->input_current;
320 isl_chg->max_system_voltage = pdata->max_system_voltage;
321 isl_chg->min_system_voltage = pdata->min_system_voltage;
322 isl_chg->valid_n_gpio = pdata->valid_n_gpio;
323
324 /* h/w ignores lower 7 bits of charging current and input current */
325 isl_chg->chgcurrent &= ~0x7F;
326 isl_chg->input_current &= ~0x7F;
327
328 isl_chg->adapter_hw_chg.type = CHG_TYPE_AC;
329 isl_chg->adapter_hw_chg.rating = 2;
330 isl_chg->adapter_hw_chg.name = "isl-adapter";
331 isl_chg->adapter_hw_chg.start_charging = isl9519q_start_charging;
332 isl_chg->adapter_hw_chg.stop_charging = isl9519q_stop_charging;
333 isl_chg->adapter_hw_chg.charging_switched = isl9519q_charging_switched;
334
335 if (pdata->chg_detection_config) {
336 ret = pdata->chg_detection_config();
337 if (ret) {
338 dev_err(&client->dev, "%s valid config failed ret=%d\n",
339 __func__, ret);
340 goto free_isl_chg;
341 }
342 }
343
344 ret = gpio_request(pdata->valid_n_gpio, "isl_charger_valid");
345 if (ret) {
346 dev_err(&client->dev, "%s gpio_request failed for %d ret=%d\n",
347 __func__, pdata->valid_n_gpio, ret);
348 goto free_isl_chg;
349 }
350
351 i2c_set_clientdata(client, isl_chg);
352
353 ret = msm_charger_register(&isl_chg->adapter_hw_chg);
354 if (ret) {
355 dev_err(&client->dev,
356 "%s msm_charger_register failed for ret =%d\n",
357 __func__, ret);
358 goto free_gpio;
359 }
360
361 ret = request_threaded_irq(client->irq, NULL,
362 isl_valid_handler,
363 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
364 "isl_charger_valid", client);
365 if (ret) {
366 dev_err(&client->dev,
367 "%s request_threaded_irq failed for %d ret =%d\n",
368 __func__, client->irq, ret);
369 goto unregister;
370 }
371 irq_set_irq_wake(client->irq, 1);
372
373 isl_chg->max_system_voltage &= MAX_VOLTAGE_REG_MASK;
374 isl_chg->min_system_voltage &= MIN_VOLTAGE_REG_MASK;
375 if (isl_chg->max_system_voltage == 0)
376 isl_chg->max_system_voltage = DEFAULT_MAX_VOLTAGE_REG_VALUE;
377 if (isl_chg->min_system_voltage == 0)
378 isl_chg->min_system_voltage = DEFAULT_MIN_VOLTAGE_REG_VALUE;
379
380 ret = isl9519q_write_reg(isl_chg->client, MAX_SYS_VOLTAGE_REG,
381 isl_chg->max_system_voltage);
382 if (ret) {
383 dev_err(&client->dev,
384 "%s couldnt write to MAX_SYS_VOLTAGE_REG ret=%d\n",
385 __func__, ret);
386 goto free_irq;
387 }
388
389 ret = isl9519q_write_reg(isl_chg->client, MIN_SYS_VOLTAGE_REG,
390 isl_chg->min_system_voltage);
391 if (ret) {
392 dev_err(&client->dev,
393 "%s couldnt write to MIN_SYS_VOLTAGE_REG ret=%d\n",
394 __func__, ret);
395 goto free_irq;
396 }
397
398 if (isl_chg->input_current) {
399 ret = isl9519q_write_reg(isl_chg->client,
400 INPUT_CURRENT_REG,
401 isl_chg->input_current);
402 if (ret) {
403 dev_err(&client->dev,
404 "%s couldnt write INPUT_CURRENT_REG ret=%d\n",
405 __func__, ret);
406 goto free_irq;
407 }
408 }
409
410 ret = gpio_get_value_cansleep(isl_chg->valid_n_gpio);
411 if (ret < 0) {
412 dev_err(&client->dev,
413 "%s gpio_get_value failed for %d ret=%d\n", __func__,
414 pdata->valid_n_gpio, ret);
415 /* assume absent */
416 ret = 1;
417 }
418 if (!ret) {
419 msm_charger_notify_event(&isl_chg->adapter_hw_chg,
420 CHG_INSERTED_EVENT);
421 isl_chg->present = 1;
422 }
423
424 pr_debug("%s OK chg_present=%d\n", __func__, isl_chg->present);
425 return 0;
426
427free_irq:
428 free_irq(client->irq, NULL);
429unregister:
Abhijeet Dharmapurikar9ed4b192011-07-11 12:37:59 -0700430 msm_charger_unregister(&isl_chg->adapter_hw_chg);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700431free_gpio:
432 gpio_free(pdata->valid_n_gpio);
433free_isl_chg:
434 kfree(isl_chg);
435out:
436 return ret;
437}
438
439static int __devexit isl9519q_remove(struct i2c_client *client)
440{
441 struct isl_platform_data *pdata;
442 struct isl9519q_struct *isl_chg = i2c_get_clientdata(client);
443
444 pdata = client->dev.platform_data;
445 gpio_free(pdata->valid_n_gpio);
446 free_irq(client->irq, client);
447 cancel_delayed_work_sync(&isl_chg->charge_work);
448 msm_charger_notify_event(&isl_chg->adapter_hw_chg, CHG_REMOVED_EVENT);
449 msm_charger_unregister(&isl_chg->adapter_hw_chg);
450 return 0;
451}
452
453static const struct i2c_device_id isl9519q_id[] = {
454 {"isl9519q", 0},
455 {},
456};
457
458#ifdef CONFIG_PM
459static int isl9519q_suspend(struct device *dev)
460{
461 struct isl9519q_struct *isl_chg = dev_get_drvdata(dev);
462
463 dev_dbg(&isl_chg->client->dev, "%s\n", __func__);
464 /*
465 * do not suspend while we are charging
466 * because we need to periodically update the register
467 * for charging to proceed
468 */
469 if (isl_chg->charging)
470 return -EBUSY;
471 return 0;
472}
473
474static int isl9519q_resume(struct device *dev)
475{
476 struct isl9519q_struct *isl_chg = dev_get_drvdata(dev);
477
478 dev_dbg(&isl_chg->client->dev, "%s\n", __func__);
479 return 0;
480}
481
482static const struct dev_pm_ops isl9519q_pm_ops = {
483 .suspend = isl9519q_suspend,
484 .resume = isl9519q_resume,
485};
486#endif
487
488static struct i2c_driver isl9519q_driver = {
489 .driver = {
490 .name = "isl9519q",
491 .owner = THIS_MODULE,
492#ifdef CONFIG_PM
493 .pm = &isl9519q_pm_ops,
494#endif
495 },
496 .probe = isl9519q_probe,
497 .remove = __devexit_p(isl9519q_remove),
498 .id_table = isl9519q_id,
499};
500
501static int __init isl9519q_init(void)
502{
503 return i2c_add_driver(&isl9519q_driver);
504}
505
506module_init(isl9519q_init);
507
508static void __exit isl9519q_exit(void)
509{
510 return i2c_del_driver(&isl9519q_driver);
511}
512
513module_exit(isl9519q_exit);
514
515MODULE_AUTHOR("Abhijeet Dharmapurikar <adharmap@codeaurora.org>");
516MODULE_DESCRIPTION("Driver for ISL9519Q Charger chip");
517MODULE_LICENSE("GPL v2");