blob: f0793b291bae9a89b85ab306fc9ad06b016d1241 [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];
149};
150
151struct qpnp_iadc_drv *qpnp_iadc;
152
153static int32_t qpnp_iadc_read_reg(uint32_t reg, u8 *data)
154{
155 struct qpnp_iadc_drv *iadc = qpnp_iadc;
156 int rc;
157
158 rc = spmi_ext_register_readl(iadc->adc->spmi->ctrl, iadc->adc->slave,
Siddartha Mohanadossae1da732012-08-08 16:39:02 -0700159 (iadc->adc->offset + reg), data, 1);
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700160 if (rc < 0) {
161 pr_err("qpnp iadc read reg %d failed with %d\n", reg, rc);
162 return rc;
163 }
164
165 return 0;
166}
167
168static int32_t qpnp_iadc_write_reg(uint32_t reg, u8 data)
169{
170 struct qpnp_iadc_drv *iadc = qpnp_iadc;
171 int rc;
172 u8 *buf;
173
174 buf = &data;
175 rc = spmi_ext_register_writel(iadc->adc->spmi->ctrl, iadc->adc->slave,
Siddartha Mohanadossae1da732012-08-08 16:39:02 -0700176 (iadc->adc->offset + reg), buf, 1);
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700177 if (rc < 0) {
178 pr_err("qpnp iadc write reg %d failed with %d\n", reg, rc);
179 return rc;
180 }
181
182 return 0;
183}
184
Siddartha Mohanadosse2363592012-12-14 18:59:01 -0800185static void trigger_iadc_completion(struct work_struct *work)
186{
187 struct qpnp_iadc_drv *iadc = qpnp_iadc;
188
Siddartha Mohanadossb60f6462012-11-20 18:06:51 -0800189 if (!iadc || !iadc->iadc_initialized)
190 return;
191
Siddartha Mohanadosse2363592012-12-14 18:59:01 -0800192 complete(&iadc->adc->adc_rslt_completion);
193
194 return;
195}
196DECLARE_WORK(trigger_iadc_completion_work, trigger_iadc_completion);
197
198static irqreturn_t qpnp_iadc_isr(int irq, void *dev_id)
199{
200 schedule_work(&trigger_iadc_completion_work);
201
202 return IRQ_HANDLED;
203}
204
205static int32_t qpnp_iadc_enable(bool state)
206{
207 int rc = 0;
208 u8 data = 0;
209
210 data = QPNP_IADC_ADC_EN;
211 if (state) {
212 rc = qpnp_iadc_write_reg(QPNP_IADC_EN_CTL1,
213 data);
214 if (rc < 0) {
215 pr_err("IADC enable failed\n");
216 return rc;
217 }
218 } else {
219 rc = qpnp_iadc_write_reg(QPNP_IADC_EN_CTL1,
220 (~data & QPNP_IADC_ADC_EN));
221 if (rc < 0) {
222 pr_err("IADC disable failed\n");
223 return rc;
224 }
225 }
226
227 return 0;
228}
229
Siddartha Mohanadossd3a3c952012-12-10 16:55:19 -0800230static int32_t qpnp_iadc_status_debug(void)
231{
232 int rc = 0;
233 u8 mode = 0, status1 = 0, chan = 0, dig = 0, en = 0;
234
235 rc = qpnp_iadc_read_reg(QPNP_IADC_MODE_CTL, &mode);
236 if (rc < 0) {
237 pr_err("mode ctl register read failed with %d\n", rc);
238 return rc;
239 }
240
241 rc = qpnp_iadc_read_reg(QPNP_ADC_DIG_PARAM, &dig);
242 if (rc < 0) {
243 pr_err("digital param read failed with %d\n", rc);
244 return rc;
245 }
246
247 rc = qpnp_iadc_read_reg(QPNP_IADC_ADC_CH_SEL_CTL, &chan);
248 if (rc < 0) {
249 pr_err("channel read failed with %d\n", rc);
250 return rc;
251 }
252
253 rc = qpnp_iadc_read_reg(QPNP_STATUS1, &status1);
254 if (rc < 0) {
255 pr_err("status1 read failed with %d\n", rc);
256 return rc;
257 }
258
259 rc = qpnp_iadc_read_reg(QPNP_IADC_EN_CTL1, &en);
260 if (rc < 0) {
261 pr_err("en read failed with %d\n", rc);
262 return rc;
263 }
264
Siddartha Mohanadoss73ae69b2013-04-03 17:34:03 -0700265 pr_debug("EOC not set with status:%x, dig:%x, ch:%x, mode:%x, en:%x\n",
Siddartha Mohanadossd3a3c952012-12-10 16:55:19 -0800266 status1, dig, chan, mode, en);
267
Siddartha Mohanadosse2363592012-12-14 18:59:01 -0800268 rc = qpnp_iadc_enable(false);
269 if (rc < 0) {
270 pr_err("IADC disable failed with %d\n", rc);
271 return rc;
Siddartha Mohanadoss5ace1102012-08-20 23:18:10 -0700272 }
273
274 return 0;
275}
276
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700277static int32_t qpnp_iadc_read_conversion_result(uint16_t *data)
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700278{
279 uint8_t rslt_lsb, rslt_msb;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700280 uint16_t rslt;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700281 int32_t rc;
282
283 rc = qpnp_iadc_read_reg(QPNP_IADC_DATA0, &rslt_lsb);
284 if (rc < 0) {
285 pr_err("qpnp adc result read failed with %d\n", rc);
286 return rc;
287 }
288
289 rc = qpnp_iadc_read_reg(QPNP_IADC_DATA1, &rslt_msb);
290 if (rc < 0) {
291 pr_err("qpnp adc result read failed with %d\n", rc);
292 return rc;
293 }
294
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700295 rslt = (rslt_msb << 8) | rslt_lsb;
296 *data = rslt;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700297
Siddartha Mohanadoss5ace1102012-08-20 23:18:10 -0700298 rc = qpnp_iadc_enable(false);
299 if (rc)
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700300 return rc;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700301
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700302 return 0;
303}
304
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700305static int32_t qpnp_iadc_comp(int64_t *result, struct qpnp_iadc_comp comp,
306 int64_t die_temp)
307{
Xiaozhe Shi62ad5e12013-05-13 12:37:41 -0700308 int64_t temp_var = 0, sign_coeff = 0, sys_gain_coeff = 0, old;
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700309
Xiaozhe Shi62ad5e12013-05-13 12:37:41 -0700310 old = *result;
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700311 *result = *result * 1000000;
312
313 if (comp.revision == QPNP_IADC_VER_3_1) {
314 /* revision 3.1 */
315 if (comp.sys_gain > 127)
316 sys_gain_coeff = -QPNP_COEFF_6 * (comp.sys_gain - 128);
317 else
318 sys_gain_coeff = QPNP_COEFF_6 * comp.sys_gain;
Xiaozhe Shi62ad5e12013-05-13 12:37:41 -0700319 } else if (comp.revision != QPNP_IADC_VER_3_0) {
320 /* unsupported revision, do not compensate */
321 *result = old;
322 return 0;
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700323 }
324
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700325 if (!comp.ext_rsense) {
326 /* internal rsense */
327 switch (comp.id) {
328 case COMP_ID_TSMC:
329 temp_var = ((QPNP_COEFF_2 * die_temp) -
330 QPNP_COEFF_3_TYPEB);
331 break;
332 case COMP_ID_GF:
333 default:
334 temp_var = ((QPNP_COEFF_2 * die_temp) -
335 QPNP_COEFF_3_TYPEA);
336 break;
337 }
338 temp_var = div64_s64(temp_var, QPNP_COEFF_4);
339 if (comp.revision == QPNP_IADC_VER_3_0)
340 temp_var = QPNP_COEFF_1 * (1000000 - temp_var);
341 else if (comp.revision == QPNP_IADC_VER_3_1)
Xiaozhe Shi62ad5e12013-05-13 12:37:41 -0700342 temp_var = 1000000 * (1000000 - temp_var);
343 *result = div64_s64(*result * 1000000, temp_var);
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700344 }
345
346 sign_coeff = *result < 0 ? QPNP_COEFF_7 : QPNP_COEFF_5;
347 if (comp.ext_rsense) {
348 /* external rsense and current charging */
349 temp_var = div64_s64((-sign_coeff * die_temp) + QPNP_COEFF_8,
350 QPNP_COEFF_4);
351 temp_var = 1000000000 - temp_var;
352 if (comp.revision == QPNP_IADC_VER_3_1) {
353 sys_gain_coeff = (1000000 +
354 div64_s64(sys_gain_coeff, QPNP_COEFF_4));
355 temp_var = div64_s64(temp_var * sys_gain_coeff,
356 1000000000);
357 }
358 *result = div64_s64(*result, temp_var);
359 }
Xiaozhe Shi62ad5e12013-05-13 12:37:41 -0700360 pr_debug("%lld compensated into %lld\n", old, *result);
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700361
362 return 0;
363}
364
365int32_t qpnp_iadc_comp_result(int64_t *result)
366{
367 struct qpnp_iadc_drv *iadc = qpnp_iadc;
368
369 return qpnp_iadc_comp(result, iadc->iadc_comp, iadc->die_temp);
370}
371EXPORT_SYMBOL(qpnp_iadc_comp_result);
372
373static int32_t qpnp_iadc_comp_info(void)
374{
375 struct qpnp_iadc_drv *iadc = qpnp_iadc;
376 int rc = 0;
377
378 rc = qpnp_iadc_read_reg(QPNP_INT_TEST_VAL, &iadc->iadc_comp.id);
379 if (rc < 0) {
380 pr_err("qpnp adc comp id failed with %d\n", rc);
381 return rc;
382 }
383
384 rc = qpnp_iadc_read_reg(QPNP_IADC_REVISION2, &iadc->iadc_comp.revision);
385 if (rc < 0) {
386 pr_err("qpnp adc revision read failed with %d\n", rc);
387 return rc;
388 }
389
390 rc = qpnp_iadc_read_reg(QPNP_IADC_ATE_GAIN_CALIB_OFFSET,
391 &iadc->iadc_comp.sys_gain);
Siddartha Mohanadoss7bea8442013-06-17 17:50:22 -0700392 if (rc < 0) {
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700393 pr_err("full scale read failed with %d\n", rc);
Siddartha Mohanadoss7bea8442013-06-17 17:50:22 -0700394 return rc;
395 }
396
397 if (iadc->external_rsense)
398 iadc->iadc_comp.ext_rsense = true;
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700399
400 pr_debug("fab id = %u, revision = %u, sys gain = %u, external_rsense = %d\n",
401 iadc->iadc_comp.id,
402 iadc->iadc_comp.revision,
403 iadc->iadc_comp.sys_gain,
404 iadc->iadc_comp.ext_rsense);
405 return rc;
406}
407
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700408static int32_t qpnp_iadc_configure(enum qpnp_iadc_channels channel,
Siddartha Mohanadossa32ea2a2013-02-12 09:58:31 -0800409 uint16_t *raw_code, uint32_t mode_sel)
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700410{
411 struct qpnp_iadc_drv *iadc = qpnp_iadc;
412 u8 qpnp_iadc_mode_reg = 0, qpnp_iadc_ch_sel_reg = 0;
413 u8 qpnp_iadc_conv_req = 0, qpnp_iadc_dig_param_reg = 0;
414 int32_t rc = 0;
415
Siddartha Mohanadoss5ace1102012-08-20 23:18:10 -0700416 qpnp_iadc_ch_sel_reg = channel;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700417
418 qpnp_iadc_dig_param_reg |= iadc->adc->amux_prop->decimation <<
419 QPNP_IADC_DEC_RATIO_SEL;
Siddartha Mohanadossa32ea2a2013-02-12 09:58:31 -0800420 if (iadc->iadc_mode_sel)
421 qpnp_iadc_mode_reg |= (QPNP_ADC_TRIM_EN | QPNP_VADC_SYNCH_EN);
422 else
423 qpnp_iadc_mode_reg |= QPNP_ADC_TRIM_EN;
424
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700425 qpnp_iadc_conv_req = QPNP_IADC_CONV_REQ;
426
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700427 rc = qpnp_iadc_write_reg(QPNP_IADC_MODE_CTL, qpnp_iadc_mode_reg);
428 if (rc) {
429 pr_err("qpnp adc read adc failed with %d\n", rc);
430 return rc;
431 }
432
433 rc = qpnp_iadc_write_reg(QPNP_IADC_ADC_CH_SEL_CTL,
434 qpnp_iadc_ch_sel_reg);
435 if (rc) {
436 pr_err("qpnp adc read adc failed with %d\n", rc);
437 return rc;
438 }
439
440 rc = qpnp_iadc_write_reg(QPNP_ADC_DIG_PARAM,
441 qpnp_iadc_dig_param_reg);
442 if (rc) {
443 pr_err("qpnp adc read adc failed with %d\n", rc);
444 return rc;
445 }
446
447 rc = qpnp_iadc_write_reg(QPNP_HW_SETTLE_DELAY,
448 iadc->adc->amux_prop->hw_settle_time);
449 if (rc < 0) {
450 pr_err("qpnp adc configure error for hw settling time setup\n");
451 return rc;
452 }
453
454 rc = qpnp_iadc_write_reg(QPNP_FAST_AVG_CTL,
455 iadc->adc->amux_prop->fast_avg_setup);
456 if (rc < 0) {
457 pr_err("qpnp adc fast averaging configure error\n");
458 return rc;
459 }
460
Siddartha Mohanadoss3f219c42013-04-02 11:01:28 -0700461 INIT_COMPLETION(iadc->adc->adc_rslt_completion);
462
Siddartha Mohanadoss5ace1102012-08-20 23:18:10 -0700463 rc = qpnp_iadc_enable(true);
464 if (rc)
465 return rc;
466
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700467 rc = qpnp_iadc_write_reg(QPNP_CONV_REQ, qpnp_iadc_conv_req);
468 if (rc) {
469 pr_err("qpnp adc read adc failed with %d\n", rc);
470 return rc;
471 }
472
Siddartha Mohanadoss1a0d2032012-11-01 11:22:29 -0700473 rc = wait_for_completion_timeout(&iadc->adc->adc_rslt_completion,
474 QPNP_ADC_COMPLETION_TIMEOUT);
475 if (!rc) {
476 u8 status1 = 0;
477 rc = qpnp_iadc_read_reg(QPNP_STATUS1, &status1);
478 if (rc < 0)
479 return rc;
480 status1 &= (QPNP_STATUS1_REQ_STS | QPNP_STATUS1_EOC);
481 if (status1 == QPNP_STATUS1_EOC)
482 pr_debug("End of conversion status set\n");
483 else {
Siddartha Mohanadossd3a3c952012-12-10 16:55:19 -0800484 rc = qpnp_iadc_status_debug();
485 if (rc < 0) {
486 pr_err("status1 read failed with %d\n", rc);
487 return rc;
488 }
Siddartha Mohanadoss1a0d2032012-11-01 11:22:29 -0700489 return -EINVAL;
490 }
491 }
492
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700493 rc = qpnp_iadc_read_conversion_result(raw_code);
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700494 if (rc) {
495 pr_err("qpnp adc read adc failed with %d\n", rc);
496 return rc;
497 }
498
499 return 0;
500}
501
Abhijeet Dharmapurikar29005fa2013-07-15 17:57:27 -0700502#define IADC_CENTER 0xC000
503#define IADC_READING_RESOLUTION_N 542535
504#define IADC_READING_RESOLUTION_D 100000
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700505static int32_t qpnp_convert_raw_offset_voltage(void)
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700506{
507 struct qpnp_iadc_drv *iadc = qpnp_iadc;
Abhijeet Dharmapurikar29005fa2013-07-15 17:57:27 -0700508 s64 numerator;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700509
Siddartha Mohanadossd752e472013-02-26 18:30:14 -0800510 if ((iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw) == 0) {
511 pr_err("raw offset errors! raw_gain:0x%x and raw_offset:0x%x\n",
512 iadc->adc->calib.gain_raw, iadc->adc->calib.offset_raw);
513 return -EINVAL;
514 }
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700515
Abhijeet Dharmapurikar29005fa2013-07-15 17:57:27 -0700516 numerator = iadc->adc->calib.offset_raw - IADC_CENTER;
517 numerator *= IADC_READING_RESOLUTION_N;
518 iadc->adc->calib.offset_uv = div_s64(numerator,
519 IADC_READING_RESOLUTION_D);
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700520
Abhijeet Dharmapurikar29005fa2013-07-15 17:57:27 -0700521 numerator = iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw;
522 numerator *= IADC_READING_RESOLUTION_N;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700523
Abhijeet Dharmapurikar29005fa2013-07-15 17:57:27 -0700524 iadc->adc->calib.gain_uv = div_s64(numerator,
525 IADC_READING_RESOLUTION_D);
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700526
Siddartha Mohanadossb2a42372013-03-26 15:53:41 -0700527 pr_debug("gain_uv:%d offset_uv:%d\n",
528 iadc->adc->calib.gain_uv, iadc->adc->calib.offset_uv);
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700529 return 0;
530}
531
Abhijeet Dharmapurikar0ef9b5c2013-07-15 18:24:38 -0700532int32_t qpnp_iadc_calibrate_for_trim(bool batfet_closed)
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700533{
534 struct qpnp_iadc_drv *iadc = qpnp_iadc;
535 uint8_t rslt_lsb, rslt_msb;
536 int32_t rc = 0;
537 uint16_t raw_data;
Siddartha Mohanadossa32ea2a2013-02-12 09:58:31 -0800538 uint32_t mode_sel = 0;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700539
Siddartha Mohanadossce7694c2013-07-08 16:47:28 -0700540 if (!iadc || !iadc->iadc_initialized)
541 return -EPROBE_DEFER;
542
Siddartha Mohanadossa9b91672013-02-22 18:32:27 -0800543 mutex_lock(&iadc->adc->adc_lock);
544
Siddartha Mohanadossa32ea2a2013-02-12 09:58:31 -0800545 rc = qpnp_iadc_configure(GAIN_CALIBRATION_17P857MV,
546 &raw_data, mode_sel);
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700547 if (rc < 0) {
548 pr_err("qpnp adc result read failed with %d\n", rc);
549 goto fail;
550 }
551
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700552 iadc->adc->calib.gain_raw = raw_data;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700553
Abhijeet Dharmapurikar0ef9b5c2013-07-15 18:24:38 -0700554 /*
555 * there is a features in the BMS where if the batfet is opened
556 * the BMS reads from INTERNAL_RSENSE (channel 0) actually go to
557 * OFFSET_CALIBRATION_CSP_CSN (channel 5). Hence if batfet is opened
558 * we have to calibrate based on OFFSET_CALIBRATION_CSP_CSN even for
559 * internal rsense.
560 */
561 if (!batfet_closed || iadc->external_rsense) {
Siddartha Mohanadossce7694c2013-07-08 16:47:28 -0700562 /* external offset calculation */
563 rc = qpnp_iadc_configure(OFFSET_CALIBRATION_CSP_CSN,
Siddartha Mohanadossa32ea2a2013-02-12 09:58:31 -0800564 &raw_data, mode_sel);
Siddartha Mohanadossce7694c2013-07-08 16:47:28 -0700565 if (rc < 0) {
566 pr_err("qpnp adc result read failed with %d\n", rc);
567 goto fail;
568 }
569 } else {
570 /* internal offset calculation */
571 rc = qpnp_iadc_configure(OFFSET_CALIBRATION_CSP2_CSN2,
572 &raw_data, mode_sel);
573 if (rc < 0) {
574 pr_err("qpnp adc result read failed with %d\n", rc);
575 goto fail;
576 }
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700577 }
578
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700579 iadc->adc->calib.offset_raw = raw_data;
580 if (rc < 0) {
581 pr_err("qpnp adc offset/gain calculation failed\n");
582 goto fail;
583 }
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700584
Siddartha Mohanadossb2a42372013-03-26 15:53:41 -0700585 pr_debug("raw gain:0x%x, raw offset:0x%x\n",
586 iadc->adc->calib.gain_raw, iadc->adc->calib.offset_raw);
587
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700588 rc = qpnp_convert_raw_offset_voltage();
Siddartha Mohanadossd752e472013-02-26 18:30:14 -0800589 if (rc < 0) {
590 pr_err("qpnp raw_voltage conversion failed\n");
591 goto fail;
592 }
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700593
594 rslt_msb = (raw_data & QPNP_RAW_CODE_16_BIT_MSB_MASK) >>
595 QPNP_BIT_SHIFT_8;
596 rslt_lsb = raw_data & QPNP_RAW_CODE_16_BIT_LSB_MASK;
597
Siddartha Mohanadossb2a42372013-03-26 15:53:41 -0700598 pr_debug("trim values:lsb:0x%x and msb:0x%x\n", rslt_lsb, rslt_msb);
599
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700600 rc = qpnp_iadc_write_reg(QPNP_IADC_SEC_ACCESS,
601 QPNP_IADC_SEC_ACCESS_DATA);
602 if (rc < 0) {
603 pr_err("qpnp iadc configure error for sec access\n");
604 goto fail;
605 }
606
607 rc = qpnp_iadc_write_reg(QPNP_IADC_MSB_OFFSET,
608 rslt_msb);
609 if (rc < 0) {
610 pr_err("qpnp iadc configure error for MSB write\n");
611 goto fail;
612 }
613
614 rc = qpnp_iadc_write_reg(QPNP_IADC_SEC_ACCESS,
615 QPNP_IADC_SEC_ACCESS_DATA);
616 if (rc < 0) {
617 pr_err("qpnp iadc configure error for sec access\n");
618 goto fail;
619 }
620
621 rc = qpnp_iadc_write_reg(QPNP_IADC_LSB_OFFSET,
622 rslt_lsb);
623 if (rc < 0) {
624 pr_err("qpnp iadc configure error for LSB write\n");
625 goto fail;
626 }
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700627fail:
Siddartha Mohanadossa9b91672013-02-22 18:32:27 -0800628 mutex_unlock(&iadc->adc->adc_lock);
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700629 return rc;
630}
Siddartha Mohanadoss06673922013-03-27 11:14:19 -0700631EXPORT_SYMBOL(qpnp_iadc_calibrate_for_trim);
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700632
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700633static void qpnp_iadc_work(struct work_struct *work)
634{
635 struct qpnp_iadc_drv *iadc = qpnp_iadc;
636 int rc = 0;
637
Abhijeet Dharmapurikar0ef9b5c2013-07-15 18:24:38 -0700638 rc = qpnp_iadc_calibrate_for_trim(true);
Siddartha Mohanadoss73ae69b2013-04-03 17:34:03 -0700639 if (rc)
640 pr_debug("periodic IADC calibration failed\n");
641 else
Siddartha Mohanadoss12a15ea2013-02-05 19:13:41 -0800642 schedule_delayed_work(&iadc->iadc_work,
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700643 round_jiffies_relative(msecs_to_jiffies
644 (QPNP_IADC_CALIB_SECONDS)));
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700645 return;
646}
647
Siddartha Mohanadoss5ace1102012-08-20 23:18:10 -0700648static int32_t qpnp_iadc_version_check(void)
649{
650 uint8_t revision;
651 int rc;
652
653 rc = qpnp_iadc_read_reg(QPNP_IADC_REVISION2, &revision);
654 if (rc < 0) {
655 pr_err("qpnp adc result read failed with %d\n", rc);
656 return rc;
657 }
658
659 if (revision < QPNP_IADC_SUPPORTED_REVISION2) {
660 pr_err("IADC Version not supported\n");
661 return -EINVAL;
662 }
663
664 return 0;
665}
666
667int32_t qpnp_iadc_is_ready(void)
668{
669 struct qpnp_iadc_drv *iadc = qpnp_iadc;
670
671 if (!iadc || !iadc->iadc_initialized)
672 return -EPROBE_DEFER;
673 else
674 return 0;
675}
676EXPORT_SYMBOL(qpnp_iadc_is_ready);
677
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700678int32_t qpnp_iadc_get_rsense(int32_t *rsense)
679{
Siddartha Mohanadossa9b91672013-02-22 18:32:27 -0800680 struct qpnp_iadc_drv *iadc = qpnp_iadc;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700681 uint8_t rslt_rsense;
Siddartha Mohanadoss58279542013-05-28 16:20:46 -0700682 int32_t rc = 0, sign_bit = 0;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700683
Siddartha Mohanadossa9b91672013-02-22 18:32:27 -0800684 if (!iadc || !iadc->iadc_initialized)
685 return -EPROBE_DEFER;
686
Siddartha Mohanadoss58279542013-05-28 16:20:46 -0700687 if (iadc->external_rsense) {
Siddartha Mohanadosse70010b2013-04-04 14:51:41 -0700688 *rsense = iadc->rsense;
Siddartha Mohanadoss58279542013-05-28 16:20:46 -0700689 return rc;
690 }
Siddartha Mohanadosse70010b2013-04-04 14:51:41 -0700691
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700692 rc = qpnp_iadc_read_reg(QPNP_IADC_NOMINAL_RSENSE, &rslt_rsense);
693 if (rc < 0) {
694 pr_err("qpnp adc rsense read failed with %d\n", rc);
695 return rc;
696 }
697
Siddartha Mohanadossb2a42372013-03-26 15:53:41 -0700698 pr_debug("rsense:0%x\n", rslt_rsense);
699
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700700 if (rslt_rsense & QPNP_RSENSE_MSB_SIGN_CHECK)
701 sign_bit = 1;
702
703 rslt_rsense &= ~QPNP_RSENSE_MSB_SIGN_CHECK;
704
705 if (sign_bit)
706 *rsense = QPNP_IADC_INTERNAL_RSENSE_N_OHMS_FACTOR -
707 (rslt_rsense * QPNP_IADC_RSENSE_LSB_N_OHMS_PER_BIT);
708 else
709 *rsense = QPNP_IADC_INTERNAL_RSENSE_N_OHMS_FACTOR +
710 (rslt_rsense * QPNP_IADC_RSENSE_LSB_N_OHMS_PER_BIT);
711
712 return rc;
713}
Xiaozhe Shi767fdb62013-01-10 15:09:08 -0800714EXPORT_SYMBOL(qpnp_iadc_get_rsense);
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700715
Siddartha Mohanadossa9b91672013-02-22 18:32:27 -0800716static int32_t qpnp_check_pmic_temp(void)
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700717{
718 struct qpnp_iadc_drv *iadc = qpnp_iadc;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700719 struct qpnp_vadc_result result_pmic_therm;
Siddartha Mohanadossd4e9edc2013-04-17 14:42:16 -0700720 int64_t die_temp_offset;
Siddartha Mohanadossd752e472013-02-26 18:30:14 -0800721 int rc = 0;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700722
723 rc = qpnp_vadc_read(DIE_TEMP, &result_pmic_therm);
724 if (rc < 0)
725 return rc;
726
Siddartha Mohanadossd4e9edc2013-04-17 14:42:16 -0700727 die_temp_offset = result_pmic_therm.physical -
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700728 iadc->die_temp;
Siddartha Mohanadossd4e9edc2013-04-17 14:42:16 -0700729 if (die_temp_offset < 0)
730 die_temp_offset = -die_temp_offset;
731
732 if (die_temp_offset > QPNP_IADC_DIE_TEMP_CALIB_OFFSET) {
Abhijeet Dharmapurikar0ef9b5c2013-07-15 18:24:38 -0700733 iadc->die_temp = result_pmic_therm.physical;
734 rc = qpnp_iadc_calibrate_for_trim(true);
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700735 if (rc)
736 pr_err("periodic IADC calibration failed\n");
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700737 }
738
Siddartha Mohanadossd752e472013-02-26 18:30:14 -0800739 return rc;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700740}
741
742int32_t qpnp_iadc_read(enum qpnp_iadc_channels channel,
743 struct qpnp_iadc_result *result)
744{
745 struct qpnp_iadc_drv *iadc = qpnp_iadc;
Siddartha Mohanadossa32ea2a2013-02-12 09:58:31 -0800746 int32_t rc, rsense_n_ohms, sign = 0, num, mode_sel = 0;
Siddartha Mohanadoss22618be2013-04-02 15:02:19 -0700747 int32_t rsense_u_ohms = 0;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700748 int64_t result_current;
749 uint16_t raw_data;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700750
Siddartha Mohanadoss5ace1102012-08-20 23:18:10 -0700751 if (!iadc || !iadc->iadc_initialized)
752 return -EPROBE_DEFER;
753
Siddartha Mohanadossa32ea2a2013-02-12 09:58:31 -0800754 if (!iadc->iadc_mode_sel) {
755 rc = qpnp_check_pmic_temp();
756 if (rc) {
757 pr_err("Error checking pmic therm temp\n");
758 return rc;
759 }
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700760 }
761
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700762 mutex_lock(&iadc->adc->adc_lock);
763
Siddartha Mohanadossa32ea2a2013-02-12 09:58:31 -0800764 rc = qpnp_iadc_configure(channel, &raw_data, mode_sel);
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700765 if (rc < 0) {
766 pr_err("qpnp adc result read failed with %d\n", rc);
767 goto fail;
768 }
769
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700770 rc = qpnp_iadc_get_rsense(&rsense_n_ohms);
Siddartha Mohanadossb2a42372013-03-26 15:53:41 -0700771 pr_debug("current raw:0%x and rsense:%d\n",
772 raw_data, rsense_n_ohms);
Siddartha Mohanadoss22618be2013-04-02 15:02:19 -0700773 rsense_u_ohms = rsense_n_ohms/1000;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700774 num = raw_data - iadc->adc->calib.offset_raw;
775 if (num < 0) {
776 sign = 1;
777 num = -num;
778 }
779
780 result->result_uv = (num * QPNP_ADC_GAIN_NV)/
781 (iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw);
782 result_current = result->result_uv;
783 result_current *= QPNP_IADC_NANO_VOLTS_FACTOR;
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700784 /* Intentional fall through. Process the result w/o comp */
Siddartha Mohanadoss22618be2013-04-02 15:02:19 -0700785 do_div(result_current, rsense_u_ohms);
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700786
787 if (sign) {
788 result->result_uv = -result->result_uv;
789 result_current = -result_current;
790 }
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -0700791 rc = qpnp_iadc_comp_result(&result_current);
792 if (rc < 0)
793 pr_err("Error during compensating the IADC\n");
794 rc = 0;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700795
796 result->result_ua = (int32_t) result_current;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700797fail:
798 mutex_unlock(&iadc->adc->adc_lock);
799
800 return rc;
801}
802EXPORT_SYMBOL(qpnp_iadc_read);
803
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700804int32_t qpnp_iadc_get_gain_and_offset(struct qpnp_iadc_calib *result)
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700805{
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700806 struct qpnp_iadc_drv *iadc = qpnp_iadc;
807 int rc;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700808
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700809 if (!iadc || !iadc->iadc_initialized)
810 return -EPROBE_DEFER;
811
812 rc = qpnp_check_pmic_temp();
813 if (rc) {
814 pr_err("Error checking pmic therm temp\n");
815 return rc;
816 }
817
818 mutex_lock(&iadc->adc->adc_lock);
819 result->gain_raw = iadc->adc->calib.gain_raw;
820 result->ideal_gain_nv = QPNP_ADC_GAIN_NV;
821 result->gain_uv = iadc->adc->calib.gain_uv;
822 result->offset_raw = iadc->adc->calib.offset_raw;
823 result->ideal_offset_uv =
824 QPNP_OFFSET_CALIBRATION_SHORT_CADC_LEADS_IDEAL;
825 result->offset_uv = iadc->adc->calib.offset_uv;
Siddartha Mohanadossb2a42372013-03-26 15:53:41 -0700826 pr_debug("raw gain:0%x, raw offset:0%x\n",
827 result->gain_raw, result->offset_raw);
828 pr_debug("gain_uv:%d offset_uv:%d\n",
829 result->gain_uv, result->offset_uv);
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700830 mutex_unlock(&iadc->adc->adc_lock);
831
832 return 0;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700833}
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700834EXPORT_SYMBOL(qpnp_iadc_get_gain_and_offset);
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700835
Siddartha Mohanadossa32ea2a2013-02-12 09:58:31 -0800836int32_t qpnp_iadc_vadc_sync_read(
837 enum qpnp_iadc_channels i_channel, struct qpnp_iadc_result *i_result,
838 enum qpnp_vadc_channels v_channel, struct qpnp_vadc_result *v_result)
839{
840 struct qpnp_iadc_drv *iadc = qpnp_iadc;
841 int rc = 0;
842
843 if (!iadc || !iadc->iadc_initialized)
844 return -EPROBE_DEFER;
845
846 mutex_lock(&iadc->iadc_vadc_lock);
847
848 rc = qpnp_check_pmic_temp();
849 if (rc) {
850 pr_err("PMIC die temp check failed\n");
851 goto fail;
852 }
853
854 iadc->iadc_mode_sel = true;
855
856 rc = qpnp_vadc_iadc_sync_request(v_channel);
857 if (rc) {
858 pr_err("Configuring VADC failed\n");
859 goto fail;
860 }
861
862 rc = qpnp_iadc_read(i_channel, i_result);
863 if (rc)
864 pr_err("Configuring IADC failed\n");
865 /* Intentional fall through to release VADC */
866
867 rc = qpnp_vadc_iadc_sync_complete_request(v_channel,
868 v_result);
869 if (rc)
870 pr_err("Releasing VADC failed\n");
871fail:
872 iadc->iadc_mode_sel = false;
873
874 mutex_unlock(&iadc->iadc_vadc_lock);
875
876 return rc;
877}
878EXPORT_SYMBOL(qpnp_iadc_vadc_sync_read);
879
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700880static ssize_t qpnp_iadc_show(struct device *dev,
881 struct device_attribute *devattr, char *buf)
882{
883 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700884 struct qpnp_iadc_result result;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700885 int rc = -1;
886
887 rc = qpnp_iadc_read(attr->index, &result);
888
889 if (rc)
890 return 0;
891
892 return snprintf(buf, QPNP_ADC_HWMON_NAME_LENGTH,
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -0700893 "Result:%d\n", result.result_ua);
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700894}
895
896static struct sensor_device_attribute qpnp_adc_attr =
897 SENSOR_ATTR(NULL, S_IRUGO, qpnp_iadc_show, NULL, 0);
898
899static int32_t qpnp_iadc_init_hwmon(struct spmi_device *spmi)
900{
901 struct qpnp_iadc_drv *iadc = qpnp_iadc;
902 struct device_node *child;
903 struct device_node *node = spmi->dev.of_node;
904 int rc = 0, i = 0, channel;
905
906 for_each_child_of_node(node, child) {
907 channel = iadc->adc->adc_channels[i].channel_num;
908 qpnp_adc_attr.index = iadc->adc->adc_channels[i].channel_num;
909 qpnp_adc_attr.dev_attr.attr.name =
910 iadc->adc->adc_channels[i].name;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700911 memcpy(&iadc->sens_attr[i], &qpnp_adc_attr,
912 sizeof(qpnp_adc_attr));
Stephen Boyd8a5c4e42012-10-30 11:07:22 -0700913 sysfs_attr_init(&iadc->sens_attr[i].dev_attr.attr);
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700914 rc = device_create_file(&spmi->dev,
915 &iadc->sens_attr[i].dev_attr);
916 if (rc) {
917 dev_err(&spmi->dev,
918 "device_create_file failed for dev %s\n",
919 iadc->adc->adc_channels[i].name);
920 goto hwmon_err_sens;
921 }
922 i++;
923 }
924
925 return 0;
926hwmon_err_sens:
927 pr_err("Init HWMON failed for qpnp_iadc with %d\n", rc);
928 return rc;
929}
930
931static int __devinit qpnp_iadc_probe(struct spmi_device *spmi)
932{
933 struct qpnp_iadc_drv *iadc;
934 struct qpnp_adc_drv *adc_qpnp;
935 struct device_node *node = spmi->dev.of_node;
936 struct device_node *child;
937 int rc, count_adc_channel_list = 0;
938
939 if (!node)
940 return -EINVAL;
941
942 if (qpnp_iadc) {
943 pr_err("IADC already in use\n");
944 return -EBUSY;
945 }
946
947 for_each_child_of_node(node, child)
948 count_adc_channel_list++;
949
950 if (!count_adc_channel_list) {
951 pr_err("No channel listing\n");
952 return -EINVAL;
953 }
954
955 iadc = devm_kzalloc(&spmi->dev, sizeof(struct qpnp_iadc_drv) +
956 (sizeof(struct sensor_device_attribute) *
957 count_adc_channel_list), GFP_KERNEL);
958 if (!iadc) {
959 dev_err(&spmi->dev, "Unable to allocate memory\n");
960 return -ENOMEM;
961 }
962
963 adc_qpnp = devm_kzalloc(&spmi->dev, sizeof(struct qpnp_adc_drv),
964 GFP_KERNEL);
965 if (!adc_qpnp) {
966 dev_err(&spmi->dev, "Unable to allocate memory\n");
Siddartha Mohanadossb60f6462012-11-20 18:06:51 -0800967 rc = -ENOMEM;
968 goto fail;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700969 }
970
971 iadc->adc = adc_qpnp;
972
973 rc = qpnp_adc_get_devicetree_data(spmi, iadc->adc);
974 if (rc) {
975 dev_err(&spmi->dev, "failed to read device tree\n");
Siddartha Mohanadossb60f6462012-11-20 18:06:51 -0800976 goto fail;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700977 }
978
Stephen Boydbeab4502013-04-25 10:18:17 -0700979 mutex_init(&iadc->adc->adc_lock);
980
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700981 rc = of_property_read_u32(node, "qcom,rsense",
982 &iadc->rsense);
Siddartha Mohanadosse70010b2013-04-04 14:51:41 -0700983 if (rc)
984 pr_debug("Defaulting to internal rsense\n");
985 else {
986 pr_debug("Use external rsense\n");
987 iadc->external_rsense = true;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700988 }
989
Siddartha Mohanadoss12109952012-11-20 14:57:51 -0800990 rc = devm_request_irq(&spmi->dev, iadc->adc->adc_irq_eoc,
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700991 qpnp_iadc_isr,
992 IRQF_TRIGGER_RISING, "qpnp_iadc_interrupt", iadc);
993 if (rc) {
994 dev_err(&spmi->dev, "failed to request adc irq\n");
Siddartha Mohanadossb60f6462012-11-20 18:06:51 -0800995 goto fail;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700996 } else
Siddartha Mohanadoss12109952012-11-20 14:57:51 -0800997 enable_irq_wake(iadc->adc->adc_irq_eoc);
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700998
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -0700999 dev_set_drvdata(&spmi->dev, iadc);
1000 qpnp_iadc = iadc;
1001
1002 rc = qpnp_iadc_init_hwmon(spmi);
1003 if (rc) {
1004 dev_err(&spmi->dev, "failed to initialize qpnp hwmon adc\n");
Siddartha Mohanadossb60f6462012-11-20 18:06:51 -08001005 goto fail;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -07001006 }
1007 iadc->iadc_hwmon = hwmon_device_register(&iadc->adc->spmi->dev);
1008
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -07001009 rc = qpnp_iadc_version_check();
1010 if (rc) {
1011 dev_err(&spmi->dev, "IADC version not supported\n");
Siddartha Mohanadossb60f6462012-11-20 18:06:51 -08001012 goto fail;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -07001013 }
1014
Siddartha Mohanadossa32ea2a2013-02-12 09:58:31 -08001015 mutex_init(&iadc->iadc_vadc_lock);
Siddartha Mohanadossa9b91672013-02-22 18:32:27 -08001016 INIT_DELAYED_WORK(&iadc->iadc_work, qpnp_iadc_work);
Siddartha Mohanadoss4e64f8c2013-04-08 15:57:32 -07001017 rc = qpnp_iadc_comp_info();
1018 if (rc) {
1019 dev_err(&spmi->dev, "abstracting IADC comp info failed!\n");
1020 goto fail;
1021 }
Siddartha Mohanadoss12a15ea2013-02-05 19:13:41 -08001022 iadc->iadc_initialized = true;
Siddartha Mohanadoss5e2988d2012-09-24 17:03:56 -07001023
Abhijeet Dharmapurikar0ef9b5c2013-07-15 18:24:38 -07001024 rc = qpnp_iadc_calibrate_for_trim(true);
Siddartha Mohanadossa9b91672013-02-22 18:32:27 -08001025 if (rc)
1026 dev_err(&spmi->dev, "failed to calibrate for USR trim\n");
1027 schedule_delayed_work(&iadc->iadc_work,
1028 round_jiffies_relative(msecs_to_jiffies
1029 (QPNP_IADC_CALIB_SECONDS)));
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -07001030 return 0;
Siddartha Mohanadossb60f6462012-11-20 18:06:51 -08001031fail:
Siddartha Mohanadoss32019b52012-12-23 17:05:45 -08001032 qpnp_iadc = NULL;
Siddartha Mohanadossb60f6462012-11-20 18:06:51 -08001033 return rc;
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -07001034}
1035
1036static int __devexit qpnp_iadc_remove(struct spmi_device *spmi)
1037{
1038 struct qpnp_iadc_drv *iadc = dev_get_drvdata(&spmi->dev);
1039 struct device_node *node = spmi->dev.of_node;
1040 struct device_node *child;
1041 int i = 0;
1042
Siddartha Mohanadossa9b91672013-02-22 18:32:27 -08001043 cancel_delayed_work(&iadc->iadc_work);
Siddartha Mohanadossa32ea2a2013-02-12 09:58:31 -08001044 mutex_destroy(&iadc->iadc_vadc_lock);
Siddartha Mohanadossc4a6af12012-07-13 18:50:12 -07001045 for_each_child_of_node(node, child) {
1046 device_remove_file(&spmi->dev,
1047 &iadc->sens_attr[i].dev_attr);
1048 i++;
1049 }
1050 dev_set_drvdata(&spmi->dev, NULL);
1051
1052 return 0;
1053}
1054
1055static const struct of_device_id qpnp_iadc_match_table[] = {
1056 { .compatible = "qcom,qpnp-iadc",
1057 },
1058 {}
1059};
1060
1061static struct spmi_driver qpnp_iadc_driver = {
1062 .driver = {
1063 .name = "qcom,qpnp-iadc",
1064 .of_match_table = qpnp_iadc_match_table,
1065 },
1066 .probe = qpnp_iadc_probe,
1067 .remove = qpnp_iadc_remove,
1068};
1069
1070static int __init qpnp_iadc_init(void)
1071{
1072 return spmi_driver_register(&qpnp_iadc_driver);
1073}
1074module_init(qpnp_iadc_init);
1075
1076static void __exit qpnp_iadc_exit(void)
1077{
1078 spmi_driver_unregister(&qpnp_iadc_driver);
1079}
1080module_exit(qpnp_iadc_exit);
1081
1082MODULE_DESCRIPTION("QPNP PMIC current ADC driver");
1083MODULE_LICENSE("GPL v2");