diff options
author | Linus Torvalds | 2019-11-27 09:55:39 -0800 |
---|---|---|
committer | Linus Torvalds | 2019-11-27 09:55:39 -0800 |
commit | 00074a7007c24f947a9f9baf66bf5db2fe651ee9 (patch) | |
tree | 828f1ae0a06b2cf839293ae0c1a49e00c1c59b1f /drivers/power | |
parent | 89d57dddd7d319ded00415790a0bb3c954b7e386 (diff) | |
parent | 9480029fe5c24d482efad38dc631bd555fc7afe2 (diff) |
Merge tag 'for-v5.5' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply
Pull power supply and reset updates from Sebastian Reichel:
- test_power: add support for current and charge_counter
- cpcap-charger: improve charge calculation and limit default charge
voltage
- ab8500: convert to IIO
- misc small fixes all over drivers
* tag 'for-v5.5' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (29 commits)
power: supply: bd70528: Add MODULE_ALIAS to allow module auto loading
power: supply: ab8500_charger: Fix inconsistent IS_ERR and PTR_ERR
power: supply: cpcap-charger: cpcap_charger_voltage_to_regval() can be static
power: supply: cpcap-battery: Add basic coulomb counter calibrate support
power: supply: cpcap-battery: Read and save integrator register CCI
power: supply: cpcap-battery: Simplify short term power average calculation
power: supply: cpcap-battery: Simplify coulomb counter calculation with div_s64
power: supply: cpcap-battery: Move coulomb counter units per lsb to ddata
power: supply: cpcap-charger: Allow changing constant charge voltage
power: supply: cpcap-battery: Fix handling of lowered charger voltage
power: supply: cpcap-charger: Improve battery detection
power: supply: cpcap-battery: Check voltage before orderly_poweroff
power: supply: cpcap-charger: Limit voltage to 4.2V for battery
power: supply: ab8500: Handle invalid IRQ from platform_get_irq_byname()
power: supply: ab8500_fg: Do not free non-requested IRQs in probe's error path
power: supply: ab8500: Cleanup probe in reverse order
power: reset: at91: fix __le32 cast in reset code
power: supply: abx500_chargalg: Fix code indentation
mfd: Switch the AB8500 GPADC to IIO
iio: adc: New driver for the AB8500 GPADC
...
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/reset/at91-reset.c | 6 | ||||
-rw-r--r-- | drivers/power/reset/at91-sama5d2_shdwc.c | 8 | ||||
-rw-r--r-- | drivers/power/supply/Kconfig | 2 | ||||
-rw-r--r-- | drivers/power/supply/ab8500_btemp.c | 50 | ||||
-rw-r--r-- | drivers/power/supply/ab8500_charger.c | 83 | ||||
-rw-r--r-- | drivers/power/supply/ab8500_fg.c | 49 | ||||
-rw-r--r-- | drivers/power/supply/abx500_chargalg.c | 8 | ||||
-rw-r--r-- | drivers/power/supply/axp20x_usb_power.c | 8 | ||||
-rw-r--r-- | drivers/power/supply/bd70528-charger.c | 1 | ||||
-rw-r--r-- | drivers/power/supply/cpcap-battery.c | 271 | ||||
-rw-r--r-- | drivers/power/supply/cpcap-charger.c | 222 | ||||
-rw-r--r-- | drivers/power/supply/test_power.c | 61 |
12 files changed, 642 insertions, 127 deletions
diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c index 44ca983a49a1..d94e3267c3b6 100644 --- a/drivers/power/reset/at91-reset.c +++ b/drivers/power/reset/at91-reset.c @@ -131,7 +131,7 @@ static int at91sam9g45_restart(struct notifier_block *this, unsigned long mode, static int sama5d3_restart(struct notifier_block *this, unsigned long mode, void *cmd) { - writel(cpu_to_le32(AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST), + writel(AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST, at91_rstc_base); return NOTIFY_DONE; @@ -140,9 +140,7 @@ static int sama5d3_restart(struct notifier_block *this, unsigned long mode, static int samx7_restart(struct notifier_block *this, unsigned long mode, void *cmd) { - writel(cpu_to_le32(AT91_RSTC_KEY | AT91_RSTC_PROCRST), - at91_rstc_base); - + writel(AT91_RSTC_KEY | AT91_RSTC_PROCRST, at91_rstc_base); return NOTIFY_DONE; } diff --git a/drivers/power/reset/at91-sama5d2_shdwc.c b/drivers/power/reset/at91-sama5d2_shdwc.c index e341cc5c0ea6..1c18f465a245 100644 --- a/drivers/power/reset/at91-sama5d2_shdwc.c +++ b/drivers/power/reset/at91-sama5d2_shdwc.c @@ -269,6 +269,12 @@ static const struct of_device_id at91_shdwc_of_match[] = { }; MODULE_DEVICE_TABLE(of, at91_shdwc_of_match); +static const struct of_device_id at91_pmc_ids[] = { + { .compatible = "atmel,sama5d2-pmc" }, + { .compatible = "microchip,sam9x60-pmc" }, + { /* Sentinel. */ } +}; + static int __init at91_shdwc_probe(struct platform_device *pdev) { struct resource *res; @@ -313,7 +319,7 @@ static int __init at91_shdwc_probe(struct platform_device *pdev) at91_shdwc_dt_configure(pdev); - np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-pmc"); + np = of_find_matching_node(NULL, at91_pmc_ids); if (!np) { ret = -ENODEV; goto clk_disable; diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index c84a7b1caeb6..27164a1d3c7c 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -629,7 +629,7 @@ config BATTERY_GAUGE_LTC2941 config AB8500_BM bool "AB8500 Battery Management Driver" - depends on AB8500_CORE && AB8500_GPADC + depends on AB8500_CORE && AB8500_GPADC && (IIO = y) help Say Y to include support for AB8500 battery management. diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c index 8fe81259bfd9..909f0242bacb 100644 --- a/drivers/power/supply/ab8500_btemp.c +++ b/drivers/power/supply/ab8500_btemp.c @@ -26,7 +26,7 @@ #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500.h> #include <linux/mfd/abx500/ab8500-bm.h> -#include <linux/mfd/abx500/ab8500-gpadc.h> +#include <linux/iio/consumer.h> #define VTVOUT_V 1800 @@ -79,7 +79,8 @@ struct ab8500_btemp_ranges { * @bat_temp: Dispatched battery temperature in degree Celsius * @prev_bat_temp Last measured battery temperature in degree Celsius * @parent: Pointer to the struct ab8500 - * @gpadc: Pointer to the struct gpadc + * @adc_btemp_ball: ADC channel for the battery ball temperature + * @adc_bat_ctrl: ADC channel for the battery control * @fg: Pointer to the struct fg * @bm: Platform specific battery management information * @btemp_psy: Structure for BTEMP specific battery properties @@ -96,7 +97,8 @@ struct ab8500_btemp { int bat_temp; int prev_bat_temp; struct ab8500 *parent; - struct ab8500_gpadc *gpadc; + struct iio_channel *btemp_ball; + struct iio_channel *bat_ctrl; struct ab8500_fg *fg; struct abx500_bm_data *bm; struct power_supply *btemp_psy; @@ -177,13 +179,13 @@ static int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di, */ static int ab8500_btemp_read_batctrl_voltage(struct ab8500_btemp *di) { - int vbtemp; + int vbtemp, ret; static int prev; - vbtemp = ab8500_gpadc_convert(di->gpadc, BAT_CTRL); - if (vbtemp < 0) { + ret = iio_read_channel_processed(di->bat_ctrl, &vbtemp); + if (ret < 0) { dev_err(di->dev, - "%s gpadc conversion failed, using previous value", + "%s ADC conversion failed, using previous value", __func__); return prev; } @@ -455,7 +457,7 @@ static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di, */ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di) { - int temp; + int temp, ret; static int prev; int rbat, rntc, vntc; u8 id; @@ -480,10 +482,10 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di) di->bm->bat_type[id].r_to_t_tbl, di->bm->bat_type[id].n_temp_tbl_elements, rbat); } else { - vntc = ab8500_gpadc_convert(di->gpadc, BTEMP_BALL); - if (vntc < 0) { + ret = iio_read_channel_processed(di->btemp_ball, &vntc); + if (ret < 0) { dev_err(di->dev, - "%s gpadc conversion failed," + "%s ADC conversion failed," " using previous value\n", __func__); return prev; } @@ -1024,7 +1026,22 @@ static int ab8500_btemp_probe(struct platform_device *pdev) /* get parent data */ di->dev = &pdev->dev; di->parent = dev_get_drvdata(pdev->dev.parent); - di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); + + /* Get ADC channels */ + di->btemp_ball = devm_iio_channel_get(&pdev->dev, "btemp_ball"); + if (IS_ERR(di->btemp_ball)) { + if (PTR_ERR(di->btemp_ball) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "failed to get BTEMP BALL ADC channel\n"); + return PTR_ERR(di->btemp_ball); + } + di->bat_ctrl = devm_iio_channel_get(&pdev->dev, "bat_ctrl"); + if (IS_ERR(di->bat_ctrl)) { + if (PTR_ERR(di->bat_ctrl) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "failed to get BAT CTRL ADC channel\n"); + return PTR_ERR(di->bat_ctrl); + } di->initialized = false; @@ -1082,6 +1099,11 @@ static int ab8500_btemp_probe(struct platform_device *pdev) /* Register interrupts */ for (i = 0; i < ARRAY_SIZE(ab8500_btemp_irq); i++) { irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name); + if (irq < 0) { + ret = irq; + goto free_irq; + } + ret = request_threaded_irq(irq, NULL, ab8500_btemp_irq[i].isr, IRQF_SHARED | IRQF_NO_SUSPEND, ab8500_btemp_irq[i].name, di); @@ -1104,13 +1126,13 @@ static int ab8500_btemp_probe(struct platform_device *pdev) return ret; free_irq: - power_supply_unregister(di->btemp_psy); - /* We also have to free all successfully registered irqs */ for (i = i - 1; i >= 0; i--) { irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name); free_irq(irq, di); } + + power_supply_unregister(di->btemp_psy); free_btemp_wq: destroy_workqueue(di->btemp_wq); return ret; diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c index e51d0e72beea..8a0f9d769690 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -29,10 +29,10 @@ #include <linux/mfd/abx500/ab8500.h> #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500-bm.h> -#include <linux/mfd/abx500/ab8500-gpadc.h> #include <linux/mfd/abx500/ux500_chargalg.h> #include <linux/usb/otg.h> #include <linux/mutex.h> +#include <linux/iio/consumer.h> /* Charger constants */ #define NO_PW_CONN 0 @@ -233,7 +233,10 @@ struct ab8500_charger_max_usb_in_curr { * @current_stepping_sessions: * Counter for current stepping sessions * @parent: Pointer to the struct ab8500 - * @gpadc: Pointer to the struct gpadc + * @adc_main_charger_v ADC channel for main charger voltage + * @adc_main_charger_c ADC channel for main charger current + * @adc_vbus_v ADC channel for USB charger voltage + * @adc_usb_charger_c ADC channel for USB charger current * @bm: Platform specific battery management information * @flags: Structure for information about events triggered * @usb_state: Structure for usb stack information @@ -283,7 +286,10 @@ struct ab8500_charger { int is_aca_rid; atomic_t current_stepping_sessions; struct ab8500 *parent; - struct ab8500_gpadc *gpadc; + struct iio_channel *adc_main_charger_v; + struct iio_channel *adc_main_charger_c; + struct iio_channel *adc_vbus_v; + struct iio_channel *adc_usb_charger_c; struct abx500_bm_data *bm; struct ab8500_charger_event_flags flags; struct ab8500_charger_usb_state usb_state; @@ -459,13 +465,13 @@ static void ab8500_charger_set_usb_connected(struct ab8500_charger *di, */ static int ab8500_charger_get_ac_voltage(struct ab8500_charger *di) { - int vch; + int vch, ret; /* Only measure voltage if the charger is connected */ if (di->ac.charger_connected) { - vch = ab8500_gpadc_convert(di->gpadc, MAIN_CHARGER_V); - if (vch < 0) - dev_err(di->dev, "%s gpadc conv failed,\n", __func__); + ret = iio_read_channel_processed(di->adc_main_charger_v, &vch); + if (ret < 0) + dev_err(di->dev, "%s ADC conv failed,\n", __func__); } else { vch = 0; } @@ -510,13 +516,13 @@ static int ab8500_charger_ac_cv(struct ab8500_charger *di) */ static int ab8500_charger_get_vbus_voltage(struct ab8500_charger *di) { - int vch; + int vch, ret; /* Only measure voltage if the charger is connected */ if (di->usb.charger_connected) { - vch = ab8500_gpadc_convert(di->gpadc, VBUS_V); - if (vch < 0) - dev_err(di->dev, "%s gpadc conv failed\n", __func__); + ret = iio_read_channel_processed(di->adc_vbus_v, &vch); + if (ret < 0) + dev_err(di->dev, "%s ADC conv failed,\n", __func__); } else { vch = 0; } @@ -532,13 +538,13 @@ static int ab8500_charger_get_vbus_voltage(struct ab8500_charger *di) */ static int ab8500_charger_get_usb_current(struct ab8500_charger *di) { - int ich; + int ich, ret; /* Only measure current if the charger is online */ if (di->usb.charger_online) { - ich = ab8500_gpadc_convert(di->gpadc, USB_CHARGER_C); - if (ich < 0) - dev_err(di->dev, "%s gpadc conv failed\n", __func__); + ret = iio_read_channel_processed(di->adc_usb_charger_c, &ich); + if (ret < 0) + dev_err(di->dev, "%s ADC conv failed,\n", __func__); } else { ich = 0; } @@ -554,13 +560,13 @@ static int ab8500_charger_get_usb_current(struct ab8500_charger *di) */ static int ab8500_charger_get_ac_current(struct ab8500_charger *di) { - int ich; + int ich, ret; /* Only measure current if the charger is online */ if (di->ac.charger_online) { - ich = ab8500_gpadc_convert(di->gpadc, MAIN_CHARGER_C); - if (ich < 0) - dev_err(di->dev, "%s gpadc conv failed\n", __func__); + ret = iio_read_channel_processed(di->adc_main_charger_c, &ich); + if (ret < 0) + dev_err(di->dev, "%s ADC conv failed,\n", __func__); } else { ich = 0; } @@ -3371,7 +3377,39 @@ static int ab8500_charger_probe(struct platform_device *pdev) /* get parent data */ di->dev = &pdev->dev; di->parent = dev_get_drvdata(pdev->dev.parent); - di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); + + /* Get ADC channels */ + di->adc_main_charger_v = devm_iio_channel_get(&pdev->dev, + "main_charger_v"); + if (IS_ERR(di->adc_main_charger_v)) { + if (PTR_ERR(di->adc_main_charger_v) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "failed to get ADC main charger voltage\n"); + return PTR_ERR(di->adc_main_charger_v); + } + di->adc_main_charger_c = devm_iio_channel_get(&pdev->dev, + "main_charger_c"); + if (IS_ERR(di->adc_main_charger_c)) { + if (PTR_ERR(di->adc_main_charger_c) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "failed to get ADC main charger current\n"); + return PTR_ERR(di->adc_main_charger_c); + } + di->adc_vbus_v = devm_iio_channel_get(&pdev->dev, "vbus_v"); + if (IS_ERR(di->adc_vbus_v)) { + if (PTR_ERR(di->adc_vbus_v) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "failed to get ADC USB charger voltage\n"); + return PTR_ERR(di->adc_vbus_v); + } + di->adc_usb_charger_c = devm_iio_channel_get(&pdev->dev, + "usb_charger_c"); + if (IS_ERR(di->adc_usb_charger_c)) { + if (PTR_ERR(di->adc_usb_charger_c) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "failed to get ADC USB charger current\n"); + return PTR_ERR(di->adc_usb_charger_c); + } /* initialize lock */ spin_lock_init(&di->usb_state.usb_lock); @@ -3556,6 +3594,11 @@ static int ab8500_charger_probe(struct platform_device *pdev) /* Register interrupts */ for (i = 0; i < ARRAY_SIZE(ab8500_charger_irq); i++) { irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name); + if (irq < 0) { + ret = irq; + goto free_irq; + } + ret = request_threaded_irq(irq, NULL, ab8500_charger_irq[i].isr, IRQF_SHARED | IRQF_NO_SUSPEND, ab8500_charger_irq[i].name, di); diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index 6fc4bc30644c..c3912ee9eb99 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -32,7 +32,7 @@ #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500.h> #include <linux/mfd/abx500/ab8500-bm.h> -#include <linux/mfd/abx500/ab8500-gpadc.h> +#include <linux/iio/consumer.h> #include <linux/kernel.h> #define MILLI_TO_MICRO 1000 @@ -182,7 +182,7 @@ struct inst_curr_result_list { * @bat_cap: Structure for battery capacity specific parameters * @avg_cap: Average capacity filter * @parent: Pointer to the struct ab8500 - * @gpadc: Pointer to the struct gpadc + * @main_bat_v: ADC channel for the main battery voltage * @bm: Platform specific battery management information * @fg_psy: Structure that holds the FG specific battery properties * @fg_wq: Work queue for running the FG algorithm @@ -224,7 +224,7 @@ struct ab8500_fg { struct ab8500_fg_battery_capacity bat_cap; struct ab8500_fg_avg_cap avg_cap; struct ab8500 *parent; - struct ab8500_gpadc *gpadc; + struct iio_channel *main_bat_v; struct abx500_bm_data *bm; struct power_supply *fg_psy; struct workqueue_struct *fg_wq; @@ -829,13 +829,13 @@ exit: */ static int ab8500_fg_bat_voltage(struct ab8500_fg *di) { - int vbat; + int vbat, ret; static int prev; - vbat = ab8500_gpadc_convert(di->gpadc, MAIN_BAT_V); - if (vbat < 0) { + ret = iio_read_channel_processed(di->main_bat_v, &vbat); + if (ret < 0) { dev_err(di->dev, - "%s gpadc conversion failed, using previous value\n", + "%s ADC conversion failed, using previous value\n", __func__); return prev; } @@ -3066,7 +3066,14 @@ static int ab8500_fg_probe(struct platform_device *pdev) /* get parent data */ di->dev = &pdev->dev; di->parent = dev_get_drvdata(pdev->dev.parent); - di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); + + di->main_bat_v = devm_iio_channel_get(&pdev->dev, "main_bat_v"); + if (IS_ERR(di->main_bat_v)) { + if (PTR_ERR(di->main_bat_v) == -ENODEV) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "failed to get main battery ADC channel\n"); + return PTR_ERR(di->main_bat_v); + } psy_cfg.supplied_to = supply_interface; psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface); @@ -3151,6 +3158,11 @@ static int ab8500_fg_probe(struct platform_device *pdev) /* Register primary interrupt handlers */ for (i = 0; i < ARRAY_SIZE(ab8500_fg_irq_th); i++) { irq = platform_get_irq_byname(pdev, ab8500_fg_irq_th[i].name); + if (irq < 0) { + ret = irq; + goto free_irq_th; + } + ret = request_irq(irq, ab8500_fg_irq_th[i].isr, IRQF_SHARED | IRQF_NO_SUSPEND, ab8500_fg_irq_th[i].name, di); @@ -3158,7 +3170,7 @@ static int ab8500_fg_probe(struct platform_device *pdev) if (ret != 0) { dev_err(di->dev, "failed to request %s IRQ %d: %d\n", ab8500_fg_irq_th[i].name, irq, ret); - goto free_irq; + goto free_irq_th; } dev_dbg(di->dev, "Requested %s IRQ %d: %d\n", ab8500_fg_irq_th[i].name, irq, ret); @@ -3166,6 +3178,11 @@ static int ab8500_fg_probe(struct platform_device *pdev) /* Register threaded interrupt handler */ irq = platform_get_irq_byname(pdev, ab8500_fg_irq_bh[0].name); + if (irq < 0) { + ret = irq; + goto free_irq_th; + } + ret = request_threaded_irq(irq, NULL, ab8500_fg_irq_bh[0].isr, IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT, ab8500_fg_irq_bh[0].name, di); @@ -3173,7 +3190,7 @@ static int ab8500_fg_probe(struct platform_device *pdev) if (ret != 0) { dev_err(di->dev, "failed to request %s IRQ %d: %d\n", ab8500_fg_irq_bh[0].name, irq, ret); - goto free_irq; + goto free_irq_th; } dev_dbg(di->dev, "Requested %s IRQ %d: %d\n", ab8500_fg_irq_bh[0].name, irq, ret); @@ -3212,15 +3229,17 @@ static int ab8500_fg_probe(struct platform_device *pdev) return ret; free_irq: - power_supply_unregister(di->fg_psy); - /* We also have to free all registered irqs */ - for (i = 0; i < ARRAY_SIZE(ab8500_fg_irq_th); i++) { + irq = platform_get_irq_byname(pdev, ab8500_fg_irq_bh[0].name); + free_irq(irq, di); +free_irq_th: + while (--i >= 0) { + /* Last assignment of i from primary interrupt handlers */ irq = platform_get_irq_byname(pdev, ab8500_fg_irq_th[i].name); free_irq(irq, di); } - irq = platform_get_irq_byname(pdev, ab8500_fg_irq_bh[0].name); - free_irq(irq, di); + + power_supply_unregister(di->fg_psy); free_inst_curr_wq: destroy_workqueue(di->fg_wq); return ret; diff --git a/drivers/power/supply/abx500_chargalg.c b/drivers/power/supply/abx500_chargalg.c index 23757fb10479..e6e37d4f20e4 100644 --- a/drivers/power/supply/abx500_chargalg.c +++ b/drivers/power/supply/abx500_chargalg.c @@ -354,13 +354,13 @@ static int abx500_chargalg_check_charger_enable(struct abx500_chargalg *di) if (di->chg_info.charger_type & USB_CHG) { return di->usb_chg->ops.check_enable(di->usb_chg, - di->bm->bat_type[di->bm->batt_id].normal_vol_lvl, - di->bm->bat_type[di->bm->batt_id].normal_cur_lvl); + di->bm->bat_type[di->bm->batt_id].normal_vol_lvl, + di->bm->bat_type[di->bm->batt_id].normal_cur_lvl); } else if ((di->chg_info.charger_type & AC_CHG) && !(di->ac_chg->external)) { return di->ac_chg->ops.check_enable(di->ac_chg, - di->bm->bat_type[di->bm->batt_id].normal_vol_lvl, - di->bm->bat_type[di->bm->batt_id].normal_cur_lvl); + di->bm->bat_type[di->bm->batt_id].normal_vol_lvl, + di->bm->bat_type[di->bm->batt_id].normal_cur_lvl); } return 0; } diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c index dc4c316eff81..5f0a5722b19e 100644 --- a/drivers/power/supply/axp20x_usb_power.c +++ b/drivers/power/supply/axp20x_usb_power.c @@ -48,6 +48,8 @@ #define AXP20X_VBUS_MON_VBUS_VALID BIT(3) +#define AXP813_BC_EN BIT(0) + /* * Note do not raise the debounce time, we must report Vusb high within * 100ms otherwise we get Vbus errors in musb. @@ -495,6 +497,12 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) return -EINVAL; } + if (power->axp20x_id == AXP813_ID) { + /* Enable USB Battery Charging specification detection */ + regmap_update_bits(axp20x->regmap, AXP288_BC_GLOBAL, + AXP813_BC_EN, AXP813_BC_EN); + } + psy_cfg.of_node = pdev->dev.of_node; psy_cfg.drv_data = power; diff --git a/drivers/power/supply/bd70528-charger.c b/drivers/power/supply/bd70528-charger.c index 1bb32b7226d7..b8e1ec106627 100644 --- a/drivers/power/supply/bd70528-charger.c +++ b/drivers/power/supply/bd70528-charger.c @@ -741,3 +741,4 @@ module_platform_driver(bd70528_power); MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); MODULE_DESCRIPTION("BD70528 power-supply driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:bd70528-power"); diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 61d6447d1966..6e9392901b0a 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -33,8 +33,6 @@ #include <linux/iio/types.h> #include <linux/mfd/motorola-cpcap.h> -#include <asm/div64.h> - /* * Register bit defines for CPCAP_REG_BPEOL. Some of these seem to * map to MC13783UG.pdf "Table 5-19. Register 13, Power Control 0" @@ -52,6 +50,26 @@ #define CPCAP_REG_BPEOL_BIT_BATTDETEN BIT(1) /* Enable battery detect */ #define CPCAP_REG_BPEOL_BIT_EOLSEL BIT(0) /* BPDET = 0, EOL = 1 */ +/* + * Register bit defines for CPCAP_REG_CCC1. These seem similar to the twl6030 + * coulomb counter registers rather than the mc13892 registers. Both twl6030 + * and mc13892 set bits 2 and 1 to reset and clear registers. But mc13892 + * sets bit 0 to start the coulomb counter while twl6030 sets bit 0 to stop + * the coulomb counter like cpcap does. So for now, we use the twl6030 style + * naming for the registers. + */ +#define CPCAP_REG_CCC1_ACTIVE_MODE1 BIT(4) /* Update rate */ +#define CPCAP_REG_CCC1_ACTIVE_MODE0 BIT(3) /* Update rate */ +#define CPCAP_REG_CCC1_AUTOCLEAR BIT(2) /* Resets sample registers */ +#define CPCAP_REG_CCC1_CAL_EN BIT(1) /* Clears after write in 1s */ +#define CPCAP_REG_CCC1_PAUSE BIT(0) /* Stop counters, allow write */ +#define CPCAP_REG_CCC1_RESET_MASK (CPCAP_REG_CCC1_AUTOCLEAR | \ + CPCAP_REG_CCC1_CAL_EN) + +#define CPCAP_REG_CCCC2_RATE1 BIT(5) +#define CPCAP_REG_CCCC2_RATE0 BIT(4) +#define CPCAP_REG_CCCC2_ENABLE BIT(3) + #define CPCAP_BATTERY_CC_SAMPLE_PERIOD_MS 250 enum { @@ -64,6 +82,7 @@ enum { enum cpcap_battery_irq_action { CPCAP_BATTERY_IRQ_ACTION_NONE, + CPCAP_BATTERY_IRQ_ACTION_CC_CAL_DONE, CPCAP_BATTERY_IRQ_ACTION_BATTERY_LOW, CPCAP_BATTERY_IRQ_ACTION_POWEROFF, }; @@ -76,15 +95,16 @@ struct cpcap_interrupt_desc { }; struct cpcap_battery_config { - int ccm; int cd_factor; struct power_supply_info info; + struct power_supply_battery_info bat; }; struct cpcap_coulomb_counter_data { s32 sample; /* 24 or 32 bits */ s32 accumulator; s16 offset; /* 9 bits */ + s16 integrator; /* 13 or 16 bits */ }; enum cpcap_battery_state { @@ -110,6 +130,7 @@ struct cpcap_battery_ddata { struct power_supply *psy; struct cpcap_battery_config config; struct cpcap_battery_state_data state[CPCAP_BATTERY_STATE_NR]; + u32 cc_lsb; /* μAms per LSB */ atomic_t active; int status; u16 vendor; @@ -217,41 +238,17 @@ static int cpcap_battery_cc_raw_div(struct cpcap_battery_ddata *ddata, s16 offset, u32 divider) { s64 acc; - u64 tmp; - int avg_current; - u32 cc_lsb; if (!divider) return 0; - switch (ddata->vendor) { - case CPCAP_VENDOR_ST: - cc_lsb = 95374; /* μAms per LSB */ - break; - case CPCAP_VENDOR_TI: - cc_lsb = 91501; /* μAms per LSB */ - break; - default: - return -EINVAL; - } - acc = accumulator; - acc = acc - ((s64)sample * offset); - cc_lsb = (cc_lsb * ddata->config.cd_factor) / 1000; + acc -= (s64)sample * offset; + acc *= ddata->cc_lsb; + acc *= -1; + acc = div_s64(acc, divider); - if (acc >= 0) - tmp = acc; - else - tmp = acc * -1; - - tmp = tmp * cc_lsb; - do_div(tmp, divider); - avg_current = tmp; - - if (acc >= 0) - return -avg_current; - else - return avg_current; + return acc; } /* 3600000μAms = 1μAh */ @@ -293,12 +290,13 @@ static int cpcap_battery_read_accumulated(struct cpcap_battery_ddata *ddata, struct cpcap_coulomb_counter_data *ccd) { - u16 buf[7]; /* CPCAP_REG_CC1 to CCI */ + u16 buf[7]; /* CPCAP_REG_CCS1 to CCI */ int error; ccd->sample = 0; ccd->accumulator = 0; ccd->offset = 0; + ccd->integrator = 0; /* Read coulomb counter register range */ error = regmap_bulk_read(ddata->reg, CPCAP_REG_CCS1, @@ -323,6 +321,12 @@ cpcap_battery_read_accumulated(struct cpcap_battery_ddata *ddata, ccd->offset = buf[4]; ccd->offset = sign_extend32(ccd->offset, 9); + /* Integrator register CPCAP_REG_CCI */ + if (ddata->vendor == CPCAP_VENDOR_TI) + ccd->integrator = sign_extend32(buf[6], 13); + else + ccd->integrator = (s16)buf[6]; + return cpcap_battery_cc_to_uah(ddata, ccd->sample, ccd->accumulator, @@ -336,31 +340,28 @@ cpcap_battery_read_accumulated(struct cpcap_battery_ddata *ddata, static int cpcap_battery_cc_get_avg_current(struct cpcap_battery_ddata *ddata) { int value, acc, error; - s32 sample = 1; + s32 sample; s16 offset; - if (ddata->vendor == CPCAP_VENDOR_ST) - sample = 4; - /* Coulomb counter integrator */ error = regmap_read(ddata->reg, CPCAP_REG_CCI, &value); if (error) return error; - if ((ddata->vendor == CPCAP_VENDOR_TI) && (value > 0x2000)) - value = value | 0xc000; - - acc = (s16)value; + if (ddata->vendor == CPCAP_VENDOR_TI) { + acc = sign_extend32(value, 13); + sample = 1; + } else { + acc = (s16)value; + sample = 4; + } - /* Coulomb counter sample time */ + /* Coulomb counter calibration offset */ error = regmap_read(ddata->reg, CPCAP_REG_CCM, &value); if (error) return error; - if (value < 0x200) - offset = value; - else - offset = value | 0xfc00; + offset = sign_extend32(value, 9); return cpcap_battery_cc_to_ua(ddata, sample, acc, offset); } @@ -369,8 +370,8 @@ static bool cpcap_battery_full(struct cpcap_battery_ddata *ddata) { struct cpcap_battery_state_data *state = cpcap_battery_latest(ddata); - /* Basically anything that measures above 4347000 is full */ - if (state->voltage >= (ddata->config.info.voltage_max_design - 4000)) + if (state->voltage >= + (ddata->config.bat.constant_charge_voltage_max_uv - 18000)) return true; return false; @@ -417,6 +418,7 @@ static enum power_supply_property cpcap_battery_props[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, POWER_SUPPLY_PROP_CURRENT_AVG, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, @@ -475,6 +477,9 @@ static int cpcap_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: val->intval = ddata->config.info.voltage_min_design; break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + val->intval = ddata->config.bat.constant_charge_voltage_max_uv; + break; case POWER_SUPPLY_PROP_CURRENT_AVG: sample = latest->cc.sample - previous->cc.sample; if (!sample) { @@ -540,6 +545,69 @@ static int cpcap_battery_get_property(struct power_supply *psy, return 0; } +static int cpcap_battery_update_charger(struct cpcap_battery_ddata *ddata, + int const_charge_voltage) +{ + union power_supply_propval prop; + union power_supply_propval val; + struct power_supply *charger; + int error; + + charger = power_supply_get_by_name("usb"); + if (!charger) + return -ENODEV; + + error = power_supply_get_property(charger, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + &prop); + if (error) + return error; + + /* Allow charger const voltage lower than battery const voltage */ + if (const_charge_voltage > prop.intval) + return 0; + + val.intval = const_charge_voltage; + + return power_supply_set_property(charger, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + &val); +} + +static int cpcap_battery_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct cpcap_battery_ddata *ddata = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + if (val->intval < ddata->config.info.voltage_min_design) + return -EINVAL; + if (val->intval > ddata->config.info.voltage_max_design) + return -EINVAL; + + ddata->config.bat.constant_charge_voltage_max_uv = val->intval; + + return cpcap_battery_update_charger(ddata, val->intval); + default: + return -EINVAL; + } + + return 0; +} + +static int cpcap_battery_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + return 1; + default: + return 0; + } +} + static irqreturn_t cpcap_battery_irq_thread(int irq, void *data) { struct cpcap_battery_ddata *ddata = data; @@ -560,14 +628,19 @@ static irqreturn_t cpcap_battery_irq_thread(int irq, void *data) latest = cpcap_battery_latest(ddata); switch (d->action) { + case CPCAP_BATTERY_IRQ_ACTION_CC_CAL_DONE: + dev_info(ddata->dev, "Coulomb counter calibration done\n"); + break; case CPCAP_BATTERY_IRQ_ACTION_BATTERY_LOW: if (latest->current_ua >= 0) - dev_warn(ddata->dev, "Battery low at 3.3V!\n"); + dev_warn(ddata->dev, "Battery low at %imV!\n", + latest->voltage / 1000); break; case CPCAP_BATTERY_IRQ_ACTION_POWEROFF: - if (latest->current_ua >= 0) { + if (latest->current_ua >= 0 && latest->voltage <= 3200000) { dev_emerg(ddata->dev, - "Battery empty at 3.1V, powering off\n"); + "Battery empty at %imV, powering off\n", + latest->voltage / 1000); orderly_poweroff(true); } break; @@ -609,7 +682,9 @@ static int cpcap_battery_init_irq(struct platform_device *pdev, d->name = name; d->irq = irq; - if (!strncmp(name, "lowbph", 6)) + if (!strncmp(name, "cccal", 5)) + d->action = CPCAP_BATTERY_IRQ_ACTION_CC_CAL_DONE; + else if (!strncmp(name, "lowbph", 6)) d->action = CPCAP_BATTERY_IRQ_ACTION_BATTERY_LOW; else if (!strncmp(name, "lowbpl", 6)) d->action = CPCAP_BATTERY_IRQ_ACTION_POWEROFF; @@ -635,6 +710,9 @@ static int cpcap_battery_init_interrupts(struct platform_device *pdev, return error; } + /* Enable calibration interrupt if already available in dts */ + cpcap_battery_init_irq(pdev, ddata, "cccal"); + /* Enable low battery interrupts for 3.3V high and 3.1V low */ error = regmap_update_bits(ddata->reg, CPCAP_REG_BPEOL, 0xffff, @@ -676,6 +754,60 @@ out_err: return error; } +/* Calibrate coulomb counter */ +static int cpcap_battery_calibrate(struct cpcap_battery_ddata *ddata) +{ + int error, ccc1, value; + unsigned long timeout; + + error = regmap_read(ddata->reg, CPCAP_REG_CCC1, &ccc1); + if (error) + return error; + + timeout = jiffies + msecs_to_jiffies(6000); + + /* Start calibration */ + error = regmap_update_bits(ddata->reg, CPCAP_REG_CCC1, + 0xffff, + CPCAP_REG_CCC1_CAL_EN); + if (error) + goto restore; + + while (time_before(jiffies, timeout)) { + error = regmap_read(ddata->reg, CPCAP_REG_CCC1, &value); + if (error) + goto restore; + + if (!(value & CPCAP_REG_CCC1_CAL_EN)) + break; + + error = regmap_read(ddata->reg, CPCAP_REG_CCM, &value); + if (error) + goto restore; + + msleep(300); + } + + /* Read calibration offset from CCM */ + error = regmap_read(ddata->reg, CPCAP_REG_CCM, &value); + if (error) + goto restore; + + dev_info(ddata->dev, "calibration done: 0x%04x\n", value); + +restore: + if (error) + dev_err(ddata->dev, "%s: error %i\n", __func__, error); + + error = regmap_update_bits(ddata->reg, CPCAP_REG_CCC1, + 0xffff, ccc1); + if (error) + dev_err(ddata->dev, "%s: restore error %i\n", + __func__, error); + + return error; +} + /* * Based on the values from Motorola mapphone Linux kernel. In the * the Motorola mapphone Linux kernel tree the value for pm_cd_factor @@ -687,12 +819,12 @@ out_err: * at 3078000. The device will die around 2743000. */ static const struct cpcap_battery_config cpcap_battery_default_data = { - .ccm = 0x3ff, .cd_factor = 0x3cc, .info.technology = POWER_SUPPLY_TECHNOLOGY_LION, .info.voltage_max_design = 4351000, .info.voltage_min_design = 3100000, .info.charge_full_design = 1740000, + .bat.constant_charge_voltage_max_uv = 4200000, }; #ifdef CONFIG_OF @@ -741,12 +873,19 @@ static int cpcap_battery_probe(struct platform_device *pdev) if (error) return error; - platform_set_drvdata(pdev, ddata); + switch (ddata->vendor) { + case CPCAP_VENDOR_ST: + ddata->cc_lsb = 95374; /* μAms per LSB */ + break; + case CPCAP_VENDOR_TI: + ddata->cc_lsb = 91501; /* μAms per LSB */ + break; + default: + return -EINVAL; + } + ddata->cc_lsb = (ddata->cc_lsb * ddata->config.cd_factor) / 1000; - error = regmap_update_bits(ddata->reg, CPCAP_REG_CCM, - 0xffff, ddata->config.ccm); - if (error) - return error; + platform_set_drvdata(pdev, ddata); error = cpcap_battery_init_interrupts(pdev, ddata); if (error) @@ -760,11 +899,13 @@ static int cpcap_battery_probe(struct platform_device *pdev) if (!psy_desc) return -ENOMEM; - psy_desc->name = "battery", - psy_desc->type = POWER_SUPPLY_TYPE_BATTERY, - psy_desc->properties = cpcap_battery_props, - psy_desc->num_properties = ARRAY_SIZE(cpcap_battery_props), - psy_desc->get_property = cpcap_battery_get_property, + psy_desc->name = "battery"; + psy_desc->type = POWER_SUPPLY_TYPE_BATTERY; + psy_desc->properties = cpcap_battery_props; + psy_desc->num_properties = ARRAY_SIZE(cpcap_battery_props); + psy_desc->get_property = cpcap_battery_get_property; + psy_desc->set_property = cpcap_battery_set_property; + psy_desc->property_is_writeable = cpcap_battery_property_is_writeable; psy_cfg.of_node = pdev->dev.of_node; psy_cfg.drv_data = ddata; @@ -779,6 +920,10 @@ static int cpcap_battery_probe(struct platform_device *pdev) atomic_set(&ddata->active, 1); + error = cpcap_battery_calibrate(ddata); + if (error) + return error; + return 0; } diff --git a/drivers/power/supply/cpcap-charger.c b/drivers/power/supply/cpcap-charger.c index 74258c7fe17d..c0d452e3dc8b 100644 --- a/drivers/power/supply/cpcap-charger.c +++ b/drivers/power/supply/cpcap-charger.c @@ -120,6 +120,13 @@ enum { CPCAP_CHARGER_IIO_NR, }; +enum { + CPCAP_CHARGER_DISCONNECTED, + CPCAP_CHARGER_DETECTING, + CPCAP_CHARGER_CHARGING, + CPCAP_CHARGER_DONE, +}; + struct cpcap_charger_ddata { struct device *dev; struct regmap *reg; @@ -138,6 +145,8 @@ struct cpcap_charger_ddata { atomic_t active; int status; + int state; + int voltage; }; struct cpcap_interrupt_desc { @@ -153,6 +162,7 @@ struct cpcap_charger_ints_state { bool chrg_se1b; bool rvrs_mode; + bool chrgcurr2; bool chrgcurr1; bool vbusvld; @@ -162,24 +172,26 @@ struct cpcap_charger_ints_state { static enum power_supply_property cpcap_charger_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, }; +/* No battery always shows temperature of -40000 */ static bool cpcap_charger_battery_found(struct cpcap_charger_ddata *ddata) { struct iio_channel *channel; - int error, value; + int error, temperature; channel = ddata->channels[CPCAP_CHARGER_IIO_BATTDET]; - error = iio_read_channel_raw(channel, &value); + error = iio_read_channel_processed(channel, &temperature); if (error < 0) { dev_warn(ddata->dev, "%s failed: %i\n", __func__, error); return false; } - return value == 1; + return temperature > -20000 && temperature < 60000; } static int cpcap_charger_get_charge_voltage(struct cpcap_charger_ddata *ddata) @@ -224,6 +236,9 @@ static int cpcap_charger_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_STATUS: val->intval = ddata->status; break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + val->intval = ddata->voltage; + break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: if (ddata->status == POWER_SUPPLY_STATUS_CHARGING) val->intval = cpcap_charger_get_charge_voltage(ddata) * @@ -248,6 +263,83 @@ static int cpcap_charger_get_property(struct power_supply *psy, return 0; } +static int cpcap_charger_match_voltage(int voltage) +{ + switch (voltage) { + case 0 ... 4100000 - 1: return 3800000; + case 4100000 ... 4120000 - 1: return 4100000; + case 4120000 ... 4150000 - 1: return 4120000; + case 4150000 ... 4170000 - 1: return 4150000; + case 4170000 ... 4200000 - 1: return 4170000; + case 4200000 ... 4230000 - 1: return 4200000; + case 4230000 ... 4250000 - 1: return 4230000; + case 4250000 ... 4270000 - 1: return 4250000; + case 4270000 ... 4300000 - 1: return 4270000; + case 4300000 ... 4330000 - 1: return 4300000; + case 4330000 ... 4350000 - 1: return 4330000; + case 4350000 ... 4380000 - 1: return 4350000; + case 4380000 ... 4400000 - 1: return 4380000; + case 4400000 ... 4420000 - 1: return 4400000; + case 4420000 ... 4440000 - 1: return 4420000; + case 4440000: return 4440000; + default: return 0; + } +} + +static int +cpcap_charger_get_bat_const_charge_voltage(struct cpcap_charger_ddata *ddata) +{ + union power_supply_propval prop; + struct power_supply *battery; + int voltage = ddata->voltage; + int error; + + battery = power_supply_get_by_name("battery"); + if (battery) { + error = power_supply_get_property(battery, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + &prop); + if (!error) + voltage = prop.intval; + } + + return voltage; +} + +static int cpcap_charger_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct cpcap_charger_ddata *ddata = dev_get_drvdata(psy->dev.parent); + int voltage, batvolt; + + switch (psp) { + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + voltage = cpcap_charger_match_voltage(val->intval); + batvolt = cpcap_charger_get_bat_const_charge_voltage(ddata); + if (voltage > batvolt) + voltage = batvolt; + ddata->voltage = voltage; + schedule_delayed_work(&ddata->detect_work, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int cpcap_charger_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + return 1; + default: + return 0; + } +} + static void cpcap_charger_set_cable_path(struct cpcap_charger_ddata *ddata, bool enabled) { @@ -422,6 +514,7 @@ static int cpcap_charger_get_ints_state(struct cpcap_charger_ddata *ddata, s->chrg_se1b = val & BIT(13); s->rvrs_mode = val & BIT(6); + s->chrgcurr2 = val & BIT(5); s->chrgcurr1 = val & BIT(4); s->vbusvld = val & BIT(3); @@ -434,6 +527,79 @@ static int cpcap_charger_get_ints_state(struct cpcap_charger_ddata *ddata, return 0; } +static void cpcap_charger_update_state(struct cpcap_charger_ddata *ddata, + int state) +{ + const char *status; + + if (state > CPCAP_CHARGER_DONE) { + dev_warn(ddata->dev, "unknown state: %i\n", state); + + return; + } + + ddata->state = state; + + switch (state) { + case CPCAP_CHARGER_DISCONNECTED: + status = "DISCONNECTED"; + break; + case CPCAP_CHARGER_DETECTING: + status = "DETECTING"; + break; + case CPCAP_CHARGER_CHARGING: + status = "CHARGING"; + break; + case CPCAP_CHARGER_DONE: + status = "DONE"; + break; + default: + return; + } + + dev_dbg(ddata->dev, "state: %s\n", status); +} + +static int cpcap_charger_voltage_to_regval(int voltage) +{ + int offset; + + switch (voltage) { + case 0 ... 4100000 - 1: + return 0; + case 4100000 ... 4200000 - 1: + offset = 1; + break; + case 4200000 ... 4300000 - 1: + offset = 0; + break; + case 4300000 ... 4380000 - 1: + offset = -1; + break; + case 4380000 ... 4440000: + offset = -2; + break; + default: + return 0; + } + + return ((voltage - 4100000) / 20000) + offset; +} + +static void cpcap_charger_disconnect(struct cpcap_charger_ddata *ddata, + int state, unsigned long delay) +{ + int error; + + error = cpcap_charger_set_state(ddata, 0, 0, 0); + if (error) + return; + + cpcap_charger_update_state(ddata, state); + power_supply_changed(ddata->usb); + schedule_delayed_work(&ddata->detect_work, delay); +} + static void cpcap_usb_detect(struct work_struct *work) { struct cpcap_charger_ddata *ddata; @@ -447,24 +613,67 @@ static void cpcap_usb_detect(struct work_struct *work) if (error) return; + /* Just init the state if a charger is connected with no chrg_det set */ + if (!s.chrg_det && s.chrgcurr1 && s.vbusvld) { + cpcap_charger_update_state(ddata, CPCAP_CHARGER_DETECTING); + + return; + } + + /* + * If battery voltage is higher than charge voltage, it may have been + * charged to 4.35V by Android. Try again in 10 minutes. + */ + if (cpcap_charger_get_charge_voltage(ddata) > ddata->voltage) { + cpcap_charger_disconnect(ddata, CPCAP_CHARGER_DETECTING, + HZ * 60 * 10); + + return; + } + + /* Throttle chrgcurr2 interrupt for charger done and retry */ + switch (ddata->state) { + case CPCAP_CHARGER_CHARGING: + if (s.chrgcurr2) + break; + if (s.chrgcurr1 && s.vbusvld) { + cpcap_charger_disconnect(ddata, CPCAP_CHARGER_DONE, + HZ * 5); + return; + } + break; + case CPCAP_CHARGER_DONE: + if (!s.chrgcurr2) + break; + cpcap_charger_disconnect(ddata, CPCAP_CHARGER_DETECTING, + HZ * 5); + return; + default: + break; + } + if (!ddata->feeding_vbus && cpcap_charger_vbus_valid(ddata) && s.chrgcurr1) { int max_current; + int vchrg; if (cpcap_charger_battery_found(ddata)) max_current = CPCAP_REG_CRM_ICHRG_1A596; else max_current = CPCAP_REG_CRM_ICHRG_0A532; + vchrg = cpcap_charger_voltage_to_regval(ddata->voltage); error = cpcap_charger_set_state(ddata, - CPCAP_REG_CRM_VCHRG_4V35, + CPCAP_REG_CRM_VCHRG(vchrg), max_current, 0); if (error) goto out_err; + cpcap_charger_update_state(ddata, CPCAP_CHARGER_CHARGING); } else { error = cpcap_charger_set_state(ddata, 0, 0, 0); if (error) goto out_err; + cpcap_charger_update_state(ddata, CPCAP_CHARGER_DISCONNECTED); } power_supply_changed(ddata->usb); @@ -524,7 +733,7 @@ static const char * const cpcap_charger_irqs[] = { "chrg_det", "rvrs_chrg", /* REG_INT1 */ - "chrg_se1b", "se0conn", "rvrs_mode", "chrgcurr1", "vbusvld", + "chrg_se1b", "se0conn", "rvrs_mode", "chrgcurr2", "chrgcurr1", "vbusvld", /* REG_INT_3 */ "battdetb", @@ -596,6 +805,8 @@ static const struct power_supply_desc cpcap_charger_usb_desc = { .properties = cpcap_charger_props, .num_properties = ARRAY_SIZE(cpcap_charger_props), .get_property = cpcap_charger_get_property, + .set_property = cpcap_charger_set_property, + .property_is_writeable = cpcap_charger_property_is_writeable, }; #ifdef CONFIG_OF @@ -625,6 +836,7 @@ static int cpcap_charger_probe(struct platform_device *pdev) return -ENOMEM; ddata->dev = &pdev->dev; + ddata->voltage = 4200000; ddata->reg = dev_get_regmap(ddata->dev->parent, NULL); if (!ddata->reg) diff --git a/drivers/power/supply/test_power.c b/drivers/power/supply/test_power.c index c3cad2b6daba..65c23ef6408d 100644 --- a/drivers/power/supply/test_power.c +++ b/drivers/power/supply/test_power.c @@ -33,6 +33,8 @@ static int battery_present = 1; /* true */ static int battery_technology = POWER_SUPPLY_TECHNOLOGY_LION; static int battery_capacity = 50; static int battery_voltage = 3300; +static int battery_charge_counter = -1000; +static int battery_current = 1600; static bool module_initialized; @@ -100,6 +102,9 @@ static int test_power_get_battery_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CHARGE_NOW: val->intval = battery_capacity; break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + val->intval = battery_charge_counter; + break; case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: case POWER_SUPPLY_PROP_CHARGE_FULL: val->intval = 100; @@ -114,6 +119,10 @@ static int test_power_get_battery_property(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_NOW: val->intval = battery_voltage; break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = battery_current; + break; default: pr_info("%s: some properties deliberately report errors.\n", __func__); @@ -135,6 +144,7 @@ static enum power_supply_property test_power_battery_props[] = { POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_CHARGE_COUNTER, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, @@ -144,6 +154,8 @@ static enum power_supply_property test_power_battery_props[] = { POWER_SUPPLY_PROP_SERIAL_NUMBER, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CURRENT_NOW, }; static char *test_power_ac_supplied_to[] = { @@ -447,6 +459,36 @@ static int param_set_battery_voltage(const char *key, #define param_get_battery_voltage param_get_int +static int param_set_battery_charge_counter(const char *key, + const struct kernel_param *kp) +{ + int tmp; + + if (1 != sscanf(key, "%d", &tmp)) + return -EINVAL; + + battery_charge_counter = tmp; + signal_power_supply_changed(test_power_supplies[TEST_BATTERY]); + return 0; +} + +#define param_get_battery_charge_counter param_get_int + +static int param_set_battery_current(const char *key, + const struct kernel_param *kp) +{ + int tmp; + + if (1 != sscanf(key, "%d", &tmp)) + return -EINVAL; + + battery_current = tmp; + signal_power_supply_changed(test_power_supplies[TEST_BATTERY]); + return 0; +} + +#define param_get_battery_current param_get_int + static const struct kernel_param_ops param_ops_ac_online = { .set = param_set_ac_online, .get = param_get_ac_online, @@ -487,6 +529,16 @@ static const struct kernel_param_ops param_ops_battery_voltage = { .get = param_get_battery_voltage, }; +static const struct kernel_param_ops param_ops_battery_charge_counter = { + .set = param_set_battery_charge_counter, + .get = param_get_battery_charge_counter, +}; + +static const struct kernel_param_ops param_ops_battery_current = { + .set = param_set_battery_current, + .get = param_get_battery_current, +}; + #define param_check_ac_online(name, p) __param_check(name, p, void); #define param_check_usb_online(name, p) __param_check(name, p, void); #define param_check_battery_status(name, p) __param_check(name, p, void); @@ -495,6 +547,8 @@ static const struct kernel_param_ops param_ops_battery_voltage = { #define param_check_battery_health(name, p) __param_check(name, p, void); #define param_check_battery_capacity(name, p) __param_check(name, p, void); #define param_check_battery_voltage(name, p) __param_check(name, p, void); +#define param_check_battery_charge_counter(name, p) __param_check(name, p, void); +#define param_check_battery_current(name, p) __param_check(name, p, void); module_param(ac_online, ac_online, 0644); @@ -525,6 +579,13 @@ MODULE_PARM_DESC(battery_capacity, "battery capacity (percentage)"); module_param(battery_voltage, battery_voltage, 0644); MODULE_PARM_DESC(battery_voltage, "battery voltage (millivolts)"); +module_param(battery_charge_counter, battery_charge_counter, 0644); +MODULE_PARM_DESC(battery_charge_counter, + "battery charge counter (microampere-hours)"); + +module_param(battery_current, battery_current, 0644); +MODULE_PARM_DESC(battery_current, "battery current (milliampere)"); + MODULE_DESCRIPTION("Power supply driver for testing"); MODULE_AUTHOR("Anton Vorontsov <cbouatmailru@gmail.com>"); MODULE_LICENSE("GPL"); |