blob: 69ea39ad57449df7329991df0c858de426a8f4f7 [file] [log] [blame]
Abhijeet Dharmapurikar50c0f2c2017-01-04 19:18:32 -08001/* Copyright (c) 2016-2017 The Linux Foundation. All rights reserved.
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -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#include <linux/device.h>
14#include <linux/module.h>
15#include <linux/platform_device.h>
16#include <linux/regmap.h>
17#include <linux/power_supply.h>
18#include <linux/interrupt.h>
19#include <linux/of.h>
20#include <linux/of_irq.h>
21#include <linux/qpnp/qpnp-revid.h>
Abhijeet Dharmapurikardd119c62017-03-20 15:54:59 -070022#include <linux/pmic-voter.h>
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -070023
24#define QNOVO_REVISION1 0x00
25#define QNOVO_REVISION2 0x01
26#define QNOVO_PERPH_TYPE 0x04
27#define QNOVO_PERPH_SUBTYPE 0x05
28#define QNOVO_PTTIME_STS 0x07
29#define QNOVO_PTRAIN_STS 0x08
30#define QNOVO_ERROR_STS 0x09
31#define QNOVO_ERROR_BIT BIT(0)
Harry Yange7a96842017-03-23 21:41:48 -070032#define QNOVO_ERROR_STS2 0x0A
Harry Yang96ec88e2017-03-23 23:43:53 -070033#define QNOVO_ERROR_CHARGING_DISABLED BIT(1)
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -070034#define QNOVO_INT_RT_STS 0x10
35#define QNOVO_INT_SET_TYPE 0x11
36#define QNOVO_INT_POLARITY_HIGH 0x12
37#define QNOVO_INT_POLARITY_LOW 0x13
38#define QNOVO_INT_LATCHED_CLR 0x14
39#define QNOVO_INT_EN_SET 0x15
40#define QNOVO_INT_EN_CLR 0x16
41#define QNOVO_INT_LATCHED_STS 0x18
42#define QNOVO_INT_PENDING_STS 0x19
43#define QNOVO_INT_MID_SEL 0x1A
44#define QNOVO_INT_PRIORITY 0x1B
45#define QNOVO_PE_CTRL 0x40
46#define QNOVO_PREST1_CTRL 0x41
47#define QNOVO_PPULS1_LSB_CTRL 0x42
48#define QNOVO_PPULS1_MSB_CTRL 0x43
49#define QNOVO_NREST1_CTRL 0x44
50#define QNOVO_NPULS1_CTRL 0x45
51#define QNOVO_PPCNT_CTRL 0x46
52#define QNOVO_VLIM1_LSB_CTRL 0x47
53#define QNOVO_VLIM1_MSB_CTRL 0x48
54#define QNOVO_PTRAIN_EN 0x49
55#define QNOVO_PTRAIN_EN_BIT BIT(0)
56#define QNOVO_PE_CTRL2 0x4A
57#define QNOVO_PREST2_LSB_CTRL 0x50
58#define QNOVO_PREST2_MSB_CTRL 0x51
59#define QNOVO_PPULS2_LSB_CTRL 0x52
60#define QNOVO_PPULS2_MSB_CTRL 0x53
61#define QNOVO_NREST2_CTRL 0x54
62#define QNOVO_NPULS2_CTRL 0x55
63#define QNOVO_VLIM2_LSB_CTRL 0x56
64#define QNOVO_VLIM2_MSB_CTRL 0x57
65#define QNOVO_PVOLT1_LSB 0x60
66#define QNOVO_PVOLT1_MSB 0x61
67#define QNOVO_PCUR1_LSB 0x62
68#define QNOVO_PCUR1_MSB 0x63
69#define QNOVO_PVOLT2_LSB 0x70
70#define QNOVO_PVOLT2_MSB 0x71
71#define QNOVO_RVOLT2_LSB 0x72
72#define QNOVO_RVOLT2_MSB 0x73
73#define QNOVO_PCUR2_LSB 0x74
74#define QNOVO_PCUR2_MSB 0x75
75#define QNOVO_SCNT 0x80
76#define QNOVO_VMAX_LSB 0x90
77#define QNOVO_VMAX_MSB 0x91
78#define QNOVO_SNUM 0x92
79
80/* Registers ending in 0 imply external rsense */
81#define QNOVO_IADC_OFFSET_0 0xA0
82#define QNOVO_IADC_OFFSET_1 0xA1
83#define QNOVO_IADC_GAIN_0 0xA2
84#define QNOVO_IADC_GAIN_1 0xA3
85#define QNOVO_VADC_OFFSET 0xA4
86#define QNOVO_VADC_GAIN 0xA5
87#define QNOVO_IADC_GAIN_2 0xA6
88#define QNOVO_SPARE 0xA7
89#define QNOVO_STRM_CTRL 0xA8
90#define QNOVO_IADC_OFFSET_OVR_VAL 0xA9
91#define QNOVO_IADC_OFFSET_OVR 0xAA
Harry Yang58acd162017-04-07 17:08:06 -070092
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -070093#define QNOVO_DISABLE_CHARGING 0xAB
Harry Yang58acd162017-04-07 17:08:06 -070094#define ERR_SWITCHER_DISABLED BIT(7)
95#define ERR_JEITA_SOFT_CONDITION BIT(6)
96#define ERR_BAT_OV BIT(5)
97#define ERR_CV_MODE BIT(4)
98#define ERR_BATTERY_MISSING BIT(3)
99#define ERR_SAFETY_TIMER_EXPIRED BIT(2)
100#define ERR_CHARGING_DISABLED BIT(1)
101#define ERR_JEITA_HARD_CONDITION BIT(0)
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700102
103#define QNOVO_TR_IADC_OFFSET_0 0xF1
104#define QNOVO_TR_IADC_OFFSET_1 0xF2
105
106#define DRV_MAJOR_VERSION 1
107#define DRV_MINOR_VERSION 0
108
109#define IADC_LSB_NA 2441400
110#define VADC_LSB_NA 1220700
111#define GAIN_LSB_FACTOR 976560
112
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700113#define USER_VOTER "user_voter"
114#define OK_TO_QNOVO_VOTER "ok_to_qnovo_voter"
115
116#define QNOVO_VOTER "qnovo_voter"
Abhijeet Dharmapurikar933d4942017-07-07 13:49:29 -0700117#define FG_AVAILABLE_VOTER "FG_AVAILABLE_VOTER"
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -0700118#define QNOVO_OVERALL_VOTER "QNOVO_OVERALL_VOTER"
119#define QNI_PT_VOTER "QNI_PT_VOTER"
120#define ESR_VOTER "ESR_VOTER"
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700121
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -0700122#define HW_OK_TO_QNOVO_VOTER "HW_OK_TO_QNOVO_VOTER"
123#define CHG_READY_VOTER "CHG_READY_VOTER"
124#define USB_READY_VOTER "USB_READY_VOTER"
125#define DC_READY_VOTER "DC_READY_VOTER"
126
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700127struct qnovo_dt_props {
128 bool external_rsense;
129 struct device_node *revid_dev_node;
130};
131
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700132struct qnovo {
133 int base;
134 struct mutex write_lock;
135 struct regmap *regmap;
136 struct qnovo_dt_props dt;
137 struct device *dev;
138 struct votable *disable_votable;
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -0700139 struct votable *pt_dis_votable;
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -0700140 struct votable *not_ok_to_qnovo_votable;
141 struct votable *chg_ready_votable;
142 struct votable *awake_votable;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700143 struct class qnovo_class;
144 struct pmic_revid_data *pmic_rev_id;
145 u32 wa_flags;
146 s64 external_offset_nA;
147 s64 internal_offset_nA;
148 s64 offset_nV;
149 s64 external_i_gain_mega;
150 s64 internal_i_gain_mega;
151 s64 v_gain_mega;
152 struct notifier_block nb;
153 struct power_supply *batt_psy;
Abhijeet Dharmapurikar933d4942017-07-07 13:49:29 -0700154 struct power_supply *bms_psy;
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -0700155 struct power_supply *usb_psy;
156 struct power_supply *dc_psy;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700157 struct work_struct status_change_work;
158 int fv_uV_request;
159 int fcc_uA_request;
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -0700160 int usb_present;
161 int dc_present;
162 struct delayed_work usb_debounce_work;
163 struct delayed_work dc_debounce_work;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700164};
165
166static int debug_mask;
167module_param_named(debug_mask, debug_mask, int, 0600);
168
169#define qnovo_dbg(chip, reason, fmt, ...) \
170 do { \
171 if (debug_mask & (reason)) \
172 dev_info(chip->dev, fmt, ##__VA_ARGS__); \
173 else \
174 dev_dbg(chip->dev, fmt, ##__VA_ARGS__); \
175 } while (0)
176
177static bool is_secure(struct qnovo *chip, int addr)
178{
179 /* assume everything above 0x40 is secure */
180 return (bool)(addr >= 0x40);
181}
182
183static int qnovo_read(struct qnovo *chip, u16 addr, u8 *buf, int len)
184{
185 return regmap_bulk_read(chip->regmap, chip->base + addr, buf, len);
186}
187
188static int qnovo_masked_write(struct qnovo *chip, u16 addr, u8 mask, u8 val)
189{
190 int rc = 0;
191
192 mutex_lock(&chip->write_lock);
193 if (is_secure(chip, addr)) {
194 rc = regmap_write(chip->regmap,
195 ((chip->base + addr) & ~(0xFF)) | 0xD0, 0xA5);
196 if (rc < 0)
197 goto unlock;
198 }
199
200 rc = regmap_update_bits(chip->regmap, chip->base + addr, mask, val);
201
202unlock:
203 mutex_unlock(&chip->write_lock);
204 return rc;
205}
206
207static int qnovo_write(struct qnovo *chip, u16 addr, u8 *buf, int len)
208{
209 int i, rc = 0;
210 bool is_start_secure, is_end_secure;
211
212 is_start_secure = is_secure(chip, addr);
213 is_end_secure = is_secure(chip, addr + len);
214
215 if (!is_start_secure && !is_end_secure) {
216 mutex_lock(&chip->write_lock);
217 rc = regmap_bulk_write(chip->regmap, chip->base + addr,
218 buf, len);
219 goto unlock;
220 }
221
222 mutex_lock(&chip->write_lock);
223 for (i = addr; i < addr + len; i++) {
224 if (is_secure(chip, i)) {
225 rc = regmap_write(chip->regmap,
226 ((chip->base + i) & ~(0xFF)) | 0xD0, 0xA5);
227 if (rc < 0)
228 goto unlock;
229 }
230 rc = regmap_write(chip->regmap, chip->base + i, buf[i - addr]);
231 if (rc < 0)
232 goto unlock;
233 }
234
235unlock:
236 mutex_unlock(&chip->write_lock);
237 return rc;
238}
239
Harry Yang2e324ba2017-02-07 21:33:19 -0800240static bool is_batt_available(struct qnovo *chip)
241{
242 if (!chip->batt_psy)
243 chip->batt_psy = power_supply_get_by_name("battery");
244
245 if (!chip->batt_psy)
246 return false;
247
248 return true;
249}
250
Abhijeet Dharmapurikar933d4942017-07-07 13:49:29 -0700251static bool is_fg_available(struct qnovo *chip)
252{
253 if (!chip->bms_psy)
254 chip->bms_psy = power_supply_get_by_name("bms");
255
256 if (!chip->bms_psy)
257 return false;
258
259 return true;
260}
261
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -0700262static bool is_usb_available(struct qnovo *chip)
263{
264 if (!chip->usb_psy)
265 chip->usb_psy = power_supply_get_by_name("usb");
266
267 if (!chip->usb_psy)
268 return false;
269
270 return true;
271}
272
273static bool is_dc_available(struct qnovo *chip)
274{
275 if (!chip->dc_psy)
276 chip->dc_psy = power_supply_get_by_name("dc");
277
278 if (!chip->dc_psy)
279 return false;
280
281 return true;
282}
283
Harry Yang2e324ba2017-02-07 21:33:19 -0800284static int qnovo_batt_psy_update(struct qnovo *chip, bool disable)
285{
286 union power_supply_propval pval = {0};
287 int rc = 0;
288
289 if (!is_batt_available(chip))
290 return -EINVAL;
291
292 if (chip->fv_uV_request != -EINVAL) {
293 pval.intval = disable ? -EINVAL : chip->fv_uV_request;
294 rc = power_supply_set_property(chip->batt_psy,
295 POWER_SUPPLY_PROP_VOLTAGE_QNOVO,
296 &pval);
297 if (rc < 0) {
298 pr_err("Couldn't set prop qnovo_fv rc = %d\n", rc);
299 return -EINVAL;
300 }
301 }
302
303 if (chip->fcc_uA_request != -EINVAL) {
304 pval.intval = disable ? -EINVAL : chip->fcc_uA_request;
305 rc = power_supply_set_property(chip->batt_psy,
306 POWER_SUPPLY_PROP_CURRENT_QNOVO,
307 &pval);
308 if (rc < 0) {
309 pr_err("Couldn't set prop qnovo_fcc rc = %d\n", rc);
310 return -EINVAL;
311 }
312 }
313
314 return rc;
315}
316
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700317static int qnovo_disable_cb(struct votable *votable, void *data, int disable,
318 const char *client)
319{
320 struct qnovo *chip = data;
Harry Yang87111f72017-02-24 01:06:00 -0800321 union power_supply_propval pval = {0};
322 int rc;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700323
Harry Yang87111f72017-02-24 01:06:00 -0800324 if (!is_batt_available(chip))
325 return -EINVAL;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700326
Harry Yang87111f72017-02-24 01:06:00 -0800327 pval.intval = !disable;
328 rc = power_supply_set_property(chip->batt_psy,
329 POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE,
330 &pval);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700331 if (rc < 0) {
Harry Yang87111f72017-02-24 01:06:00 -0800332 pr_err("Couldn't set prop qnovo_enable rc = %d\n", rc);
333 return -EINVAL;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700334 }
335
Abhijeet Dharmapurikar933d4942017-07-07 13:49:29 -0700336 /*
337 * fg must be available for enable FG_AVAILABLE_VOTER
338 * won't enable it otherwise
339 */
340
341 if (is_fg_available(chip))
342 power_supply_set_property(chip->bms_psy,
343 POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE,
344 &pval);
345
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -0700346 vote(chip->pt_dis_votable, QNOVO_OVERALL_VOTER, disable, 0);
Harry Yang87111f72017-02-24 01:06:00 -0800347 rc = qnovo_batt_psy_update(chip, disable);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700348 return rc;
349}
350
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -0700351static int pt_dis_votable_cb(struct votable *votable, void *data, int disable,
352 const char *client)
353{
354 struct qnovo *chip = data;
355 int rc;
356
357 rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT,
358 (bool)disable ? 0 : QNOVO_PTRAIN_EN_BIT);
359 if (rc < 0) {
360 dev_err(chip->dev, "Couldn't %s pulse train rc=%d\n",
361 (bool)disable ? "disable" : "enable", rc);
362 return rc;
363 }
364
365 return 0;
366}
367
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -0700368static int not_ok_to_qnovo_cb(struct votable *votable, void *data,
369 int not_ok_to_qnovo,
370 const char *client)
371{
372 struct qnovo *chip = data;
373
374 vote(chip->disable_votable, OK_TO_QNOVO_VOTER, not_ok_to_qnovo, 0);
375 if (not_ok_to_qnovo)
376 vote(chip->disable_votable, USER_VOTER, true, 0);
377
378 kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE);
379 return 0;
380}
381
382static int chg_ready_cb(struct votable *votable, void *data, int ready,
383 const char *client)
384{
385 struct qnovo *chip = data;
386
387 vote(chip->not_ok_to_qnovo_votable, CHG_READY_VOTER, !ready, 0);
388
389 return 0;
390}
391
392static int awake_cb(struct votable *votable, void *data, int awake,
393 const char *client)
394{
395 struct qnovo *chip = data;
396
397 if (awake)
398 pm_stay_awake(chip->dev);
399 else
400 pm_relax(chip->dev);
401
402 return 0;
403}
404
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700405static int qnovo_parse_dt(struct qnovo *chip)
406{
407 struct device_node *node = chip->dev->of_node;
408 int rc;
409
410 if (!node) {
411 pr_err("device tree node missing\n");
412 return -EINVAL;
413 }
414
415 rc = of_property_read_u32(node, "reg", &chip->base);
416 if (rc < 0) {
417 pr_err("Couldn't read base rc = %d\n", rc);
418 return rc;
419 }
420
421 chip->dt.external_rsense = of_property_read_bool(node,
422 "qcom,external-rsense");
423
424 chip->dt.revid_dev_node = of_parse_phandle(node, "qcom,pmic-revid", 0);
425 if (!chip->dt.revid_dev_node) {
426 pr_err("Missing qcom,pmic-revid property - driver failed\n");
427 return -EINVAL;
428 }
429
430 return 0;
431}
432
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700433enum {
434 VER = 0,
435 OK_TO_QNOVO,
Harry Yang87111f72017-02-24 01:06:00 -0800436 QNOVO_ENABLE,
437 PT_ENABLE,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700438 FV_REQUEST,
439 FCC_REQUEST,
440 PE_CTRL_REG,
441 PE_CTRL2_REG,
442 PTRAIN_STS_REG,
443 INT_RT_STS_REG,
Harry Yange7a96842017-03-23 21:41:48 -0700444 ERR_STS2_REG,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700445 PREST1,
446 PPULS1,
447 NREST1,
448 NPULS1,
449 PPCNT,
450 VLIM1,
451 PVOLT1,
452 PCUR1,
453 PTTIME,
454 PREST2,
455 PPULS2,
456 NREST2,
457 NPULS2,
458 VLIM2,
459 PVOLT2,
460 RVOLT2,
461 PCUR2,
462 SCNT,
463 VMAX,
464 SNUM,
465 VBATT,
466 IBATT,
467 BATTTEMP,
468 BATTSOC,
469};
470
471struct param_info {
472 char *name;
473 int start_addr;
474 int num_regs;
475 int reg_to_unit_multiplier;
476 int reg_to_unit_divider;
477 int reg_to_unit_offset;
478 int min_val;
479 int max_val;
480 char *units_str;
481};
482
483static struct param_info params[] = {
Harry Yang87111f72017-02-24 01:06:00 -0800484 [PT_ENABLE] = {
485 .name = "PT_ENABLE",
486 .start_addr = QNOVO_PTRAIN_EN,
487 .num_regs = 1,
488 .units_str = "",
489 },
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700490 [FV_REQUEST] = {
491 .units_str = "uV",
492 },
493 [FCC_REQUEST] = {
494 .units_str = "uA",
495 },
496 [PE_CTRL_REG] = {
497 .name = "CTRL_REG",
498 .start_addr = QNOVO_PE_CTRL,
499 .num_regs = 1,
500 .units_str = "",
501 },
502 [PE_CTRL2_REG] = {
503 .name = "PE_CTRL2_REG",
504 .start_addr = QNOVO_PE_CTRL2,
505 .num_regs = 1,
506 .units_str = "",
507 },
508 [PTRAIN_STS_REG] = {
509 .name = "PTRAIN_STS",
510 .start_addr = QNOVO_PTRAIN_STS,
511 .num_regs = 1,
512 .units_str = "",
513 },
514 [INT_RT_STS_REG] = {
515 .name = "INT_RT_STS",
516 .start_addr = QNOVO_INT_RT_STS,
517 .num_regs = 1,
518 .units_str = "",
519 },
Harry Yange7a96842017-03-23 21:41:48 -0700520 [ERR_STS2_REG] = {
521 .name = "RAW_CHGR_ERR",
522 .start_addr = QNOVO_ERROR_STS2,
523 .num_regs = 1,
524 .units_str = "",
525 },
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700526 [PREST1] = {
527 .name = "PREST1",
528 .start_addr = QNOVO_PREST1_CTRL,
529 .num_regs = 1,
530 .reg_to_unit_multiplier = 5,
531 .reg_to_unit_divider = 1,
532 .min_val = 5,
Harry Yangaf2f67e2017-02-23 14:16:51 -0800533 .max_val = 255,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700534 .units_str = "mS",
535 },
536 [PPULS1] = {
537 .name = "PPULS1",
538 .start_addr = QNOVO_PPULS1_LSB_CTRL,
539 .num_regs = 2,
540 .reg_to_unit_multiplier = 1600, /* converts to uC */
541 .reg_to_unit_divider = 1,
Harry Yangaf2f67e2017-02-23 14:16:51 -0800542 .min_val = 30000,
543 .max_val = 65535000,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700544 .units_str = "uC",
545 },
546 [NREST1] = {
547 .name = "NREST1",
548 .start_addr = QNOVO_NREST1_CTRL,
549 .num_regs = 1,
550 .reg_to_unit_multiplier = 5,
551 .reg_to_unit_divider = 1,
552 .min_val = 5,
Harry Yangaf2f67e2017-02-23 14:16:51 -0800553 .max_val = 255,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700554 .units_str = "mS",
555 },
556 [NPULS1] = {
557 .name = "NPULS1",
558 .start_addr = QNOVO_NPULS1_CTRL,
559 .num_regs = 1,
560 .reg_to_unit_multiplier = 5,
561 .reg_to_unit_divider = 1,
Harry Yangaf2f67e2017-02-23 14:16:51 -0800562 .min_val = 0,
563 .max_val = 255,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700564 .units_str = "mS",
565 },
566 [PPCNT] = {
567 .name = "PPCNT",
568 .start_addr = QNOVO_PPCNT_CTRL,
569 .num_regs = 1,
570 .reg_to_unit_multiplier = 1,
571 .reg_to_unit_divider = 1,
Harry Yangaf2f67e2017-02-23 14:16:51 -0800572 .min_val = 1,
Harry Yang8dc14eb2017-02-06 13:12:15 -0800573 .max_val = 255,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700574 .units_str = "pulses",
575 },
576 [VLIM1] = {
577 .name = "VLIM1",
578 .start_addr = QNOVO_VLIM1_LSB_CTRL,
579 .num_regs = 2,
580 .reg_to_unit_multiplier = 610350, /* converts to nV */
581 .reg_to_unit_divider = 1,
Harry Yangaf2f67e2017-02-23 14:16:51 -0800582 .min_val = 2200000,
583 .max_val = 4500000,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700584 .units_str = "uV",
585 },
586 [PVOLT1] = {
587 .name = "PVOLT1",
588 .start_addr = QNOVO_PVOLT1_LSB,
589 .num_regs = 2,
590 .reg_to_unit_multiplier = 610350, /* converts to nV */
591 .reg_to_unit_divider = 1,
592 .units_str = "uV",
593 },
594 [PCUR1] = {
595 .name = "PCUR1",
596 .start_addr = QNOVO_PCUR1_LSB,
597 .num_regs = 2,
598 .reg_to_unit_multiplier = 1220700, /* converts to nA */
599 .reg_to_unit_divider = 1,
600 .units_str = "uA",
601 },
602 [PTTIME] = {
603 .name = "PTTIME",
604 .start_addr = QNOVO_PTTIME_STS,
605 .num_regs = 1,
606 .reg_to_unit_multiplier = 2,
607 .reg_to_unit_divider = 1,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700608 .units_str = "S",
609 },
610 [PREST2] = {
611 .name = "PREST2",
612 .start_addr = QNOVO_PREST2_LSB_CTRL,
613 .num_regs = 2,
614 .reg_to_unit_multiplier = 5,
615 .reg_to_unit_divider = 1,
616 .min_val = 5,
Harry Yangaf2f67e2017-02-23 14:16:51 -0800617 .max_val = 65535,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700618 .units_str = "mS",
619 },
620 [PPULS2] = {
621 .name = "PPULS2",
622 .start_addr = QNOVO_PPULS2_LSB_CTRL,
623 .num_regs = 2,
624 .reg_to_unit_multiplier = 1600, /* converts to uC */
625 .reg_to_unit_divider = 1,
Harry Yangaf2f67e2017-02-23 14:16:51 -0800626 .min_val = 30000,
627 .max_val = 65535000,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700628 .units_str = "uC",
629 },
630 [NREST2] = {
631 .name = "NREST2",
632 .start_addr = QNOVO_NREST2_CTRL,
633 .num_regs = 1,
634 .reg_to_unit_multiplier = 5,
635 .reg_to_unit_divider = 1,
636 .reg_to_unit_offset = -5,
637 .min_val = 5,
Harry Yangaf2f67e2017-02-23 14:16:51 -0800638 .max_val = 255,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700639 .units_str = "mS",
640 },
641 [NPULS2] = {
642 .name = "NPULS2",
643 .start_addr = QNOVO_NPULS2_CTRL,
644 .num_regs = 1,
645 .reg_to_unit_multiplier = 5,
646 .reg_to_unit_divider = 1,
Harry Yangaf2f67e2017-02-23 14:16:51 -0800647 .min_val = 0,
648 .max_val = 255,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700649 .units_str = "mS",
650 },
651 [VLIM2] = {
Harry Yangaf2f67e2017-02-23 14:16:51 -0800652 .name = "VLIM2",
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700653 .start_addr = QNOVO_VLIM2_LSB_CTRL,
654 .num_regs = 2,
655 .reg_to_unit_multiplier = 610350, /* converts to nV */
656 .reg_to_unit_divider = 1,
Harry Yangaf2f67e2017-02-23 14:16:51 -0800657 .min_val = 2200000,
658 .max_val = 4500000,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700659 .units_str = "uV",
660 },
661 [PVOLT2] = {
662 .name = "PVOLT2",
663 .start_addr = QNOVO_PVOLT2_LSB,
664 .num_regs = 2,
665 .reg_to_unit_multiplier = 610350, /* converts to nV */
666 .reg_to_unit_divider = 1,
667 .units_str = "uV",
668 },
669 [RVOLT2] = {
670 .name = "RVOLT2",
671 .start_addr = QNOVO_RVOLT2_LSB,
672 .num_regs = 2,
673 .reg_to_unit_multiplier = 610350,
674 .reg_to_unit_divider = 1,
675 .units_str = "uV",
676 },
677 [PCUR2] = {
678 .name = "PCUR2",
679 .start_addr = QNOVO_PCUR2_LSB,
680 .num_regs = 2,
681 .reg_to_unit_multiplier = 1220700, /* converts to nA */
682 .reg_to_unit_divider = 1,
683 .units_str = "uA",
684 },
685 [SCNT] = {
686 .name = "SCNT",
687 .start_addr = QNOVO_SCNT,
688 .num_regs = 1,
689 .reg_to_unit_multiplier = 1,
690 .reg_to_unit_divider = 1,
Harry Yangaf2f67e2017-02-23 14:16:51 -0800691 .min_val = 0,
692 .max_val = 255,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700693 .units_str = "pulses",
694 },
695 [VMAX] = {
696 .name = "VMAX",
697 .start_addr = QNOVO_VMAX_LSB,
698 .num_regs = 2,
699 .reg_to_unit_multiplier = 814000, /* converts to nV */
700 .reg_to_unit_divider = 1,
701 .units_str = "uV",
702 },
703 [SNUM] = {
704 .name = "SNUM",
705 .start_addr = QNOVO_SNUM,
706 .num_regs = 1,
707 .reg_to_unit_multiplier = 1,
708 .reg_to_unit_divider = 1,
709 .units_str = "pulses",
710 },
711 [VBATT] = {
712 .name = "POWER_SUPPLY_PROP_VOLTAGE_NOW",
713 .start_addr = POWER_SUPPLY_PROP_VOLTAGE_NOW,
714 .units_str = "uV",
715 },
716 [IBATT] = {
717 .name = "POWER_SUPPLY_PROP_CURRENT_NOW",
718 .start_addr = POWER_SUPPLY_PROP_CURRENT_NOW,
719 .units_str = "uA",
720 },
721 [BATTTEMP] = {
722 .name = "POWER_SUPPLY_PROP_TEMP",
723 .start_addr = POWER_SUPPLY_PROP_TEMP,
724 .units_str = "uV",
725 },
726 [BATTSOC] = {
727 .name = "POWER_SUPPLY_PROP_CAPACITY",
728 .start_addr = POWER_SUPPLY_PROP_CAPACITY,
729 .units_str = "%",
730 },
731};
732
733static struct class_attribute qnovo_attributes[];
734
735static ssize_t version_show(struct class *c, struct class_attribute *attr,
736 char *buf)
737{
738 return snprintf(buf, PAGE_SIZE, "%d.%d\n",
739 DRV_MAJOR_VERSION, DRV_MINOR_VERSION);
740}
741
742static ssize_t ok_to_qnovo_show(struct class *c, struct class_attribute *attr,
743 char *buf)
744{
745 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -0700746 int val = get_effective_result(chip->not_ok_to_qnovo_votable);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700747
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -0700748 return snprintf(buf, PAGE_SIZE, "%d\n", !val);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700749}
750
Harry Yang87111f72017-02-24 01:06:00 -0800751static ssize_t qnovo_enable_show(struct class *c, struct class_attribute *attr,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700752 char *ubuf)
753{
754 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
Harry Yang87111f72017-02-24 01:06:00 -0800755 int val = get_effective_result(chip->disable_votable);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700756
Harry Yang87111f72017-02-24 01:06:00 -0800757 return snprintf(ubuf, PAGE_SIZE, "%d\n", !val);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700758}
759
Harry Yang87111f72017-02-24 01:06:00 -0800760static ssize_t qnovo_enable_store(struct class *c, struct class_attribute *attr,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700761 const char *ubuf, size_t count)
762{
763 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
764 unsigned long val;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700765
Harry Yangaf2f67e2017-02-23 14:16:51 -0800766 if (kstrtoul(ubuf, 0, &val))
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700767 return -EINVAL;
768
Harry Yang87111f72017-02-24 01:06:00 -0800769 vote(chip->disable_votable, USER_VOTER, !val, 0);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700770
Harry Yang87111f72017-02-24 01:06:00 -0800771 return count;
772}
773
774static ssize_t pt_enable_show(struct class *c, struct class_attribute *attr,
775 char *ubuf)
776{
Harry Yang87111f72017-02-24 01:06:00 -0800777 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -0700778 int val = get_effective_result(chip->pt_dis_votable);
Harry Yang87111f72017-02-24 01:06:00 -0800779
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -0700780 return snprintf(ubuf, PAGE_SIZE, "%d\n", !val);
Harry Yang87111f72017-02-24 01:06:00 -0800781}
782
783static ssize_t pt_enable_store(struct class *c, struct class_attribute *attr,
784 const char *ubuf, size_t count)
785{
786 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
787 unsigned long val;
Harry Yang87111f72017-02-24 01:06:00 -0800788
789 if (kstrtoul(ubuf, 0, &val))
790 return -EINVAL;
791
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -0700792 /* val being 0, userspace wishes to disable pt so vote true */
793 vote(chip->pt_dis_votable, QNI_PT_VOTER, val ? false : true, 0);
Harry Yang87111f72017-02-24 01:06:00 -0800794
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700795 return count;
796}
797
798static ssize_t val_show(struct class *c, struct class_attribute *attr,
799 char *ubuf)
800{
801 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
802 int i = attr - qnovo_attributes;
803 int val = 0;
804
805 if (i == FV_REQUEST)
806 val = chip->fv_uV_request;
807
808 if (i == FCC_REQUEST)
809 val = chip->fcc_uA_request;
810
Harry Yangaf2f67e2017-02-23 14:16:51 -0800811 return snprintf(ubuf, PAGE_SIZE, "%d\n", val);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700812}
813
814static ssize_t val_store(struct class *c, struct class_attribute *attr,
815 const char *ubuf, size_t count)
816{
817 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
818 int i = attr - qnovo_attributes;
819 unsigned long val;
820
Harry Yangaf2f67e2017-02-23 14:16:51 -0800821 if (kstrtoul(ubuf, 0, &val))
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700822 return -EINVAL;
823
824 if (i == FV_REQUEST)
825 chip->fv_uV_request = val;
826
827 if (i == FCC_REQUEST)
828 chip->fcc_uA_request = val;
829
Harry Yang96ec88e2017-03-23 23:43:53 -0700830 if (!get_effective_result(chip->disable_votable))
831 qnovo_batt_psy_update(chip, false);
Harry Yang87111f72017-02-24 01:06:00 -0800832
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700833 return count;
834}
835
836static ssize_t reg_show(struct class *c, struct class_attribute *attr,
837 char *ubuf)
838{
839 int i = attr - qnovo_attributes;
840 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
841 u8 buf[2] = {0, 0};
842 u16 regval;
843 int rc;
844
845 rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs);
846 if (rc < 0) {
847 pr_err("Couldn't read %s rc = %d\n", params[i].name, rc);
848 return -EINVAL;
849 }
850 regval = buf[1] << 8 | buf[0];
851
Harry Yangaf2f67e2017-02-23 14:16:51 -0800852 return snprintf(ubuf, PAGE_SIZE, "0x%04x\n", regval);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700853}
854
855static ssize_t reg_store(struct class *c, struct class_attribute *attr,
856 const char *ubuf, size_t count)
857{
858 int i = attr - qnovo_attributes;
859 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
860 u8 buf[2] = {0, 0};
861 unsigned long val;
862 int rc;
863
Harry Yangaf2f67e2017-02-23 14:16:51 -0800864 if (kstrtoul(ubuf, 0, &val))
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700865 return -EINVAL;
866
867 buf[0] = val & 0xFF;
868 buf[1] = (val >> 8) & 0xFF;
869
870 rc = qnovo_write(chip, params[i].start_addr, buf, params[i].num_regs);
871 if (rc < 0) {
872 pr_err("Couldn't write %s rc = %d\n", params[i].name, rc);
873 return -EINVAL;
874 }
875 return count;
876}
877
878static ssize_t time_show(struct class *c, struct class_attribute *attr,
879 char *ubuf)
880{
881 int i = attr - qnovo_attributes;
882 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
883 u8 buf[2] = {0, 0};
884 u16 regval;
885 int val;
886 int rc;
887
888 rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs);
889 if (rc < 0) {
890 pr_err("Couldn't read %s rc = %d\n", params[i].name, rc);
891 return -EINVAL;
892 }
893 regval = buf[1] << 8 | buf[0];
894
895 val = ((regval * params[i].reg_to_unit_multiplier)
896 / params[i].reg_to_unit_divider)
897 - params[i].reg_to_unit_offset;
898
Harry Yangaf2f67e2017-02-23 14:16:51 -0800899 return snprintf(ubuf, PAGE_SIZE, "%d\n", val);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700900}
901
902static ssize_t time_store(struct class *c, struct class_attribute *attr,
903 const char *ubuf, size_t count)
904{
905 int i = attr - qnovo_attributes;
906 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
907 u8 buf[2] = {0, 0};
908 u16 regval;
909 unsigned long val;
910 int rc;
911
Harry Yangaf2f67e2017-02-23 14:16:51 -0800912 if (kstrtoul(ubuf, 0, &val))
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700913 return -EINVAL;
914
915 if (val < params[i].min_val || val > params[i].max_val) {
916 pr_err("Out of Range %d%s for %s\n", (int)val,
917 params[i].units_str,
918 params[i].name);
919 return -ERANGE;
920 }
921
922 regval = (((int)val + params[i].reg_to_unit_offset)
923 * params[i].reg_to_unit_divider)
924 / params[i].reg_to_unit_multiplier;
925 buf[0] = regval & 0xFF;
926 buf[1] = (regval >> 8) & 0xFF;
927
928 rc = qnovo_write(chip, params[i].start_addr, buf, params[i].num_regs);
929 if (rc < 0) {
930 pr_err("Couldn't write %s rc = %d\n", params[i].name, rc);
931 return -EINVAL;
932 }
933
934 return count;
935}
936
937static ssize_t current_show(struct class *c, struct class_attribute *attr,
938 char *ubuf)
939{
940 int i = attr - qnovo_attributes;
941 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
942 u8 buf[2] = {0, 0};
943 int rc;
944 int comp_val_uA;
945 s64 regval_nA;
946 s64 gain, offset_nA, comp_val_nA;
947
948 rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs);
949 if (rc < 0) {
950 pr_err("Couldn't read %s rc = %d\n", params[i].name, rc);
951 return -EINVAL;
952 }
Harry Yangf2d57a22017-03-31 14:28:20 -0700953
954 if (buf[1] & BIT(5))
955 buf[1] |= GENMASK(7, 6);
956
957 regval_nA = (s16)(buf[1] << 8 | buf[0]);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700958 regval_nA = div_s64(regval_nA * params[i].reg_to_unit_multiplier,
959 params[i].reg_to_unit_divider)
960 - params[i].reg_to_unit_offset;
961
962 if (chip->dt.external_rsense) {
963 offset_nA = chip->external_offset_nA;
964 gain = chip->external_i_gain_mega;
965 } else {
966 offset_nA = chip->internal_offset_nA;
967 gain = chip->internal_i_gain_mega;
968 }
969
Harry Yangd9e49562017-02-17 15:37:09 -0800970 comp_val_nA = div_s64(regval_nA * gain, 1000000) - offset_nA;
Tirupathi Reddy4d8234a2017-02-10 18:10:43 +0530971 comp_val_uA = div_s64(comp_val_nA, 1000);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700972
Harry Yangaf2f67e2017-02-23 14:16:51 -0800973 return snprintf(ubuf, PAGE_SIZE, "%d\n", comp_val_uA);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700974}
975
976static ssize_t voltage_show(struct class *c, struct class_attribute *attr,
977 char *ubuf)
978{
979 int i = attr - qnovo_attributes;
980 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
981 u8 buf[2] = {0, 0};
982 int rc;
983 int comp_val_uV;
984 s64 regval_nV;
985 s64 gain, offset_nV, comp_val_nV;
986
987 rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs);
988 if (rc < 0) {
989 pr_err("Couldn't read %s rc = %d\n", params[i].name, rc);
990 return -EINVAL;
991 }
992 regval_nV = buf[1] << 8 | buf[0];
993 regval_nV = div_s64(regval_nV * params[i].reg_to_unit_multiplier,
994 params[i].reg_to_unit_divider)
995 - params[i].reg_to_unit_offset;
996
997 offset_nV = chip->offset_nV;
998 gain = chip->v_gain_mega;
999
1000 comp_val_nV = div_s64(regval_nV * gain, 1000000) + offset_nV;
Tirupathi Reddy4d8234a2017-02-10 18:10:43 +05301001 comp_val_uV = div_s64(comp_val_nV, 1000);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001002
Harry Yangaf2f67e2017-02-23 14:16:51 -08001003 return snprintf(ubuf, PAGE_SIZE, "%d\n", comp_val_uV);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001004}
1005
1006static ssize_t voltage_store(struct class *c, struct class_attribute *attr,
1007 const char *ubuf, size_t count)
1008{
1009 int i = attr - qnovo_attributes;
1010 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
1011 u8 buf[2] = {0, 0};
1012 int rc;
1013 unsigned long val_uV;
1014 s64 regval_nV;
1015 s64 gain, offset_nV;
1016
Harry Yangaf2f67e2017-02-23 14:16:51 -08001017 if (kstrtoul(ubuf, 0, &val_uV))
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001018 return -EINVAL;
1019
1020 if (val_uV < params[i].min_val || val_uV > params[i].max_val) {
1021 pr_err("Out of Range %d%s for %s\n", (int)val_uV,
1022 params[i].units_str,
1023 params[i].name);
1024 return -ERANGE;
1025 }
1026
1027 offset_nV = chip->offset_nV;
1028 gain = chip->v_gain_mega;
1029
1030 regval_nV = (s64)val_uV * 1000 - offset_nV;
1031 regval_nV = div_s64(regval_nV * 1000000, gain);
1032
1033 regval_nV = div_s64((regval_nV + params[i].reg_to_unit_offset)
1034 * params[i].reg_to_unit_divider,
1035 params[i].reg_to_unit_multiplier);
1036 buf[0] = regval_nV & 0xFF;
1037 buf[1] = ((u64)regval_nV >> 8) & 0xFF;
1038
1039 rc = qnovo_write(chip, params[i].start_addr, buf, params[i].num_regs);
1040 if (rc < 0) {
1041 pr_err("Couldn't write %s rc = %d\n", params[i].name, rc);
1042 return -EINVAL;
1043 }
1044
1045 return count;
1046}
1047
1048static ssize_t coulomb_show(struct class *c, struct class_attribute *attr,
1049 char *ubuf)
1050{
1051 int i = attr - qnovo_attributes;
1052 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
1053 u8 buf[2] = {0, 0};
1054 int rc;
1055 int comp_val_uC;
1056 s64 regval_uC, gain;
1057
1058 rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs);
1059 if (rc < 0) {
1060 pr_err("Couldn't read %s rc = %d\n", params[i].name, rc);
1061 return -EINVAL;
1062 }
1063 regval_uC = buf[1] << 8 | buf[0];
1064 regval_uC = div_s64(regval_uC * params[i].reg_to_unit_multiplier,
1065 params[i].reg_to_unit_divider)
1066 - params[i].reg_to_unit_offset;
1067
1068 if (chip->dt.external_rsense)
1069 gain = chip->external_i_gain_mega;
1070 else
1071 gain = chip->internal_i_gain_mega;
1072
1073 comp_val_uC = div_s64(regval_uC * gain, 1000000);
Harry Yangaf2f67e2017-02-23 14:16:51 -08001074 return snprintf(ubuf, PAGE_SIZE, "%d\n", comp_val_uC);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001075}
1076
1077static ssize_t coulomb_store(struct class *c, struct class_attribute *attr,
1078 const char *ubuf, size_t count)
1079{
1080 int i = attr - qnovo_attributes;
1081 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
1082 u8 buf[2] = {0, 0};
1083 int rc;
1084 unsigned long val_uC;
1085 s64 regval;
1086 s64 gain;
1087
Harry Yangaf2f67e2017-02-23 14:16:51 -08001088 if (kstrtoul(ubuf, 0, &val_uC))
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001089 return -EINVAL;
1090
1091 if (val_uC < params[i].min_val || val_uC > params[i].max_val) {
1092 pr_err("Out of Range %d%s for %s\n", (int)val_uC,
1093 params[i].units_str,
1094 params[i].name);
1095 return -ERANGE;
1096 }
1097
1098 if (chip->dt.external_rsense)
1099 gain = chip->external_i_gain_mega;
1100 else
1101 gain = chip->internal_i_gain_mega;
1102
1103 regval = div_s64((s64)val_uC * 1000000, gain);
1104
1105 regval = div_s64((regval + params[i].reg_to_unit_offset)
1106 * params[i].reg_to_unit_divider,
1107 params[i].reg_to_unit_multiplier);
1108
1109 buf[0] = regval & 0xFF;
1110 buf[1] = ((u64)regval >> 8) & 0xFF;
1111
1112 rc = qnovo_write(chip, params[i].start_addr, buf, params[i].num_regs);
1113 if (rc < 0) {
1114 pr_err("Couldn't write %s rc = %d\n", params[i].name, rc);
1115 return -EINVAL;
1116 }
1117
1118 return count;
1119}
1120
1121static ssize_t batt_prop_show(struct class *c, struct class_attribute *attr,
1122 char *ubuf)
1123{
1124 int i = attr - qnovo_attributes;
1125 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
1126 int rc = -EINVAL;
1127 int prop = params[i].start_addr;
1128 union power_supply_propval pval = {0};
1129
Harry Yang2e324ba2017-02-07 21:33:19 -08001130 if (!is_batt_available(chip))
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001131 return -EINVAL;
1132
1133 rc = power_supply_get_property(chip->batt_psy, prop, &pval);
1134 if (rc < 0) {
1135 pr_err("Couldn't read battery prop %s rc = %d\n",
1136 params[i].name, rc);
1137 return -EINVAL;
1138 }
1139
Harry Yangaf2f67e2017-02-23 14:16:51 -08001140 return snprintf(ubuf, PAGE_SIZE, "%d\n", pval.intval);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001141}
1142
1143static struct class_attribute qnovo_attributes[] = {
1144 [VER] = __ATTR_RO(version),
1145 [OK_TO_QNOVO] = __ATTR_RO(ok_to_qnovo),
Harry Yang87111f72017-02-24 01:06:00 -08001146 [QNOVO_ENABLE] = __ATTR_RW(qnovo_enable),
1147 [PT_ENABLE] = __ATTR_RW(pt_enable),
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001148 [FV_REQUEST] = __ATTR(fv_uV_request, 0644,
1149 val_show, val_store),
1150 [FCC_REQUEST] = __ATTR(fcc_uA_request, 0644,
1151 val_show, val_store),
1152 [PE_CTRL_REG] = __ATTR(PE_CTRL_REG, 0644,
1153 reg_show, reg_store),
1154 [PE_CTRL2_REG] = __ATTR(PE_CTRL2_REG, 0644,
1155 reg_show, reg_store),
Harry Yangaf2f67e2017-02-23 14:16:51 -08001156 [PTRAIN_STS_REG] = __ATTR(PTRAIN_STS_REG, 0444,
1157 reg_show, NULL),
1158 [INT_RT_STS_REG] = __ATTR(INT_RT_STS_REG, 0444,
1159 reg_show, NULL),
Harry Yange7a96842017-03-23 21:41:48 -07001160 [ERR_STS2_REG] = __ATTR(ERR_STS2_REG, 0444,
1161 reg_show, NULL),
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001162 [PREST1] = __ATTR(PREST1_mS, 0644,
1163 time_show, time_store),
1164 [PPULS1] = __ATTR(PPULS1_uC, 0644,
1165 coulomb_show, coulomb_store),
1166 [NREST1] = __ATTR(NREST1_mS, 0644,
1167 time_show, time_store),
1168 [NPULS1] = __ATTR(NPULS1_mS, 0644,
1169 time_show, time_store),
1170 [PPCNT] = __ATTR(PPCNT, 0644,
1171 time_show, time_store),
1172 [VLIM1] = __ATTR(VLIM1_uV, 0644,
1173 voltage_show, voltage_store),
1174 [PVOLT1] = __ATTR(PVOLT1_uV, 0444,
1175 voltage_show, NULL),
1176 [PCUR1] = __ATTR(PCUR1_uA, 0444,
1177 current_show, NULL),
1178 [PTTIME] = __ATTR(PTTIME_S, 0444,
1179 time_show, NULL),
1180 [PREST2] = __ATTR(PREST2_mS, 0644,
1181 time_show, time_store),
Harry Yang87111f72017-02-24 01:06:00 -08001182 [PPULS2] = __ATTR(PPULS2_uC, 0644,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001183 coulomb_show, coulomb_store),
1184 [NREST2] = __ATTR(NREST2_mS, 0644,
1185 time_show, time_store),
1186 [NPULS2] = __ATTR(NPULS2_mS, 0644,
1187 time_show, time_store),
1188 [VLIM2] = __ATTR(VLIM2_uV, 0644,
1189 voltage_show, voltage_store),
1190 [PVOLT2] = __ATTR(PVOLT2_uV, 0444,
1191 voltage_show, NULL),
1192 [RVOLT2] = __ATTR(RVOLT2_uV, 0444,
1193 voltage_show, NULL),
1194 [PCUR2] = __ATTR(PCUR2_uA, 0444,
1195 current_show, NULL),
1196 [SCNT] = __ATTR(SCNT, 0644,
1197 time_show, time_store),
1198 [VMAX] = __ATTR(VMAX_uV, 0444,
1199 voltage_show, NULL),
Harry Yangaf2f67e2017-02-23 14:16:51 -08001200 [SNUM] = __ATTR(SNUM, 0444,
1201 time_show, NULL),
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001202 [VBATT] = __ATTR(VBATT_uV, 0444,
1203 batt_prop_show, NULL),
1204 [IBATT] = __ATTR(IBATT_uA, 0444,
1205 batt_prop_show, NULL),
1206 [BATTTEMP] = __ATTR(BATTTEMP_deciDegC, 0444,
1207 batt_prop_show, NULL),
1208 [BATTSOC] = __ATTR(BATTSOC, 0444,
1209 batt_prop_show, NULL),
1210 __ATTR_NULL,
1211};
1212
Harry Yang96ec88e2017-03-23 23:43:53 -07001213static int qnovo_update_status(struct qnovo *chip)
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001214{
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001215 u8 val = 0;
1216 int rc;
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001217 bool hw_ok_to_qnovo;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001218
Harry Yang96ec88e2017-03-23 23:43:53 -07001219 rc = qnovo_read(chip, QNOVO_ERROR_STS2, &val, 1);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001220 if (rc < 0) {
1221 pr_err("Couldn't read error sts rc = %d\n", rc);
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001222 hw_ok_to_qnovo = false;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001223 } else {
Harry Yang58acd162017-04-07 17:08:06 -07001224 /*
1225 * For CV mode keep qnovo enabled, userspace is expected to
1226 * disable it after few runs
1227 */
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001228 hw_ok_to_qnovo = (val == ERR_CV_MODE || val == 0) ?
1229 true : false;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001230 }
1231
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001232 vote(chip->not_ok_to_qnovo_votable, HW_OK_TO_QNOVO_VOTER,
1233 !hw_ok_to_qnovo, 0);
1234 return 0;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001235}
1236
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001237static void usb_debounce_work(struct work_struct *work)
1238{
1239 struct qnovo *chip = container_of(work,
1240 struct qnovo, usb_debounce_work.work);
1241
1242 vote(chip->chg_ready_votable, USB_READY_VOTER, true, 0);
1243 vote(chip->awake_votable, USB_READY_VOTER, false, 0);
1244}
1245
1246static void dc_debounce_work(struct work_struct *work)
1247{
1248 struct qnovo *chip = container_of(work,
1249 struct qnovo, dc_debounce_work.work);
1250
1251 vote(chip->chg_ready_votable, DC_READY_VOTER, true, 0);
1252 vote(chip->awake_votable, DC_READY_VOTER, false, 0);
1253}
1254
1255#define DEBOUNCE_MS 15000 /* 15 seconds */
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001256static void status_change_work(struct work_struct *work)
1257{
1258 struct qnovo *chip = container_of(work,
1259 struct qnovo, status_change_work);
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001260 union power_supply_propval pval;
1261 bool usb_present = false, dc_present = false;
1262 int rc;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001263
Abhijeet Dharmapurikar933d4942017-07-07 13:49:29 -07001264 if (is_fg_available(chip))
1265 vote(chip->disable_votable, FG_AVAILABLE_VOTER, false, 0);
1266
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001267 if (is_usb_available(chip)) {
1268 rc = power_supply_get_property(chip->usb_psy,
1269 POWER_SUPPLY_PROP_PRESENT, &pval);
1270 usb_present = (rc < 0) ? 0 : pval.intval;
1271 }
1272
1273 if (chip->usb_present && !usb_present) {
1274 /* removal */
1275 chip->usb_present = 0;
1276 cancel_delayed_work_sync(&chip->usb_debounce_work);
1277 vote(chip->awake_votable, USB_READY_VOTER, false, 0);
1278 vote(chip->chg_ready_votable, USB_READY_VOTER, false, 0);
1279 } else if (!chip->usb_present && usb_present) {
1280 /* insertion */
1281 chip->usb_present = 1;
1282 vote(chip->awake_votable, USB_READY_VOTER, true, 0);
1283 schedule_delayed_work(&chip->usb_debounce_work,
1284 msecs_to_jiffies(DEBOUNCE_MS));
1285 }
1286
1287 if (is_dc_available(chip)) {
1288 rc = power_supply_get_property(chip->dc_psy,
1289 POWER_SUPPLY_PROP_PRESENT,
1290 &pval);
1291 dc_present = (rc < 0) ? 0 : pval.intval;
1292 }
1293
1294 if (usb_present)
1295 dc_present = 0;
1296
1297 if (chip->dc_present && !dc_present) {
1298 /* removal */
1299 chip->dc_present = 0;
1300 cancel_delayed_work_sync(&chip->dc_debounce_work);
1301 vote(chip->awake_votable, DC_READY_VOTER, false, 0);
1302 vote(chip->chg_ready_votable, DC_READY_VOTER, false, 0);
1303 } else if (!chip->dc_present && dc_present) {
1304 /* insertion */
1305 chip->dc_present = 1;
1306 vote(chip->awake_votable, DC_READY_VOTER, true, 0);
1307 schedule_delayed_work(&chip->dc_debounce_work,
1308 msecs_to_jiffies(DEBOUNCE_MS));
1309 }
1310
1311 qnovo_update_status(chip);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001312}
1313
1314static int qnovo_notifier_call(struct notifier_block *nb,
1315 unsigned long ev, void *v)
1316{
1317 struct power_supply *psy = v;
1318 struct qnovo *chip = container_of(nb, struct qnovo, nb);
1319
1320 if (ev != PSY_EVENT_PROP_CHANGED)
1321 return NOTIFY_OK;
Harry Yang96ec88e2017-03-23 23:43:53 -07001322
Abhijeet Dharmapurikar933d4942017-07-07 13:49:29 -07001323 if (strcmp(psy->desc->name, "battery") == 0
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001324 || strcmp(psy->desc->name, "bms") == 0
1325 || strcmp(psy->desc->name, "usb") == 0
1326 || strcmp(psy->desc->name, "dc") == 0)
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001327 schedule_work(&chip->status_change_work);
1328
1329 return NOTIFY_OK;
1330}
1331
1332static irqreturn_t handle_ptrain_done(int irq, void *data)
1333{
1334 struct qnovo *chip = data;
Abhijeet Dharmapurikar933d4942017-07-07 13:49:29 -07001335 union power_supply_propval pval = {0};
1336
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -07001337 /*
1338 * hw resets pt_en bit once ptrain_done triggers.
1339 * vote on behalf of QNI to disable it such that
1340 * once QNI enables it, the votable state changes
1341 * and the callback that sets it is indeed invoked
1342 */
1343 vote(chip->pt_dis_votable, QNI_PT_VOTER, true, 0);
1344
1345 vote(chip->pt_dis_votable, ESR_VOTER, true, 0);
Abhijeet Dharmapurikar933d4942017-07-07 13:49:29 -07001346 if (is_fg_available(chip))
1347 power_supply_set_property(chip->bms_psy,
1348 POWER_SUPPLY_PROP_RESISTANCE,
1349 &pval);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001350
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -07001351 vote(chip->pt_dis_votable, ESR_VOTER, false, 0);
Harry Yang96ec88e2017-03-23 23:43:53 -07001352 qnovo_update_status(chip);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001353 kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE);
1354 return IRQ_HANDLED;
1355}
1356
1357static int qnovo_hw_init(struct qnovo *chip)
1358{
1359 int rc;
1360 u8 iadc_offset_external, iadc_offset_internal;
1361 u8 iadc_gain_external, iadc_gain_internal;
1362 u8 vadc_offset, vadc_gain;
Harry Yang8dc14eb2017-02-06 13:12:15 -08001363 u8 val;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001364
Harry Yang87111f72017-02-24 01:06:00 -08001365 vote(chip->disable_votable, USER_VOTER, true, 0);
Abhijeet Dharmapurikar933d4942017-07-07 13:49:29 -07001366 vote(chip->disable_votable, FG_AVAILABLE_VOTER, true, 0);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001367
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -07001368 vote(chip->pt_dis_votable, QNI_PT_VOTER, true, 0);
1369 vote(chip->pt_dis_votable, QNOVO_OVERALL_VOTER, true, 0);
1370 vote(chip->pt_dis_votable, ESR_VOTER, false, 0);
1371
Harry Yangd9e49562017-02-17 15:37:09 -08001372 val = 0;
1373 rc = qnovo_write(chip, QNOVO_STRM_CTRL, &val, 1);
1374 if (rc < 0) {
1375 pr_err("Couldn't write iadc bitstream control rc = %d\n", rc);
1376 return rc;
1377 }
1378
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001379 rc = qnovo_read(chip, QNOVO_IADC_OFFSET_0, &iadc_offset_external, 1);
1380 if (rc < 0) {
1381 pr_err("Couldn't read iadc exernal offset rc = %d\n", rc);
1382 return rc;
1383 }
1384
Harry Yangd9e49562017-02-17 15:37:09 -08001385 /* stored as an 8 bit 2's complement signed integer */
1386 val = -1 * iadc_offset_external;
1387 rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_0, &val, 1);
1388 if (rc < 0) {
1389 pr_err("Couldn't write iadc offset rc = %d\n", rc);
1390 return rc;
1391 }
1392
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001393 rc = qnovo_read(chip, QNOVO_IADC_OFFSET_1, &iadc_offset_internal, 1);
1394 if (rc < 0) {
1395 pr_err("Couldn't read iadc internal offset rc = %d\n", rc);
1396 return rc;
1397 }
1398
Harry Yangd9e49562017-02-17 15:37:09 -08001399 /* stored as an 8 bit 2's complement signed integer */
1400 val = -1 * iadc_offset_internal;
1401 rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_1, &val, 1);
1402 if (rc < 0) {
1403 pr_err("Couldn't write iadc offset rc = %d\n", rc);
1404 return rc;
1405 }
1406
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001407 rc = qnovo_read(chip, QNOVO_IADC_GAIN_0, &iadc_gain_external, 1);
1408 if (rc < 0) {
1409 pr_err("Couldn't read iadc external gain rc = %d\n", rc);
1410 return rc;
1411 }
1412
1413 rc = qnovo_read(chip, QNOVO_IADC_GAIN_1, &iadc_gain_internal, 1);
1414 if (rc < 0) {
1415 pr_err("Couldn't read iadc internal gain rc = %d\n", rc);
1416 return rc;
1417 }
1418
1419 rc = qnovo_read(chip, QNOVO_VADC_OFFSET, &vadc_offset, 1);
1420 if (rc < 0) {
1421 pr_err("Couldn't read vadc offset rc = %d\n", rc);
1422 return rc;
1423 }
1424
1425 rc = qnovo_read(chip, QNOVO_VADC_GAIN, &vadc_gain, 1);
1426 if (rc < 0) {
1427 pr_err("Couldn't read vadc external gain rc = %d\n", rc);
1428 return rc;
1429 }
1430
Harry Yangd9e49562017-02-17 15:37:09 -08001431 chip->external_offset_nA = (s64)(s8)iadc_offset_external * IADC_LSB_NA;
1432 chip->internal_offset_nA = (s64)(s8)iadc_offset_internal * IADC_LSB_NA;
1433 chip->offset_nV = (s64)(s8)vadc_offset * VADC_LSB_NA;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001434 chip->external_i_gain_mega
Harry Yangd9e49562017-02-17 15:37:09 -08001435 = 1000000000 + (s64)(s8)iadc_gain_external * GAIN_LSB_FACTOR;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001436 chip->external_i_gain_mega
1437 = div_s64(chip->external_i_gain_mega, 1000);
1438 chip->internal_i_gain_mega
Harry Yangd9e49562017-02-17 15:37:09 -08001439 = 1000000000 + (s64)(s8)iadc_gain_internal * GAIN_LSB_FACTOR;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001440 chip->internal_i_gain_mega
1441 = div_s64(chip->internal_i_gain_mega, 1000);
Harry Yangd9e49562017-02-17 15:37:09 -08001442 chip->v_gain_mega = 1000000000 + (s64)(s8)vadc_gain * GAIN_LSB_FACTOR;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001443 chip->v_gain_mega = div_s64(chip->v_gain_mega, 1000);
1444
Harry Yang58acd162017-04-07 17:08:06 -07001445 /* allow charger error conditions to disable qnovo, CV mode excluded */
1446 val = ERR_SWITCHER_DISABLED | ERR_JEITA_SOFT_CONDITION | ERR_BAT_OV |
1447 ERR_BATTERY_MISSING | ERR_SAFETY_TIMER_EXPIRED |
1448 ERR_CHARGING_DISABLED | ERR_JEITA_HARD_CONDITION;
1449 rc = qnovo_write(chip, QNOVO_DISABLE_CHARGING, &val, 1);
1450 if (rc < 0) {
1451 pr_err("Couldn't write QNOVO_DISABLE_CHARGING rc = %d\n", rc);
1452 return rc;
1453 }
1454
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001455 return 0;
1456}
1457
1458static int qnovo_register_notifier(struct qnovo *chip)
1459{
1460 int rc;
1461
1462 chip->nb.notifier_call = qnovo_notifier_call;
1463 rc = power_supply_reg_notifier(&chip->nb);
1464 if (rc < 0) {
1465 pr_err("Couldn't register psy notifier rc = %d\n", rc);
1466 return rc;
1467 }
1468
1469 return 0;
1470}
1471
1472static int qnovo_determine_initial_status(struct qnovo *chip)
1473{
1474 status_change_work(&chip->status_change_work);
1475 return 0;
1476}
1477
1478static int qnovo_request_interrupts(struct qnovo *chip)
1479{
1480 int rc = 0;
1481 int irq_ptrain_done = of_irq_get_byname(chip->dev->of_node,
1482 "ptrain-done");
1483
1484 rc = devm_request_threaded_irq(chip->dev, irq_ptrain_done, NULL,
1485 handle_ptrain_done,
1486 IRQF_ONESHOT, "ptrain-done", chip);
1487 if (rc < 0) {
1488 pr_err("Couldn't request irq %d rc = %d\n",
1489 irq_ptrain_done, rc);
1490 return rc;
1491 }
Harry Yang87111f72017-02-24 01:06:00 -08001492
1493 enable_irq_wake(irq_ptrain_done);
1494
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001495 return rc;
1496}
1497
1498static int qnovo_probe(struct platform_device *pdev)
1499{
1500 struct qnovo *chip;
1501 int rc = 0;
1502
1503 chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
1504 if (!chip)
1505 return -ENOMEM;
1506
1507 chip->fv_uV_request = -EINVAL;
1508 chip->fcc_uA_request = -EINVAL;
1509 chip->dev = &pdev->dev;
1510 mutex_init(&chip->write_lock);
1511
1512 chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
1513 if (!chip->regmap) {
1514 pr_err("parent regmap is missing\n");
1515 return -EINVAL;
1516 }
1517
1518 rc = qnovo_parse_dt(chip);
1519 if (rc < 0) {
1520 pr_err("Couldn't parse device tree rc=%d\n", rc);
1521 return rc;
1522 }
1523
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001524 /* set driver data before resources request it */
1525 platform_set_drvdata(pdev, chip);
1526
1527 chip->disable_votable = create_votable("QNOVO_DISABLE", VOTE_SET_ANY,
1528 qnovo_disable_cb, chip);
1529 if (IS_ERR(chip->disable_votable)) {
1530 rc = PTR_ERR(chip->disable_votable);
1531 goto cleanup;
1532 }
1533
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -07001534 chip->pt_dis_votable = create_votable("QNOVO_PT_DIS", VOTE_SET_ANY,
1535 pt_dis_votable_cb, chip);
1536 if (IS_ERR(chip->pt_dis_votable)) {
1537 rc = PTR_ERR(chip->pt_dis_votable);
1538 goto destroy_disable_votable;
1539 }
1540
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001541 chip->not_ok_to_qnovo_votable = create_votable("QNOVO_NOT_OK",
1542 VOTE_SET_ANY,
1543 not_ok_to_qnovo_cb, chip);
1544 if (IS_ERR(chip->not_ok_to_qnovo_votable)) {
1545 rc = PTR_ERR(chip->not_ok_to_qnovo_votable);
1546 goto destroy_pt_dis_votable;
1547 }
1548
1549 chip->chg_ready_votable = create_votable("QNOVO_CHG_READY",
1550 VOTE_SET_ANY,
1551 chg_ready_cb, chip);
1552 if (IS_ERR(chip->chg_ready_votable)) {
1553 rc = PTR_ERR(chip->chg_ready_votable);
1554 goto destroy_not_ok_to_qnovo_votable;
1555 }
1556
1557 chip->awake_votable = create_votable("QNOVO_AWAKE", VOTE_SET_ANY,
1558 awake_cb, chip);
1559 if (IS_ERR(chip->awake_votable)) {
1560 rc = PTR_ERR(chip->awake_votable);
1561 goto destroy_chg_ready_votable;
1562 }
1563
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001564 INIT_WORK(&chip->status_change_work, status_change_work);
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001565 INIT_DELAYED_WORK(&chip->dc_debounce_work, dc_debounce_work);
1566 INIT_DELAYED_WORK(&chip->usb_debounce_work, usb_debounce_work);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001567
1568 rc = qnovo_hw_init(chip);
1569 if (rc < 0) {
1570 pr_err("Couldn't initialize hardware rc=%d\n", rc);
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001571 goto destroy_awake_votable;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001572 }
1573
1574 rc = qnovo_register_notifier(chip);
1575 if (rc < 0) {
1576 pr_err("Couldn't register psy notifier rc = %d\n", rc);
1577 goto unreg_notifier;
1578 }
1579
1580 rc = qnovo_determine_initial_status(chip);
1581 if (rc < 0) {
1582 pr_err("Couldn't determine initial status rc=%d\n", rc);
1583 goto unreg_notifier;
1584 }
1585
1586 rc = qnovo_request_interrupts(chip);
1587 if (rc < 0) {
1588 pr_err("Couldn't request interrupts rc=%d\n", rc);
1589 goto unreg_notifier;
1590 }
1591 chip->qnovo_class.name = "qnovo",
1592 chip->qnovo_class.owner = THIS_MODULE,
1593 chip->qnovo_class.class_attrs = qnovo_attributes;
1594
1595 rc = class_register(&chip->qnovo_class);
1596 if (rc < 0) {
1597 pr_err("couldn't register qnovo sysfs class rc = %d\n", rc);
1598 goto unreg_notifier;
1599 }
1600
Harry Yang87111f72017-02-24 01:06:00 -08001601 device_init_wakeup(chip->dev, true);
1602
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001603 return rc;
1604
1605unreg_notifier:
1606 power_supply_unreg_notifier(&chip->nb);
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001607destroy_awake_votable:
1608 destroy_votable(chip->awake_votable);
1609destroy_chg_ready_votable:
1610 destroy_votable(chip->chg_ready_votable);
1611destroy_not_ok_to_qnovo_votable:
1612 destroy_votable(chip->not_ok_to_qnovo_votable);
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -07001613destroy_pt_dis_votable:
1614 destroy_votable(chip->pt_dis_votable);
1615destroy_disable_votable:
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001616 destroy_votable(chip->disable_votable);
1617cleanup:
1618 platform_set_drvdata(pdev, NULL);
1619 return rc;
1620}
1621
1622static int qnovo_remove(struct platform_device *pdev)
1623{
1624 struct qnovo *chip = platform_get_drvdata(pdev);
1625
1626 class_unregister(&chip->qnovo_class);
1627 power_supply_unreg_notifier(&chip->nb);
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001628 destroy_votable(chip->chg_ready_votable);
1629 destroy_votable(chip->not_ok_to_qnovo_votable);
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -07001630 destroy_votable(chip->pt_dis_votable);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001631 destroy_votable(chip->disable_votable);
1632 platform_set_drvdata(pdev, NULL);
1633 return 0;
1634}
1635
1636static const struct of_device_id match_table[] = {
1637 { .compatible = "qcom,qpnp-qnovo", },
1638 { },
1639};
1640
1641static struct platform_driver qnovo_driver = {
1642 .driver = {
1643 .name = "qcom,qnovo-driver",
1644 .owner = THIS_MODULE,
1645 .of_match_table = match_table,
1646 },
1647 .probe = qnovo_probe,
1648 .remove = qnovo_remove,
1649};
1650module_platform_driver(qnovo_driver);
1651
1652MODULE_DESCRIPTION("QPNP Qnovo Driver");
1653MODULE_LICENSE("GPL v2");