blob: a4531597f3858fcb3e397e02d306cbe73be6af52 [file] [log] [blame]
Xiaozhe Shi767fdb62013-01-10 15:09:08 -08001/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -07002 *
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#define pr_fmt(fmt) "%s: " fmt, __func__
14
15#include <linux/kernel.h>
16#include <linux/of.h>
17#include <linux/err.h>
18#include <linux/init.h>
19#include <linux/slab.h>
20#include <linux/delay.h>
21#include <linux/mutex.h>
22#include <linux/types.h>
23#include <linux/hwmon.h>
24#include <linux/module.h>
25#include <linux/debugfs.h>
26#include <linux/spmi.h>
27#include <linux/of_irq.h>
28#include <linux/wakelock.h>
29#include <linux/interrupt.h>
30#include <linux/completion.h>
31#include <linux/hwmon-sysfs.h>
32#include <linux/qpnp/qpnp-adc.h>
33#include <linux/platform_device.h>
34
35/* QPNP IADC register definition */
Siddartha Mohanadoss5ace1102012-08-20 23:18:10 -070036#define QPNP_IADC_REVISION1 0x0
37#define QPNP_IADC_REVISION2 0x1
38#define QPNP_IADC_REVISION3 0x2
39#define QPNP_IADC_REVISION4 0x3
40#define QPNP_IADC_PERPH_TYPE 0x4
41#define QPNP_IADC_PERH_SUBTYPE 0x5
42
43#define QPNP_IADC_SUPPORTED_REVISION2 1
44
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -070045#define QPNP_STATUS1 0x8
46#define QPNP_STATUS1_OP_MODE 4
47#define QPNP_STATUS1_MULTI_MEAS_EN BIT(3)
48#define QPNP_STATUS1_MEAS_INTERVAL_EN_STS BIT(2)
49#define QPNP_STATUS1_REQ_STS BIT(1)
50#define QPNP_STATUS1_EOC BIT(0)
51#define QPNP_STATUS2 0x9
52#define QPNP_STATUS2_CONV_SEQ_STATE_SHIFT 4
53#define QPNP_STATUS2_FIFO_NOT_EMPTY_FLAG BIT(1)
54#define QPNP_STATUS2_CONV_SEQ_TIMEOUT_STS BIT(0)
55#define QPNP_CONV_TIMEOUT_ERR 2
56
Siddartha Mohanadoss5ace1102012-08-20 23:18:10 -070057#define QPNP_IADC_MODE_CTL 0x40
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -070058#define QPNP_OP_MODE_SHIFT 4
59#define QPNP_USE_BMS_DATA BIT(4)
60#define QPNP_VADC_SYNCH_EN BIT(2)
61#define QPNP_OFFSET_RMV_EN BIT(1)
62#define QPNP_ADC_TRIM_EN BIT(0)
Siddartha Mohanadoss5ace1102012-08-20 23:18:10 -070063#define QPNP_IADC_EN_CTL1 0x46
64#define QPNP_IADC_ADC_EN BIT(7)
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -070065#define QPNP_ADC_CH_SEL_CTL 0x48
66#define QPNP_ADC_DIG_PARAM 0x50
67#define QPNP_ADC_CLK_SEL_MASK 0x3
68#define QPNP_ADC_DEC_RATIO_SEL_MASK 0xc
69#define QPNP_ADC_DIG_DEC_RATIO_SEL_SHIFT 2
70
71#define QPNP_HW_SETTLE_DELAY 0x51
72#define QPNP_CONV_REQ 0x52
73#define QPNP_CONV_REQ_SET BIT(7)
74#define QPNP_CONV_SEQ_CTL 0x54
75#define QPNP_CONV_SEQ_HOLDOFF_SHIFT 4
76#define QPNP_CONV_SEQ_TRIG_CTL 0x55
77#define QPNP_FAST_AVG_CTL 0x5a
78
79#define QPNP_M0_LOW_THR_LSB 0x5c
80#define QPNP_M0_LOW_THR_MSB 0x5d
81#define QPNP_M0_HIGH_THR_LSB 0x5e
82#define QPNP_M0_HIGH_THR_MSB 0x5f
83#define QPNP_M1_LOW_THR_LSB 0x69
84#define QPNP_M1_LOW_THR_MSB 0x6a
85#define QPNP_M1_HIGH_THR_LSB 0x6b
86#define QPNP_M1_HIGH_THR_MSB 0x6c
87
88#define QPNP_DATA0 0x60
89#define QPNP_DATA1 0x61
90#define QPNP_CONV_TIMEOUT_ERR 2
91
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -070092#define QPNP_IADC_SEC_ACCESS 0xD0
93#define QPNP_IADC_SEC_ACCESS_DATA 0xA5
94#define QPNP_IADC_MSB_OFFSET 0xF2
95#define QPNP_IADC_LSB_OFFSET 0xF3
96#define QPNP_IADC_NOMINAL_RSENSE 0xF4
97#define QPNP_IADC_ATE_GAIN_CALIB_OFFSET 0xF5
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -070098#define QPNP_INT_TEST_VAL 0xE1
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -070099
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700100#define QPNP_IADC_ADC_CH_SEL_CTL 0x48
101#define QPNP_IADC_ADC_CHX_SEL_SHIFT 3
102
103#define QPNP_IADC_ADC_DIG_PARAM 0x50
104#define QPNP_IADC_CLK_SEL_SHIFT 1
105#define QPNP_IADC_DEC_RATIO_SEL 3
106
107#define QPNP_IADC_CONV_REQUEST 0x52
108#define QPNP_IADC_CONV_REQ BIT(7)
109
110#define QPNP_IADC_DATA0 0x60
111#define QPNP_IADC_DATA1 0x61
112
Siddartha Mohanadoss5ace1102012-08-20 23:18:10 -0700113#define QPNP_ADC_CONV_TIME_MIN 8000
114#define QPNP_ADC_CONV_TIME_MAX 8200
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700115
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700116#define QPNP_ADC_GAIN_NV 17857
117#define QPNP_OFFSET_CALIBRATION_SHORT_CADC_LEADS_IDEAL 0
118#define QPNP_IADC_INTERNAL_RSENSE_N_OHMS_FACTOR 10000000
Siddartha Mohanadoss22618be2013-04-02 15:02:19 -0700119#define QPNP_IADC_NANO_VOLTS_FACTOR 1000000
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700120#define QPNP_IADC_CALIB_SECONDS 300000
121#define QPNP_IADC_RSENSE_LSB_N_OHMS_PER_BIT 15625
122#define QPNP_IADC_DIE_TEMP_CALIB_OFFSET 5000
123
124#define QPNP_RAW_CODE_16_BIT_MSB_MASK 0xff00
125#define QPNP_RAW_CODE_16_BIT_LSB_MASK 0xff
126#define QPNP_BIT_SHIFT_8 8
127#define QPNP_RSENSE_MSB_SIGN_CHECK 0x80
Siddartha Mohanadoss1a0d2032012-11-01 11:22:29 -0700128#define QPNP_ADC_COMPLETION_TIMEOUT HZ
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700129
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700130struct qpnp_iadc_comp {
131 bool ext_rsense;
132 u8 id;
133 u8 sys_gain;
134 u8 revision;
135};
136
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700137struct qpnp_iadc_drv {
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700138 struct qpnp_adc_drv *adc;
139 int32_t rsense;
Siddartha Mohanadosse70010b2013-04-04 14:51:41 -0700140 bool external_rsense;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700141 struct device *iadc_hwmon;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700142 bool iadc_initialized;
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700143 int64_t die_temp;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700144 struct delayed_work iadc_work;
Siddartha Mohanadossa32ea2a2013-02-12 09:58:31 -0800145 struct mutex iadc_vadc_lock;
146 bool iadc_mode_sel;
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700147 struct qpnp_iadc_comp iadc_comp;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700148 struct sensor_device_attribute sens_attr[0];
Abhijeet Dharmapurikar84b13dd2013-07-08 18:43:56 -0700149 bool skip_auto_calibrations;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700150};
151
Abhijeet Dharmapurikar84b13dd2013-07-08 18:43:56 -0700152static struct qpnp_iadc_drv *qpnp_iadc;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700153
154static int32_t qpnp_iadc_read_reg(uint32_t reg, u8 *data)
155{
156 struct qpnp_iadc_drv *iadc = qpnp_iadc;
157 int rc;
158
159 rc = spmi_ext_register_readl(iadc->adc->spmi->ctrl, iadc->adc->slave,
Siddartha Mohanadossae1da732012-08-08 16:39:02 -0700160 (iadc->adc->offset + reg), data, 1);
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700161 if (rc < 0) {
162 pr_err("qpnp iadc read reg %d failed with %d\n", reg, rc);
163 return rc;
164 }
165
166 return 0;
167}
168
169static int32_t qpnp_iadc_write_reg(uint32_t reg, u8 data)
170{
171 struct qpnp_iadc_drv *iadc = qpnp_iadc;
172 int rc;
173 u8 *buf;
174
175 buf = &data;
176 rc = spmi_ext_register_writel(iadc->adc->spmi->ctrl, iadc->adc->slave,
Siddartha Mohanadossae1da732012-08-08 16:39:02 -0700177 (iadc->adc->offset + reg), buf, 1);
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700178 if (rc < 0) {
179 pr_err("qpnp iadc write reg %d failed with %d\n", reg, rc);
180 return rc;
181 }
182
183 return 0;
184}
185
Siddartha Mohanadosse2363592012-12-14 18:59:01 -0800186static void trigger_iadc_completion(struct work_struct *work)
187{
188 struct qpnp_iadc_drv *iadc = qpnp_iadc;
189
Siddartha Mohanadossb60f6462012-11-20 18:06:51 -0800190 if (!iadc || !iadc->iadc_initialized)
191 return;
192
Siddartha Mohanadosse2363592012-12-14 18:59:01 -0800193 complete(&iadc->adc->adc_rslt_completion);
194
195 return;
196}
197DECLARE_WORK(trigger_iadc_completion_work, trigger_iadc_completion);
198
199static irqreturn_t qpnp_iadc_isr(int irq, void *dev_id)
200{
201 schedule_work(&trigger_iadc_completion_work);
202
203 return IRQ_HANDLED;
204}
205
206static int32_t qpnp_iadc_enable(bool state)
207{
208 int rc = 0;
209 u8 data = 0;
210
211 data = QPNP_IADC_ADC_EN;
212 if (state) {
213 rc = qpnp_iadc_write_reg(QPNP_IADC_EN_CTL1,
214 data);
215 if (rc < 0) {
216 pr_err("IADC enable failed\n");
217 return rc;
218 }
219 } else {
220 rc = qpnp_iadc_write_reg(QPNP_IADC_EN_CTL1,
221 (~data & QPNP_IADC_ADC_EN));
222 if (rc < 0) {
223 pr_err("IADC disable failed\n");
224 return rc;
225 }
226 }
227
228 return 0;
229}
230
Siddartha Mohanadossd3a3c952012-12-10 16:55:19 -0800231static int32_t qpnp_iadc_status_debug(void)
232{
233 int rc = 0;
234 u8 mode = 0, status1 = 0, chan = 0, dig = 0, en = 0;
235
236 rc = qpnp_iadc_read_reg(QPNP_IADC_MODE_CTL, &mode);
237 if (rc < 0) {
238 pr_err("mode ctl register read failed with %d\n", rc);
239 return rc;
240 }
241
242 rc = qpnp_iadc_read_reg(QPNP_ADC_DIG_PARAM, &dig);
243 if (rc < 0) {
244 pr_err("digital param read failed with %d\n", rc);
245 return rc;
246 }
247
248 rc = qpnp_iadc_read_reg(QPNP_IADC_ADC_CH_SEL_CTL, &chan);
249 if (rc < 0) {
250 pr_err("channel read failed with %d\n", rc);
251 return rc;
252 }
253
254 rc = qpnp_iadc_read_reg(QPNP_STATUS1, &status1);
255 if (rc < 0) {
256 pr_err("status1 read failed with %d\n", rc);
257 return rc;
258 }
259
260 rc = qpnp_iadc_read_reg(QPNP_IADC_EN_CTL1, &en);
261 if (rc < 0) {
262 pr_err("en read failed with %d\n", rc);
263 return rc;
264 }
265
Siddartha Mohanadoss73ae69b2013-04-03 17:34:03 -0700266 pr_debug("EOC not set with status:%x, dig:%x, ch:%x, mode:%x, en:%x\n",
Siddartha Mohanadossd3a3c952012-12-10 16:55:19 -0800267 status1, dig, chan, mode, en);
268
Siddartha Mohanadosse2363592012-12-14 18:59:01 -0800269 rc = qpnp_iadc_enable(false);
270 if (rc < 0) {
271 pr_err("IADC disable failed with %d\n", rc);
272 return rc;
Siddartha Mohanadoss5ace1102012-08-20 23:18:10 -0700273 }
274
275 return 0;
276}
277
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700278static int32_t qpnp_iadc_read_conversion_result(uint16_t *data)
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700279{
280 uint8_t rslt_lsb, rslt_msb;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700281 uint16_t rslt;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700282 int32_t rc;
283
284 rc = qpnp_iadc_read_reg(QPNP_IADC_DATA0, &rslt_lsb);
285 if (rc < 0) {
286 pr_err("qpnp adc result read failed with %d\n", rc);
287 return rc;
288 }
289
290 rc = qpnp_iadc_read_reg(QPNP_IADC_DATA1, &rslt_msb);
291 if (rc < 0) {
292 pr_err("qpnp adc result read failed with %d\n", rc);
293 return rc;
294 }
295
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700296 rslt = (rslt_msb << 8) | rslt_lsb;
297 *data = rslt;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700298
Siddartha Mohanadoss5ace1102012-08-20 23:18:10 -0700299 rc = qpnp_iadc_enable(false);
300 if (rc)
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700301 return rc;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700302
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700303 return 0;
304}
305
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700306static int32_t qpnp_iadc_comp(int64_t *result, struct qpnp_iadc_comp comp,
307 int64_t die_temp)
308{
Xiaozhe Shi62ad5e12013-05-13 12:37:41 -0700309 int64_t temp_var = 0, sign_coeff = 0, sys_gain_coeff = 0, old;
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700310
Xiaozhe Shi62ad5e12013-05-13 12:37:41 -0700311 old = *result;
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700312 *result = *result * 1000000;
313
314 if (comp.revision == QPNP_IADC_VER_3_1) {
315 /* revision 3.1 */
316 if (comp.sys_gain > 127)
317 sys_gain_coeff = -QPNP_COEFF_6 * (comp.sys_gain - 128);
318 else
319 sys_gain_coeff = QPNP_COEFF_6 * comp.sys_gain;
Xiaozhe Shi62ad5e12013-05-13 12:37:41 -0700320 } else if (comp.revision != QPNP_IADC_VER_3_0) {
321 /* unsupported revision, do not compensate */
322 *result = old;
323 return 0;
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700324 }
325
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700326 if (!comp.ext_rsense) {
327 /* internal rsense */
328 switch (comp.id) {
329 case COMP_ID_TSMC:
330 temp_var = ((QPNP_COEFF_2 * die_temp) -
331 QPNP_COEFF_3_TYPEB);
332 break;
333 case COMP_ID_GF:
334 default:
335 temp_var = ((QPNP_COEFF_2 * die_temp) -
336 QPNP_COEFF_3_TYPEA);
337 break;
338 }
339 temp_var = div64_s64(temp_var, QPNP_COEFF_4);
340 if (comp.revision == QPNP_IADC_VER_3_0)
341 temp_var = QPNP_COEFF_1 * (1000000 - temp_var);
342 else if (comp.revision == QPNP_IADC_VER_3_1)
Xiaozhe Shi62ad5e12013-05-13 12:37:41 -0700343 temp_var = 1000000 * (1000000 - temp_var);
344 *result = div64_s64(*result * 1000000, temp_var);
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700345 }
346
347 sign_coeff = *result < 0 ? QPNP_COEFF_7 : QPNP_COEFF_5;
348 if (comp.ext_rsense) {
349 /* external rsense and current charging */
350 temp_var = div64_s64((-sign_coeff * die_temp) + QPNP_COEFF_8,
351 QPNP_COEFF_4);
352 temp_var = 1000000000 - temp_var;
353 if (comp.revision == QPNP_IADC_VER_3_1) {
354 sys_gain_coeff = (1000000 +
355 div64_s64(sys_gain_coeff, QPNP_COEFF_4));
356 temp_var = div64_s64(temp_var * sys_gain_coeff,
357 1000000000);
358 }
359 *result = div64_s64(*result, temp_var);
360 }
Xiaozhe Shi62ad5e12013-05-13 12:37:41 -0700361 pr_debug("%lld compensated into %lld\n", old, *result);
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700362
363 return 0;
364}
365
366int32_t qpnp_iadc_comp_result(int64_t *result)
367{
368 struct qpnp_iadc_drv *iadc = qpnp_iadc;
369
370 return qpnp_iadc_comp(result, iadc->iadc_comp, iadc->die_temp);
371}
372EXPORT_SYMBOL(qpnp_iadc_comp_result);
373
374static int32_t qpnp_iadc_comp_info(void)
375{
376 struct qpnp_iadc_drv *iadc = qpnp_iadc;
377 int rc = 0;
378
379 rc = qpnp_iadc_read_reg(QPNP_INT_TEST_VAL, &iadc->iadc_comp.id);
380 if (rc < 0) {
381 pr_err("qpnp adc comp id failed with %d\n", rc);
382 return rc;
383 }
384
385 rc = qpnp_iadc_read_reg(QPNP_IADC_REVISION2, &iadc->iadc_comp.revision);
386 if (rc < 0) {
387 pr_err("qpnp adc revision read failed with %d\n", rc);
388 return rc;
389 }
390
391 rc = qpnp_iadc_read_reg(QPNP_IADC_ATE_GAIN_CALIB_OFFSET,
392 &iadc->iadc_comp.sys_gain);
Siddartha Mohanadoss7bea8442013-06-17 17:50:22 -0700393 if (rc < 0) {
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700394 pr_err("full scale read failed with %d\n", rc);
Siddartha Mohanadoss7bea8442013-06-17 17:50:22 -0700395 return rc;
396 }
397
398 if (iadc->external_rsense)
399 iadc->iadc_comp.ext_rsense = true;
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700400
401 pr_debug("fab id = %u, revision = %u, sys gain = %u, external_rsense = %d\n",
402 iadc->iadc_comp.id,
403 iadc->iadc_comp.revision,
404 iadc->iadc_comp.sys_gain,
405 iadc->iadc_comp.ext_rsense);
406 return rc;
407}
408
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700409static int32_t qpnp_iadc_configure(enum qpnp_iadc_channels channel,
Siddartha Mohanadossa32ea2a2013-02-12 09:58:31 -0800410 uint16_t *raw_code, uint32_t mode_sel)
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700411{
412 struct qpnp_iadc_drv *iadc = qpnp_iadc;
413 u8 qpnp_iadc_mode_reg = 0, qpnp_iadc_ch_sel_reg = 0;
414 u8 qpnp_iadc_conv_req = 0, qpnp_iadc_dig_param_reg = 0;
415 int32_t rc = 0;
416
Siddartha Mohanadoss5ace1102012-08-20 23:18:10 -0700417 qpnp_iadc_ch_sel_reg = channel;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700418
419 qpnp_iadc_dig_param_reg |= iadc->adc->amux_prop->decimation <<
420 QPNP_IADC_DEC_RATIO_SEL;
Siddartha Mohanadossa32ea2a2013-02-12 09:58:31 -0800421 if (iadc->iadc_mode_sel)
422 qpnp_iadc_mode_reg |= (QPNP_ADC_TRIM_EN | QPNP_VADC_SYNCH_EN);
423 else
424 qpnp_iadc_mode_reg |= QPNP_ADC_TRIM_EN;
425
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700426 qpnp_iadc_conv_req = QPNP_IADC_CONV_REQ;
427
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700428 rc = qpnp_iadc_write_reg(QPNP_IADC_MODE_CTL, qpnp_iadc_mode_reg);
429 if (rc) {
430 pr_err("qpnp adc read adc failed with %d\n", rc);
431 return rc;
432 }
433
434 rc = qpnp_iadc_write_reg(QPNP_IADC_ADC_CH_SEL_CTL,
435 qpnp_iadc_ch_sel_reg);
436 if (rc) {
437 pr_err("qpnp adc read adc failed with %d\n", rc);
438 return rc;
439 }
440
441 rc = qpnp_iadc_write_reg(QPNP_ADC_DIG_PARAM,
442 qpnp_iadc_dig_param_reg);
443 if (rc) {
444 pr_err("qpnp adc read adc failed with %d\n", rc);
445 return rc;
446 }
447
448 rc = qpnp_iadc_write_reg(QPNP_HW_SETTLE_DELAY,
449 iadc->adc->amux_prop->hw_settle_time);
450 if (rc < 0) {
451 pr_err("qpnp adc configure error for hw settling time setup\n");
452 return rc;
453 }
454
455 rc = qpnp_iadc_write_reg(QPNP_FAST_AVG_CTL,
456 iadc->adc->amux_prop->fast_avg_setup);
457 if (rc < 0) {
458 pr_err("qpnp adc fast averaging configure error\n");
459 return rc;
460 }
461
Siddartha Mohanadoss3f219c42013-04-02 11:01:28 -0700462 INIT_COMPLETION(iadc->adc->adc_rslt_completion);
463
Siddartha Mohanadoss5ace1102012-08-20 23:18:10 -0700464 rc = qpnp_iadc_enable(true);
465 if (rc)
466 return rc;
467
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700468 rc = qpnp_iadc_write_reg(QPNP_CONV_REQ, qpnp_iadc_conv_req);
469 if (rc) {
470 pr_err("qpnp adc read adc failed with %d\n", rc);
471 return rc;
472 }
473
Siddartha Mohanadoss1a0d2032012-11-01 11:22:29 -0700474 rc = wait_for_completion_timeout(&iadc->adc->adc_rslt_completion,
475 QPNP_ADC_COMPLETION_TIMEOUT);
476 if (!rc) {
477 u8 status1 = 0;
478 rc = qpnp_iadc_read_reg(QPNP_STATUS1, &status1);
479 if (rc < 0)
480 return rc;
481 status1 &= (QPNP_STATUS1_REQ_STS | QPNP_STATUS1_EOC);
482 if (status1 == QPNP_STATUS1_EOC)
483 pr_debug("End of conversion status set\n");
484 else {
Siddartha Mohanadossd3a3c952012-12-10 16:55:19 -0800485 rc = qpnp_iadc_status_debug();
486 if (rc < 0) {
487 pr_err("status1 read failed with %d\n", rc);
488 return rc;
489 }
Siddartha Mohanadoss1a0d2032012-11-01 11:22:29 -0700490 return -EINVAL;
491 }
492 }
493
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700494 rc = qpnp_iadc_read_conversion_result(raw_code);
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700495 if (rc) {
496 pr_err("qpnp adc read adc failed with %d\n", rc);
497 return rc;
498 }
499
500 return 0;
501}
502
Abhijeet Dharmapurikar29005fa2013-07-15 17:57:27 -0700503#define IADC_CENTER 0xC000
504#define IADC_READING_RESOLUTION_N 542535
505#define IADC_READING_RESOLUTION_D 100000
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700506static int32_t qpnp_convert_raw_offset_voltage(void)
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700507{
508 struct qpnp_iadc_drv *iadc = qpnp_iadc;
Abhijeet Dharmapurikar29005fa2013-07-15 17:57:27 -0700509 s64 numerator;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700510
Siddartha Mohanadossd752e472013-02-26 18:30:14 -0800511 if ((iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw) == 0) {
512 pr_err("raw offset errors! raw_gain:0x%x and raw_offset:0x%x\n",
513 iadc->adc->calib.gain_raw, iadc->adc->calib.offset_raw);
514 return -EINVAL;
515 }
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700516
Abhijeet Dharmapurikar29005fa2013-07-15 17:57:27 -0700517 numerator = iadc->adc->calib.offset_raw - IADC_CENTER;
518 numerator *= IADC_READING_RESOLUTION_N;
519 iadc->adc->calib.offset_uv = div_s64(numerator,
520 IADC_READING_RESOLUTION_D);
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700521
Abhijeet Dharmapurikar29005fa2013-07-15 17:57:27 -0700522 numerator = iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw;
523 numerator *= IADC_READING_RESOLUTION_N;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700524
Abhijeet Dharmapurikar29005fa2013-07-15 17:57:27 -0700525 iadc->adc->calib.gain_uv = div_s64(numerator,
526 IADC_READING_RESOLUTION_D);
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700527
Siddartha Mohanadossb2a42372013-03-26 15:53:41 -0700528 pr_debug("gain_uv:%d offset_uv:%d\n",
529 iadc->adc->calib.gain_uv, iadc->adc->calib.offset_uv);
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700530 return 0;
531}
532
Abhijeet Dharmapurikar0ef9b5c2013-07-15 18:24:38 -0700533int32_t qpnp_iadc_calibrate_for_trim(bool batfet_closed)
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700534{
535 struct qpnp_iadc_drv *iadc = qpnp_iadc;
536 uint8_t rslt_lsb, rslt_msb;
537 int32_t rc = 0;
538 uint16_t raw_data;
Siddartha Mohanadossa32ea2a2013-02-12 09:58:31 -0800539 uint32_t mode_sel = 0;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700540
Siddartha Mohanadossce7694c2013-07-08 16:47:28 -0700541 if (!iadc || !iadc->iadc_initialized)
542 return -EPROBE_DEFER;
543
Siddartha Mohanadossa9b91672013-02-22 18:32:27 -0800544 mutex_lock(&iadc->adc->adc_lock);
545
Siddartha Mohanadossa32ea2a2013-02-12 09:58:31 -0800546 rc = qpnp_iadc_configure(GAIN_CALIBRATION_17P857MV,
547 &raw_data, mode_sel);
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700548 if (rc < 0) {
549 pr_err("qpnp adc result read failed with %d\n", rc);
550 goto fail;
551 }
552
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700553 iadc->adc->calib.gain_raw = raw_data;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700554
Abhijeet Dharmapurikar0ef9b5c2013-07-15 18:24:38 -0700555 /*
556 * there is a features in the BMS where if the batfet is opened
557 * the BMS reads from INTERNAL_RSENSE (channel 0) actually go to
558 * OFFSET_CALIBRATION_CSP_CSN (channel 5). Hence if batfet is opened
559 * we have to calibrate based on OFFSET_CALIBRATION_CSP_CSN even for
560 * internal rsense.
561 */
562 if (!batfet_closed || iadc->external_rsense) {
Siddartha Mohanadossce7694c2013-07-08 16:47:28 -0700563 /* external offset calculation */
564 rc = qpnp_iadc_configure(OFFSET_CALIBRATION_CSP_CSN,
Siddartha Mohanadossa32ea2a2013-02-12 09:58:31 -0800565 &raw_data, mode_sel);
Siddartha Mohanadossce7694c2013-07-08 16:47:28 -0700566 if (rc < 0) {
567 pr_err("qpnp adc result read failed with %d\n", rc);
568 goto fail;
569 }
570 } else {
571 /* internal offset calculation */
572 rc = qpnp_iadc_configure(OFFSET_CALIBRATION_CSP2_CSN2,
573 &raw_data, mode_sel);
574 if (rc < 0) {
575 pr_err("qpnp adc result read failed with %d\n", rc);
576 goto fail;
577 }
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700578 }
579
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700580 iadc->adc->calib.offset_raw = raw_data;
581 if (rc < 0) {
582 pr_err("qpnp adc offset/gain calculation failed\n");
583 goto fail;
584 }
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700585
Siddartha Mohanadossb2a42372013-03-26 15:53:41 -0700586 pr_debug("raw gain:0x%x, raw offset:0x%x\n",
587 iadc->adc->calib.gain_raw, iadc->adc->calib.offset_raw);
588
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700589 rc = qpnp_convert_raw_offset_voltage();
Siddartha Mohanadossd752e472013-02-26 18:30:14 -0800590 if (rc < 0) {
591 pr_err("qpnp raw_voltage conversion failed\n");
592 goto fail;
593 }
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700594
595 rslt_msb = (raw_data & QPNP_RAW_CODE_16_BIT_MSB_MASK) >>
596 QPNP_BIT_SHIFT_8;
597 rslt_lsb = raw_data & QPNP_RAW_CODE_16_BIT_LSB_MASK;
598
Siddartha Mohanadossb2a42372013-03-26 15:53:41 -0700599 pr_debug("trim values:lsb:0x%x and msb:0x%x\n", rslt_lsb, rslt_msb);
600
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700601 rc = qpnp_iadc_write_reg(QPNP_IADC_SEC_ACCESS,
602 QPNP_IADC_SEC_ACCESS_DATA);
603 if (rc < 0) {
604 pr_err("qpnp iadc configure error for sec access\n");
605 goto fail;
606 }
607
608 rc = qpnp_iadc_write_reg(QPNP_IADC_MSB_OFFSET,
609 rslt_msb);
610 if (rc < 0) {
611 pr_err("qpnp iadc configure error for MSB write\n");
612 goto fail;
613 }
614
615 rc = qpnp_iadc_write_reg(QPNP_IADC_SEC_ACCESS,
616 QPNP_IADC_SEC_ACCESS_DATA);
617 if (rc < 0) {
618 pr_err("qpnp iadc configure error for sec access\n");
619 goto fail;
620 }
621
622 rc = qpnp_iadc_write_reg(QPNP_IADC_LSB_OFFSET,
623 rslt_lsb);
624 if (rc < 0) {
625 pr_err("qpnp iadc configure error for LSB write\n");
626 goto fail;
627 }
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700628fail:
Siddartha Mohanadossa9b91672013-02-22 18:32:27 -0800629 mutex_unlock(&iadc->adc->adc_lock);
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700630 return rc;
631}
Siddartha Mohanadoss06673922013-03-27 11:14:19 -0700632EXPORT_SYMBOL(qpnp_iadc_calibrate_for_trim);
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700633
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700634static void qpnp_iadc_work(struct work_struct *work)
635{
636 struct qpnp_iadc_drv *iadc = qpnp_iadc;
637 int rc = 0;
638
Abhijeet Dharmapurikar84b13dd2013-07-08 18:43:56 -0700639 if (!iadc->skip_auto_calibrations) {
640 rc = qpnp_iadc_calibrate_for_trim(true);
641 if (rc)
642 pr_debug("periodic IADC calibration failed\n");
643 }
644
645 schedule_delayed_work(&iadc->iadc_work,
646 round_jiffies_relative(msecs_to_jiffies
647 (QPNP_IADC_CALIB_SECONDS)));
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700648 return;
649}
650
Siddartha Mohanadoss5ace1102012-08-20 23:18:10 -0700651static int32_t qpnp_iadc_version_check(void)
652{
653 uint8_t revision;
654 int rc;
655
656 rc = qpnp_iadc_read_reg(QPNP_IADC_REVISION2, &revision);
657 if (rc < 0) {
658 pr_err("qpnp adc result read failed with %d\n", rc);
659 return rc;
660 }
661
662 if (revision < QPNP_IADC_SUPPORTED_REVISION2) {
663 pr_err("IADC Version not supported\n");
664 return -EINVAL;
665 }
666
667 return 0;
668}
669
670int32_t qpnp_iadc_is_ready(void)
671{
672 struct qpnp_iadc_drv *iadc = qpnp_iadc;
673
674 if (!iadc || !iadc->iadc_initialized)
675 return -EPROBE_DEFER;
676 else
677 return 0;
678}
679EXPORT_SYMBOL(qpnp_iadc_is_ready);
680
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700681int32_t qpnp_iadc_get_rsense(int32_t *rsense)
682{
Siddartha Mohanadossa9b91672013-02-22 18:32:27 -0800683 struct qpnp_iadc_drv *iadc = qpnp_iadc;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700684 uint8_t rslt_rsense;
Siddartha Mohanadoss58279542013-05-28 16:20:46 -0700685 int32_t rc = 0, sign_bit = 0;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700686
Siddartha Mohanadossa9b91672013-02-22 18:32:27 -0800687 if (!iadc || !iadc->iadc_initialized)
688 return -EPROBE_DEFER;
689
Siddartha Mohanadoss58279542013-05-28 16:20:46 -0700690 if (iadc->external_rsense) {
Siddartha Mohanadosse70010b2013-04-04 14:51:41 -0700691 *rsense = iadc->rsense;
Siddartha Mohanadoss58279542013-05-28 16:20:46 -0700692 return rc;
693 }
Siddartha Mohanadosse70010b2013-04-04 14:51:41 -0700694
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700695 rc = qpnp_iadc_read_reg(QPNP_IADC_NOMINAL_RSENSE, &rslt_rsense);
696 if (rc < 0) {
697 pr_err("qpnp adc rsense read failed with %d\n", rc);
698 return rc;
699 }
700
Siddartha Mohanadossb2a42372013-03-26 15:53:41 -0700701 pr_debug("rsense:0%x\n", rslt_rsense);
702
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700703 if (rslt_rsense & QPNP_RSENSE_MSB_SIGN_CHECK)
704 sign_bit = 1;
705
706 rslt_rsense &= ~QPNP_RSENSE_MSB_SIGN_CHECK;
707
708 if (sign_bit)
709 *rsense = QPNP_IADC_INTERNAL_RSENSE_N_OHMS_FACTOR -
710 (rslt_rsense * QPNP_IADC_RSENSE_LSB_N_OHMS_PER_BIT);
711 else
712 *rsense = QPNP_IADC_INTERNAL_RSENSE_N_OHMS_FACTOR +
713 (rslt_rsense * QPNP_IADC_RSENSE_LSB_N_OHMS_PER_BIT);
714
715 return rc;
716}
Xiaozhe Shi767fdb62013-01-10 15:09:08 -0800717EXPORT_SYMBOL(qpnp_iadc_get_rsense);
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700718
Siddartha Mohanadossa9b91672013-02-22 18:32:27 -0800719static int32_t qpnp_check_pmic_temp(void)
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700720{
721 struct qpnp_iadc_drv *iadc = qpnp_iadc;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700722 struct qpnp_vadc_result result_pmic_therm;
Siddartha Mohanadossd4e9edc2013-04-17 14:42:16 -0700723 int64_t die_temp_offset;
Siddartha Mohanadossd752e472013-02-26 18:30:14 -0800724 int rc = 0;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700725
726 rc = qpnp_vadc_read(DIE_TEMP, &result_pmic_therm);
727 if (rc < 0)
728 return rc;
729
Siddartha Mohanadossd4e9edc2013-04-17 14:42:16 -0700730 die_temp_offset = result_pmic_therm.physical -
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700731 iadc->die_temp;
Siddartha Mohanadossd4e9edc2013-04-17 14:42:16 -0700732 if (die_temp_offset < 0)
733 die_temp_offset = -die_temp_offset;
734
735 if (die_temp_offset > QPNP_IADC_DIE_TEMP_CALIB_OFFSET) {
Abhijeet Dharmapurikar0ef9b5c2013-07-15 18:24:38 -0700736 iadc->die_temp = result_pmic_therm.physical;
Abhijeet Dharmapurikar84b13dd2013-07-08 18:43:56 -0700737 if (!iadc->skip_auto_calibrations) {
738 rc = qpnp_iadc_calibrate_for_trim(true);
739 if (rc)
740 pr_err("IADC calibration failed rc = %d\n", rc);
741 }
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700742 }
743
Siddartha Mohanadossd752e472013-02-26 18:30:14 -0800744 return rc;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700745}
746
747int32_t qpnp_iadc_read(enum qpnp_iadc_channels channel,
748 struct qpnp_iadc_result *result)
749{
750 struct qpnp_iadc_drv *iadc = qpnp_iadc;
Siddartha Mohanadossa32ea2a2013-02-12 09:58:31 -0800751 int32_t rc, rsense_n_ohms, sign = 0, num, mode_sel = 0;
Siddartha Mohanadoss22618be2013-04-02 15:02:19 -0700752 int32_t rsense_u_ohms = 0;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700753 int64_t result_current;
754 uint16_t raw_data;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700755
Siddartha Mohanadoss5ace1102012-08-20 23:18:10 -0700756 if (!iadc || !iadc->iadc_initialized)
757 return -EPROBE_DEFER;
758
Siddartha Mohanadossa32ea2a2013-02-12 09:58:31 -0800759 if (!iadc->iadc_mode_sel) {
760 rc = qpnp_check_pmic_temp();
761 if (rc) {
762 pr_err("Error checking pmic therm temp\n");
763 return rc;
764 }
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700765 }
766
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700767 mutex_lock(&iadc->adc->adc_lock);
768
Siddartha Mohanadossa32ea2a2013-02-12 09:58:31 -0800769 rc = qpnp_iadc_configure(channel, &raw_data, mode_sel);
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700770 if (rc < 0) {
771 pr_err("qpnp adc result read failed with %d\n", rc);
772 goto fail;
773 }
774
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700775 rc = qpnp_iadc_get_rsense(&rsense_n_ohms);
Siddartha Mohanadossb2a42372013-03-26 15:53:41 -0700776 pr_debug("current raw:0%x and rsense:%d\n",
777 raw_data, rsense_n_ohms);
Siddartha Mohanadoss22618be2013-04-02 15:02:19 -0700778 rsense_u_ohms = rsense_n_ohms/1000;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700779 num = raw_data - iadc->adc->calib.offset_raw;
780 if (num < 0) {
781 sign = 1;
782 num = -num;
783 }
784
785 result->result_uv = (num * QPNP_ADC_GAIN_NV)/
786 (iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw);
787 result_current = result->result_uv;
788 result_current *= QPNP_IADC_NANO_VOLTS_FACTOR;
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700789 /* Intentional fall through. Process the result w/o comp */
Siddartha Mohanadoss22618be2013-04-02 15:02:19 -0700790 do_div(result_current, rsense_u_ohms);
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700791
792 if (sign) {
793 result->result_uv = -result->result_uv;
794 result_current = -result_current;
795 }
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700796 rc = qpnp_iadc_comp_result(&result_current);
797 if (rc < 0)
798 pr_err("Error during compensating the IADC\n");
799 rc = 0;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700800
801 result->result_ua = (int32_t) result_current;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700802fail:
803 mutex_unlock(&iadc->adc->adc_lock);
804
805 return rc;
806}
807EXPORT_SYMBOL(qpnp_iadc_read);
808
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700809int32_t qpnp_iadc_get_gain_and_offset(struct qpnp_iadc_calib *result)
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700810{
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700811 struct qpnp_iadc_drv *iadc = qpnp_iadc;
812 int rc;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700813
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700814 if (!iadc || !iadc->iadc_initialized)
815 return -EPROBE_DEFER;
816
817 rc = qpnp_check_pmic_temp();
818 if (rc) {
819 pr_err("Error checking pmic therm temp\n");
820 return rc;
821 }
822
823 mutex_lock(&iadc->adc->adc_lock);
824 result->gain_raw = iadc->adc->calib.gain_raw;
825 result->ideal_gain_nv = QPNP_ADC_GAIN_NV;
826 result->gain_uv = iadc->adc->calib.gain_uv;
827 result->offset_raw = iadc->adc->calib.offset_raw;
828 result->ideal_offset_uv =
829 QPNP_OFFSET_CALIBRATION_SHORT_CADC_LEADS_IDEAL;
830 result->offset_uv = iadc->adc->calib.offset_uv;
Siddartha Mohanadossb2a42372013-03-26 15:53:41 -0700831 pr_debug("raw gain:0%x, raw offset:0%x\n",
832 result->gain_raw, result->offset_raw);
833 pr_debug("gain_uv:%d offset_uv:%d\n",
834 result->gain_uv, result->offset_uv);
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700835 mutex_unlock(&iadc->adc->adc_lock);
836
837 return 0;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700838}
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700839EXPORT_SYMBOL(qpnp_iadc_get_gain_and_offset);
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700840
Abhijeet Dharmapurikar84b13dd2013-07-08 18:43:56 -0700841int qpnp_iadc_skip_calibration(void)
842{
843 struct qpnp_iadc_drv *iadc = qpnp_iadc;
844
845 if (!iadc || !iadc->iadc_initialized)
846 return -EPROBE_DEFER;
847
848 iadc->skip_auto_calibrations = true;
849 return 0;
850}
851EXPORT_SYMBOL(qpnp_iadc_skip_calibration);
852
853int qpnp_iadc_resume_calibration(void)
854{
855 struct qpnp_iadc_drv *iadc = qpnp_iadc;
856
857 if (!iadc || !iadc->iadc_initialized)
858 return -EPROBE_DEFER;
859
860 iadc->skip_auto_calibrations = false;
861 return 0;
862}
863EXPORT_SYMBOL(qpnp_iadc_resume_calibration);
864
Siddartha Mohanadossa32ea2a2013-02-12 09:58:31 -0800865int32_t qpnp_iadc_vadc_sync_read(
866 enum qpnp_iadc_channels i_channel, struct qpnp_iadc_result *i_result,
867 enum qpnp_vadc_channels v_channel, struct qpnp_vadc_result *v_result)
868{
869 struct qpnp_iadc_drv *iadc = qpnp_iadc;
870 int rc = 0;
871
872 if (!iadc || !iadc->iadc_initialized)
873 return -EPROBE_DEFER;
874
875 mutex_lock(&iadc->iadc_vadc_lock);
876
877 rc = qpnp_check_pmic_temp();
878 if (rc) {
879 pr_err("PMIC die temp check failed\n");
880 goto fail;
881 }
882
883 iadc->iadc_mode_sel = true;
884
885 rc = qpnp_vadc_iadc_sync_request(v_channel);
886 if (rc) {
887 pr_err("Configuring VADC failed\n");
888 goto fail;
889 }
890
891 rc = qpnp_iadc_read(i_channel, i_result);
892 if (rc)
893 pr_err("Configuring IADC failed\n");
894 /* Intentional fall through to release VADC */
895
896 rc = qpnp_vadc_iadc_sync_complete_request(v_channel,
897 v_result);
898 if (rc)
899 pr_err("Releasing VADC failed\n");
900fail:
901 iadc->iadc_mode_sel = false;
902
903 mutex_unlock(&iadc->iadc_vadc_lock);
904
905 return rc;
906}
907EXPORT_SYMBOL(qpnp_iadc_vadc_sync_read);
908
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700909static ssize_t qpnp_iadc_show(struct device *dev,
910 struct device_attribute *devattr, char *buf)
911{
912 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700913 struct qpnp_iadc_result result;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700914 int rc = -1;
915
916 rc = qpnp_iadc_read(attr->index, &result);
917
918 if (rc)
919 return 0;
920
921 return snprintf(buf, QPNP_ADC_HWMON_NAME_LENGTH,
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700922 "Result:%d\n", result.result_ua);
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700923}
924
925static struct sensor_device_attribute qpnp_adc_attr =
926 SENSOR_ATTR(NULL, S_IRUGO, qpnp_iadc_show, NULL, 0);
927
928static int32_t qpnp_iadc_init_hwmon(struct spmi_device *spmi)
929{
930 struct qpnp_iadc_drv *iadc = qpnp_iadc;
931 struct device_node *child;
932 struct device_node *node = spmi->dev.of_node;
933 int rc = 0, i = 0, channel;
934
935 for_each_child_of_node(node, child) {
936 channel = iadc->adc->adc_channels[i].channel_num;
937 qpnp_adc_attr.index = iadc->adc->adc_channels[i].channel_num;
938 qpnp_adc_attr.dev_attr.attr.name =
939 iadc->adc->adc_channels[i].name;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700940 memcpy(&iadc->sens_attr[i], &qpnp_adc_attr,
941 sizeof(qpnp_adc_attr));
Stephen Boyd8a5c4e42012-10-30 11:07:22 -0700942 sysfs_attr_init(&iadc->sens_attr[i].dev_attr.attr);
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700943 rc = device_create_file(&spmi->dev,
944 &iadc->sens_attr[i].dev_attr);
945 if (rc) {
946 dev_err(&spmi->dev,
947 "device_create_file failed for dev %s\n",
948 iadc->adc->adc_channels[i].name);
949 goto hwmon_err_sens;
950 }
951 i++;
952 }
953
954 return 0;
955hwmon_err_sens:
956 pr_err("Init HWMON failed for qpnp_iadc with %d\n", rc);
957 return rc;
958}
959
960static int __devinit qpnp_iadc_probe(struct spmi_device *spmi)
961{
962 struct qpnp_iadc_drv *iadc;
963 struct qpnp_adc_drv *adc_qpnp;
964 struct device_node *node = spmi->dev.of_node;
965 struct device_node *child;
966 int rc, count_adc_channel_list = 0;
967
968 if (!node)
969 return -EINVAL;
970
971 if (qpnp_iadc) {
972 pr_err("IADC already in use\n");
973 return -EBUSY;
974 }
975
976 for_each_child_of_node(node, child)
977 count_adc_channel_list++;
978
979 if (!count_adc_channel_list) {
980 pr_err("No channel listing\n");
981 return -EINVAL;
982 }
983
984 iadc = devm_kzalloc(&spmi->dev, sizeof(struct qpnp_iadc_drv) +
985 (sizeof(struct sensor_device_attribute) *
986 count_adc_channel_list), GFP_KERNEL);
987 if (!iadc) {
988 dev_err(&spmi->dev, "Unable to allocate memory\n");
989 return -ENOMEM;
990 }
991
992 adc_qpnp = devm_kzalloc(&spmi->dev, sizeof(struct qpnp_adc_drv),
993 GFP_KERNEL);
994 if (!adc_qpnp) {
995 dev_err(&spmi->dev, "Unable to allocate memory\n");
Siddartha Mohanadossb60f6462012-11-20 18:06:51 -0800996 rc = -ENOMEM;
997 goto fail;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700998 }
999
1000 iadc->adc = adc_qpnp;
1001
1002 rc = qpnp_adc_get_devicetree_data(spmi, iadc->adc);
1003 if (rc) {
1004 dev_err(&spmi->dev, "failed to read device tree\n");
Siddartha Mohanadossb60f6462012-11-20 18:06:51 -08001005 goto fail;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -07001006 }
1007
Stephen Boydbeab4502013-04-25 10:18:17 -07001008 mutex_init(&iadc->adc->adc_lock);
1009
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -07001010 rc = of_property_read_u32(node, "qcom,rsense",
1011 &iadc->rsense);
Siddartha Mohanadosse70010b2013-04-04 14:51:41 -07001012 if (rc)
1013 pr_debug("Defaulting to internal rsense\n");
1014 else {
1015 pr_debug("Use external rsense\n");
1016 iadc->external_rsense = true;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -07001017 }
1018
Siddartha Mohanadoss12109952012-11-20 14:57:51 -08001019 rc = devm_request_irq(&spmi->dev, iadc->adc->adc_irq_eoc,
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -07001020 qpnp_iadc_isr,
1021 IRQF_TRIGGER_RISING, "qpnp_iadc_interrupt", iadc);
1022 if (rc) {
1023 dev_err(&spmi->dev, "failed to request adc irq\n");
Siddartha Mohanadossb60f6462012-11-20 18:06:51 -08001024 goto fail;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -07001025 } else
Siddartha Mohanadoss12109952012-11-20 14:57:51 -08001026 enable_irq_wake(iadc->adc->adc_irq_eoc);
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -07001027
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -07001028 dev_set_drvdata(&spmi->dev, iadc);
1029 qpnp_iadc = iadc;
1030
1031 rc = qpnp_iadc_init_hwmon(spmi);
1032 if (rc) {
1033 dev_err(&spmi->dev, "failed to initialize qpnp hwmon adc\n");
Siddartha Mohanadossb60f6462012-11-20 18:06:51 -08001034 goto fail;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -07001035 }
1036 iadc->iadc_hwmon = hwmon_device_register(&iadc->adc->spmi->dev);
1037
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -07001038 rc = qpnp_iadc_version_check();
1039 if (rc) {
1040 dev_err(&spmi->dev, "IADC version not supported\n");
Siddartha Mohanadossb60f6462012-11-20 18:06:51 -08001041 goto fail;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -07001042 }
1043
Siddartha Mohanadossa32ea2a2013-02-12 09:58:31 -08001044 mutex_init(&iadc->iadc_vadc_lock);
Siddartha Mohanadossa9b91672013-02-22 18:32:27 -08001045 INIT_DELAYED_WORK(&iadc->iadc_work, qpnp_iadc_work);
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -07001046 rc = qpnp_iadc_comp_info();
1047 if (rc) {
1048 dev_err(&spmi->dev, "abstracting IADC comp info failed!\n");
1049 goto fail;
1050 }
Siddartha Mohanadoss12a15ea2013-02-05 19:13:41 -08001051 iadc->iadc_initialized = true;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -07001052
Abhijeet Dharmapurikar0ef9b5c2013-07-15 18:24:38 -07001053 rc = qpnp_iadc_calibrate_for_trim(true);
Siddartha Mohanadossa9b91672013-02-22 18:32:27 -08001054 if (rc)
1055 dev_err(&spmi->dev, "failed to calibrate for USR trim\n");
1056 schedule_delayed_work(&iadc->iadc_work,
1057 round_jiffies_relative(msecs_to_jiffies
1058 (QPNP_IADC_CALIB_SECONDS)));
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -07001059 return 0;
Siddartha Mohanadossb60f6462012-11-20 18:06:51 -08001060fail:
Siddartha Mohanadoss32019b52012-12-23 17:05:45 -08001061 qpnp_iadc = NULL;
Siddartha Mohanadossb60f6462012-11-20 18:06:51 -08001062 return rc;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -07001063}
1064
1065static int __devexit qpnp_iadc_remove(struct spmi_device *spmi)
1066{
1067 struct qpnp_iadc_drv *iadc = dev_get_drvdata(&spmi->dev);
1068 struct device_node *node = spmi->dev.of_node;
1069 struct device_node *child;
1070 int i = 0;
1071
Siddartha Mohanadossa9b91672013-02-22 18:32:27 -08001072 cancel_delayed_work(&iadc->iadc_work);
Siddartha Mohanadossa32ea2a2013-02-12 09:58:31 -08001073 mutex_destroy(&iadc->iadc_vadc_lock);
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -07001074 for_each_child_of_node(node, child) {
1075 device_remove_file(&spmi->dev,
1076 &iadc->sens_attr[i].dev_attr);
1077 i++;
1078 }
1079 dev_set_drvdata(&spmi->dev, NULL);
1080
1081 return 0;
1082}
1083
1084static const struct of_device_id qpnp_iadc_match_table[] = {
1085 { .compatible = "qcom,qpnp-iadc",
1086 },
1087 {}
1088};
1089
1090static struct spmi_driver qpnp_iadc_driver = {
1091 .driver = {
1092 .name = "qcom,qpnp-iadc",
1093 .of_match_table = qpnp_iadc_match_table,
1094 },
1095 .probe = qpnp_iadc_probe,
1096 .remove = qpnp_iadc_remove,
1097};
1098
1099static int __init qpnp_iadc_init(void)
1100{
1101 return spmi_driver_register(&qpnp_iadc_driver);
1102}
1103module_init(qpnp_iadc_init);
1104
1105static void __exit qpnp_iadc_exit(void)
1106{
1107 spmi_driver_unregister(&qpnp_iadc_driver);
1108}
1109module_exit(qpnp_iadc_exit);
1110
1111MODULE_DESCRIPTION("QPNP PMIC current ADC driver");
1112MODULE_LICENSE("GPL v2");