| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) ST-Ericsson AB 2012 |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 8 | 9 | * battery management is not used and the supported code is available in this |
|---|
| 9 | 10 | * driver. |
|---|
| 10 | 11 | * |
|---|
| 11 | | - * License Terms: GNU General Public License v2 |
|---|
| 12 | 12 | * Author: |
|---|
| 13 | 13 | * Johan Palsson <johan.palsson@stericsson.com> |
|---|
| 14 | 14 | * Karl Komierowski <karl.komierowski@stericsson.com> |
|---|
| .. | .. |
|---|
| 32 | 32 | #include <linux/mfd/abx500.h> |
|---|
| 33 | 33 | #include <linux/mfd/abx500/ab8500.h> |
|---|
| 34 | 34 | #include <linux/mfd/abx500/ab8500-bm.h> |
|---|
| 35 | | -#include <linux/mfd/abx500/ab8500-gpadc.h> |
|---|
| 35 | +#include <linux/iio/consumer.h> |
|---|
| 36 | 36 | #include <linux/kernel.h> |
|---|
| 37 | 37 | |
|---|
| 38 | 38 | #define MILLI_TO_MICRO 1000 |
|---|
| .. | .. |
|---|
| 182 | 182 | * @bat_cap: Structure for battery capacity specific parameters |
|---|
| 183 | 183 | * @avg_cap: Average capacity filter |
|---|
| 184 | 184 | * @parent: Pointer to the struct ab8500 |
|---|
| 185 | | - * @gpadc: Pointer to the struct gpadc |
|---|
| 185 | + * @main_bat_v: ADC channel for the main battery voltage |
|---|
| 186 | 186 | * @bm: Platform specific battery management information |
|---|
| 187 | 187 | * @fg_psy: Structure that holds the FG specific battery properties |
|---|
| 188 | 188 | * @fg_wq: Work queue for running the FG algorithm |
|---|
| .. | .. |
|---|
| 224 | 224 | struct ab8500_fg_battery_capacity bat_cap; |
|---|
| 225 | 225 | struct ab8500_fg_avg_cap avg_cap; |
|---|
| 226 | 226 | struct ab8500 *parent; |
|---|
| 227 | | - struct ab8500_gpadc *gpadc; |
|---|
| 227 | + struct iio_channel *main_bat_v; |
|---|
| 228 | 228 | struct abx500_bm_data *bm; |
|---|
| 229 | 229 | struct power_supply *fg_psy; |
|---|
| 230 | 230 | struct workqueue_struct *fg_wq; |
|---|
| .. | .. |
|---|
| 653 | 653 | |
|---|
| 654 | 654 | /* |
|---|
| 655 | 655 | * negative value for Discharging |
|---|
| 656 | | - * convert 2's compliment into decimal |
|---|
| 656 | + * convert 2's complement into decimal |
|---|
| 657 | 657 | */ |
|---|
| 658 | 658 | if (high & 0x10) |
|---|
| 659 | 659 | val = (low | (high << 8) | 0xFFFFE000); |
|---|
| .. | .. |
|---|
| 781 | 781 | if (ret < 0) |
|---|
| 782 | 782 | goto exit; |
|---|
| 783 | 783 | |
|---|
| 784 | | - /* Check for sign bit in case of negative value, 2's compliment */ |
|---|
| 784 | + /* Check for sign bit in case of negative value, 2's complement */ |
|---|
| 785 | 785 | if (high & 0x10) |
|---|
| 786 | 786 | val = (low | (med << 8) | (high << 16) | 0xFFE00000); |
|---|
| 787 | 787 | else |
|---|
| .. | .. |
|---|
| 829 | 829 | */ |
|---|
| 830 | 830 | static int ab8500_fg_bat_voltage(struct ab8500_fg *di) |
|---|
| 831 | 831 | { |
|---|
| 832 | | - int vbat; |
|---|
| 832 | + int vbat, ret; |
|---|
| 833 | 833 | static int prev; |
|---|
| 834 | 834 | |
|---|
| 835 | | - vbat = ab8500_gpadc_convert(di->gpadc, MAIN_BAT_V); |
|---|
| 836 | | - if (vbat < 0) { |
|---|
| 835 | + ret = iio_read_channel_processed(di->main_bat_v, &vbat); |
|---|
| 836 | + if (ret < 0) { |
|---|
| 837 | 837 | dev_err(di->dev, |
|---|
| 838 | | - "%s gpadc conversion failed, using previous value\n", |
|---|
| 838 | + "%s ADC conversion failed, using previous value\n", |
|---|
| 839 | 839 | __func__); |
|---|
| 840 | 840 | return prev; |
|---|
| 841 | 841 | } |
|---|
| .. | .. |
|---|
| 1542 | 1542 | ab8500_fg_discharge_state_to(di, |
|---|
| 1543 | 1543 | AB8500_FG_DISCHARGE_INITMEASURING); |
|---|
| 1544 | 1544 | |
|---|
| 1545 | | - /* Intentional fallthrough */ |
|---|
| 1545 | + fallthrough; |
|---|
| 1546 | 1546 | case AB8500_FG_DISCHARGE_INITMEASURING: |
|---|
| 1547 | 1547 | /* |
|---|
| 1548 | 1548 | * Discard a number of samples during startup. |
|---|
| .. | .. |
|---|
| 1572 | 1572 | ab8500_fg_discharge_state_to(di, |
|---|
| 1573 | 1573 | AB8500_FG_DISCHARGE_RECOVERY); |
|---|
| 1574 | 1574 | |
|---|
| 1575 | | - /* Intentional fallthrough */ |
|---|
| 1575 | + fallthrough; |
|---|
| 1576 | 1576 | |
|---|
| 1577 | 1577 | case AB8500_FG_DISCHARGE_RECOVERY: |
|---|
| 1578 | 1578 | sleep_time = di->bm->fg_params->recovery_sleep_timer; |
|---|
| .. | .. |
|---|
| 2221 | 2221 | ab8500_fg_update_cap_scalers(di); |
|---|
| 2222 | 2222 | queue_work(di->fg_wq, &di->fg_work); |
|---|
| 2223 | 2223 | break; |
|---|
| 2224 | | - }; |
|---|
| 2224 | + } |
|---|
| 2225 | 2225 | default: |
|---|
| 2226 | 2226 | break; |
|---|
| 2227 | | - }; |
|---|
| 2227 | + } |
|---|
| 2228 | 2228 | break; |
|---|
| 2229 | 2229 | case POWER_SUPPLY_PROP_TECHNOLOGY: |
|---|
| 2230 | 2230 | switch (ext->desc->type) { |
|---|
| .. | .. |
|---|
| 2331 | 2331 | if (ret) { |
|---|
| 2332 | 2332 | dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_MAX_TIME_REG\n", __func__); |
|---|
| 2333 | 2333 | goto out; |
|---|
| 2334 | | - }; |
|---|
| 2334 | + } |
|---|
| 2335 | 2335 | |
|---|
| 2336 | 2336 | ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, |
|---|
| 2337 | 2337 | AB8505_RTC_PCUT_FLAG_TIME_REG, di->bm->fg_params->pcut_flag_time); |
|---|
| .. | .. |
|---|
| 2339 | 2339 | if (ret) { |
|---|
| 2340 | 2340 | dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_FLAG_TIME_REG\n", __func__); |
|---|
| 2341 | 2341 | goto out; |
|---|
| 2342 | | - }; |
|---|
| 2342 | + } |
|---|
| 2343 | 2343 | |
|---|
| 2344 | 2344 | ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, |
|---|
| 2345 | 2345 | AB8505_RTC_PCUT_RESTART_REG, di->bm->fg_params->pcut_max_restart); |
|---|
| .. | .. |
|---|
| 2347 | 2347 | if (ret) { |
|---|
| 2348 | 2348 | dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_RESTART_REG\n", __func__); |
|---|
| 2349 | 2349 | goto out; |
|---|
| 2350 | | - }; |
|---|
| 2350 | + } |
|---|
| 2351 | 2351 | |
|---|
| 2352 | 2352 | ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, |
|---|
| 2353 | 2353 | AB8505_RTC_PCUT_DEBOUNCE_REG, di->bm->fg_params->pcut_debounce_time); |
|---|
| .. | .. |
|---|
| 2355 | 2355 | if (ret) { |
|---|
| 2356 | 2356 | dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_DEBOUNCE_REG\n", __func__); |
|---|
| 2357 | 2357 | goto out; |
|---|
| 2358 | | - }; |
|---|
| 2358 | + } |
|---|
| 2359 | 2359 | |
|---|
| 2360 | 2360 | ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, |
|---|
| 2361 | 2361 | AB8505_RTC_PCUT_CTL_STATUS_REG, di->bm->fg_params->pcut_enable); |
|---|
| .. | .. |
|---|
| 2363 | 2363 | if (ret) { |
|---|
| 2364 | 2364 | dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_CTL_STATUS_REG\n", __func__); |
|---|
| 2365 | 2365 | goto out; |
|---|
| 2366 | | - }; |
|---|
| 2366 | + } |
|---|
| 2367 | 2367 | } |
|---|
| 2368 | 2368 | out: |
|---|
| 2369 | 2369 | return ret; |
|---|
| .. | .. |
|---|
| 2380 | 2380 | */ |
|---|
| 2381 | 2381 | static void ab8500_fg_external_power_changed(struct power_supply *psy) |
|---|
| 2382 | 2382 | { |
|---|
| 2383 | | - struct ab8500_fg *di = power_supply_get_drvdata(psy); |
|---|
| 2384 | | - |
|---|
| 2385 | | - class_for_each_device(power_supply_class, NULL, |
|---|
| 2386 | | - di->fg_psy, ab8500_fg_get_ext_psy_data); |
|---|
| 2383 | + class_for_each_device(power_supply_class, NULL, psy, |
|---|
| 2384 | + ab8500_fg_get_ext_psy_data); |
|---|
| 2387 | 2385 | } |
|---|
| 2388 | 2386 | |
|---|
| 2389 | 2387 | /** |
|---|
| .. | .. |
|---|
| 2399 | 2397 | struct ab8500_fg *di = container_of(work, struct ab8500_fg, |
|---|
| 2400 | 2398 | fg_reinit_work.work); |
|---|
| 2401 | 2399 | |
|---|
| 2402 | | - if (di->flags.calibrate == false) { |
|---|
| 2400 | + if (!di->flags.calibrate) { |
|---|
| 2403 | 2401 | dev_dbg(di->dev, "Resetting FG state machine to init.\n"); |
|---|
| 2404 | 2402 | ab8500_fg_clear_cap_samples(di); |
|---|
| 2405 | 2403 | ab8500_fg_calc_cap_discharge_voltage(di, true); |
|---|
| .. | .. |
|---|
| 2541 | 2539 | ret = kobject_init_and_add(&di->fg_kobject, |
|---|
| 2542 | 2540 | &ab8500_fg_ktype, |
|---|
| 2543 | 2541 | NULL, "battery"); |
|---|
| 2544 | | - if (ret < 0) |
|---|
| 2542 | + if (ret < 0) { |
|---|
| 2543 | + kobject_put(&di->fg_kobject); |
|---|
| 2545 | 2544 | dev_err(di->dev, "failed to create sysfs entry\n"); |
|---|
| 2545 | + } |
|---|
| 2546 | 2546 | |
|---|
| 2547 | 2547 | return ret; |
|---|
| 2548 | 2548 | } |
|---|
| .. | .. |
|---|
| 2575 | 2575 | const char *buf, size_t count) |
|---|
| 2576 | 2576 | { |
|---|
| 2577 | 2577 | int ret; |
|---|
| 2578 | | - long unsigned reg_value; |
|---|
| 2578 | + int reg_value; |
|---|
| 2579 | 2579 | struct power_supply *psy = dev_get_drvdata(dev); |
|---|
| 2580 | 2580 | struct ab8500_fg *di = power_supply_get_drvdata(psy); |
|---|
| 2581 | 2581 | |
|---|
| 2582 | | - reg_value = simple_strtoul(buf, NULL, 10); |
|---|
| 2582 | + if (kstrtoint(buf, 10, ®_value)) |
|---|
| 2583 | + goto fail; |
|---|
| 2583 | 2584 | |
|---|
| 2584 | 2585 | if (reg_value > 0x7F) { |
|---|
| 2585 | 2586 | dev_err(dev, "Incorrect parameter, echo 0 (1.98s) - 127 (15.625ms) for flagtime\n"); |
|---|
| .. | .. |
|---|
| 2629 | 2630 | struct power_supply *psy = dev_get_drvdata(dev); |
|---|
| 2630 | 2631 | struct ab8500_fg *di = power_supply_get_drvdata(psy); |
|---|
| 2631 | 2632 | |
|---|
| 2632 | | - reg_value = simple_strtoul(buf, NULL, 10); |
|---|
| 2633 | + if (kstrtoint(buf, 10, ®_value)) |
|---|
| 2634 | + goto fail; |
|---|
| 2635 | + |
|---|
| 2633 | 2636 | if (reg_value > 0x7F) { |
|---|
| 2634 | 2637 | dev_err(dev, "Incorrect parameter, echo 0 (0.0s) - 127 (1.98s) for maxtime\n"); |
|---|
| 2635 | 2638 | goto fail; |
|---|
| .. | .. |
|---|
| 2677 | 2680 | struct power_supply *psy = dev_get_drvdata(dev); |
|---|
| 2678 | 2681 | struct ab8500_fg *di = power_supply_get_drvdata(psy); |
|---|
| 2679 | 2682 | |
|---|
| 2680 | | - reg_value = simple_strtoul(buf, NULL, 10); |
|---|
| 2683 | + if (kstrtoint(buf, 10, ®_value)) |
|---|
| 2684 | + goto fail; |
|---|
| 2685 | + |
|---|
| 2681 | 2686 | if (reg_value > 0xF) { |
|---|
| 2682 | 2687 | dev_err(dev, "Incorrect parameter, echo 0 - 15 for number of restart\n"); |
|---|
| 2683 | 2688 | goto fail; |
|---|
| .. | .. |
|---|
| 2770 | 2775 | struct power_supply *psy = dev_get_drvdata(dev); |
|---|
| 2771 | 2776 | struct ab8500_fg *di = power_supply_get_drvdata(psy); |
|---|
| 2772 | 2777 | |
|---|
| 2773 | | - reg_value = simple_strtoul(buf, NULL, 10); |
|---|
| 2778 | + if (kstrtoint(buf, 10, ®_value)) |
|---|
| 2779 | + goto fail; |
|---|
| 2780 | + |
|---|
| 2774 | 2781 | if (reg_value > 0x1) { |
|---|
| 2775 | 2782 | dev_err(dev, "Incorrect parameter, echo 0/1 to disable/enable Pcut feature\n"); |
|---|
| 2776 | 2783 | goto fail; |
|---|
| .. | .. |
|---|
| 2842 | 2849 | struct power_supply *psy = dev_get_drvdata(dev); |
|---|
| 2843 | 2850 | struct ab8500_fg *di = power_supply_get_drvdata(psy); |
|---|
| 2844 | 2851 | |
|---|
| 2845 | | - reg_value = simple_strtoul(buf, NULL, 10); |
|---|
| 2852 | + if (kstrtoint(buf, 10, ®_value)) |
|---|
| 2853 | + goto fail; |
|---|
| 2854 | + |
|---|
| 2846 | 2855 | if (reg_value > 0x7) { |
|---|
| 2847 | 2856 | dev_err(dev, "Incorrect parameter, echo 0 to 7 for debounce setting\n"); |
|---|
| 2848 | 2857 | goto fail; |
|---|
| .. | .. |
|---|
| 3057 | 3066 | /* get parent data */ |
|---|
| 3058 | 3067 | di->dev = &pdev->dev; |
|---|
| 3059 | 3068 | di->parent = dev_get_drvdata(pdev->dev.parent); |
|---|
| 3060 | | - di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); |
|---|
| 3069 | + |
|---|
| 3070 | + di->main_bat_v = devm_iio_channel_get(&pdev->dev, "main_bat_v"); |
|---|
| 3071 | + if (IS_ERR(di->main_bat_v)) { |
|---|
| 3072 | + if (PTR_ERR(di->main_bat_v) == -ENODEV) |
|---|
| 3073 | + return -EPROBE_DEFER; |
|---|
| 3074 | + dev_err(&pdev->dev, "failed to get main battery ADC channel\n"); |
|---|
| 3075 | + return PTR_ERR(di->main_bat_v); |
|---|
| 3076 | + } |
|---|
| 3061 | 3077 | |
|---|
| 3062 | 3078 | psy_cfg.supplied_to = supply_interface; |
|---|
| 3063 | 3079 | psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface); |
|---|
| .. | .. |
|---|
| 3142 | 3158 | /* Register primary interrupt handlers */ |
|---|
| 3143 | 3159 | for (i = 0; i < ARRAY_SIZE(ab8500_fg_irq_th); i++) { |
|---|
| 3144 | 3160 | irq = platform_get_irq_byname(pdev, ab8500_fg_irq_th[i].name); |
|---|
| 3161 | + if (irq < 0) { |
|---|
| 3162 | + ret = irq; |
|---|
| 3163 | + goto free_irq_th; |
|---|
| 3164 | + } |
|---|
| 3165 | + |
|---|
| 3145 | 3166 | ret = request_irq(irq, ab8500_fg_irq_th[i].isr, |
|---|
| 3146 | 3167 | IRQF_SHARED | IRQF_NO_SUSPEND, |
|---|
| 3147 | 3168 | ab8500_fg_irq_th[i].name, di); |
|---|
| .. | .. |
|---|
| 3149 | 3170 | if (ret != 0) { |
|---|
| 3150 | 3171 | dev_err(di->dev, "failed to request %s IRQ %d: %d\n", |
|---|
| 3151 | 3172 | ab8500_fg_irq_th[i].name, irq, ret); |
|---|
| 3152 | | - goto free_irq; |
|---|
| 3173 | + goto free_irq_th; |
|---|
| 3153 | 3174 | } |
|---|
| 3154 | 3175 | dev_dbg(di->dev, "Requested %s IRQ %d: %d\n", |
|---|
| 3155 | 3176 | ab8500_fg_irq_th[i].name, irq, ret); |
|---|
| .. | .. |
|---|
| 3157 | 3178 | |
|---|
| 3158 | 3179 | /* Register threaded interrupt handler */ |
|---|
| 3159 | 3180 | irq = platform_get_irq_byname(pdev, ab8500_fg_irq_bh[0].name); |
|---|
| 3181 | + if (irq < 0) { |
|---|
| 3182 | + ret = irq; |
|---|
| 3183 | + goto free_irq_th; |
|---|
| 3184 | + } |
|---|
| 3185 | + |
|---|
| 3160 | 3186 | ret = request_threaded_irq(irq, NULL, ab8500_fg_irq_bh[0].isr, |
|---|
| 3161 | 3187 | IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT, |
|---|
| 3162 | 3188 | ab8500_fg_irq_bh[0].name, di); |
|---|
| .. | .. |
|---|
| 3164 | 3190 | if (ret != 0) { |
|---|
| 3165 | 3191 | dev_err(di->dev, "failed to request %s IRQ %d: %d\n", |
|---|
| 3166 | 3192 | ab8500_fg_irq_bh[0].name, irq, ret); |
|---|
| 3167 | | - goto free_irq; |
|---|
| 3193 | + goto free_irq_th; |
|---|
| 3168 | 3194 | } |
|---|
| 3169 | 3195 | dev_dbg(di->dev, "Requested %s IRQ %d: %d\n", |
|---|
| 3170 | 3196 | ab8500_fg_irq_bh[0].name, irq, ret); |
|---|
| .. | .. |
|---|
| 3203 | 3229 | return ret; |
|---|
| 3204 | 3230 | |
|---|
| 3205 | 3231 | free_irq: |
|---|
| 3206 | | - power_supply_unregister(di->fg_psy); |
|---|
| 3207 | | - |
|---|
| 3208 | 3232 | /* We also have to free all registered irqs */ |
|---|
| 3209 | | - for (i = 0; i < ARRAY_SIZE(ab8500_fg_irq_th); i++) { |
|---|
| 3233 | + irq = platform_get_irq_byname(pdev, ab8500_fg_irq_bh[0].name); |
|---|
| 3234 | + free_irq(irq, di); |
|---|
| 3235 | +free_irq_th: |
|---|
| 3236 | + while (--i >= 0) { |
|---|
| 3237 | + /* Last assignment of i from primary interrupt handlers */ |
|---|
| 3210 | 3238 | irq = platform_get_irq_byname(pdev, ab8500_fg_irq_th[i].name); |
|---|
| 3211 | 3239 | free_irq(irq, di); |
|---|
| 3212 | 3240 | } |
|---|
| 3213 | | - irq = platform_get_irq_byname(pdev, ab8500_fg_irq_bh[0].name); |
|---|
| 3214 | | - free_irq(irq, di); |
|---|
| 3241 | + |
|---|
| 3242 | + power_supply_unregister(di->fg_psy); |
|---|
| 3215 | 3243 | free_inst_curr_wq: |
|---|
| 3216 | 3244 | destroy_workqueue(di->fg_wq); |
|---|
| 3217 | 3245 | return ret; |
|---|