blob: cf90f90419353f62a43f6dee3241c0d9fc22a639 [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>
Harry Yangc11edf42017-07-17 11:44:37 -070023#include <linux/delay.h>
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -070024
25#define QNOVO_REVISION1 0x00
26#define QNOVO_REVISION2 0x01
27#define QNOVO_PERPH_TYPE 0x04
28#define QNOVO_PERPH_SUBTYPE 0x05
29#define QNOVO_PTTIME_STS 0x07
30#define QNOVO_PTRAIN_STS 0x08
31#define QNOVO_ERROR_STS 0x09
32#define QNOVO_ERROR_BIT BIT(0)
Harry Yange7a96842017-03-23 21:41:48 -070033#define QNOVO_ERROR_STS2 0x0A
Harry Yang96ec88e2017-03-23 23:43:53 -070034#define QNOVO_ERROR_CHARGING_DISABLED BIT(1)
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -070035#define QNOVO_INT_RT_STS 0x10
36#define QNOVO_INT_SET_TYPE 0x11
37#define QNOVO_INT_POLARITY_HIGH 0x12
38#define QNOVO_INT_POLARITY_LOW 0x13
39#define QNOVO_INT_LATCHED_CLR 0x14
40#define QNOVO_INT_EN_SET 0x15
41#define QNOVO_INT_EN_CLR 0x16
42#define QNOVO_INT_LATCHED_STS 0x18
43#define QNOVO_INT_PENDING_STS 0x19
44#define QNOVO_INT_MID_SEL 0x1A
45#define QNOVO_INT_PRIORITY 0x1B
46#define QNOVO_PE_CTRL 0x40
47#define QNOVO_PREST1_CTRL 0x41
48#define QNOVO_PPULS1_LSB_CTRL 0x42
49#define QNOVO_PPULS1_MSB_CTRL 0x43
50#define QNOVO_NREST1_CTRL 0x44
51#define QNOVO_NPULS1_CTRL 0x45
52#define QNOVO_PPCNT_CTRL 0x46
53#define QNOVO_VLIM1_LSB_CTRL 0x47
54#define QNOVO_VLIM1_MSB_CTRL 0x48
55#define QNOVO_PTRAIN_EN 0x49
56#define QNOVO_PTRAIN_EN_BIT BIT(0)
57#define QNOVO_PE_CTRL2 0x4A
58#define QNOVO_PREST2_LSB_CTRL 0x50
59#define QNOVO_PREST2_MSB_CTRL 0x51
60#define QNOVO_PPULS2_LSB_CTRL 0x52
61#define QNOVO_PPULS2_MSB_CTRL 0x53
62#define QNOVO_NREST2_CTRL 0x54
63#define QNOVO_NPULS2_CTRL 0x55
64#define QNOVO_VLIM2_LSB_CTRL 0x56
65#define QNOVO_VLIM2_MSB_CTRL 0x57
66#define QNOVO_PVOLT1_LSB 0x60
67#define QNOVO_PVOLT1_MSB 0x61
68#define QNOVO_PCUR1_LSB 0x62
69#define QNOVO_PCUR1_MSB 0x63
70#define QNOVO_PVOLT2_LSB 0x70
71#define QNOVO_PVOLT2_MSB 0x71
72#define QNOVO_RVOLT2_LSB 0x72
73#define QNOVO_RVOLT2_MSB 0x73
74#define QNOVO_PCUR2_LSB 0x74
75#define QNOVO_PCUR2_MSB 0x75
76#define QNOVO_SCNT 0x80
77#define QNOVO_VMAX_LSB 0x90
78#define QNOVO_VMAX_MSB 0x91
79#define QNOVO_SNUM 0x92
80
81/* Registers ending in 0 imply external rsense */
82#define QNOVO_IADC_OFFSET_0 0xA0
83#define QNOVO_IADC_OFFSET_1 0xA1
84#define QNOVO_IADC_GAIN_0 0xA2
85#define QNOVO_IADC_GAIN_1 0xA3
86#define QNOVO_VADC_OFFSET 0xA4
87#define QNOVO_VADC_GAIN 0xA5
88#define QNOVO_IADC_GAIN_2 0xA6
89#define QNOVO_SPARE 0xA7
90#define QNOVO_STRM_CTRL 0xA8
91#define QNOVO_IADC_OFFSET_OVR_VAL 0xA9
92#define QNOVO_IADC_OFFSET_OVR 0xAA
Harry Yang58acd162017-04-07 17:08:06 -070093
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -070094#define QNOVO_DISABLE_CHARGING 0xAB
Harry Yang58acd162017-04-07 17:08:06 -070095#define ERR_SWITCHER_DISABLED BIT(7)
96#define ERR_JEITA_SOFT_CONDITION BIT(6)
97#define ERR_BAT_OV BIT(5)
98#define ERR_CV_MODE BIT(4)
99#define ERR_BATTERY_MISSING BIT(3)
100#define ERR_SAFETY_TIMER_EXPIRED BIT(2)
101#define ERR_CHARGING_DISABLED BIT(1)
102#define ERR_JEITA_HARD_CONDITION BIT(0)
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700103
104#define QNOVO_TR_IADC_OFFSET_0 0xF1
105#define QNOVO_TR_IADC_OFFSET_1 0xF2
106
107#define DRV_MAJOR_VERSION 1
108#define DRV_MINOR_VERSION 0
109
110#define IADC_LSB_NA 2441400
111#define VADC_LSB_NA 1220700
112#define GAIN_LSB_FACTOR 976560
113
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700114#define USER_VOTER "user_voter"
115#define OK_TO_QNOVO_VOTER "ok_to_qnovo_voter"
116
117#define QNOVO_VOTER "qnovo_voter"
Abhijeet Dharmapurikar933d4942017-07-07 13:49:29 -0700118#define FG_AVAILABLE_VOTER "FG_AVAILABLE_VOTER"
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -0700119#define QNOVO_OVERALL_VOTER "QNOVO_OVERALL_VOTER"
120#define QNI_PT_VOTER "QNI_PT_VOTER"
121#define ESR_VOTER "ESR_VOTER"
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700122
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -0700123#define HW_OK_TO_QNOVO_VOTER "HW_OK_TO_QNOVO_VOTER"
124#define CHG_READY_VOTER "CHG_READY_VOTER"
125#define USB_READY_VOTER "USB_READY_VOTER"
126#define DC_READY_VOTER "DC_READY_VOTER"
127
Harry Yangc11edf42017-07-17 11:44:37 -0700128#define PT_RESTART_VOTER "PT_RESTART_VOTER"
129
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700130struct qnovo_dt_props {
131 bool external_rsense;
132 struct device_node *revid_dev_node;
133};
134
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700135struct qnovo {
136 int base;
137 struct mutex write_lock;
138 struct regmap *regmap;
139 struct qnovo_dt_props dt;
140 struct device *dev;
141 struct votable *disable_votable;
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -0700142 struct votable *pt_dis_votable;
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -0700143 struct votable *not_ok_to_qnovo_votable;
144 struct votable *chg_ready_votable;
145 struct votable *awake_votable;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700146 struct class qnovo_class;
147 struct pmic_revid_data *pmic_rev_id;
148 u32 wa_flags;
149 s64 external_offset_nA;
150 s64 internal_offset_nA;
151 s64 offset_nV;
152 s64 external_i_gain_mega;
153 s64 internal_i_gain_mega;
154 s64 v_gain_mega;
155 struct notifier_block nb;
156 struct power_supply *batt_psy;
Abhijeet Dharmapurikar933d4942017-07-07 13:49:29 -0700157 struct power_supply *bms_psy;
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -0700158 struct power_supply *usb_psy;
159 struct power_supply *dc_psy;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700160 struct work_struct status_change_work;
161 int fv_uV_request;
162 int fcc_uA_request;
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -0700163 int usb_present;
164 int dc_present;
165 struct delayed_work usb_debounce_work;
166 struct delayed_work dc_debounce_work;
Harry Yangc11edf42017-07-17 11:44:37 -0700167
168 struct delayed_work ptrain_restart_work;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700169};
170
171static int debug_mask;
172module_param_named(debug_mask, debug_mask, int, 0600);
173
174#define qnovo_dbg(chip, reason, fmt, ...) \
175 do { \
176 if (debug_mask & (reason)) \
177 dev_info(chip->dev, fmt, ##__VA_ARGS__); \
178 else \
179 dev_dbg(chip->dev, fmt, ##__VA_ARGS__); \
180 } while (0)
181
182static bool is_secure(struct qnovo *chip, int addr)
183{
184 /* assume everything above 0x40 is secure */
185 return (bool)(addr >= 0x40);
186}
187
188static int qnovo_read(struct qnovo *chip, u16 addr, u8 *buf, int len)
189{
190 return regmap_bulk_read(chip->regmap, chip->base + addr, buf, len);
191}
192
193static int qnovo_masked_write(struct qnovo *chip, u16 addr, u8 mask, u8 val)
194{
195 int rc = 0;
196
197 mutex_lock(&chip->write_lock);
198 if (is_secure(chip, addr)) {
199 rc = regmap_write(chip->regmap,
200 ((chip->base + addr) & ~(0xFF)) | 0xD0, 0xA5);
201 if (rc < 0)
202 goto unlock;
203 }
204
205 rc = regmap_update_bits(chip->regmap, chip->base + addr, mask, val);
206
207unlock:
208 mutex_unlock(&chip->write_lock);
209 return rc;
210}
211
212static int qnovo_write(struct qnovo *chip, u16 addr, u8 *buf, int len)
213{
214 int i, rc = 0;
215 bool is_start_secure, is_end_secure;
216
217 is_start_secure = is_secure(chip, addr);
218 is_end_secure = is_secure(chip, addr + len);
219
220 if (!is_start_secure && !is_end_secure) {
221 mutex_lock(&chip->write_lock);
222 rc = regmap_bulk_write(chip->regmap, chip->base + addr,
223 buf, len);
224 goto unlock;
225 }
226
227 mutex_lock(&chip->write_lock);
228 for (i = addr; i < addr + len; i++) {
229 if (is_secure(chip, i)) {
230 rc = regmap_write(chip->regmap,
231 ((chip->base + i) & ~(0xFF)) | 0xD0, 0xA5);
232 if (rc < 0)
233 goto unlock;
234 }
235 rc = regmap_write(chip->regmap, chip->base + i, buf[i - addr]);
236 if (rc < 0)
237 goto unlock;
238 }
239
240unlock:
241 mutex_unlock(&chip->write_lock);
242 return rc;
243}
244
Harry Yang2e324ba2017-02-07 21:33:19 -0800245static bool is_batt_available(struct qnovo *chip)
246{
247 if (!chip->batt_psy)
248 chip->batt_psy = power_supply_get_by_name("battery");
249
250 if (!chip->batt_psy)
251 return false;
252
253 return true;
254}
255
Abhijeet Dharmapurikar933d4942017-07-07 13:49:29 -0700256static bool is_fg_available(struct qnovo *chip)
257{
258 if (!chip->bms_psy)
259 chip->bms_psy = power_supply_get_by_name("bms");
260
261 if (!chip->bms_psy)
262 return false;
263
264 return true;
265}
266
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -0700267static bool is_usb_available(struct qnovo *chip)
268{
269 if (!chip->usb_psy)
270 chip->usb_psy = power_supply_get_by_name("usb");
271
272 if (!chip->usb_psy)
273 return false;
274
275 return true;
276}
277
278static bool is_dc_available(struct qnovo *chip)
279{
280 if (!chip->dc_psy)
281 chip->dc_psy = power_supply_get_by_name("dc");
282
283 if (!chip->dc_psy)
284 return false;
285
286 return true;
287}
288
Harry Yang2e324ba2017-02-07 21:33:19 -0800289static int qnovo_batt_psy_update(struct qnovo *chip, bool disable)
290{
291 union power_supply_propval pval = {0};
292 int rc = 0;
293
294 if (!is_batt_available(chip))
295 return -EINVAL;
296
297 if (chip->fv_uV_request != -EINVAL) {
298 pval.intval = disable ? -EINVAL : chip->fv_uV_request;
299 rc = power_supply_set_property(chip->batt_psy,
300 POWER_SUPPLY_PROP_VOLTAGE_QNOVO,
301 &pval);
302 if (rc < 0) {
303 pr_err("Couldn't set prop qnovo_fv rc = %d\n", rc);
304 return -EINVAL;
305 }
306 }
307
308 if (chip->fcc_uA_request != -EINVAL) {
309 pval.intval = disable ? -EINVAL : chip->fcc_uA_request;
310 rc = power_supply_set_property(chip->batt_psy,
311 POWER_SUPPLY_PROP_CURRENT_QNOVO,
312 &pval);
313 if (rc < 0) {
314 pr_err("Couldn't set prop qnovo_fcc rc = %d\n", rc);
315 return -EINVAL;
316 }
317 }
318
319 return rc;
320}
321
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700322static int qnovo_disable_cb(struct votable *votable, void *data, int disable,
323 const char *client)
324{
325 struct qnovo *chip = data;
Harry Yang87111f72017-02-24 01:06:00 -0800326 union power_supply_propval pval = {0};
327 int rc;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700328
Harry Yang87111f72017-02-24 01:06:00 -0800329 if (!is_batt_available(chip))
330 return -EINVAL;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700331
Harry Yang87111f72017-02-24 01:06:00 -0800332 pval.intval = !disable;
333 rc = power_supply_set_property(chip->batt_psy,
334 POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE,
335 &pval);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700336 if (rc < 0) {
Harry Yang87111f72017-02-24 01:06:00 -0800337 pr_err("Couldn't set prop qnovo_enable rc = %d\n", rc);
338 return -EINVAL;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700339 }
340
Abhijeet Dharmapurikar933d4942017-07-07 13:49:29 -0700341 /*
342 * fg must be available for enable FG_AVAILABLE_VOTER
343 * won't enable it otherwise
344 */
345
346 if (is_fg_available(chip))
347 power_supply_set_property(chip->bms_psy,
348 POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE,
349 &pval);
350
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -0700351 vote(chip->pt_dis_votable, QNOVO_OVERALL_VOTER, disable, 0);
Harry Yang87111f72017-02-24 01:06:00 -0800352 rc = qnovo_batt_psy_update(chip, disable);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700353 return rc;
354}
355
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -0700356static int pt_dis_votable_cb(struct votable *votable, void *data, int disable,
357 const char *client)
358{
359 struct qnovo *chip = data;
360 int rc;
361
Harry Yangc11edf42017-07-17 11:44:37 -0700362 if (disable) {
363 cancel_delayed_work_sync(&chip->ptrain_restart_work);
364 vote(chip->awake_votable, PT_RESTART_VOTER, false, 0);
365 }
366
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -0700367 rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT,
368 (bool)disable ? 0 : QNOVO_PTRAIN_EN_BIT);
369 if (rc < 0) {
370 dev_err(chip->dev, "Couldn't %s pulse train rc=%d\n",
371 (bool)disable ? "disable" : "enable", rc);
372 return rc;
373 }
374
Harry Yangc11edf42017-07-17 11:44:37 -0700375 if (!disable) {
376 vote(chip->awake_votable, PT_RESTART_VOTER, true, 0);
377 schedule_delayed_work(&chip->ptrain_restart_work,
378 msecs_to_jiffies(20));
379 }
380
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -0700381 return 0;
382}
383
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -0700384static int not_ok_to_qnovo_cb(struct votable *votable, void *data,
385 int not_ok_to_qnovo,
386 const char *client)
387{
388 struct qnovo *chip = data;
389
390 vote(chip->disable_votable, OK_TO_QNOVO_VOTER, not_ok_to_qnovo, 0);
391 if (not_ok_to_qnovo)
392 vote(chip->disable_votable, USER_VOTER, true, 0);
393
394 kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE);
395 return 0;
396}
397
398static int chg_ready_cb(struct votable *votable, void *data, int ready,
399 const char *client)
400{
401 struct qnovo *chip = data;
402
403 vote(chip->not_ok_to_qnovo_votable, CHG_READY_VOTER, !ready, 0);
404
405 return 0;
406}
407
408static int awake_cb(struct votable *votable, void *data, int awake,
409 const char *client)
410{
411 struct qnovo *chip = data;
412
413 if (awake)
414 pm_stay_awake(chip->dev);
415 else
416 pm_relax(chip->dev);
417
418 return 0;
419}
420
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700421static int qnovo_parse_dt(struct qnovo *chip)
422{
423 struct device_node *node = chip->dev->of_node;
424 int rc;
425
426 if (!node) {
427 pr_err("device tree node missing\n");
428 return -EINVAL;
429 }
430
431 rc = of_property_read_u32(node, "reg", &chip->base);
432 if (rc < 0) {
433 pr_err("Couldn't read base rc = %d\n", rc);
434 return rc;
435 }
436
437 chip->dt.external_rsense = of_property_read_bool(node,
438 "qcom,external-rsense");
439
440 chip->dt.revid_dev_node = of_parse_phandle(node, "qcom,pmic-revid", 0);
441 if (!chip->dt.revid_dev_node) {
442 pr_err("Missing qcom,pmic-revid property - driver failed\n");
443 return -EINVAL;
444 }
445
446 return 0;
447}
448
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700449enum {
450 VER = 0,
451 OK_TO_QNOVO,
Harry Yang87111f72017-02-24 01:06:00 -0800452 QNOVO_ENABLE,
453 PT_ENABLE,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700454 FV_REQUEST,
455 FCC_REQUEST,
456 PE_CTRL_REG,
457 PE_CTRL2_REG,
458 PTRAIN_STS_REG,
459 INT_RT_STS_REG,
Harry Yange7a96842017-03-23 21:41:48 -0700460 ERR_STS2_REG,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700461 PREST1,
462 PPULS1,
463 NREST1,
464 NPULS1,
465 PPCNT,
466 VLIM1,
467 PVOLT1,
468 PCUR1,
469 PTTIME,
470 PREST2,
471 PPULS2,
472 NREST2,
473 NPULS2,
474 VLIM2,
475 PVOLT2,
476 RVOLT2,
477 PCUR2,
478 SCNT,
479 VMAX,
480 SNUM,
481 VBATT,
482 IBATT,
483 BATTTEMP,
484 BATTSOC,
485};
486
487struct param_info {
488 char *name;
489 int start_addr;
490 int num_regs;
491 int reg_to_unit_multiplier;
492 int reg_to_unit_divider;
493 int reg_to_unit_offset;
494 int min_val;
495 int max_val;
496 char *units_str;
497};
498
499static struct param_info params[] = {
Harry Yang87111f72017-02-24 01:06:00 -0800500 [PT_ENABLE] = {
501 .name = "PT_ENABLE",
502 .start_addr = QNOVO_PTRAIN_EN,
503 .num_regs = 1,
504 .units_str = "",
505 },
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700506 [FV_REQUEST] = {
507 .units_str = "uV",
508 },
509 [FCC_REQUEST] = {
510 .units_str = "uA",
511 },
512 [PE_CTRL_REG] = {
513 .name = "CTRL_REG",
514 .start_addr = QNOVO_PE_CTRL,
515 .num_regs = 1,
516 .units_str = "",
517 },
518 [PE_CTRL2_REG] = {
519 .name = "PE_CTRL2_REG",
520 .start_addr = QNOVO_PE_CTRL2,
521 .num_regs = 1,
522 .units_str = "",
523 },
524 [PTRAIN_STS_REG] = {
525 .name = "PTRAIN_STS",
526 .start_addr = QNOVO_PTRAIN_STS,
527 .num_regs = 1,
528 .units_str = "",
529 },
530 [INT_RT_STS_REG] = {
531 .name = "INT_RT_STS",
532 .start_addr = QNOVO_INT_RT_STS,
533 .num_regs = 1,
534 .units_str = "",
535 },
Harry Yange7a96842017-03-23 21:41:48 -0700536 [ERR_STS2_REG] = {
537 .name = "RAW_CHGR_ERR",
538 .start_addr = QNOVO_ERROR_STS2,
539 .num_regs = 1,
540 .units_str = "",
541 },
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700542 [PREST1] = {
543 .name = "PREST1",
544 .start_addr = QNOVO_PREST1_CTRL,
545 .num_regs = 1,
546 .reg_to_unit_multiplier = 5,
547 .reg_to_unit_divider = 1,
548 .min_val = 5,
Harry Yangaf2f67e2017-02-23 14:16:51 -0800549 .max_val = 255,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700550 .units_str = "mS",
551 },
552 [PPULS1] = {
553 .name = "PPULS1",
554 .start_addr = QNOVO_PPULS1_LSB_CTRL,
555 .num_regs = 2,
556 .reg_to_unit_multiplier = 1600, /* converts to uC */
557 .reg_to_unit_divider = 1,
Harry Yangaf2f67e2017-02-23 14:16:51 -0800558 .min_val = 30000,
559 .max_val = 65535000,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700560 .units_str = "uC",
561 },
562 [NREST1] = {
563 .name = "NREST1",
564 .start_addr = QNOVO_NREST1_CTRL,
565 .num_regs = 1,
566 .reg_to_unit_multiplier = 5,
567 .reg_to_unit_divider = 1,
568 .min_val = 5,
Harry Yangaf2f67e2017-02-23 14:16:51 -0800569 .max_val = 255,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700570 .units_str = "mS",
571 },
572 [NPULS1] = {
573 .name = "NPULS1",
574 .start_addr = QNOVO_NPULS1_CTRL,
575 .num_regs = 1,
576 .reg_to_unit_multiplier = 5,
577 .reg_to_unit_divider = 1,
Harry Yangaf2f67e2017-02-23 14:16:51 -0800578 .min_val = 0,
579 .max_val = 255,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700580 .units_str = "mS",
581 },
582 [PPCNT] = {
583 .name = "PPCNT",
584 .start_addr = QNOVO_PPCNT_CTRL,
585 .num_regs = 1,
586 .reg_to_unit_multiplier = 1,
587 .reg_to_unit_divider = 1,
Harry Yangaf2f67e2017-02-23 14:16:51 -0800588 .min_val = 1,
Harry Yang8dc14eb2017-02-06 13:12:15 -0800589 .max_val = 255,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700590 .units_str = "pulses",
591 },
592 [VLIM1] = {
593 .name = "VLIM1",
594 .start_addr = QNOVO_VLIM1_LSB_CTRL,
595 .num_regs = 2,
596 .reg_to_unit_multiplier = 610350, /* converts to nV */
597 .reg_to_unit_divider = 1,
Harry Yangaf2f67e2017-02-23 14:16:51 -0800598 .min_val = 2200000,
599 .max_val = 4500000,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700600 .units_str = "uV",
601 },
602 [PVOLT1] = {
603 .name = "PVOLT1",
604 .start_addr = QNOVO_PVOLT1_LSB,
605 .num_regs = 2,
606 .reg_to_unit_multiplier = 610350, /* converts to nV */
607 .reg_to_unit_divider = 1,
608 .units_str = "uV",
609 },
610 [PCUR1] = {
611 .name = "PCUR1",
612 .start_addr = QNOVO_PCUR1_LSB,
613 .num_regs = 2,
614 .reg_to_unit_multiplier = 1220700, /* converts to nA */
615 .reg_to_unit_divider = 1,
616 .units_str = "uA",
617 },
618 [PTTIME] = {
619 .name = "PTTIME",
620 .start_addr = QNOVO_PTTIME_STS,
621 .num_regs = 1,
622 .reg_to_unit_multiplier = 2,
623 .reg_to_unit_divider = 1,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700624 .units_str = "S",
625 },
626 [PREST2] = {
627 .name = "PREST2",
628 .start_addr = QNOVO_PREST2_LSB_CTRL,
629 .num_regs = 2,
630 .reg_to_unit_multiplier = 5,
631 .reg_to_unit_divider = 1,
632 .min_val = 5,
Harry Yangaf2f67e2017-02-23 14:16:51 -0800633 .max_val = 65535,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700634 .units_str = "mS",
635 },
636 [PPULS2] = {
637 .name = "PPULS2",
638 .start_addr = QNOVO_PPULS2_LSB_CTRL,
639 .num_regs = 2,
640 .reg_to_unit_multiplier = 1600, /* converts to uC */
641 .reg_to_unit_divider = 1,
Harry Yangaf2f67e2017-02-23 14:16:51 -0800642 .min_val = 30000,
643 .max_val = 65535000,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700644 .units_str = "uC",
645 },
646 [NREST2] = {
647 .name = "NREST2",
648 .start_addr = QNOVO_NREST2_CTRL,
649 .num_regs = 1,
650 .reg_to_unit_multiplier = 5,
651 .reg_to_unit_divider = 1,
652 .reg_to_unit_offset = -5,
653 .min_val = 5,
Harry Yangaf2f67e2017-02-23 14:16:51 -0800654 .max_val = 255,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700655 .units_str = "mS",
656 },
657 [NPULS2] = {
658 .name = "NPULS2",
659 .start_addr = QNOVO_NPULS2_CTRL,
660 .num_regs = 1,
661 .reg_to_unit_multiplier = 5,
662 .reg_to_unit_divider = 1,
Harry Yangaf2f67e2017-02-23 14:16:51 -0800663 .min_val = 0,
664 .max_val = 255,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700665 .units_str = "mS",
666 },
667 [VLIM2] = {
Harry Yangaf2f67e2017-02-23 14:16:51 -0800668 .name = "VLIM2",
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700669 .start_addr = QNOVO_VLIM2_LSB_CTRL,
670 .num_regs = 2,
671 .reg_to_unit_multiplier = 610350, /* converts to nV */
672 .reg_to_unit_divider = 1,
Harry Yangaf2f67e2017-02-23 14:16:51 -0800673 .min_val = 2200000,
674 .max_val = 4500000,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700675 .units_str = "uV",
676 },
677 [PVOLT2] = {
678 .name = "PVOLT2",
679 .start_addr = QNOVO_PVOLT2_LSB,
680 .num_regs = 2,
681 .reg_to_unit_multiplier = 610350, /* converts to nV */
682 .reg_to_unit_divider = 1,
683 .units_str = "uV",
684 },
685 [RVOLT2] = {
686 .name = "RVOLT2",
687 .start_addr = QNOVO_RVOLT2_LSB,
688 .num_regs = 2,
689 .reg_to_unit_multiplier = 610350,
690 .reg_to_unit_divider = 1,
691 .units_str = "uV",
692 },
693 [PCUR2] = {
694 .name = "PCUR2",
695 .start_addr = QNOVO_PCUR2_LSB,
696 .num_regs = 2,
697 .reg_to_unit_multiplier = 1220700, /* converts to nA */
698 .reg_to_unit_divider = 1,
699 .units_str = "uA",
700 },
701 [SCNT] = {
702 .name = "SCNT",
703 .start_addr = QNOVO_SCNT,
704 .num_regs = 1,
705 .reg_to_unit_multiplier = 1,
706 .reg_to_unit_divider = 1,
Harry Yangaf2f67e2017-02-23 14:16:51 -0800707 .min_val = 0,
708 .max_val = 255,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700709 .units_str = "pulses",
710 },
711 [VMAX] = {
712 .name = "VMAX",
713 .start_addr = QNOVO_VMAX_LSB,
714 .num_regs = 2,
715 .reg_to_unit_multiplier = 814000, /* converts to nV */
716 .reg_to_unit_divider = 1,
717 .units_str = "uV",
718 },
719 [SNUM] = {
720 .name = "SNUM",
721 .start_addr = QNOVO_SNUM,
722 .num_regs = 1,
723 .reg_to_unit_multiplier = 1,
724 .reg_to_unit_divider = 1,
725 .units_str = "pulses",
726 },
727 [VBATT] = {
728 .name = "POWER_SUPPLY_PROP_VOLTAGE_NOW",
729 .start_addr = POWER_SUPPLY_PROP_VOLTAGE_NOW,
730 .units_str = "uV",
731 },
732 [IBATT] = {
733 .name = "POWER_SUPPLY_PROP_CURRENT_NOW",
734 .start_addr = POWER_SUPPLY_PROP_CURRENT_NOW,
735 .units_str = "uA",
736 },
737 [BATTTEMP] = {
738 .name = "POWER_SUPPLY_PROP_TEMP",
739 .start_addr = POWER_SUPPLY_PROP_TEMP,
740 .units_str = "uV",
741 },
742 [BATTSOC] = {
743 .name = "POWER_SUPPLY_PROP_CAPACITY",
744 .start_addr = POWER_SUPPLY_PROP_CAPACITY,
745 .units_str = "%",
746 },
747};
748
749static struct class_attribute qnovo_attributes[];
750
751static ssize_t version_show(struct class *c, struct class_attribute *attr,
752 char *buf)
753{
754 return snprintf(buf, PAGE_SIZE, "%d.%d\n",
755 DRV_MAJOR_VERSION, DRV_MINOR_VERSION);
756}
757
758static ssize_t ok_to_qnovo_show(struct class *c, struct class_attribute *attr,
759 char *buf)
760{
761 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -0700762 int val = get_effective_result(chip->not_ok_to_qnovo_votable);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700763
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -0700764 return snprintf(buf, PAGE_SIZE, "%d\n", !val);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700765}
766
Harry Yang87111f72017-02-24 01:06:00 -0800767static ssize_t qnovo_enable_show(struct class *c, struct class_attribute *attr,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700768 char *ubuf)
769{
770 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
Harry Yang87111f72017-02-24 01:06:00 -0800771 int val = get_effective_result(chip->disable_votable);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700772
Harry Yang87111f72017-02-24 01:06:00 -0800773 return snprintf(ubuf, PAGE_SIZE, "%d\n", !val);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700774}
775
Harry Yang87111f72017-02-24 01:06:00 -0800776static ssize_t qnovo_enable_store(struct class *c, struct class_attribute *attr,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700777 const char *ubuf, size_t count)
778{
779 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
780 unsigned long val;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700781
Harry Yangaf2f67e2017-02-23 14:16:51 -0800782 if (kstrtoul(ubuf, 0, &val))
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700783 return -EINVAL;
784
Harry Yang87111f72017-02-24 01:06:00 -0800785 vote(chip->disable_votable, USER_VOTER, !val, 0);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700786
Harry Yang87111f72017-02-24 01:06:00 -0800787 return count;
788}
789
790static ssize_t pt_enable_show(struct class *c, struct class_attribute *attr,
791 char *ubuf)
792{
Harry Yang87111f72017-02-24 01:06:00 -0800793 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -0700794 int val = get_effective_result(chip->pt_dis_votable);
Harry Yang87111f72017-02-24 01:06:00 -0800795
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -0700796 return snprintf(ubuf, PAGE_SIZE, "%d\n", !val);
Harry Yang87111f72017-02-24 01:06:00 -0800797}
798
799static ssize_t pt_enable_store(struct class *c, struct class_attribute *attr,
800 const char *ubuf, size_t count)
801{
802 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
803 unsigned long val;
Harry Yang87111f72017-02-24 01:06:00 -0800804
805 if (kstrtoul(ubuf, 0, &val))
806 return -EINVAL;
807
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -0700808 /* val being 0, userspace wishes to disable pt so vote true */
809 vote(chip->pt_dis_votable, QNI_PT_VOTER, val ? false : true, 0);
Harry Yang87111f72017-02-24 01:06:00 -0800810
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700811 return count;
812}
813
814static ssize_t val_show(struct class *c, struct class_attribute *attr,
815 char *ubuf)
816{
817 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
818 int i = attr - qnovo_attributes;
819 int val = 0;
820
821 if (i == FV_REQUEST)
822 val = chip->fv_uV_request;
823
824 if (i == FCC_REQUEST)
825 val = chip->fcc_uA_request;
826
Harry Yangaf2f67e2017-02-23 14:16:51 -0800827 return snprintf(ubuf, PAGE_SIZE, "%d\n", val);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700828}
829
830static ssize_t val_store(struct class *c, struct class_attribute *attr,
831 const char *ubuf, size_t count)
832{
833 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
834 int i = attr - qnovo_attributes;
835 unsigned long val;
836
Harry Yangaf2f67e2017-02-23 14:16:51 -0800837 if (kstrtoul(ubuf, 0, &val))
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700838 return -EINVAL;
839
840 if (i == FV_REQUEST)
841 chip->fv_uV_request = val;
842
843 if (i == FCC_REQUEST)
844 chip->fcc_uA_request = val;
845
Harry Yang96ec88e2017-03-23 23:43:53 -0700846 if (!get_effective_result(chip->disable_votable))
847 qnovo_batt_psy_update(chip, false);
Harry Yang87111f72017-02-24 01:06:00 -0800848
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700849 return count;
850}
851
852static ssize_t reg_show(struct class *c, struct class_attribute *attr,
853 char *ubuf)
854{
855 int i = attr - qnovo_attributes;
856 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
857 u8 buf[2] = {0, 0};
858 u16 regval;
859 int rc;
860
861 rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs);
862 if (rc < 0) {
863 pr_err("Couldn't read %s rc = %d\n", params[i].name, rc);
864 return -EINVAL;
865 }
866 regval = buf[1] << 8 | buf[0];
867
Harry Yangaf2f67e2017-02-23 14:16:51 -0800868 return snprintf(ubuf, PAGE_SIZE, "0x%04x\n", regval);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700869}
870
871static ssize_t reg_store(struct class *c, struct class_attribute *attr,
872 const char *ubuf, size_t count)
873{
874 int i = attr - qnovo_attributes;
875 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
876 u8 buf[2] = {0, 0};
877 unsigned long val;
878 int rc;
879
Harry Yangaf2f67e2017-02-23 14:16:51 -0800880 if (kstrtoul(ubuf, 0, &val))
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700881 return -EINVAL;
882
883 buf[0] = val & 0xFF;
884 buf[1] = (val >> 8) & 0xFF;
885
886 rc = qnovo_write(chip, params[i].start_addr, buf, params[i].num_regs);
887 if (rc < 0) {
888 pr_err("Couldn't write %s rc = %d\n", params[i].name, rc);
889 return -EINVAL;
890 }
891 return count;
892}
893
894static ssize_t time_show(struct class *c, struct class_attribute *attr,
895 char *ubuf)
896{
897 int i = attr - qnovo_attributes;
898 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
899 u8 buf[2] = {0, 0};
900 u16 regval;
901 int val;
902 int rc;
903
904 rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs);
905 if (rc < 0) {
906 pr_err("Couldn't read %s rc = %d\n", params[i].name, rc);
907 return -EINVAL;
908 }
909 regval = buf[1] << 8 | buf[0];
910
911 val = ((regval * params[i].reg_to_unit_multiplier)
912 / params[i].reg_to_unit_divider)
913 - params[i].reg_to_unit_offset;
914
Harry Yangaf2f67e2017-02-23 14:16:51 -0800915 return snprintf(ubuf, PAGE_SIZE, "%d\n", val);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700916}
917
918static ssize_t time_store(struct class *c, struct class_attribute *attr,
919 const char *ubuf, size_t count)
920{
921 int i = attr - qnovo_attributes;
922 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
923 u8 buf[2] = {0, 0};
924 u16 regval;
925 unsigned long val;
926 int rc;
927
Harry Yangaf2f67e2017-02-23 14:16:51 -0800928 if (kstrtoul(ubuf, 0, &val))
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700929 return -EINVAL;
930
931 if (val < params[i].min_val || val > params[i].max_val) {
932 pr_err("Out of Range %d%s for %s\n", (int)val,
933 params[i].units_str,
934 params[i].name);
935 return -ERANGE;
936 }
937
938 regval = (((int)val + params[i].reg_to_unit_offset)
939 * params[i].reg_to_unit_divider)
940 / params[i].reg_to_unit_multiplier;
941 buf[0] = regval & 0xFF;
942 buf[1] = (regval >> 8) & 0xFF;
943
944 rc = qnovo_write(chip, params[i].start_addr, buf, params[i].num_regs);
945 if (rc < 0) {
946 pr_err("Couldn't write %s rc = %d\n", params[i].name, rc);
947 return -EINVAL;
948 }
949
950 return count;
951}
952
953static ssize_t current_show(struct class *c, struct class_attribute *attr,
954 char *ubuf)
955{
956 int i = attr - qnovo_attributes;
957 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
958 u8 buf[2] = {0, 0};
959 int rc;
960 int comp_val_uA;
961 s64 regval_nA;
962 s64 gain, offset_nA, comp_val_nA;
963
964 rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs);
965 if (rc < 0) {
966 pr_err("Couldn't read %s rc = %d\n", params[i].name, rc);
967 return -EINVAL;
968 }
Harry Yangf2d57a22017-03-31 14:28:20 -0700969
970 if (buf[1] & BIT(5))
971 buf[1] |= GENMASK(7, 6);
972
973 regval_nA = (s16)(buf[1] << 8 | buf[0]);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700974 regval_nA = div_s64(regval_nA * params[i].reg_to_unit_multiplier,
975 params[i].reg_to_unit_divider)
976 - params[i].reg_to_unit_offset;
977
978 if (chip->dt.external_rsense) {
979 offset_nA = chip->external_offset_nA;
980 gain = chip->external_i_gain_mega;
981 } else {
982 offset_nA = chip->internal_offset_nA;
983 gain = chip->internal_i_gain_mega;
984 }
985
Harry Yangd9e49562017-02-17 15:37:09 -0800986 comp_val_nA = div_s64(regval_nA * gain, 1000000) - offset_nA;
Tirupathi Reddy4d8234a2017-02-10 18:10:43 +0530987 comp_val_uA = div_s64(comp_val_nA, 1000);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700988
Harry Yangaf2f67e2017-02-23 14:16:51 -0800989 return snprintf(ubuf, PAGE_SIZE, "%d\n", comp_val_uA);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -0700990}
991
992static ssize_t voltage_show(struct class *c, struct class_attribute *attr,
993 char *ubuf)
994{
995 int i = attr - qnovo_attributes;
996 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
997 u8 buf[2] = {0, 0};
998 int rc;
999 int comp_val_uV;
1000 s64 regval_nV;
1001 s64 gain, offset_nV, comp_val_nV;
1002
1003 rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs);
1004 if (rc < 0) {
1005 pr_err("Couldn't read %s rc = %d\n", params[i].name, rc);
1006 return -EINVAL;
1007 }
1008 regval_nV = buf[1] << 8 | buf[0];
1009 regval_nV = div_s64(regval_nV * params[i].reg_to_unit_multiplier,
1010 params[i].reg_to_unit_divider)
1011 - params[i].reg_to_unit_offset;
1012
1013 offset_nV = chip->offset_nV;
1014 gain = chip->v_gain_mega;
1015
1016 comp_val_nV = div_s64(regval_nV * gain, 1000000) + offset_nV;
Tirupathi Reddy4d8234a2017-02-10 18:10:43 +05301017 comp_val_uV = div_s64(comp_val_nV, 1000);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001018
Harry Yangaf2f67e2017-02-23 14:16:51 -08001019 return snprintf(ubuf, PAGE_SIZE, "%d\n", comp_val_uV);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001020}
1021
1022static ssize_t voltage_store(struct class *c, struct class_attribute *attr,
1023 const char *ubuf, size_t count)
1024{
1025 int i = attr - qnovo_attributes;
1026 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
1027 u8 buf[2] = {0, 0};
1028 int rc;
1029 unsigned long val_uV;
1030 s64 regval_nV;
1031 s64 gain, offset_nV;
1032
Harry Yangaf2f67e2017-02-23 14:16:51 -08001033 if (kstrtoul(ubuf, 0, &val_uV))
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001034 return -EINVAL;
1035
1036 if (val_uV < params[i].min_val || val_uV > params[i].max_val) {
1037 pr_err("Out of Range %d%s for %s\n", (int)val_uV,
1038 params[i].units_str,
1039 params[i].name);
1040 return -ERANGE;
1041 }
1042
1043 offset_nV = chip->offset_nV;
1044 gain = chip->v_gain_mega;
1045
1046 regval_nV = (s64)val_uV * 1000 - offset_nV;
1047 regval_nV = div_s64(regval_nV * 1000000, gain);
1048
1049 regval_nV = div_s64((regval_nV + params[i].reg_to_unit_offset)
1050 * params[i].reg_to_unit_divider,
1051 params[i].reg_to_unit_multiplier);
1052 buf[0] = regval_nV & 0xFF;
1053 buf[1] = ((u64)regval_nV >> 8) & 0xFF;
1054
1055 rc = qnovo_write(chip, params[i].start_addr, buf, params[i].num_regs);
1056 if (rc < 0) {
1057 pr_err("Couldn't write %s rc = %d\n", params[i].name, rc);
1058 return -EINVAL;
1059 }
1060
1061 return count;
1062}
1063
1064static ssize_t coulomb_show(struct class *c, struct class_attribute *attr,
1065 char *ubuf)
1066{
1067 int i = attr - qnovo_attributes;
1068 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
1069 u8 buf[2] = {0, 0};
1070 int rc;
1071 int comp_val_uC;
1072 s64 regval_uC, gain;
1073
1074 rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs);
1075 if (rc < 0) {
1076 pr_err("Couldn't read %s rc = %d\n", params[i].name, rc);
1077 return -EINVAL;
1078 }
1079 regval_uC = buf[1] << 8 | buf[0];
1080 regval_uC = div_s64(regval_uC * params[i].reg_to_unit_multiplier,
1081 params[i].reg_to_unit_divider)
1082 - params[i].reg_to_unit_offset;
1083
1084 if (chip->dt.external_rsense)
1085 gain = chip->external_i_gain_mega;
1086 else
1087 gain = chip->internal_i_gain_mega;
1088
1089 comp_val_uC = div_s64(regval_uC * gain, 1000000);
Harry Yangaf2f67e2017-02-23 14:16:51 -08001090 return snprintf(ubuf, PAGE_SIZE, "%d\n", comp_val_uC);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001091}
1092
1093static ssize_t coulomb_store(struct class *c, struct class_attribute *attr,
1094 const char *ubuf, size_t count)
1095{
1096 int i = attr - qnovo_attributes;
1097 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
1098 u8 buf[2] = {0, 0};
1099 int rc;
1100 unsigned long val_uC;
1101 s64 regval;
1102 s64 gain;
1103
Harry Yangaf2f67e2017-02-23 14:16:51 -08001104 if (kstrtoul(ubuf, 0, &val_uC))
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001105 return -EINVAL;
1106
1107 if (val_uC < params[i].min_val || val_uC > params[i].max_val) {
1108 pr_err("Out of Range %d%s for %s\n", (int)val_uC,
1109 params[i].units_str,
1110 params[i].name);
1111 return -ERANGE;
1112 }
1113
1114 if (chip->dt.external_rsense)
1115 gain = chip->external_i_gain_mega;
1116 else
1117 gain = chip->internal_i_gain_mega;
1118
1119 regval = div_s64((s64)val_uC * 1000000, gain);
1120
1121 regval = div_s64((regval + params[i].reg_to_unit_offset)
1122 * params[i].reg_to_unit_divider,
1123 params[i].reg_to_unit_multiplier);
1124
1125 buf[0] = regval & 0xFF;
1126 buf[1] = ((u64)regval >> 8) & 0xFF;
1127
1128 rc = qnovo_write(chip, params[i].start_addr, buf, params[i].num_regs);
1129 if (rc < 0) {
1130 pr_err("Couldn't write %s rc = %d\n", params[i].name, rc);
1131 return -EINVAL;
1132 }
1133
1134 return count;
1135}
1136
1137static ssize_t batt_prop_show(struct class *c, struct class_attribute *attr,
1138 char *ubuf)
1139{
1140 int i = attr - qnovo_attributes;
1141 struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
1142 int rc = -EINVAL;
1143 int prop = params[i].start_addr;
1144 union power_supply_propval pval = {0};
1145
Harry Yang2e324ba2017-02-07 21:33:19 -08001146 if (!is_batt_available(chip))
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001147 return -EINVAL;
1148
1149 rc = power_supply_get_property(chip->batt_psy, prop, &pval);
1150 if (rc < 0) {
1151 pr_err("Couldn't read battery prop %s rc = %d\n",
1152 params[i].name, rc);
1153 return -EINVAL;
1154 }
1155
Harry Yangaf2f67e2017-02-23 14:16:51 -08001156 return snprintf(ubuf, PAGE_SIZE, "%d\n", pval.intval);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001157}
1158
1159static struct class_attribute qnovo_attributes[] = {
1160 [VER] = __ATTR_RO(version),
1161 [OK_TO_QNOVO] = __ATTR_RO(ok_to_qnovo),
Harry Yang87111f72017-02-24 01:06:00 -08001162 [QNOVO_ENABLE] = __ATTR_RW(qnovo_enable),
1163 [PT_ENABLE] = __ATTR_RW(pt_enable),
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001164 [FV_REQUEST] = __ATTR(fv_uV_request, 0644,
1165 val_show, val_store),
1166 [FCC_REQUEST] = __ATTR(fcc_uA_request, 0644,
1167 val_show, val_store),
1168 [PE_CTRL_REG] = __ATTR(PE_CTRL_REG, 0644,
1169 reg_show, reg_store),
1170 [PE_CTRL2_REG] = __ATTR(PE_CTRL2_REG, 0644,
1171 reg_show, reg_store),
Harry Yangaf2f67e2017-02-23 14:16:51 -08001172 [PTRAIN_STS_REG] = __ATTR(PTRAIN_STS_REG, 0444,
1173 reg_show, NULL),
1174 [INT_RT_STS_REG] = __ATTR(INT_RT_STS_REG, 0444,
1175 reg_show, NULL),
Harry Yange7a96842017-03-23 21:41:48 -07001176 [ERR_STS2_REG] = __ATTR(ERR_STS2_REG, 0444,
1177 reg_show, NULL),
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001178 [PREST1] = __ATTR(PREST1_mS, 0644,
1179 time_show, time_store),
1180 [PPULS1] = __ATTR(PPULS1_uC, 0644,
1181 coulomb_show, coulomb_store),
1182 [NREST1] = __ATTR(NREST1_mS, 0644,
1183 time_show, time_store),
1184 [NPULS1] = __ATTR(NPULS1_mS, 0644,
1185 time_show, time_store),
1186 [PPCNT] = __ATTR(PPCNT, 0644,
1187 time_show, time_store),
1188 [VLIM1] = __ATTR(VLIM1_uV, 0644,
1189 voltage_show, voltage_store),
1190 [PVOLT1] = __ATTR(PVOLT1_uV, 0444,
1191 voltage_show, NULL),
1192 [PCUR1] = __ATTR(PCUR1_uA, 0444,
1193 current_show, NULL),
1194 [PTTIME] = __ATTR(PTTIME_S, 0444,
1195 time_show, NULL),
1196 [PREST2] = __ATTR(PREST2_mS, 0644,
1197 time_show, time_store),
Harry Yang87111f72017-02-24 01:06:00 -08001198 [PPULS2] = __ATTR(PPULS2_uC, 0644,
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001199 coulomb_show, coulomb_store),
1200 [NREST2] = __ATTR(NREST2_mS, 0644,
1201 time_show, time_store),
1202 [NPULS2] = __ATTR(NPULS2_mS, 0644,
1203 time_show, time_store),
1204 [VLIM2] = __ATTR(VLIM2_uV, 0644,
1205 voltage_show, voltage_store),
1206 [PVOLT2] = __ATTR(PVOLT2_uV, 0444,
1207 voltage_show, NULL),
1208 [RVOLT2] = __ATTR(RVOLT2_uV, 0444,
1209 voltage_show, NULL),
1210 [PCUR2] = __ATTR(PCUR2_uA, 0444,
1211 current_show, NULL),
1212 [SCNT] = __ATTR(SCNT, 0644,
1213 time_show, time_store),
1214 [VMAX] = __ATTR(VMAX_uV, 0444,
1215 voltage_show, NULL),
Harry Yangaf2f67e2017-02-23 14:16:51 -08001216 [SNUM] = __ATTR(SNUM, 0444,
1217 time_show, NULL),
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001218 [VBATT] = __ATTR(VBATT_uV, 0444,
1219 batt_prop_show, NULL),
1220 [IBATT] = __ATTR(IBATT_uA, 0444,
1221 batt_prop_show, NULL),
1222 [BATTTEMP] = __ATTR(BATTTEMP_deciDegC, 0444,
1223 batt_prop_show, NULL),
1224 [BATTSOC] = __ATTR(BATTSOC, 0444,
1225 batt_prop_show, NULL),
1226 __ATTR_NULL,
1227};
1228
Harry Yang96ec88e2017-03-23 23:43:53 -07001229static int qnovo_update_status(struct qnovo *chip)
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001230{
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001231 u8 val = 0;
1232 int rc;
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001233 bool hw_ok_to_qnovo;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001234
Harry Yang96ec88e2017-03-23 23:43:53 -07001235 rc = qnovo_read(chip, QNOVO_ERROR_STS2, &val, 1);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001236 if (rc < 0) {
1237 pr_err("Couldn't read error sts rc = %d\n", rc);
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001238 hw_ok_to_qnovo = false;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001239 } else {
Harry Yang58acd162017-04-07 17:08:06 -07001240 /*
1241 * For CV mode keep qnovo enabled, userspace is expected to
1242 * disable it after few runs
1243 */
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001244 hw_ok_to_qnovo = (val == ERR_CV_MODE || val == 0) ?
1245 true : false;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001246 }
1247
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001248 vote(chip->not_ok_to_qnovo_votable, HW_OK_TO_QNOVO_VOTER,
1249 !hw_ok_to_qnovo, 0);
1250 return 0;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001251}
1252
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001253static void usb_debounce_work(struct work_struct *work)
1254{
1255 struct qnovo *chip = container_of(work,
1256 struct qnovo, usb_debounce_work.work);
1257
1258 vote(chip->chg_ready_votable, USB_READY_VOTER, true, 0);
1259 vote(chip->awake_votable, USB_READY_VOTER, false, 0);
1260}
1261
1262static void dc_debounce_work(struct work_struct *work)
1263{
1264 struct qnovo *chip = container_of(work,
1265 struct qnovo, dc_debounce_work.work);
1266
1267 vote(chip->chg_ready_votable, DC_READY_VOTER, true, 0);
1268 vote(chip->awake_votable, DC_READY_VOTER, false, 0);
1269}
1270
1271#define DEBOUNCE_MS 15000 /* 15 seconds */
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001272static void status_change_work(struct work_struct *work)
1273{
1274 struct qnovo *chip = container_of(work,
1275 struct qnovo, status_change_work);
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001276 union power_supply_propval pval;
1277 bool usb_present = false, dc_present = false;
1278 int rc;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001279
Abhijeet Dharmapurikar933d4942017-07-07 13:49:29 -07001280 if (is_fg_available(chip))
1281 vote(chip->disable_votable, FG_AVAILABLE_VOTER, false, 0);
1282
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001283 if (is_usb_available(chip)) {
1284 rc = power_supply_get_property(chip->usb_psy,
1285 POWER_SUPPLY_PROP_PRESENT, &pval);
1286 usb_present = (rc < 0) ? 0 : pval.intval;
1287 }
1288
1289 if (chip->usb_present && !usb_present) {
1290 /* removal */
1291 chip->usb_present = 0;
1292 cancel_delayed_work_sync(&chip->usb_debounce_work);
1293 vote(chip->awake_votable, USB_READY_VOTER, false, 0);
1294 vote(chip->chg_ready_votable, USB_READY_VOTER, false, 0);
1295 } else if (!chip->usb_present && usb_present) {
1296 /* insertion */
1297 chip->usb_present = 1;
1298 vote(chip->awake_votable, USB_READY_VOTER, true, 0);
1299 schedule_delayed_work(&chip->usb_debounce_work,
1300 msecs_to_jiffies(DEBOUNCE_MS));
1301 }
1302
1303 if (is_dc_available(chip)) {
1304 rc = power_supply_get_property(chip->dc_psy,
1305 POWER_SUPPLY_PROP_PRESENT,
1306 &pval);
1307 dc_present = (rc < 0) ? 0 : pval.intval;
1308 }
1309
1310 if (usb_present)
1311 dc_present = 0;
1312
1313 if (chip->dc_present && !dc_present) {
1314 /* removal */
1315 chip->dc_present = 0;
1316 cancel_delayed_work_sync(&chip->dc_debounce_work);
1317 vote(chip->awake_votable, DC_READY_VOTER, false, 0);
1318 vote(chip->chg_ready_votable, DC_READY_VOTER, false, 0);
1319 } else if (!chip->dc_present && dc_present) {
1320 /* insertion */
1321 chip->dc_present = 1;
1322 vote(chip->awake_votable, DC_READY_VOTER, true, 0);
1323 schedule_delayed_work(&chip->dc_debounce_work,
1324 msecs_to_jiffies(DEBOUNCE_MS));
1325 }
1326
1327 qnovo_update_status(chip);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001328}
1329
Harry Yangc11edf42017-07-17 11:44:37 -07001330static void ptrain_restart_work(struct work_struct *work)
1331{
1332 struct qnovo *chip = container_of(work,
1333 struct qnovo, ptrain_restart_work.work);
1334 u8 pt_t1, pt_t2;
1335 int rc;
1336
1337 rc = qnovo_read(chip, QNOVO_PTTIME_STS, &pt_t1, 1);
1338 if (rc < 0) {
1339 dev_err(chip->dev, "Couldn't read QNOVO_PTTIME_STS rc = %d\n",
1340 rc);
1341 goto clean_up;
1342 }
1343
1344 /* pttime increments every 2 seconds */
1345 msleep(2100);
1346
1347 rc = qnovo_read(chip, QNOVO_PTTIME_STS, &pt_t2, 1);
1348 if (rc < 0) {
1349 dev_err(chip->dev, "Couldn't read QNOVO_PTTIME_STS rc = %d\n",
1350 rc);
1351 goto clean_up;
1352 }
1353
1354 if (pt_t1 != pt_t2)
1355 goto clean_up;
1356
1357 /* Toggle pt enable to restart pulse train */
1358 rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT, 0);
1359 if (rc < 0) {
1360 dev_err(chip->dev, "Couldn't disable pulse train rc=%d\n", rc);
1361 goto clean_up;
1362 }
1363 msleep(1000);
1364 rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT,
1365 QNOVO_PTRAIN_EN_BIT);
1366 if (rc < 0) {
1367 dev_err(chip->dev, "Couldn't enable pulse train rc=%d\n", rc);
1368 goto clean_up;
1369 }
1370
1371clean_up:
1372 vote(chip->awake_votable, PT_RESTART_VOTER, false, 0);
1373}
1374
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001375static int qnovo_notifier_call(struct notifier_block *nb,
1376 unsigned long ev, void *v)
1377{
1378 struct power_supply *psy = v;
1379 struct qnovo *chip = container_of(nb, struct qnovo, nb);
1380
1381 if (ev != PSY_EVENT_PROP_CHANGED)
1382 return NOTIFY_OK;
Harry Yang96ec88e2017-03-23 23:43:53 -07001383
Abhijeet Dharmapurikar933d4942017-07-07 13:49:29 -07001384 if (strcmp(psy->desc->name, "battery") == 0
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001385 || strcmp(psy->desc->name, "bms") == 0
1386 || strcmp(psy->desc->name, "usb") == 0
1387 || strcmp(psy->desc->name, "dc") == 0)
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001388 schedule_work(&chip->status_change_work);
1389
1390 return NOTIFY_OK;
1391}
1392
1393static irqreturn_t handle_ptrain_done(int irq, void *data)
1394{
1395 struct qnovo *chip = data;
Abhijeet Dharmapurikar933d4942017-07-07 13:49:29 -07001396 union power_supply_propval pval = {0};
1397
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -07001398 /*
1399 * hw resets pt_en bit once ptrain_done triggers.
1400 * vote on behalf of QNI to disable it such that
1401 * once QNI enables it, the votable state changes
1402 * and the callback that sets it is indeed invoked
1403 */
1404 vote(chip->pt_dis_votable, QNI_PT_VOTER, true, 0);
1405
1406 vote(chip->pt_dis_votable, ESR_VOTER, true, 0);
Abhijeet Dharmapurikar933d4942017-07-07 13:49:29 -07001407 if (is_fg_available(chip))
1408 power_supply_set_property(chip->bms_psy,
1409 POWER_SUPPLY_PROP_RESISTANCE,
1410 &pval);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001411
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -07001412 vote(chip->pt_dis_votable, ESR_VOTER, false, 0);
Harry Yang96ec88e2017-03-23 23:43:53 -07001413 qnovo_update_status(chip);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001414 kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE);
1415 return IRQ_HANDLED;
1416}
1417
1418static int qnovo_hw_init(struct qnovo *chip)
1419{
1420 int rc;
1421 u8 iadc_offset_external, iadc_offset_internal;
1422 u8 iadc_gain_external, iadc_gain_internal;
1423 u8 vadc_offset, vadc_gain;
Harry Yang8dc14eb2017-02-06 13:12:15 -08001424 u8 val;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001425
Harry Yang87111f72017-02-24 01:06:00 -08001426 vote(chip->disable_votable, USER_VOTER, true, 0);
Abhijeet Dharmapurikar933d4942017-07-07 13:49:29 -07001427 vote(chip->disable_votable, FG_AVAILABLE_VOTER, true, 0);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001428
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -07001429 vote(chip->pt_dis_votable, QNI_PT_VOTER, true, 0);
1430 vote(chip->pt_dis_votable, QNOVO_OVERALL_VOTER, true, 0);
1431 vote(chip->pt_dis_votable, ESR_VOTER, false, 0);
1432
Harry Yangd9e49562017-02-17 15:37:09 -08001433 val = 0;
1434 rc = qnovo_write(chip, QNOVO_STRM_CTRL, &val, 1);
1435 if (rc < 0) {
1436 pr_err("Couldn't write iadc bitstream control rc = %d\n", rc);
1437 return rc;
1438 }
1439
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001440 rc = qnovo_read(chip, QNOVO_IADC_OFFSET_0, &iadc_offset_external, 1);
1441 if (rc < 0) {
1442 pr_err("Couldn't read iadc exernal offset rc = %d\n", rc);
1443 return rc;
1444 }
1445
Harry Yangd9e49562017-02-17 15:37:09 -08001446 /* stored as an 8 bit 2's complement signed integer */
1447 val = -1 * iadc_offset_external;
1448 rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_0, &val, 1);
1449 if (rc < 0) {
1450 pr_err("Couldn't write iadc offset rc = %d\n", rc);
1451 return rc;
1452 }
1453
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001454 rc = qnovo_read(chip, QNOVO_IADC_OFFSET_1, &iadc_offset_internal, 1);
1455 if (rc < 0) {
1456 pr_err("Couldn't read iadc internal offset rc = %d\n", rc);
1457 return rc;
1458 }
1459
Harry Yangd9e49562017-02-17 15:37:09 -08001460 /* stored as an 8 bit 2's complement signed integer */
1461 val = -1 * iadc_offset_internal;
1462 rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_1, &val, 1);
1463 if (rc < 0) {
1464 pr_err("Couldn't write iadc offset rc = %d\n", rc);
1465 return rc;
1466 }
1467
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001468 rc = qnovo_read(chip, QNOVO_IADC_GAIN_0, &iadc_gain_external, 1);
1469 if (rc < 0) {
1470 pr_err("Couldn't read iadc external gain rc = %d\n", rc);
1471 return rc;
1472 }
1473
1474 rc = qnovo_read(chip, QNOVO_IADC_GAIN_1, &iadc_gain_internal, 1);
1475 if (rc < 0) {
1476 pr_err("Couldn't read iadc internal gain rc = %d\n", rc);
1477 return rc;
1478 }
1479
1480 rc = qnovo_read(chip, QNOVO_VADC_OFFSET, &vadc_offset, 1);
1481 if (rc < 0) {
1482 pr_err("Couldn't read vadc offset rc = %d\n", rc);
1483 return rc;
1484 }
1485
1486 rc = qnovo_read(chip, QNOVO_VADC_GAIN, &vadc_gain, 1);
1487 if (rc < 0) {
1488 pr_err("Couldn't read vadc external gain rc = %d\n", rc);
1489 return rc;
1490 }
1491
Harry Yangd9e49562017-02-17 15:37:09 -08001492 chip->external_offset_nA = (s64)(s8)iadc_offset_external * IADC_LSB_NA;
1493 chip->internal_offset_nA = (s64)(s8)iadc_offset_internal * IADC_LSB_NA;
1494 chip->offset_nV = (s64)(s8)vadc_offset * VADC_LSB_NA;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001495 chip->external_i_gain_mega
Harry Yangd9e49562017-02-17 15:37:09 -08001496 = 1000000000 + (s64)(s8)iadc_gain_external * GAIN_LSB_FACTOR;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001497 chip->external_i_gain_mega
1498 = div_s64(chip->external_i_gain_mega, 1000);
1499 chip->internal_i_gain_mega
Harry Yangd9e49562017-02-17 15:37:09 -08001500 = 1000000000 + (s64)(s8)iadc_gain_internal * GAIN_LSB_FACTOR;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001501 chip->internal_i_gain_mega
1502 = div_s64(chip->internal_i_gain_mega, 1000);
Harry Yangd9e49562017-02-17 15:37:09 -08001503 chip->v_gain_mega = 1000000000 + (s64)(s8)vadc_gain * GAIN_LSB_FACTOR;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001504 chip->v_gain_mega = div_s64(chip->v_gain_mega, 1000);
1505
Harry Yang58acd162017-04-07 17:08:06 -07001506 /* allow charger error conditions to disable qnovo, CV mode excluded */
1507 val = ERR_SWITCHER_DISABLED | ERR_JEITA_SOFT_CONDITION | ERR_BAT_OV |
1508 ERR_BATTERY_MISSING | ERR_SAFETY_TIMER_EXPIRED |
1509 ERR_CHARGING_DISABLED | ERR_JEITA_HARD_CONDITION;
1510 rc = qnovo_write(chip, QNOVO_DISABLE_CHARGING, &val, 1);
1511 if (rc < 0) {
1512 pr_err("Couldn't write QNOVO_DISABLE_CHARGING rc = %d\n", rc);
1513 return rc;
1514 }
1515
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001516 return 0;
1517}
1518
1519static int qnovo_register_notifier(struct qnovo *chip)
1520{
1521 int rc;
1522
1523 chip->nb.notifier_call = qnovo_notifier_call;
1524 rc = power_supply_reg_notifier(&chip->nb);
1525 if (rc < 0) {
1526 pr_err("Couldn't register psy notifier rc = %d\n", rc);
1527 return rc;
1528 }
1529
1530 return 0;
1531}
1532
1533static int qnovo_determine_initial_status(struct qnovo *chip)
1534{
1535 status_change_work(&chip->status_change_work);
1536 return 0;
1537}
1538
1539static int qnovo_request_interrupts(struct qnovo *chip)
1540{
1541 int rc = 0;
1542 int irq_ptrain_done = of_irq_get_byname(chip->dev->of_node,
1543 "ptrain-done");
1544
1545 rc = devm_request_threaded_irq(chip->dev, irq_ptrain_done, NULL,
1546 handle_ptrain_done,
1547 IRQF_ONESHOT, "ptrain-done", chip);
1548 if (rc < 0) {
1549 pr_err("Couldn't request irq %d rc = %d\n",
1550 irq_ptrain_done, rc);
1551 return rc;
1552 }
Harry Yang87111f72017-02-24 01:06:00 -08001553
1554 enable_irq_wake(irq_ptrain_done);
1555
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001556 return rc;
1557}
1558
1559static int qnovo_probe(struct platform_device *pdev)
1560{
1561 struct qnovo *chip;
1562 int rc = 0;
1563
1564 chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
1565 if (!chip)
1566 return -ENOMEM;
1567
1568 chip->fv_uV_request = -EINVAL;
1569 chip->fcc_uA_request = -EINVAL;
1570 chip->dev = &pdev->dev;
1571 mutex_init(&chip->write_lock);
1572
1573 chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
1574 if (!chip->regmap) {
1575 pr_err("parent regmap is missing\n");
1576 return -EINVAL;
1577 }
1578
1579 rc = qnovo_parse_dt(chip);
1580 if (rc < 0) {
1581 pr_err("Couldn't parse device tree rc=%d\n", rc);
1582 return rc;
1583 }
1584
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001585 /* set driver data before resources request it */
1586 platform_set_drvdata(pdev, chip);
1587
1588 chip->disable_votable = create_votable("QNOVO_DISABLE", VOTE_SET_ANY,
1589 qnovo_disable_cb, chip);
1590 if (IS_ERR(chip->disable_votable)) {
1591 rc = PTR_ERR(chip->disable_votable);
1592 goto cleanup;
1593 }
1594
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -07001595 chip->pt_dis_votable = create_votable("QNOVO_PT_DIS", VOTE_SET_ANY,
1596 pt_dis_votable_cb, chip);
1597 if (IS_ERR(chip->pt_dis_votable)) {
1598 rc = PTR_ERR(chip->pt_dis_votable);
1599 goto destroy_disable_votable;
1600 }
1601
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001602 chip->not_ok_to_qnovo_votable = create_votable("QNOVO_NOT_OK",
1603 VOTE_SET_ANY,
1604 not_ok_to_qnovo_cb, chip);
1605 if (IS_ERR(chip->not_ok_to_qnovo_votable)) {
1606 rc = PTR_ERR(chip->not_ok_to_qnovo_votable);
1607 goto destroy_pt_dis_votable;
1608 }
1609
1610 chip->chg_ready_votable = create_votable("QNOVO_CHG_READY",
1611 VOTE_SET_ANY,
1612 chg_ready_cb, chip);
1613 if (IS_ERR(chip->chg_ready_votable)) {
1614 rc = PTR_ERR(chip->chg_ready_votable);
1615 goto destroy_not_ok_to_qnovo_votable;
1616 }
1617
1618 chip->awake_votable = create_votable("QNOVO_AWAKE", VOTE_SET_ANY,
1619 awake_cb, chip);
1620 if (IS_ERR(chip->awake_votable)) {
1621 rc = PTR_ERR(chip->awake_votable);
1622 goto destroy_chg_ready_votable;
1623 }
1624
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001625 INIT_WORK(&chip->status_change_work, status_change_work);
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001626 INIT_DELAYED_WORK(&chip->dc_debounce_work, dc_debounce_work);
1627 INIT_DELAYED_WORK(&chip->usb_debounce_work, usb_debounce_work);
Harry Yangc11edf42017-07-17 11:44:37 -07001628 INIT_DELAYED_WORK(&chip->ptrain_restart_work, ptrain_restart_work);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001629
1630 rc = qnovo_hw_init(chip);
1631 if (rc < 0) {
1632 pr_err("Couldn't initialize hardware rc=%d\n", rc);
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001633 goto destroy_awake_votable;
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001634 }
1635
1636 rc = qnovo_register_notifier(chip);
1637 if (rc < 0) {
1638 pr_err("Couldn't register psy notifier rc = %d\n", rc);
1639 goto unreg_notifier;
1640 }
1641
1642 rc = qnovo_determine_initial_status(chip);
1643 if (rc < 0) {
1644 pr_err("Couldn't determine initial status rc=%d\n", rc);
1645 goto unreg_notifier;
1646 }
1647
1648 rc = qnovo_request_interrupts(chip);
1649 if (rc < 0) {
1650 pr_err("Couldn't request interrupts rc=%d\n", rc);
1651 goto unreg_notifier;
1652 }
1653 chip->qnovo_class.name = "qnovo",
1654 chip->qnovo_class.owner = THIS_MODULE,
1655 chip->qnovo_class.class_attrs = qnovo_attributes;
1656
1657 rc = class_register(&chip->qnovo_class);
1658 if (rc < 0) {
1659 pr_err("couldn't register qnovo sysfs class rc = %d\n", rc);
1660 goto unreg_notifier;
1661 }
1662
Harry Yang87111f72017-02-24 01:06:00 -08001663 device_init_wakeup(chip->dev, true);
1664
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001665 return rc;
1666
1667unreg_notifier:
1668 power_supply_unreg_notifier(&chip->nb);
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001669destroy_awake_votable:
1670 destroy_votable(chip->awake_votable);
1671destroy_chg_ready_votable:
1672 destroy_votable(chip->chg_ready_votable);
1673destroy_not_ok_to_qnovo_votable:
1674 destroy_votable(chip->not_ok_to_qnovo_votable);
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -07001675destroy_pt_dis_votable:
1676 destroy_votable(chip->pt_dis_votable);
1677destroy_disable_votable:
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001678 destroy_votable(chip->disable_votable);
1679cleanup:
1680 platform_set_drvdata(pdev, NULL);
1681 return rc;
1682}
1683
1684static int qnovo_remove(struct platform_device *pdev)
1685{
1686 struct qnovo *chip = platform_get_drvdata(pdev);
1687
1688 class_unregister(&chip->qnovo_class);
1689 power_supply_unreg_notifier(&chip->nb);
Abhijeet Dharmapurikarc0a18e22017-07-27 16:08:02 -07001690 destroy_votable(chip->chg_ready_votable);
1691 destroy_votable(chip->not_ok_to_qnovo_votable);
Abhijeet Dharmapurikar12cb0ec2017-07-27 13:08:31 -07001692 destroy_votable(chip->pt_dis_votable);
Abhijeet Dharmapurikar21f9af1d2016-05-10 18:48:39 -07001693 destroy_votable(chip->disable_votable);
1694 platform_set_drvdata(pdev, NULL);
1695 return 0;
1696}
1697
1698static const struct of_device_id match_table[] = {
1699 { .compatible = "qcom,qpnp-qnovo", },
1700 { },
1701};
1702
1703static struct platform_driver qnovo_driver = {
1704 .driver = {
1705 .name = "qcom,qnovo-driver",
1706 .owner = THIS_MODULE,
1707 .of_match_table = match_table,
1708 },
1709 .probe = qnovo_probe,
1710 .remove = qnovo_remove,
1711};
1712module_platform_driver(qnovo_driver);
1713
1714MODULE_DESCRIPTION("QPNP Qnovo Driver");
1715MODULE_LICENSE("GPL v2");