blob: 3dccff5376f45f7e0ab559585811f239932d97dc [file] [log] [blame]
Ram Chandrasekarc4433492017-03-12 18:28:45 -06001/*
Manaf Meethalavalappu Pallikunhi8ccbdb52018-01-04 16:48:06 +05302 * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
Ram Chandrasekarc4433492017-03-12 18:28:45 -06003 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__
15
16#include <linux/module.h>
17#include <linux/interrupt.h>
18#include <linux/workqueue.h>
19#include <linux/kernel.h>
20#include <linux/regmap.h>
21#include <linux/io.h>
22#include <linux/err.h>
23#include <linux/of.h>
24#include <linux/of_address.h>
25#include <linux/spmi.h>
26#include <linux/platform_device.h>
27#include <linux/mutex.h>
28#include <linux/power_supply.h>
29#include <linux/thermal.h>
30
31#include "../thermal_core.h"
32
33#define BCL_DRIVER_NAME "bcl_peripheral"
34#define BCL_VBAT_INT "bcl-low-vbat"
35#define BCL_VLOW_VBAT_INT "bcl-very-low-vbat"
36#define BCL_CLOW_VBAT_INT "bcl-crit-low-vbat"
37#define BCL_IBAT_INT "bcl-high-ibat"
38#define BCL_VHIGH_IBAT_INT "bcl-very-high-ibat"
39#define BCL_MONITOR_EN 0x46
40#define BCL_VBAT_MIN 0x5C
41#define BCL_IBAT_MAX 0x5D
42#define BCL_MAX_MIN_CLR 0x48
43#define BCL_IBAT_MAX_CLR 3
44#define BCL_VBAT_MIN_CLR 2
45#define BCL_VBAT_ADC_LOW 0x72
46#define BCL_VBAT_COMP_LOW 0x75
47#define BCL_VBAT_COMP_TLOW 0x76
48#define BCL_IBAT_HIGH 0x78
49#define BCL_IBAT_TOO_HIGH 0x79
50#define BCL_LMH_CFG 0xA3
51#define BCL_CFG 0x6A
52#define LMH_INT_POL_HIGH 0x12
53#define LMH_INT_EN 0x15
54#define BCL_VBAT_SCALING 39000
55#define BCL_IBAT_SCALING 80
56#define BCL_LMH_CFG_VAL 0x3
57#define BCL_CFG_VAL 0x81
58#define LMH_INT_VAL 0x7
59#define BCL_READ_RETRY_LIMIT 3
60#define VAL_CP_REG_BUF_LEN 3
61#define VAL_REG_BUF_OFFSET 0
62#define VAL_CP_REG_BUF_OFFSET 2
63#define BCL_STD_VBAT_NR 9
64#define BCL_VBAT_NO_READING 127
65
66enum bcl_dev_type {
67 BCL_HIGH_IBAT,
68 BCL_VHIGH_IBAT,
69 BCL_LOW_VBAT,
70 BCL_VLOW_VBAT,
71 BCL_CLOW_VBAT,
72 BCL_SOC_MONITOR,
73 BCL_TYPE_MAX,
74};
75
76struct bcl_peripheral_data {
77 int irq_num;
78 long int trip_temp;
79 int trip_val;
80 int last_val;
81 struct mutex state_trans_lock;
82 bool irq_enabled;
83 struct thermal_zone_of_device_ops ops;
84 struct thermal_zone_device *tz_dev;
85};
86
87struct bcl_device {
88 struct regmap *regmap;
89 uint16_t fg_bcl_addr;
90 uint16_t fg_lmh_addr;
91 struct notifier_block psy_nb;
92 struct work_struct soc_eval_work;
93 struct bcl_peripheral_data param[BCL_TYPE_MAX];
94};
95
96static struct bcl_device *bcl_perph;
97static int vbat_low[BCL_STD_VBAT_NR] = {
98 2400, 2500, 2600, 2700, 2800, 2900,
99 3000, 3100, 3200};
100
101static int bcl_read_multi_register(int16_t reg_offset, uint8_t *data, int len)
102{
103 int ret = 0;
104
105 if (!bcl_perph) {
106 pr_err("BCL device not initialized\n");
107 return -EINVAL;
108 }
109 ret = regmap_bulk_read(bcl_perph->regmap,
110 (bcl_perph->fg_bcl_addr + reg_offset),
111 data, len);
112 if (ret < 0) {
113 pr_err("Error reading register %d. err:%d", reg_offset, ret);
114 return ret;
115 }
116
117 return ret;
118}
119
120static int bcl_write_general_register(int16_t reg_offset,
121 uint16_t base, uint8_t data)
122{
123 int ret = 0;
124 uint8_t *write_buf = &data;
125
126 if (!bcl_perph) {
127 pr_err("BCL device not initialized\n");
128 return -EINVAL;
129 }
130 ret = regmap_write(bcl_perph->regmap, (base + reg_offset), *write_buf);
131 if (ret < 0) {
132 pr_err("Error reading register %d. err:%d", reg_offset, ret);
133 return ret;
134 }
135 pr_debug("wrote 0x%02x to 0x%04x\n", data, base + reg_offset);
136
137 return ret;
138}
139
140static int bcl_write_register(int16_t reg_offset, uint8_t data)
141{
142 return bcl_write_general_register(reg_offset,
143 bcl_perph->fg_bcl_addr, data);
144}
145
146static void convert_vbat_to_adc_val(int *val)
147{
148 *val = (*val * 1000) / BCL_VBAT_SCALING;
149}
150
151static void convert_adc_to_vbat_val(int *val)
152{
153 *val = *val * BCL_VBAT_SCALING / 1000;
154}
155
156static void convert_ibat_to_adc_val(int *val)
157{
158 *val = *val / BCL_IBAT_SCALING;
159}
160
161static void convert_adc_to_ibat_val(int *val)
162{
163 *val = *val * BCL_IBAT_SCALING;
164}
165
166static int bcl_set_ibat(void *data, int low, int high)
167{
168 int ret = 0, ibat_ua, thresh_value;
169 int8_t val = 0;
170 int16_t addr;
171 struct bcl_peripheral_data *bat_data =
172 (struct bcl_peripheral_data *)data;
173
174 thresh_value = high;
175 if (bat_data->trip_temp == thresh_value)
176 return 0;
177
178 mutex_lock(&bat_data->state_trans_lock);
179 if (bat_data->irq_num && bat_data->irq_enabled) {
180 disable_irq_nosync(bat_data->irq_num);
181 bat_data->irq_enabled = false;
182 }
183 if (thresh_value == INT_MAX) {
184 bat_data->trip_temp = thresh_value;
185 goto set_trip_exit;
186 }
187
188 ibat_ua = thresh_value;
189 convert_ibat_to_adc_val(&thresh_value);
190 val = (int8_t)thresh_value;
191 if (&bcl_perph->param[BCL_HIGH_IBAT] == bat_data) {
192 addr = BCL_IBAT_HIGH;
193 pr_debug("ibat high threshold:%d mA ADC:0x%02x\n",
194 ibat_ua, val);
195 } else if (&bcl_perph->param[BCL_VHIGH_IBAT] == bat_data) {
196 addr = BCL_IBAT_TOO_HIGH;
197 pr_debug("ibat too high threshold:%d mA ADC:0x%02x\n",
198 ibat_ua, val);
199 } else {
200 goto set_trip_exit;
201 }
202 ret = bcl_write_register(addr, val);
203 if (ret) {
204 pr_err("Error accessing BCL peripheral. err:%d\n", ret);
205 goto set_trip_exit;
206 }
207 bat_data->trip_temp = ibat_ua;
208
209 if (bat_data->irq_num && !bat_data->irq_enabled) {
210 enable_irq(bat_data->irq_num);
211 bat_data->irq_enabled = true;
212 }
213
214set_trip_exit:
215 mutex_unlock(&bat_data->state_trans_lock);
216
217 return ret;
218}
219
220static int bcl_set_vbat(void *data, int low, int high)
221{
222 int ret = 0, vbat_uv, vbat_idx, thresh_value;
223 int8_t val = 0;
224 struct bcl_peripheral_data *bat_data =
225 (struct bcl_peripheral_data *)data;
226 uint16_t addr;
227
228 thresh_value = low;
229 if (bat_data->trip_temp == thresh_value)
230 return 0;
231
232 mutex_lock(&bat_data->state_trans_lock);
233
234 if (bat_data->irq_num && bat_data->irq_enabled) {
235 disable_irq_nosync(bat_data->irq_num);
236 bat_data->irq_enabled = false;
237 }
238 if (thresh_value == INT_MIN) {
239 bat_data->trip_temp = thresh_value;
240 goto set_trip_exit;
241 }
242 vbat_uv = thresh_value;
243 convert_vbat_to_adc_val(&thresh_value);
244 val = (int8_t)thresh_value;
245 /*
246 * very low and critical low trip can support only standard
247 * trip thresholds
248 */
249 if (&bcl_perph->param[BCL_LOW_VBAT] == bat_data) {
250 addr = BCL_VBAT_ADC_LOW;
251 pr_debug("vbat low threshold:%d mv ADC:0x%02x\n",
252 vbat_uv, val);
253 } else if (&bcl_perph->param[BCL_VLOW_VBAT] == bat_data) {
254 /*
255 * Scan the standard voltage table, sorted in ascending order
256 * and find the closest threshold that is lower or equal to
257 * the requested value. Passive trip supports thresholds
258 * indexed from 1...BCL_STD_VBAT_NR in the voltage table.
259 */
260 for (vbat_idx = 2; vbat_idx < BCL_STD_VBAT_NR;
261 vbat_idx++) {
Ram Chandrasekara6431e8b2017-04-19 15:50:18 -0600262 if (vbat_uv >= vbat_low[vbat_idx])
Ram Chandrasekarc4433492017-03-12 18:28:45 -0600263 continue;
264 break;
265 }
266 addr = BCL_VBAT_COMP_LOW;
267 val = vbat_idx - 2;
268 vbat_uv = vbat_low[vbat_idx - 1];
269 pr_debug("vbat too low threshold:%d mv ADC:0x%02x\n",
270 vbat_uv, val);
271 } else if (&bcl_perph->param[BCL_CLOW_VBAT] == bat_data) {
272 /* Hot trip supports thresholds indexed from
273 * 0...BCL_STD_VBAT_NR-1 in the voltage table.
274 */
275 for (vbat_idx = 1; vbat_idx < (BCL_STD_VBAT_NR - 1);
276 vbat_idx++) {
Ram Chandrasekara6431e8b2017-04-19 15:50:18 -0600277 if (vbat_uv >= vbat_low[vbat_idx])
Ram Chandrasekarc4433492017-03-12 18:28:45 -0600278 continue;
279 break;
280 }
281 addr = BCL_VBAT_COMP_TLOW;
282 val = vbat_idx - 1;
283 vbat_uv = vbat_low[vbat_idx - 1];
284 pr_debug("vbat critic low threshold:%d mv ADC:0x%02x\n",
285 vbat_uv, val);
286 } else {
287 goto set_trip_exit;
288 }
289
290 ret = bcl_write_register(addr, val);
291 if (ret) {
292 pr_err("Error accessing BCL peripheral. err:%d\n", ret);
293 goto set_trip_exit;
294 }
295 bat_data->trip_temp = vbat_uv;
296 if (bat_data->irq_num && !bat_data->irq_enabled) {
297 enable_irq(bat_data->irq_num);
298 bat_data->irq_enabled = true;
299 }
300
301set_trip_exit:
302 mutex_unlock(&bat_data->state_trans_lock);
303 return ret;
304}
305
306static int bcl_clear_vbat_min(void)
307{
308 int ret = 0;
309
310 ret = bcl_write_register(BCL_MAX_MIN_CLR,
311 BIT(BCL_VBAT_MIN_CLR));
312 if (ret)
313 pr_err("Error in clearing vbat min reg. err:%d", ret);
314
315 return ret;
316}
317
318static int bcl_clear_ibat_max(void)
319{
320 int ret = 0;
321
322 ret = bcl_write_register(BCL_MAX_MIN_CLR,
323 BIT(BCL_IBAT_MAX_CLR));
324 if (ret)
325 pr_err("Error in clearing ibat max reg. err:%d", ret);
326
327 return ret;
328}
329
330static int bcl_read_ibat(void *data, int *adc_value)
331{
332 int ret = 0, timeout = 0;
333 int8_t val[VAL_CP_REG_BUF_LEN] = {0};
334 struct bcl_peripheral_data *bat_data =
335 (struct bcl_peripheral_data *)data;
336
337 *adc_value = (int)val[VAL_REG_BUF_OFFSET];
338 do {
339 ret = bcl_read_multi_register(BCL_IBAT_MAX, val,
340 VAL_CP_REG_BUF_LEN);
341 if (ret) {
342 pr_err("BCL register read error. err:%d\n", ret);
343 goto bcl_read_exit;
344 }
345 } while (val[VAL_REG_BUF_OFFSET] != val[VAL_CP_REG_BUF_OFFSET]
346 && timeout++ < BCL_READ_RETRY_LIMIT);
347 if (val[VAL_REG_BUF_OFFSET] != val[VAL_CP_REG_BUF_OFFSET]) {
348 ret = -ENODEV;
349 *adc_value = bat_data->last_val;
350 goto bcl_read_exit;
351 }
352 *adc_value = (int)val[VAL_REG_BUF_OFFSET];
353 if (*adc_value == 0) {
354 /*
355 * The sensor sometime can read a value 0 if there is
356 * consequtive reads
357 */
358 *adc_value = bat_data->last_val;
359 } else {
360 convert_adc_to_ibat_val(adc_value);
361 bat_data->last_val = *adc_value;
362 }
363 pr_debug("ibat:%d mA\n", bat_data->last_val);
364
365bcl_read_exit:
366 return ret;
367}
368
369static int bcl_read_ibat_and_clear(void *data, int *adc_value)
370{
371 int ret = 0;
372
373 ret = bcl_read_ibat(data, adc_value);
374 if (ret)
375 return ret;
376 return bcl_clear_ibat_max();
377}
378
379static int bcl_read_vbat(void *data, int *adc_value)
380{
381 int ret = 0, timeout = 0;
382 int8_t val[VAL_CP_REG_BUF_LEN] = {0};
383 struct bcl_peripheral_data *bat_data =
384 (struct bcl_peripheral_data *)data;
385
386 *adc_value = (int)val[VAL_REG_BUF_OFFSET];
387 do {
388 ret = bcl_read_multi_register(BCL_VBAT_MIN, val,
389 VAL_CP_REG_BUF_LEN);
390 if (ret) {
391 pr_err("BCL register read error. err:%d\n", ret);
392 goto bcl_read_exit;
393 }
394 } while (val[VAL_REG_BUF_OFFSET] != val[VAL_CP_REG_BUF_OFFSET]
395 && timeout++ < BCL_READ_RETRY_LIMIT);
396 if (val[VAL_REG_BUF_OFFSET] != val[VAL_CP_REG_BUF_OFFSET]) {
397 ret = -ENODEV;
398 goto bcl_read_exit;
399 }
400 *adc_value = (int)val[VAL_REG_BUF_OFFSET];
401 if (*adc_value == BCL_VBAT_NO_READING) {
402 *adc_value = bat_data->last_val;
403 } else {
404 convert_adc_to_vbat_val(adc_value);
405 bat_data->last_val = *adc_value;
406 }
407 pr_debug("vbat:%d mv\n", bat_data->last_val);
408
409bcl_read_exit:
410 return ret;
411}
412
413static int bcl_read_vbat_and_clear(void *data, int *adc_value)
414{
415 int ret;
416
417 ret = bcl_read_vbat(data, adc_value);
418 if (ret)
419 return ret;
420 return bcl_clear_vbat_min();
421}
422
423static irqreturn_t bcl_handle_ibat(int irq, void *data)
424{
425 struct bcl_peripheral_data *perph_data =
426 (struct bcl_peripheral_data *)data;
427
428 mutex_lock(&perph_data->state_trans_lock);
429 if (!perph_data->irq_enabled) {
430 WARN_ON(1);
431 disable_irq_nosync(irq);
432 perph_data->irq_enabled = false;
433 goto exit_intr;
434 }
435 mutex_unlock(&perph_data->state_trans_lock);
436 of_thermal_handle_trip(perph_data->tz_dev);
437
438 return IRQ_HANDLED;
439
440exit_intr:
441 mutex_unlock(&perph_data->state_trans_lock);
442 return IRQ_HANDLED;
443}
444
445static irqreturn_t bcl_handle_vbat(int irq, void *data)
446{
447 struct bcl_peripheral_data *perph_data =
448 (struct bcl_peripheral_data *)data;
449
450 mutex_lock(&perph_data->state_trans_lock);
451 if (!perph_data->irq_enabled) {
452 WARN_ON(1);
453 disable_irq_nosync(irq);
454 perph_data->irq_enabled = false;
455 goto exit_intr;
456 }
457 mutex_unlock(&perph_data->state_trans_lock);
458 of_thermal_handle_trip(perph_data->tz_dev);
459
460 return IRQ_HANDLED;
461
462exit_intr:
463 mutex_unlock(&perph_data->state_trans_lock);
464 return IRQ_HANDLED;
465}
466
467static int bcl_get_devicetree_data(struct platform_device *pdev)
468{
469 int ret = 0;
470 const __be32 *prop = NULL;
471 struct device_node *dev_node = pdev->dev.of_node;
472
473 prop = of_get_address(dev_node, 0, NULL, NULL);
474 if (prop) {
475 bcl_perph->fg_bcl_addr = be32_to_cpu(*prop);
476 pr_debug("fg_user_adc@%04x\n", bcl_perph->fg_bcl_addr);
477 } else {
478 dev_err(&pdev->dev, "No fg_user_adc registers found\n");
479 return -ENODEV;
480 }
481
482 prop = of_get_address(dev_node, 1, NULL, NULL);
483 if (prop) {
484 bcl_perph->fg_lmh_addr = be32_to_cpu(*prop);
485 pr_debug("fg_lmh@%04x\n", bcl_perph->fg_lmh_addr);
486 } else {
487 dev_err(&pdev->dev, "No fg_lmh registers found\n");
488 return -ENODEV;
489 }
490
491 return ret;
492}
493
494static int bcl_set_soc(void *data, int low, int high)
495{
496 struct bcl_peripheral_data *bat_data =
497 (struct bcl_peripheral_data *)data;
498
499 if (low == bat_data->trip_temp)
500 return 0;
501
502 mutex_lock(&bat_data->state_trans_lock);
503 pr_debug("low soc threshold:%d\n", low);
504 bat_data->trip_temp = low;
505 if (low == INT_MIN) {
506 bat_data->irq_enabled = false;
507 goto unlock_and_exit;
508 }
509 bat_data->irq_enabled = true;
510 schedule_work(&bcl_perph->soc_eval_work);
511
512unlock_and_exit:
513 mutex_unlock(&bat_data->state_trans_lock);
514 return 0;
515}
516
517static int bcl_read_soc(void *data, int *val)
518{
519 static struct power_supply *batt_psy;
520 union power_supply_propval ret = {0,};
521 int err = 0;
522
523 *val = 100;
524 if (!batt_psy)
525 batt_psy = power_supply_get_by_name("battery");
526 if (batt_psy) {
527 err = power_supply_get_property(batt_psy,
528 POWER_SUPPLY_PROP_CAPACITY, &ret);
529 if (err) {
530 pr_err("battery percentage read error:%d\n",
531 err);
532 return err;
533 }
534 *val = ret.intval;
535 }
536 pr_debug("soc:%d\n", *val);
537
538 return err;
539}
540
541static void bcl_evaluate_soc(struct work_struct *work)
542{
543 int battery_percentage;
544 struct bcl_peripheral_data *perph_data =
545 &bcl_perph->param[BCL_SOC_MONITOR];
546
547 if (bcl_read_soc((void *)perph_data, &battery_percentage))
548 return;
549
550 mutex_lock(&perph_data->state_trans_lock);
551 if (!perph_data->irq_enabled)
552 goto eval_exit;
553 if (battery_percentage > perph_data->trip_temp)
554 goto eval_exit;
555
556 perph_data->trip_val = battery_percentage;
557 mutex_unlock(&perph_data->state_trans_lock);
558 of_thermal_handle_trip(perph_data->tz_dev);
559
560 return;
561eval_exit:
562 mutex_unlock(&perph_data->state_trans_lock);
563}
564
565static int battery_supply_callback(struct notifier_block *nb,
566 unsigned long event, void *data)
567{
568 struct power_supply *psy = data;
569
570 if (strcmp(psy->desc->name, "battery"))
571 return NOTIFY_OK;
572 schedule_work(&bcl_perph->soc_eval_work);
573
574 return NOTIFY_OK;
575}
576
577static void bcl_fetch_trip(struct platform_device *pdev, const char *int_name,
578 struct bcl_peripheral_data *data,
579 irqreturn_t (*handle)(int, void *))
580{
581 int ret = 0, irq_num = 0;
582
583 /*
584 * Allow flexibility for the HLOS to set the trip temperature for
585 * all the thresholds but handle the interrupt for only one vbat
586 * and ibat interrupt. The LMH-DCVSh will handle and mitigate for the
587 * rest of the ibat/vbat interrupts.
588 */
589 if (!handle) {
590 mutex_lock(&data->state_trans_lock);
591 data->irq_num = 0;
592 data->irq_enabled = false;
593 mutex_unlock(&data->state_trans_lock);
594 return;
595 }
596
597 irq_num = platform_get_irq_byname(pdev, int_name);
598 if (irq_num) {
599 mutex_lock(&data->state_trans_lock);
600 ret = devm_request_threaded_irq(&pdev->dev,
601 irq_num, NULL, handle,
602 IRQF_TRIGGER_RISING | IRQF_ONESHOT,
603 int_name, data);
604 if (ret) {
605 dev_err(&pdev->dev,
606 "Error requesting trip irq. err:%d",
607 ret);
608 mutex_unlock(&data->state_trans_lock);
609 return;
610 }
611 disable_irq_nosync(irq_num);
612 data->irq_num = irq_num;
613 data->irq_enabled = false;
614 mutex_unlock(&data->state_trans_lock);
615 }
616}
617
618static void bcl_probe_soc(struct platform_device *pdev)
619{
620 int ret = 0;
621 struct bcl_peripheral_data *soc_data;
622
623 soc_data = &bcl_perph->param[BCL_SOC_MONITOR];
624 mutex_init(&soc_data->state_trans_lock);
625 soc_data->ops.get_temp = bcl_read_soc;
626 soc_data->ops.set_trips = bcl_set_soc;
627 INIT_WORK(&bcl_perph->soc_eval_work, bcl_evaluate_soc);
628 bcl_perph->psy_nb.notifier_call = battery_supply_callback;
629 ret = power_supply_reg_notifier(&bcl_perph->psy_nb);
630 if (ret < 0) {
631 pr_err("Unable to register soc notifier. err:%d\n", ret);
632 return;
633 }
634 soc_data->tz_dev = thermal_zone_of_sensor_register(&pdev->dev,
635 BCL_SOC_MONITOR, soc_data, &soc_data->ops);
636 if (IS_ERR(soc_data->tz_dev)) {
637 pr_err("vbat register failed. err:%ld\n",
638 PTR_ERR(soc_data->tz_dev));
639 return;
640 }
641 thermal_zone_device_update(soc_data->tz_dev, THERMAL_DEVICE_UP);
642 schedule_work(&bcl_perph->soc_eval_work);
643}
644
645static void bcl_vbat_init(struct platform_device *pdev,
646 struct bcl_peripheral_data *vbat, enum bcl_dev_type type)
647{
648 mutex_init(&vbat->state_trans_lock);
649 switch (type) {
650 case BCL_LOW_VBAT:
651 bcl_fetch_trip(pdev, BCL_VBAT_INT, vbat, bcl_handle_vbat);
652 break;
653 case BCL_VLOW_VBAT:
654 bcl_fetch_trip(pdev, BCL_VLOW_VBAT_INT, vbat, NULL);
655 break;
656 case BCL_CLOW_VBAT:
657 bcl_fetch_trip(pdev, BCL_CLOW_VBAT_INT, vbat, NULL);
658 break;
659 default:
660 return;
661 }
662 vbat->ops.get_temp = bcl_read_vbat_and_clear;
663 vbat->ops.set_trips = bcl_set_vbat;
664 vbat->tz_dev = thermal_zone_of_sensor_register(&pdev->dev,
665 type, vbat, &vbat->ops);
666 if (IS_ERR(vbat->tz_dev)) {
667 pr_err("vbat register failed. err:%ld\n",
668 PTR_ERR(vbat->tz_dev));
669 return;
670 }
671 thermal_zone_device_update(vbat->tz_dev, THERMAL_DEVICE_UP);
672}
673
674static void bcl_probe_vbat(struct platform_device *pdev)
675{
676 bcl_vbat_init(pdev, &bcl_perph->param[BCL_LOW_VBAT], BCL_LOW_VBAT);
677 bcl_vbat_init(pdev, &bcl_perph->param[BCL_VLOW_VBAT], BCL_VLOW_VBAT);
678 bcl_vbat_init(pdev, &bcl_perph->param[BCL_CLOW_VBAT], BCL_CLOW_VBAT);
679}
680
681static void bcl_ibat_init(struct platform_device *pdev,
682 struct bcl_peripheral_data *ibat, enum bcl_dev_type type)
683{
684 mutex_init(&ibat->state_trans_lock);
685 if (type == BCL_HIGH_IBAT)
686 bcl_fetch_trip(pdev, BCL_IBAT_INT, ibat, bcl_handle_ibat);
687 else
688 bcl_fetch_trip(pdev, BCL_VHIGH_IBAT_INT, ibat, NULL);
689 ibat->ops.get_temp = bcl_read_ibat_and_clear;
690 ibat->ops.set_trips = bcl_set_ibat;
691 ibat->tz_dev = thermal_zone_of_sensor_register(&pdev->dev,
692 type, ibat, &ibat->ops);
693 if (IS_ERR(ibat->tz_dev)) {
694 pr_err("ibat register failed. err:%ld\n",
695 PTR_ERR(ibat->tz_dev));
696 return;
697 }
698 thermal_zone_device_update(ibat->tz_dev, THERMAL_DEVICE_UP);
699}
700
701static void bcl_probe_ibat(struct platform_device *pdev)
702{
703 bcl_ibat_init(pdev, &bcl_perph->param[BCL_HIGH_IBAT], BCL_HIGH_IBAT);
704 bcl_ibat_init(pdev, &bcl_perph->param[BCL_VHIGH_IBAT], BCL_VHIGH_IBAT);
705}
706
707static void bcl_configure_lmh_peripheral(void)
708{
709 bcl_write_register(BCL_LMH_CFG, BCL_LMH_CFG_VAL);
710 bcl_write_register(BCL_CFG, BCL_CFG_VAL);
711 bcl_write_general_register(LMH_INT_POL_HIGH,
712 bcl_perph->fg_lmh_addr, LMH_INT_VAL);
713 bcl_write_general_register(LMH_INT_EN,
714 bcl_perph->fg_lmh_addr, LMH_INT_VAL);
715}
716
717static int bcl_remove(struct platform_device *pdev)
718{
719 int i = 0;
720
721 for (; i < BCL_TYPE_MAX; i++) {
722 if (!bcl_perph->param[i].tz_dev)
723 continue;
724 if (i == BCL_SOC_MONITOR) {
725 power_supply_unreg_notifier(&bcl_perph->psy_nb);
726 flush_work(&bcl_perph->soc_eval_work);
727 }
728 thermal_zone_of_sensor_unregister(&pdev->dev,
729 bcl_perph->param[i].tz_dev);
730 }
731 bcl_perph = NULL;
732
733 return 0;
734}
735
736static int bcl_probe(struct platform_device *pdev)
737{
738 int ret = 0;
739
740 bcl_perph = devm_kzalloc(&pdev->dev, sizeof(*bcl_perph), GFP_KERNEL);
741 if (!bcl_perph)
742 return -ENOMEM;
743
744 bcl_perph->regmap = dev_get_regmap(pdev->dev.parent, NULL);
745 if (!bcl_perph->regmap) {
746 dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
747 return -EINVAL;
748 }
749
750 bcl_get_devicetree_data(pdev);
Manaf Meethalavalappu Pallikunhi8ccbdb52018-01-04 16:48:06 +0530751 bcl_configure_lmh_peripheral();
Ram Chandrasekarc4433492017-03-12 18:28:45 -0600752 bcl_probe_ibat(pdev);
753 bcl_probe_vbat(pdev);
754 bcl_probe_soc(pdev);
Ram Chandrasekarc4433492017-03-12 18:28:45 -0600755
756 dev_set_drvdata(&pdev->dev, bcl_perph);
757 ret = bcl_write_register(BCL_MONITOR_EN, BIT(7));
758 if (ret) {
759 pr_err("Error accessing BCL peripheral. err:%d\n", ret);
760 goto bcl_probe_exit;
761 }
762
763 return 0;
764
765bcl_probe_exit:
766 bcl_remove(pdev);
767 return ret;
768}
769
770static const struct of_device_id bcl_match[] = {
771 {
772 .compatible = "qcom,msm-bcl-lmh",
773 },
774 {},
775};
776
777static struct platform_driver bcl_driver = {
778 .probe = bcl_probe,
779 .remove = bcl_remove,
780 .driver = {
781 .name = BCL_DRIVER_NAME,
782 .owner = THIS_MODULE,
783 .of_match_table = bcl_match,
784 },
785};
786
787builtin_platform_driver(bcl_driver);