- Four new drivers:
o goldfish_battery: This is Android Emulator battery driver. Originally from Google, but Intel folks reshaped it for mainline; o pm2301_charger: A new driver for ST-Ericsson 2301 Power Management chip, uses AB8500 battery management core; o qnap-poweroff: The driver adds poweroff functionality for QNAP NAS boxes; o restart-poweroff: A generic driver that implements 'power off by restarting'. The actual poweroff functionality is implemented through a bootloader, so Linux' task is just to restart the box. The driver is useful on Buffalo Linkstation LS-XHL and LS-CHLv2 boards. Andrew Lunn worked on submitting the driver (as well as qnap-poweroff above). - A lot of fixes for ab8500 drivers. This is a part of efforts of syncing internal ST-Ericsson development tree with the mainline. Lee Jones @ Linaro worked on compilation and reshaping these series; - New health properties for the power supplies: "Watchdog timer expire" and "Safety timer expire"; - As usual, a bunch of fixes/cleanups here and there. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.19 (GNU/Linux) iQIcBAABAgAGBQJRIww1AAoJEGgI9fZJve1bJ7sP/1hR8SYQZ/k0ayxA/kIxNEDA Qa2sAcdP5pa4F/A2Wi5MTrckSHuLlRRigWRCPM09emVOvt6g2MdM3Eq6ShuYb2p0 g2bX0F9KBEa9gdiJC1wNVe9DilXbrJsk86V5E+ZkuVnNzM3S1FA1sPWGhdH66YVq 9IKxm2XntyXFpuvhSYE01KQoFY+Crv1NEGXoIR7tB6Q1KOf3XswnsFwdKJ95IO5r 8f/lVYRAYUfeplhrYd3eJTtb/GHhK7CutFV/E6kWlumGCWNVH2xt+dTlAmTUdQvO jKe9R5iq5eXFTQmN8QZDH7QdXO1DdmJZ9F112hIAud/93bmNrnc0WqMKM7CFHkfH S6ptGgeIdpGEM1KMgzKTNrQeNIYsVNtqbEiIiodR8S4e0cX2pb2n5IfzmELIVc0n QMZylORpGaH1lBxTE2RoT4raGeWF5jH0t0b0YRqccRPvQ4VjCJslDOq9IOETUe+/ YxWpELJigW7FGLMfx0SB2/3s1P4EUaufWvHANCVaPuBsOZga854R4dzms4hKyKsL JQg+OKp/Bt78yAj/8/n0FWdxgldp/NjF4485ITQ1eOZyg8VSeIp93Lk6lgSTukux R1xLruxfPgdynpmYFCGF99JG2G8RmRuJFSTf8JttvyztkAxQw6zBtdmdiPk38wst qpUxdPpNDIvt65Ix7x+D =955B -----END PGP SIGNATURE----- Merge tag 'for-v3.9' of git://git.infradead.org/battery-2.6 Pull battery updates from Anton Vorontsov: "Four new drivers: - goldfish_battery: This is Android Emulator battery driver. Originally from Google, but Intel folks reshaped it for mainline - pm2301_charger: A new driver for ST-Ericsson 2301 Power Management chip, uses AB8500 battery management core - qnap-poweroff: The driver adds poweroff functionality for QNAP NAS boxes - restart-poweroff: A generic driver that implements 'power off by restarting'. The actual poweroff functionality is implemented through a bootloader, so Linux' task is just to restart the box. The driver is useful on Buffalo Linkstation LS-XHL and LS-CHLv2 boards. Andrew Lunn worked on submitting the driver (as well as qnap-poweroff above). Additionally: - A lot of fixes for ab8500 drivers. This is a part of efforts of syncing internal ST-Ericsson development tree with the mainline. Lee Jones @ Linaro worked on compilation and reshaping these series. - New health properties for the power supplies: "Watchdog timer expire" and "Safety timer expire" - As usual, a bunch of fixes/cleanups here and there" * tag 'for-v3.9' of git://git.infradead.org/battery-2.6: (81 commits) bq2415x_charger: Add support for offline and 100mA mode generic-adc-battery: Fix forever loop in gab_remove() goldfish_battery: Add missing GENERIC_HARDIRQS dependency da9030_battery: Include notifier.h bq27x00_battery: Fix reporting battery temperature power/reset: Remove newly introduced __dev* annotations lp8727_charger: Small cleanup in naming ab8500_btemp: Demote initcall sequence ds2782_battery: Add power_supply_changed() calls for proper uevent support power: Add battery driver for goldfish emulator u8500-charger: Delay for USB enumeration ab8500-bm: Remove individual [charger|btemp|fg|chargalg] pdata structures ab8500-charger: Do not touch VBUSOVV bits ab8500-fg: Use correct battery charge full design pm2301: LPN mode control support pm2301: Enable vbat low monitoring ab8500-bm: Flush all work queues before suspending ab8500-fg: Go to INIT_RECOVERY when charger removed ab8500-charger: Add support for autopower on AB8505 and AB9540 abx500-chargalg: Add new sysfs interface to get current charge status ... Fix up fairly straightforward conflicts in the ab8500 driver. But since it seems to be ARM-specific, I can't even compile-test the result..
This commit is contained in:
commit
5a1203914a
@ -0,0 +1,13 @@
|
||||
* QNAP Power Off
|
||||
|
||||
QNAP NAS devices have a microcontroller controlling the main power
|
||||
supply. This microcontroller is connected to UART1 of the Kirkwood and
|
||||
Orion5x SoCs. Sending the charactor 'A', at 19200 baud, tells the
|
||||
microcontroller to turn the power off. This driver adds a handler to
|
||||
pm_power_off which is called to turn the power off.
|
||||
|
||||
Required Properties:
|
||||
- compatible: Should be "qnap,power-off"
|
||||
|
||||
- reg: Address and length of the register set for UART1
|
||||
- clocks: tclk clock
|
@ -0,0 +1,8 @@
|
||||
* Restart Power Off
|
||||
|
||||
Buffalo Linkstation LS-XHL and LS-CHLv2, and other devices power off
|
||||
by restarting and letting u-boot keep hold of the machine until the
|
||||
user presses a button.
|
||||
|
||||
Required Properties:
|
||||
- compatible: Should be "restart-poweroff"
|
16
MAINTAINERS
16
MAINTAINERS
@ -7612,6 +7612,22 @@ F: Documentation/backlight/lp855x-driver.txt
|
||||
F: drivers/video/backlight/lp855x_bl.c
|
||||
F: include/linux/platform_data/lp855x.h
|
||||
|
||||
TI LP8727 CHARGER DRIVER
|
||||
M: Milo Kim <milo.kim@ti.com>
|
||||
S: Maintained
|
||||
F: drivers/power/lp8727_charger.c
|
||||
F: include/linux/platform_data/lp8727.h
|
||||
|
||||
TI LP8788 MFD DRIVER
|
||||
M: Milo Kim <milo.kim@ti.com>
|
||||
S: Maintained
|
||||
F: drivers/iio/adc/lp8788_adc.c
|
||||
F: drivers/leds/leds-lp8788.c
|
||||
F: drivers/mfd/lp8788*.c
|
||||
F: drivers/power/lp8788-charger.c
|
||||
F: drivers/regulator/lp8788-*.c
|
||||
F: include/linux/mfd/lp8788*.h
|
||||
|
||||
TI TWL4030 SERIES SOC CODEC DRIVER
|
||||
M: Peter Ujfalusi <peter.ujfalusi@ti.com>
|
||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
|
@ -750,6 +750,12 @@ static struct resource ab8500_charger_resources[] = {
|
||||
.end = AB8500_INT_CH_WD_EXP,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
{
|
||||
.name = "VBUS_CH_DROP_END",
|
||||
.start = AB8500_INT_VBUS_CH_DROP_END,
|
||||
.end = AB8500_INT_VBUS_CH_DROP_END,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource ab8500_btemp_resources[] = {
|
||||
@ -1012,40 +1018,32 @@ static struct mfd_cell ab8500_bm_devs[] = {
|
||||
.of_compatible = "stericsson,ab8500-charger",
|
||||
.num_resources = ARRAY_SIZE(ab8500_charger_resources),
|
||||
.resources = ab8500_charger_resources,
|
||||
#ifndef CONFIG_OF
|
||||
.platform_data = &ab8500_bm_data,
|
||||
.pdata_size = sizeof(ab8500_bm_data),
|
||||
#endif
|
||||
},
|
||||
{
|
||||
.name = "ab8500-btemp",
|
||||
.of_compatible = "stericsson,ab8500-btemp",
|
||||
.num_resources = ARRAY_SIZE(ab8500_btemp_resources),
|
||||
.resources = ab8500_btemp_resources,
|
||||
#ifndef CONFIG_OF
|
||||
.platform_data = &ab8500_bm_data,
|
||||
.pdata_size = sizeof(ab8500_bm_data),
|
||||
#endif
|
||||
},
|
||||
{
|
||||
.name = "ab8500-fg",
|
||||
.of_compatible = "stericsson,ab8500-fg",
|
||||
.num_resources = ARRAY_SIZE(ab8500_fg_resources),
|
||||
.resources = ab8500_fg_resources,
|
||||
#ifndef CONFIG_OF
|
||||
.platform_data = &ab8500_bm_data,
|
||||
.pdata_size = sizeof(ab8500_bm_data),
|
||||
#endif
|
||||
},
|
||||
{
|
||||
.name = "ab8500-chargalg",
|
||||
.of_compatible = "stericsson,ab8500-chargalg",
|
||||
.num_resources = ARRAY_SIZE(ab8500_chargalg_resources),
|
||||
.resources = ab8500_chargalg_resources,
|
||||
#ifndef CONFIG_OF
|
||||
.platform_data = &ab8500_bm_data,
|
||||
.pdata_size = sizeof(ab8500_bm_data),
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -915,15 +915,13 @@ static int pm860x_battery_probe(struct platform_device *pdev)
|
||||
info->irq_cc = platform_get_irq(pdev, 0);
|
||||
if (info->irq_cc <= 0) {
|
||||
dev_err(&pdev->dev, "No IRQ resource!\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
info->irq_batt = platform_get_irq(pdev, 1);
|
||||
if (info->irq_batt <= 0) {
|
||||
dev_err(&pdev->dev, "No IRQ resource!\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
info->chip = chip;
|
||||
@ -957,7 +955,7 @@ static int pm860x_battery_probe(struct platform_device *pdev)
|
||||
|
||||
ret = power_supply_register(&pdev->dev, &info->battery);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
info->battery.dev->parent = &pdev->dev;
|
||||
|
||||
ret = request_threaded_irq(info->irq_cc, NULL,
|
||||
@ -984,8 +982,6 @@ out_coulomb:
|
||||
free_irq(info->irq_cc, info);
|
||||
out_reg:
|
||||
power_supply_unregister(&info->battery);
|
||||
out:
|
||||
kfree(info);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -993,10 +989,9 @@ static int pm860x_battery_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct pm860x_battery_info *info = platform_get_drvdata(pdev);
|
||||
|
||||
power_supply_unregister(&info->battery);
|
||||
free_irq(info->irq_batt, info);
|
||||
free_irq(info->irq_cc, info);
|
||||
kfree(info);
|
||||
power_supply_unregister(&info->battery);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
@ -346,6 +346,20 @@ config AB8500_BM
|
||||
help
|
||||
Say Y to include support for AB8500 battery management.
|
||||
|
||||
config BATTERY_GOLDFISH
|
||||
tristate "Goldfish battery driver"
|
||||
depends on GENERIC_HARDIRQS
|
||||
help
|
||||
Say Y to enable support for the battery and AC power in the
|
||||
Goldfish emulator.
|
||||
|
||||
config CHARGER_PM2301
|
||||
bool "PM2301 Battery Charger Driver"
|
||||
depends on AB8500_BM
|
||||
help
|
||||
Say Y to include support for PM2301 charger driver.
|
||||
Depends on AB8500 battery management core.
|
||||
|
||||
source "drivers/power/reset/Kconfig"
|
||||
|
||||
endif # POWER_SUPPLY
|
||||
|
@ -20,6 +20,7 @@ obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
|
||||
obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o
|
||||
obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o
|
||||
obj-$(CONFIG_BATTERY_DS2782) += ds2782_battery.o
|
||||
obj-$(CONFIG_BATTERY_GOLDFISH) += goldfish_battery.o
|
||||
obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o
|
||||
obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o
|
||||
obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o
|
||||
@ -38,7 +39,7 @@ obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
|
||||
obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o
|
||||
obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o
|
||||
obj-$(CONFIG_BATTERY_RX51) += rx51_battery.o
|
||||
obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_btemp.o ab8500_fg.o abx500_chargalg.o
|
||||
obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o abx500_chargalg.o
|
||||
obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o
|
||||
obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o
|
||||
@ -46,6 +47,7 @@ obj-$(CONFIG_CHARGER_LP8727) += lp8727_charger.o
|
||||
obj-$(CONFIG_CHARGER_LP8788) += lp8788-charger.o
|
||||
obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o
|
||||
obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o
|
||||
obj-$(CONFIG_CHARGER_PM2301) += pm2301_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
|
||||
obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o
|
||||
|
@ -182,206 +182,206 @@ static struct batres_vs_temp temp_to_batres_tbl_9100[] = {
|
||||
};
|
||||
|
||||
static struct abx500_battery_type bat_type_thermistor[] = {
|
||||
[BATTERY_UNKNOWN] = {
|
||||
/* First element always represent the UNKNOWN battery */
|
||||
.name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
|
||||
.resis_high = 0,
|
||||
.resis_low = 0,
|
||||
.battery_resistance = 300,
|
||||
.charge_full_design = 612,
|
||||
.nominal_voltage = 3700,
|
||||
.termination_vol = 4050,
|
||||
.termination_curr = 200,
|
||||
.recharge_vol = 3990,
|
||||
.normal_cur_lvl = 400,
|
||||
.normal_vol_lvl = 4100,
|
||||
.maint_a_cur_lvl = 400,
|
||||
.maint_a_vol_lvl = 4050,
|
||||
.maint_a_chg_timer_h = 60,
|
||||
.maint_b_cur_lvl = 400,
|
||||
.maint_b_vol_lvl = 4000,
|
||||
.maint_b_chg_timer_h = 200,
|
||||
.low_high_cur_lvl = 300,
|
||||
.low_high_vol_lvl = 4000,
|
||||
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
|
||||
.r_to_t_tbl = temp_tbl,
|
||||
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
|
||||
.v_to_cap_tbl = cap_tbl,
|
||||
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
|
||||
.batres_tbl = temp_to_batres_tbl_thermistor,
|
||||
},
|
||||
{
|
||||
.name = POWER_SUPPLY_TECHNOLOGY_LIPO,
|
||||
.resis_high = 53407,
|
||||
.resis_low = 12500,
|
||||
.battery_resistance = 300,
|
||||
.charge_full_design = 900,
|
||||
.nominal_voltage = 3600,
|
||||
.termination_vol = 4150,
|
||||
.termination_curr = 80,
|
||||
.recharge_vol = 4130,
|
||||
.normal_cur_lvl = 700,
|
||||
.normal_vol_lvl = 4200,
|
||||
.maint_a_cur_lvl = 600,
|
||||
.maint_a_vol_lvl = 4150,
|
||||
.maint_a_chg_timer_h = 60,
|
||||
.maint_b_cur_lvl = 600,
|
||||
.maint_b_vol_lvl = 4100,
|
||||
.maint_b_chg_timer_h = 200,
|
||||
.low_high_cur_lvl = 300,
|
||||
.low_high_vol_lvl = 4000,
|
||||
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_A_thermistor),
|
||||
.r_to_t_tbl = temp_tbl_A_thermistor,
|
||||
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_A_thermistor),
|
||||
.v_to_cap_tbl = cap_tbl_A_thermistor,
|
||||
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
|
||||
.batres_tbl = temp_to_batres_tbl_thermistor,
|
||||
[BATTERY_UNKNOWN] = {
|
||||
/* First element always represent the UNKNOWN battery */
|
||||
.name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
|
||||
.resis_high = 0,
|
||||
.resis_low = 0,
|
||||
.battery_resistance = 300,
|
||||
.charge_full_design = 612,
|
||||
.nominal_voltage = 3700,
|
||||
.termination_vol = 4050,
|
||||
.termination_curr = 200,
|
||||
.recharge_cap = 95,
|
||||
.normal_cur_lvl = 400,
|
||||
.normal_vol_lvl = 4100,
|
||||
.maint_a_cur_lvl = 400,
|
||||
.maint_a_vol_lvl = 4050,
|
||||
.maint_a_chg_timer_h = 60,
|
||||
.maint_b_cur_lvl = 400,
|
||||
.maint_b_vol_lvl = 4000,
|
||||
.maint_b_chg_timer_h = 200,
|
||||
.low_high_cur_lvl = 300,
|
||||
.low_high_vol_lvl = 4000,
|
||||
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
|
||||
.r_to_t_tbl = temp_tbl,
|
||||
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
|
||||
.v_to_cap_tbl = cap_tbl,
|
||||
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
|
||||
.batres_tbl = temp_to_batres_tbl_thermistor,
|
||||
},
|
||||
{
|
||||
.name = POWER_SUPPLY_TECHNOLOGY_LIPO,
|
||||
.resis_high = 53407,
|
||||
.resis_low = 12500,
|
||||
.battery_resistance = 300,
|
||||
.charge_full_design = 900,
|
||||
.nominal_voltage = 3600,
|
||||
.termination_vol = 4150,
|
||||
.termination_curr = 80,
|
||||
.recharge_cap = 95,
|
||||
.normal_cur_lvl = 700,
|
||||
.normal_vol_lvl = 4200,
|
||||
.maint_a_cur_lvl = 600,
|
||||
.maint_a_vol_lvl = 4150,
|
||||
.maint_a_chg_timer_h = 60,
|
||||
.maint_b_cur_lvl = 600,
|
||||
.maint_b_vol_lvl = 4100,
|
||||
.maint_b_chg_timer_h = 200,
|
||||
.low_high_cur_lvl = 300,
|
||||
.low_high_vol_lvl = 4000,
|
||||
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_A_thermistor),
|
||||
.r_to_t_tbl = temp_tbl_A_thermistor,
|
||||
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_A_thermistor),
|
||||
.v_to_cap_tbl = cap_tbl_A_thermistor,
|
||||
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
|
||||
.batres_tbl = temp_to_batres_tbl_thermistor,
|
||||
|
||||
},
|
||||
{
|
||||
.name = POWER_SUPPLY_TECHNOLOGY_LIPO,
|
||||
.resis_high = 200000,
|
||||
.resis_low = 82869,
|
||||
.battery_resistance = 300,
|
||||
.charge_full_design = 900,
|
||||
.nominal_voltage = 3600,
|
||||
.termination_vol = 4150,
|
||||
.termination_curr = 80,
|
||||
.recharge_vol = 4130,
|
||||
.normal_cur_lvl = 700,
|
||||
.normal_vol_lvl = 4200,
|
||||
.maint_a_cur_lvl = 600,
|
||||
.maint_a_vol_lvl = 4150,
|
||||
.maint_a_chg_timer_h = 60,
|
||||
.maint_b_cur_lvl = 600,
|
||||
.maint_b_vol_lvl = 4100,
|
||||
.maint_b_chg_timer_h = 200,
|
||||
.low_high_cur_lvl = 300,
|
||||
.low_high_vol_lvl = 4000,
|
||||
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_B_thermistor),
|
||||
.r_to_t_tbl = temp_tbl_B_thermistor,
|
||||
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_B_thermistor),
|
||||
.v_to_cap_tbl = cap_tbl_B_thermistor,
|
||||
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
|
||||
.batres_tbl = temp_to_batres_tbl_thermistor,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = POWER_SUPPLY_TECHNOLOGY_LIPO,
|
||||
.resis_high = 200000,
|
||||
.resis_low = 82869,
|
||||
.battery_resistance = 300,
|
||||
.charge_full_design = 900,
|
||||
.nominal_voltage = 3600,
|
||||
.termination_vol = 4150,
|
||||
.termination_curr = 80,
|
||||
.recharge_cap = 95,
|
||||
.normal_cur_lvl = 700,
|
||||
.normal_vol_lvl = 4200,
|
||||
.maint_a_cur_lvl = 600,
|
||||
.maint_a_vol_lvl = 4150,
|
||||
.maint_a_chg_timer_h = 60,
|
||||
.maint_b_cur_lvl = 600,
|
||||
.maint_b_vol_lvl = 4100,
|
||||
.maint_b_chg_timer_h = 200,
|
||||
.low_high_cur_lvl = 300,
|
||||
.low_high_vol_lvl = 4000,
|
||||
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_B_thermistor),
|
||||
.r_to_t_tbl = temp_tbl_B_thermistor,
|
||||
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_B_thermistor),
|
||||
.v_to_cap_tbl = cap_tbl_B_thermistor,
|
||||
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
|
||||
.batres_tbl = temp_to_batres_tbl_thermistor,
|
||||
},
|
||||
};
|
||||
|
||||
static struct abx500_battery_type bat_type_ext_thermistor[] = {
|
||||
[BATTERY_UNKNOWN] = {
|
||||
/* First element always represent the UNKNOWN battery */
|
||||
.name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
|
||||
.resis_high = 0,
|
||||
.resis_low = 0,
|
||||
.battery_resistance = 300,
|
||||
.charge_full_design = 612,
|
||||
.nominal_voltage = 3700,
|
||||
.termination_vol = 4050,
|
||||
.termination_curr = 200,
|
||||
.recharge_vol = 3990,
|
||||
.normal_cur_lvl = 400,
|
||||
.normal_vol_lvl = 4100,
|
||||
.maint_a_cur_lvl = 400,
|
||||
.maint_a_vol_lvl = 4050,
|
||||
.maint_a_chg_timer_h = 60,
|
||||
.maint_b_cur_lvl = 400,
|
||||
.maint_b_vol_lvl = 4000,
|
||||
.maint_b_chg_timer_h = 200,
|
||||
.low_high_cur_lvl = 300,
|
||||
.low_high_vol_lvl = 4000,
|
||||
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
|
||||
.r_to_t_tbl = temp_tbl,
|
||||
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
|
||||
.v_to_cap_tbl = cap_tbl,
|
||||
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
|
||||
.batres_tbl = temp_to_batres_tbl_thermistor,
|
||||
},
|
||||
[BATTERY_UNKNOWN] = {
|
||||
/* First element always represent the UNKNOWN battery */
|
||||
.name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
|
||||
.resis_high = 0,
|
||||
.resis_low = 0,
|
||||
.battery_resistance = 300,
|
||||
.charge_full_design = 612,
|
||||
.nominal_voltage = 3700,
|
||||
.termination_vol = 4050,
|
||||
.termination_curr = 200,
|
||||
.recharge_cap = 95,
|
||||
.normal_cur_lvl = 400,
|
||||
.normal_vol_lvl = 4100,
|
||||
.maint_a_cur_lvl = 400,
|
||||
.maint_a_vol_lvl = 4050,
|
||||
.maint_a_chg_timer_h = 60,
|
||||
.maint_b_cur_lvl = 400,
|
||||
.maint_b_vol_lvl = 4000,
|
||||
.maint_b_chg_timer_h = 200,
|
||||
.low_high_cur_lvl = 300,
|
||||
.low_high_vol_lvl = 4000,
|
||||
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
|
||||
.r_to_t_tbl = temp_tbl,
|
||||
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
|
||||
.v_to_cap_tbl = cap_tbl,
|
||||
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
|
||||
.batres_tbl = temp_to_batres_tbl_thermistor,
|
||||
},
|
||||
/*
|
||||
* These are the batteries that doesn't have an internal NTC resistor to measure
|
||||
* its temperature. The temperature in this case is measure with a NTC placed
|
||||
* near the battery but on the PCB.
|
||||
*/
|
||||
{
|
||||
.name = POWER_SUPPLY_TECHNOLOGY_LIPO,
|
||||
.resis_high = 76000,
|
||||
.resis_low = 53000,
|
||||
.battery_resistance = 300,
|
||||
.charge_full_design = 900,
|
||||
.nominal_voltage = 3700,
|
||||
.termination_vol = 4150,
|
||||
.termination_curr = 100,
|
||||
.recharge_vol = 4130,
|
||||
.normal_cur_lvl = 700,
|
||||
.normal_vol_lvl = 4200,
|
||||
.maint_a_cur_lvl = 600,
|
||||
.maint_a_vol_lvl = 4150,
|
||||
.maint_a_chg_timer_h = 60,
|
||||
.maint_b_cur_lvl = 600,
|
||||
.maint_b_vol_lvl = 4100,
|
||||
.maint_b_chg_timer_h = 200,
|
||||
.low_high_cur_lvl = 300,
|
||||
.low_high_vol_lvl = 4000,
|
||||
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
|
||||
.r_to_t_tbl = temp_tbl,
|
||||
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
|
||||
.v_to_cap_tbl = cap_tbl,
|
||||
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
|
||||
.batres_tbl = temp_to_batres_tbl_thermistor,
|
||||
},
|
||||
{
|
||||
.name = POWER_SUPPLY_TECHNOLOGY_LION,
|
||||
.resis_high = 30000,
|
||||
.resis_low = 10000,
|
||||
.battery_resistance = 300,
|
||||
.charge_full_design = 950,
|
||||
.nominal_voltage = 3700,
|
||||
.termination_vol = 4150,
|
||||
.termination_curr = 100,
|
||||
.recharge_vol = 4130,
|
||||
.normal_cur_lvl = 700,
|
||||
.normal_vol_lvl = 4200,
|
||||
.maint_a_cur_lvl = 600,
|
||||
.maint_a_vol_lvl = 4150,
|
||||
.maint_a_chg_timer_h = 60,
|
||||
.maint_b_cur_lvl = 600,
|
||||
.maint_b_vol_lvl = 4100,
|
||||
.maint_b_chg_timer_h = 200,
|
||||
.low_high_cur_lvl = 300,
|
||||
.low_high_vol_lvl = 4000,
|
||||
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
|
||||
.r_to_t_tbl = temp_tbl,
|
||||
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
|
||||
.v_to_cap_tbl = cap_tbl,
|
||||
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
|
||||
.batres_tbl = temp_to_batres_tbl_thermistor,
|
||||
},
|
||||
{
|
||||
.name = POWER_SUPPLY_TECHNOLOGY_LION,
|
||||
.resis_high = 95000,
|
||||
.resis_low = 76001,
|
||||
.battery_resistance = 300,
|
||||
.charge_full_design = 950,
|
||||
.nominal_voltage = 3700,
|
||||
.termination_vol = 4150,
|
||||
.termination_curr = 100,
|
||||
.recharge_vol = 4130,
|
||||
.normal_cur_lvl = 700,
|
||||
.normal_vol_lvl = 4200,
|
||||
.maint_a_cur_lvl = 600,
|
||||
.maint_a_vol_lvl = 4150,
|
||||
.maint_a_chg_timer_h = 60,
|
||||
.maint_b_cur_lvl = 600,
|
||||
.maint_b_vol_lvl = 4100,
|
||||
.maint_b_chg_timer_h = 200,
|
||||
.low_high_cur_lvl = 300,
|
||||
.low_high_vol_lvl = 4000,
|
||||
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
|
||||
.r_to_t_tbl = temp_tbl,
|
||||
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
|
||||
.v_to_cap_tbl = cap_tbl,
|
||||
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
|
||||
.batres_tbl = temp_to_batres_tbl_thermistor,
|
||||
},
|
||||
{
|
||||
.name = POWER_SUPPLY_TECHNOLOGY_LIPO,
|
||||
.resis_high = 76000,
|
||||
.resis_low = 53000,
|
||||
.battery_resistance = 300,
|
||||
.charge_full_design = 900,
|
||||
.nominal_voltage = 3700,
|
||||
.termination_vol = 4150,
|
||||
.termination_curr = 100,
|
||||
.recharge_cap = 95,
|
||||
.normal_cur_lvl = 700,
|
||||
.normal_vol_lvl = 4200,
|
||||
.maint_a_cur_lvl = 600,
|
||||
.maint_a_vol_lvl = 4150,
|
||||
.maint_a_chg_timer_h = 60,
|
||||
.maint_b_cur_lvl = 600,
|
||||
.maint_b_vol_lvl = 4100,
|
||||
.maint_b_chg_timer_h = 200,
|
||||
.low_high_cur_lvl = 300,
|
||||
.low_high_vol_lvl = 4000,
|
||||
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
|
||||
.r_to_t_tbl = temp_tbl,
|
||||
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
|
||||
.v_to_cap_tbl = cap_tbl,
|
||||
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
|
||||
.batres_tbl = temp_to_batres_tbl_thermistor,
|
||||
},
|
||||
{
|
||||
.name = POWER_SUPPLY_TECHNOLOGY_LION,
|
||||
.resis_high = 30000,
|
||||
.resis_low = 10000,
|
||||
.battery_resistance = 300,
|
||||
.charge_full_design = 950,
|
||||
.nominal_voltage = 3700,
|
||||
.termination_vol = 4150,
|
||||
.termination_curr = 100,
|
||||
.recharge_cap = 95,
|
||||
.normal_cur_lvl = 700,
|
||||
.normal_vol_lvl = 4200,
|
||||
.maint_a_cur_lvl = 600,
|
||||
.maint_a_vol_lvl = 4150,
|
||||
.maint_a_chg_timer_h = 60,
|
||||
.maint_b_cur_lvl = 600,
|
||||
.maint_b_vol_lvl = 4100,
|
||||
.maint_b_chg_timer_h = 200,
|
||||
.low_high_cur_lvl = 300,
|
||||
.low_high_vol_lvl = 4000,
|
||||
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
|
||||
.r_to_t_tbl = temp_tbl,
|
||||
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
|
||||
.v_to_cap_tbl = cap_tbl,
|
||||
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
|
||||
.batres_tbl = temp_to_batres_tbl_thermistor,
|
||||
},
|
||||
{
|
||||
.name = POWER_SUPPLY_TECHNOLOGY_LION,
|
||||
.resis_high = 95000,
|
||||
.resis_low = 76001,
|
||||
.battery_resistance = 300,
|
||||
.charge_full_design = 950,
|
||||
.nominal_voltage = 3700,
|
||||
.termination_vol = 4150,
|
||||
.termination_curr = 100,
|
||||
.recharge_cap = 95,
|
||||
.normal_cur_lvl = 700,
|
||||
.normal_vol_lvl = 4200,
|
||||
.maint_a_cur_lvl = 600,
|
||||
.maint_a_vol_lvl = 4150,
|
||||
.maint_a_chg_timer_h = 60,
|
||||
.maint_b_cur_lvl = 600,
|
||||
.maint_b_vol_lvl = 4100,
|
||||
.maint_b_chg_timer_h = 200,
|
||||
.low_high_cur_lvl = 300,
|
||||
.low_high_vol_lvl = 4000,
|
||||
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
|
||||
.r_to_t_tbl = temp_tbl,
|
||||
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
|
||||
.v_to_cap_tbl = cap_tbl,
|
||||
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
|
||||
.batres_tbl = temp_to_batres_tbl_thermistor,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct abx500_bm_capacity_levels cap_levels = {
|
||||
@ -405,8 +405,8 @@ static const struct abx500_fg_parameters fg = {
|
||||
.lowbat_threshold = 3100,
|
||||
.battok_falling_th_sel0 = 2860,
|
||||
.battok_raising_th_sel1 = 2860,
|
||||
.maint_thres = 95,
|
||||
.user_cap_limit = 15,
|
||||
.maint_thres = 97,
|
||||
};
|
||||
|
||||
static const struct abx500_maxim_parameters maxi_params = {
|
||||
@ -424,96 +424,84 @@ static const struct abx500_bm_charger_parameters chg = {
|
||||
};
|
||||
|
||||
struct abx500_bm_data ab8500_bm_data = {
|
||||
.temp_under = 3,
|
||||
.temp_low = 8,
|
||||
.temp_high = 43,
|
||||
.temp_over = 48,
|
||||
.main_safety_tmr_h = 4,
|
||||
.temp_interval_chg = 20,
|
||||
.temp_interval_nochg = 120,
|
||||
.usb_safety_tmr_h = 4,
|
||||
.bkup_bat_v = BUP_VCH_SEL_2P6V,
|
||||
.bkup_bat_i = BUP_ICH_SEL_150UA,
|
||||
.no_maintenance = false,
|
||||
.adc_therm = ABx500_ADC_THERM_BATCTRL,
|
||||
.chg_unknown_bat = false,
|
||||
.enable_overshoot = false,
|
||||
.fg_res = 100,
|
||||
.cap_levels = &cap_levels,
|
||||
.bat_type = bat_type_thermistor,
|
||||
.n_btypes = 3,
|
||||
.batt_id = 0,
|
||||
.interval_charging = 5,
|
||||
.interval_not_charging = 120,
|
||||
.temp_hysteresis = 3,
|
||||
.gnd_lift_resistance = 34,
|
||||
.maxi = &maxi_params,
|
||||
.chg_params = &chg,
|
||||
.fg_params = &fg,
|
||||
.temp_under = 3,
|
||||
.temp_low = 8,
|
||||
.temp_high = 43,
|
||||
.temp_over = 48,
|
||||
.main_safety_tmr_h = 4,
|
||||
.temp_interval_chg = 20,
|
||||
.temp_interval_nochg = 120,
|
||||
.usb_safety_tmr_h = 4,
|
||||
.bkup_bat_v = BUP_VCH_SEL_2P6V,
|
||||
.bkup_bat_i = BUP_ICH_SEL_150UA,
|
||||
.no_maintenance = false,
|
||||
.capacity_scaling = false,
|
||||
.adc_therm = ABx500_ADC_THERM_BATCTRL,
|
||||
.chg_unknown_bat = false,
|
||||
.enable_overshoot = false,
|
||||
.fg_res = 100,
|
||||
.cap_levels = &cap_levels,
|
||||
.bat_type = bat_type_thermistor,
|
||||
.n_btypes = 3,
|
||||
.batt_id = 0,
|
||||
.interval_charging = 5,
|
||||
.interval_not_charging = 120,
|
||||
.temp_hysteresis = 3,
|
||||
.gnd_lift_resistance = 34,
|
||||
.maxi = &maxi_params,
|
||||
.chg_params = &chg,
|
||||
.fg_params = &fg,
|
||||
};
|
||||
|
||||
int bmdevs_of_probe(struct device *dev, struct device_node *np,
|
||||
struct abx500_bm_data **battery)
|
||||
int ab8500_bm_of_probe(struct device *dev,
|
||||
struct device_node *np,
|
||||
struct abx500_bm_data *bm)
|
||||
{
|
||||
struct abx500_battery_type *btype;
|
||||
struct device_node *np_bat_supply;
|
||||
struct abx500_bm_data *bat;
|
||||
struct batres_vs_temp *tmp_batres_tbl;
|
||||
struct device_node *battery_node;
|
||||
const char *btech;
|
||||
char bat_tech[8];
|
||||
int i, thermistor;
|
||||
|
||||
*battery = &ab8500_bm_data;
|
||||
int i;
|
||||
|
||||
/* get phandle to 'battery-info' node */
|
||||
np_bat_supply = of_parse_phandle(np, "battery", 0);
|
||||
if (!np_bat_supply) {
|
||||
dev_err(dev, "missing property battery\n");
|
||||
battery_node = of_parse_phandle(np, "battery", 0);
|
||||
if (!battery_node) {
|
||||
dev_err(dev, "battery node or reference missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (of_property_read_bool(np_bat_supply,
|
||||
"thermistor-on-batctrl"))
|
||||
thermistor = NTC_INTERNAL;
|
||||
else
|
||||
thermistor = NTC_EXTERNAL;
|
||||
|
||||
bat = *battery;
|
||||
if (thermistor == NTC_EXTERNAL) {
|
||||
bat->n_btypes = 4;
|
||||
bat->bat_type = bat_type_ext_thermistor;
|
||||
bat->adc_therm = ABx500_ADC_THERM_BATTEMP;
|
||||
}
|
||||
btech = of_get_property(np_bat_supply,
|
||||
"stericsson,battery-type", NULL);
|
||||
btech = of_get_property(battery_node, "stericsson,battery-type", NULL);
|
||||
if (!btech) {
|
||||
dev_warn(dev, "missing property battery-name/type\n");
|
||||
strcpy(bat_tech, "UNKNOWN");
|
||||
} else {
|
||||
strcpy(bat_tech, btech);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (strncmp(bat_tech, "LION", 4) == 0) {
|
||||
bat->no_maintenance = true;
|
||||
bat->chg_unknown_bat = true;
|
||||
bat->bat_type[BATTERY_UNKNOWN].charge_full_design = 2600;
|
||||
bat->bat_type[BATTERY_UNKNOWN].termination_vol = 4150;
|
||||
bat->bat_type[BATTERY_UNKNOWN].recharge_vol = 4130;
|
||||
bat->bat_type[BATTERY_UNKNOWN].normal_cur_lvl = 520;
|
||||
bat->bat_type[BATTERY_UNKNOWN].normal_vol_lvl = 4200;
|
||||
if (strncmp(btech, "LION", 4) == 0) {
|
||||
bm->no_maintenance = true;
|
||||
bm->chg_unknown_bat = true;
|
||||
bm->bat_type[BATTERY_UNKNOWN].charge_full_design = 2600;
|
||||
bm->bat_type[BATTERY_UNKNOWN].termination_vol = 4150;
|
||||
bm->bat_type[BATTERY_UNKNOWN].recharge_cap = 95;
|
||||
bm->bat_type[BATTERY_UNKNOWN].normal_cur_lvl = 520;
|
||||
bm->bat_type[BATTERY_UNKNOWN].normal_vol_lvl = 4200;
|
||||
}
|
||||
|
||||
if (of_property_read_bool(battery_node, "thermistor-on-batctrl")) {
|
||||
if (strncmp(btech, "LION", 4) == 0)
|
||||
tmp_batres_tbl = temp_to_batres_tbl_9100;
|
||||
else
|
||||
tmp_batres_tbl = temp_to_batres_tbl_thermistor;
|
||||
} else {
|
||||
bm->n_btypes = 4;
|
||||
bm->bat_type = bat_type_ext_thermistor;
|
||||
bm->adc_therm = ABx500_ADC_THERM_BATTEMP;
|
||||
tmp_batres_tbl = temp_to_batres_tbl_ext_thermistor;
|
||||
}
|
||||
|
||||
/* select the battery resolution table */
|
||||
for (i = 0; i < bat->n_btypes; ++i) {
|
||||
btype = (bat->bat_type + i);
|
||||
if (thermistor == NTC_EXTERNAL) {
|
||||
btype->batres_tbl =
|
||||
temp_to_batres_tbl_ext_thermistor;
|
||||
} else if (strncmp(bat_tech, "LION", 4) == 0) {
|
||||
btype->batres_tbl =
|
||||
temp_to_batres_tbl_9100;
|
||||
} else {
|
||||
btype->batres_tbl =
|
||||
temp_to_batres_tbl_thermistor;
|
||||
}
|
||||
}
|
||||
of_node_put(np_bat_supply);
|
||||
for (i = 0; i < bm->n_btypes; ++i)
|
||||
bm->bat_type[i].batres_tbl = tmp_batres_tbl;
|
||||
|
||||
of_node_put(battery_node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -39,6 +39,9 @@
|
||||
#define BTEMP_BATCTRL_CURR_SRC_7UA 7
|
||||
#define BTEMP_BATCTRL_CURR_SRC_20UA 20
|
||||
|
||||
#define BTEMP_BATCTRL_CURR_SRC_16UA 16
|
||||
#define BTEMP_BATCTRL_CURR_SRC_18UA 18
|
||||
|
||||
#define to_ab8500_btemp_device_info(x) container_of((x), \
|
||||
struct ab8500_btemp, btemp_psy);
|
||||
|
||||
@ -78,12 +81,13 @@ struct ab8500_btemp_ranges {
|
||||
* @parent: Pointer to the struct ab8500
|
||||
* @gpadc: Pointer to the struct gpadc
|
||||
* @fg: Pointer to the struct fg
|
||||
* @bat: Pointer to the abx500_bm platform data
|
||||
* @bm: Platform specific battery management information
|
||||
* @btemp_psy: Structure for BTEMP specific battery properties
|
||||
* @events: Structure for information about events triggered
|
||||
* @btemp_ranges: Battery temperature range structure
|
||||
* @btemp_wq: Work queue for measuring the temperature periodically
|
||||
* @btemp_periodic_work: Work for measuring the temperature periodically
|
||||
* @initialized: True if battery id read.
|
||||
*/
|
||||
struct ab8500_btemp {
|
||||
struct device *dev;
|
||||
@ -94,12 +98,13 @@ struct ab8500_btemp {
|
||||
struct ab8500 *parent;
|
||||
struct ab8500_gpadc *gpadc;
|
||||
struct ab8500_fg *fg;
|
||||
struct abx500_bm_data *bat;
|
||||
struct abx500_bm_data *bm;
|
||||
struct power_supply btemp_psy;
|
||||
struct ab8500_btemp_events events;
|
||||
struct ab8500_btemp_ranges btemp_ranges;
|
||||
struct workqueue_struct *btemp_wq;
|
||||
struct delayed_work btemp_periodic_work;
|
||||
bool initialized;
|
||||
};
|
||||
|
||||
/* BTEMP power supply properties */
|
||||
@ -147,13 +152,13 @@ static int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di,
|
||||
return (450000 * (v_batctrl)) / (1800 - v_batctrl);
|
||||
}
|
||||
|
||||
if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL) {
|
||||
if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL) {
|
||||
/*
|
||||
* If the battery has internal NTC, we use the current
|
||||
* source to calculate the resistance, 7uA or 20uA
|
||||
*/
|
||||
rbs = (v_batctrl * 1000
|
||||
- di->bat->gnd_lift_resistance * inst_curr)
|
||||
- di->bm->gnd_lift_resistance * inst_curr)
|
||||
/ di->curr_source;
|
||||
} else {
|
||||
/*
|
||||
@ -209,11 +214,19 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
|
||||
return 0;
|
||||
|
||||
/* Only do this for batteries with internal NTC */
|
||||
if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL && enable) {
|
||||
if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_7UA)
|
||||
curr = BAT_CTRL_7U_ENA;
|
||||
else
|
||||
curr = BAT_CTRL_20U_ENA;
|
||||
if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && enable) {
|
||||
|
||||
if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
|
||||
if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_16UA)
|
||||
curr = BAT_CTRL_16U_ENA;
|
||||
else
|
||||
curr = BAT_CTRL_18U_ENA;
|
||||
} else {
|
||||
if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_7UA)
|
||||
curr = BAT_CTRL_7U_ENA;
|
||||
else
|
||||
curr = BAT_CTRL_20U_ENA;
|
||||
}
|
||||
|
||||
dev_dbg(di->dev, "Set BATCTRL %duA\n", di->curr_source);
|
||||
|
||||
@ -241,14 +254,25 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
|
||||
__func__);
|
||||
goto disable_curr_source;
|
||||
}
|
||||
} else if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL && !enable) {
|
||||
} else if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && !enable) {
|
||||
dev_dbg(di->dev, "Disable BATCTRL curr source\n");
|
||||
|
||||
/* Write 0 to the curr bits */
|
||||
ret = abx500_mask_and_set_register_interruptible(di->dev,
|
||||
AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
|
||||
BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA,
|
||||
~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA));
|
||||
if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
|
||||
/* Write 0 to the curr bits */
|
||||
ret = abx500_mask_and_set_register_interruptible(
|
||||
di->dev,
|
||||
AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
|
||||
BAT_CTRL_16U_ENA | BAT_CTRL_18U_ENA,
|
||||
~(BAT_CTRL_16U_ENA | BAT_CTRL_18U_ENA));
|
||||
} else {
|
||||
/* Write 0 to the curr bits */
|
||||
ret = abx500_mask_and_set_register_interruptible(
|
||||
di->dev,
|
||||
AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
|
||||
BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA,
|
||||
~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA));
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
dev_err(di->dev, "%s failed disabling current source\n",
|
||||
__func__);
|
||||
@ -290,11 +314,20 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
|
||||
* if we got an error above
|
||||
*/
|
||||
disable_curr_source:
|
||||
/* Write 0 to the curr bits */
|
||||
ret = abx500_mask_and_set_register_interruptible(di->dev,
|
||||
if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
|
||||
/* Write 0 to the curr bits */
|
||||
ret = abx500_mask_and_set_register_interruptible(di->dev,
|
||||
AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
|
||||
BAT_CTRL_16U_ENA | BAT_CTRL_18U_ENA,
|
||||
~(BAT_CTRL_16U_ENA | BAT_CTRL_18U_ENA));
|
||||
} else {
|
||||
/* Write 0 to the curr bits */
|
||||
ret = abx500_mask_and_set_register_interruptible(di->dev,
|
||||
AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
|
||||
BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA,
|
||||
~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA));
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
dev_err(di->dev, "%s failed disabling current source\n",
|
||||
__func__);
|
||||
@ -372,13 +405,10 @@ static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Since there is no interrupt when current measurement is done,
|
||||
* loop for over 250ms (250ms is one sample conversion time
|
||||
* with 32.768 Khz RTC clock). Note that a stop time must be set
|
||||
* since the ab8500_btemp_read_batctrl_voltage call can block and
|
||||
* take an unknown amount of time to complete.
|
||||
*/
|
||||
do {
|
||||
msleep(20);
|
||||
} while (!ab8500_fg_inst_curr_started(di->fg));
|
||||
|
||||
i = 0;
|
||||
|
||||
do {
|
||||
@ -457,9 +487,9 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
|
||||
int rbat, rntc, vntc;
|
||||
u8 id;
|
||||
|
||||
id = di->bat->batt_id;
|
||||
id = di->bm->batt_id;
|
||||
|
||||
if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL &&
|
||||
if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL &&
|
||||
id != BATTERY_UNKNOWN) {
|
||||
|
||||
rbat = ab8500_btemp_get_batctrl_res(di);
|
||||
@ -474,8 +504,8 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
|
||||
}
|
||||
|
||||
temp = ab8500_btemp_res_to_temp(di,
|
||||
di->bat->bat_type[id].r_to_t_tbl,
|
||||
di->bat->bat_type[id].n_temp_tbl_elements, rbat);
|
||||
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) {
|
||||
@ -491,8 +521,8 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
|
||||
rntc = 230000 * vntc / (VTVOUT_V - vntc);
|
||||
|
||||
temp = ab8500_btemp_res_to_temp(di,
|
||||
di->bat->bat_type[id].r_to_t_tbl,
|
||||
di->bat->bat_type[id].n_temp_tbl_elements, rntc);
|
||||
di->bm->bat_type[id].r_to_t_tbl,
|
||||
di->bm->bat_type[id].n_temp_tbl_elements, rntc);
|
||||
prev = temp;
|
||||
}
|
||||
dev_dbg(di->dev, "Battery temperature is %d\n", temp);
|
||||
@ -511,9 +541,12 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
|
||||
{
|
||||
int res;
|
||||
u8 i;
|
||||
if (is_ab9540(di->parent) || is_ab8505(di->parent))
|
||||
di->curr_source = BTEMP_BATCTRL_CURR_SRC_16UA;
|
||||
else
|
||||
di->curr_source = BTEMP_BATCTRL_CURR_SRC_7UA;
|
||||
|
||||
di->curr_source = BTEMP_BATCTRL_CURR_SRC_7UA;
|
||||
di->bat->batt_id = BATTERY_UNKNOWN;
|
||||
di->bm->batt_id = BATTERY_UNKNOWN;
|
||||
|
||||
res = ab8500_btemp_get_batctrl_res(di);
|
||||
if (res < 0) {
|
||||
@ -522,23 +555,23 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
|
||||
}
|
||||
|
||||
/* BATTERY_UNKNOWN is defined on position 0, skip it! */
|
||||
for (i = BATTERY_UNKNOWN + 1; i < di->bat->n_btypes; i++) {
|
||||
if ((res <= di->bat->bat_type[i].resis_high) &&
|
||||
(res >= di->bat->bat_type[i].resis_low)) {
|
||||
for (i = BATTERY_UNKNOWN + 1; i < di->bm->n_btypes; i++) {
|
||||
if ((res <= di->bm->bat_type[i].resis_high) &&
|
||||
(res >= di->bm->bat_type[i].resis_low)) {
|
||||
dev_dbg(di->dev, "Battery detected on %s"
|
||||
" low %d < res %d < high: %d"
|
||||
" index: %d\n",
|
||||
di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL ?
|
||||
di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL ?
|
||||
"BATCTRL" : "BATTEMP",
|
||||
di->bat->bat_type[i].resis_low, res,
|
||||
di->bat->bat_type[i].resis_high, i);
|
||||
di->bm->bat_type[i].resis_low, res,
|
||||
di->bm->bat_type[i].resis_high, i);
|
||||
|
||||
di->bat->batt_id = i;
|
||||
di->bm->batt_id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (di->bat->batt_id == BATTERY_UNKNOWN) {
|
||||
if (di->bm->batt_id == BATTERY_UNKNOWN) {
|
||||
dev_warn(di->dev, "Battery identified as unknown"
|
||||
", resistance %d Ohm\n", res);
|
||||
return -ENXIO;
|
||||
@ -548,13 +581,18 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
|
||||
* We only have to change current source if the
|
||||
* detected type is Type 1, else we use the 7uA source
|
||||
*/
|
||||
if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL &&
|
||||
di->bat->batt_id == 1) {
|
||||
dev_dbg(di->dev, "Set BATCTRL current source to 20uA\n");
|
||||
di->curr_source = BTEMP_BATCTRL_CURR_SRC_20UA;
|
||||
if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL &&
|
||||
di->bm->batt_id == 1) {
|
||||
if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
|
||||
dev_dbg(di->dev, "Set BATCTRL current source to 16uA\n");
|
||||
di->curr_source = BTEMP_BATCTRL_CURR_SRC_16UA;
|
||||
} else {
|
||||
dev_dbg(di->dev, "Set BATCTRL current source to 20uA\n");
|
||||
di->curr_source = BTEMP_BATCTRL_CURR_SRC_20UA;
|
||||
}
|
||||
}
|
||||
|
||||
return di->bat->batt_id;
|
||||
return di->bm->batt_id;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -569,6 +607,13 @@ static void ab8500_btemp_periodic_work(struct work_struct *work)
|
||||
struct ab8500_btemp *di = container_of(work,
|
||||
struct ab8500_btemp, btemp_periodic_work.work);
|
||||
|
||||
if (!di->initialized) {
|
||||
di->initialized = true;
|
||||
/* Identify the battery */
|
||||
if (ab8500_btemp_id(di) < 0)
|
||||
dev_warn(di->dev, "failed to identify the battery\n");
|
||||
}
|
||||
|
||||
di->bat_temp = ab8500_btemp_measure_temp(di);
|
||||
|
||||
if (di->bat_temp != di->prev_bat_temp) {
|
||||
@ -577,9 +622,9 @@ static void ab8500_btemp_periodic_work(struct work_struct *work)
|
||||
}
|
||||
|
||||
if (di->events.ac_conn || di->events.usb_conn)
|
||||
interval = di->bat->temp_interval_chg;
|
||||
interval = di->bm->temp_interval_chg;
|
||||
else
|
||||
interval = di->bat->temp_interval_nochg;
|
||||
interval = di->bm->temp_interval_nochg;
|
||||
|
||||
/* Schedule a new measurement */
|
||||
queue_delayed_work(di->btemp_wq,
|
||||
@ -616,9 +661,9 @@ static irqreturn_t ab8500_btemp_templow_handler(int irq, void *_di)
|
||||
{
|
||||
struct ab8500_btemp *di = _di;
|
||||
|
||||
if (is_ab8500_2p0_or_earlier(di->parent)) {
|
||||
if (is_ab8500_3p3_or_earlier(di->parent)) {
|
||||
dev_dbg(di->dev, "Ignore false btemp low irq"
|
||||
" for ABB cut 1.0, 1.1 and 2.0\n");
|
||||
" for ABB cut 1.0, 1.1, 2.0 and 3.3\n");
|
||||
} else {
|
||||
dev_crit(di->dev, "Battery temperature lower than -10deg c\n");
|
||||
|
||||
@ -732,30 +777,30 @@ static int ab8500_btemp_get_temp(struct ab8500_btemp *di)
|
||||
int temp = 0;
|
||||
|
||||
/*
|
||||
* The BTEMP events are not reliabe on AB8500 cut2.0
|
||||
* The BTEMP events are not reliabe on AB8500 cut3.3
|
||||
* and prior versions
|
||||
*/
|
||||
if (is_ab8500_2p0_or_earlier(di->parent)) {
|
||||
if (is_ab8500_3p3_or_earlier(di->parent)) {
|
||||
temp = di->bat_temp * 10;
|
||||
} else {
|
||||
if (di->events.btemp_low) {
|
||||
if (temp > di->btemp_ranges.btemp_low_limit)
|
||||
temp = di->btemp_ranges.btemp_low_limit;
|
||||
temp = di->btemp_ranges.btemp_low_limit * 10;
|
||||
else
|
||||
temp = di->bat_temp * 10;
|
||||
} else if (di->events.btemp_high) {
|
||||
if (temp < di->btemp_ranges.btemp_high_limit)
|
||||
temp = di->btemp_ranges.btemp_high_limit;
|
||||
temp = di->btemp_ranges.btemp_high_limit * 10;
|
||||
else
|
||||
temp = di->bat_temp * 10;
|
||||
} else if (di->events.btemp_lowmed) {
|
||||
if (temp > di->btemp_ranges.btemp_med_limit)
|
||||
temp = di->btemp_ranges.btemp_med_limit;
|
||||
temp = di->btemp_ranges.btemp_med_limit * 10;
|
||||
else
|
||||
temp = di->bat_temp * 10;
|
||||
} else if (di->events.btemp_medhigh) {
|
||||
if (temp < di->btemp_ranges.btemp_med_limit)
|
||||
temp = di->btemp_ranges.btemp_med_limit;
|
||||
temp = di->btemp_ranges.btemp_med_limit * 10;
|
||||
else
|
||||
temp = di->bat_temp * 10;
|
||||
} else
|
||||
@ -806,7 +851,7 @@ static int ab8500_btemp_get_property(struct power_supply *psy,
|
||||
val->intval = 1;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TECHNOLOGY:
|
||||
val->intval = di->bat->bat_type[di->bat->batt_id].name;
|
||||
val->intval = di->bm->bat_type[di->bm->batt_id].name;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TEMP:
|
||||
val->intval = ab8500_btemp_get_temp(di);
|
||||
@ -967,6 +1012,7 @@ static char *supply_interface[] = {
|
||||
static int ab8500_btemp_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct abx500_bm_data *plat = pdev->dev.platform_data;
|
||||
struct ab8500_btemp *di;
|
||||
int irq, i, ret = 0;
|
||||
u8 val;
|
||||
@ -976,21 +1022,19 @@ static int ab8500_btemp_probe(struct platform_device *pdev)
|
||||
dev_err(&pdev->dev, "%s no mem for ab8500_btemp\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
di->bat = pdev->mfd_cell->platform_data;
|
||||
if (!di->bat) {
|
||||
if (np) {
|
||||
ret = bmdevs_of_probe(&pdev->dev, np, &di->bat);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to get battery information\n");
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
dev_err(&pdev->dev, "missing dt node for ab8500_btemp\n");
|
||||
return -EINVAL;
|
||||
|
||||
if (!plat) {
|
||||
dev_err(&pdev->dev, "no battery management data supplied\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
di->bm = plat;
|
||||
|
||||
if (np) {
|
||||
ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to get battery information\n");
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
dev_info(&pdev->dev, "falling back to legacy platform data\n");
|
||||
}
|
||||
|
||||
/* get parent data */
|
||||
@ -998,6 +1042,8 @@ static int ab8500_btemp_probe(struct platform_device *pdev)
|
||||
di->parent = dev_get_drvdata(pdev->dev.parent);
|
||||
di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
|
||||
|
||||
di->initialized = false;
|
||||
|
||||
/* BTEMP supply */
|
||||
di->btemp_psy.name = "ab8500_btemp";
|
||||
di->btemp_psy.type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
@ -1022,10 +1068,6 @@ static int ab8500_btemp_probe(struct platform_device *pdev)
|
||||
INIT_DEFERRABLE_WORK(&di->btemp_periodic_work,
|
||||
ab8500_btemp_periodic_work);
|
||||
|
||||
/* Identify the battery */
|
||||
if (ab8500_btemp_id(di) < 0)
|
||||
dev_warn(di->dev, "failed to identify the battery\n");
|
||||
|
||||
/* Set BTEMP thermal limits. Low and Med are fixed */
|
||||
di->btemp_ranges.btemp_low_limit = BTEMP_THERMAL_LOW_LIMIT;
|
||||
di->btemp_ranges.btemp_med_limit = BTEMP_THERMAL_MED_LIMIT;
|
||||
@ -1123,7 +1165,7 @@ static void __exit ab8500_btemp_exit(void)
|
||||
platform_driver_unregister(&ab8500_btemp_driver);
|
||||
}
|
||||
|
||||
subsys_initcall_sync(ab8500_btemp_init);
|
||||
device_initcall(ab8500_btemp_init);
|
||||
module_exit(ab8500_btemp_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -32,6 +32,7 @@
|
||||
#include <linux/mfd/abx500/ab8500.h>
|
||||
#include <linux/mfd/abx500/ab8500-bm.h>
|
||||
#include <linux/mfd/abx500/ab8500-gpadc.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#define MILLI_TO_MICRO 1000
|
||||
#define FG_LSB_IN_MA 1627
|
||||
@ -42,7 +43,7 @@
|
||||
|
||||
#define NBR_AVG_SAMPLES 20
|
||||
|
||||
#define LOW_BAT_CHECK_INTERVAL (2 * HZ)
|
||||
#define LOW_BAT_CHECK_INTERVAL (HZ / 16) /* 62.5 ms */
|
||||
|
||||
#define VALID_CAPACITY_SEC (45 * 60) /* 45 minutes */
|
||||
#define BATT_OK_MIN 2360 /* mV */
|
||||
@ -113,6 +114,13 @@ struct ab8500_fg_avg_cap {
|
||||
int sum;
|
||||
};
|
||||
|
||||
struct ab8500_fg_cap_scaling {
|
||||
bool enable;
|
||||
int cap_to_scale[2];
|
||||
int disable_cap_level;
|
||||
int scaled_cap;
|
||||
};
|
||||
|
||||
struct ab8500_fg_battery_capacity {
|
||||
int max_mah_design;
|
||||
int max_mah;
|
||||
@ -123,6 +131,7 @@ struct ab8500_fg_battery_capacity {
|
||||
int prev_percent;
|
||||
int prev_level;
|
||||
int user_mah;
|
||||
struct ab8500_fg_cap_scaling cap_scale;
|
||||
};
|
||||
|
||||
struct ab8500_fg_flags {
|
||||
@ -160,6 +169,8 @@ struct inst_curr_result_list {
|
||||
* @recovery_cnt: Counter for recovery mode
|
||||
* @high_curr_cnt: Counter for high current mode
|
||||
* @init_cnt: Counter for init mode
|
||||
* @low_bat_cnt Counter for number of consecutive low battery measures
|
||||
* @nbr_cceoc_irq_cnt Counter for number of CCEOC irqs received since enabled
|
||||
* @recovery_needed: Indicate if recovery is needed
|
||||
* @high_curr_mode: Indicate if we're in high current mode
|
||||
* @init_capacity: Indicate if initial capacity measuring should be done
|
||||
@ -167,13 +178,14 @@ struct inst_curr_result_list {
|
||||
* @calib_state State during offset calibration
|
||||
* @discharge_state: Current discharge state
|
||||
* @charge_state: Current charge state
|
||||
* @ab8500_fg_started Completion struct used for the instant current start
|
||||
* @ab8500_fg_complete Completion struct used for the instant current reading
|
||||
* @flags: Structure for information about events triggered
|
||||
* @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
|
||||
* @bat: Pointer to the abx500_bm platform data
|
||||
* @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
|
||||
* @fg_periodic_work: Work to run the FG algorithm periodically
|
||||
@ -199,6 +211,8 @@ struct ab8500_fg {
|
||||
int recovery_cnt;
|
||||
int high_curr_cnt;
|
||||
int init_cnt;
|
||||
int low_bat_cnt;
|
||||
int nbr_cceoc_irq_cnt;
|
||||
bool recovery_needed;
|
||||
bool high_curr_mode;
|
||||
bool init_capacity;
|
||||
@ -206,13 +220,14 @@ struct ab8500_fg {
|
||||
enum ab8500_fg_calibration_state calib_state;
|
||||
enum ab8500_fg_discharge_state discharge_state;
|
||||
enum ab8500_fg_charge_state charge_state;
|
||||
struct completion ab8500_fg_started;
|
||||
struct completion ab8500_fg_complete;
|
||||
struct ab8500_fg_flags flags;
|
||||
struct ab8500_fg_battery_capacity bat_cap;
|
||||
struct ab8500_fg_avg_cap avg_cap;
|
||||
struct ab8500 *parent;
|
||||
struct ab8500_gpadc *gpadc;
|
||||
struct abx500_bm_data *bat;
|
||||
struct abx500_bm_data *bm;
|
||||
struct power_supply fg_psy;
|
||||
struct workqueue_struct *fg_wq;
|
||||
struct delayed_work fg_periodic_work;
|
||||
@ -355,7 +370,7 @@ static int ab8500_fg_is_low_curr(struct ab8500_fg *di, int curr)
|
||||
/*
|
||||
* We want to know if we're in low current mode
|
||||
*/
|
||||
if (curr > -di->bat->fg_params->high_curr_threshold)
|
||||
if (curr > -di->bm->fg_params->high_curr_threshold)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
@ -484,8 +499,9 @@ static int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable)
|
||||
di->flags.fg_enabled = true;
|
||||
} else {
|
||||
/* Clear any pending read requests */
|
||||
ret = abx500_set_register_interruptible(di->dev,
|
||||
AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 0);
|
||||
ret = abx500_mask_and_set_register_interruptible(di->dev,
|
||||
AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
|
||||
(RESET_ACCU | READ_REQ), 0);
|
||||
if (ret)
|
||||
goto cc_err;
|
||||
|
||||
@ -523,13 +539,14 @@ cc_err:
|
||||
* Note: This is part "one" and has to be called before
|
||||
* ab8500_fg_inst_curr_finalize()
|
||||
*/
|
||||
int ab8500_fg_inst_curr_start(struct ab8500_fg *di)
|
||||
int ab8500_fg_inst_curr_start(struct ab8500_fg *di)
|
||||
{
|
||||
u8 reg_val;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&di->cc_lock);
|
||||
|
||||
di->nbr_cceoc_irq_cnt = 0;
|
||||
ret = abx500_get_register_interruptible(di->dev, AB8500_RTC,
|
||||
AB8500_RTC_CC_CONF_REG, ®_val);
|
||||
if (ret < 0)
|
||||
@ -557,6 +574,7 @@ cc_err:
|
||||
}
|
||||
|
||||
/* Return and WFI */
|
||||
INIT_COMPLETION(di->ab8500_fg_started);
|
||||
INIT_COMPLETION(di->ab8500_fg_complete);
|
||||
enable_irq(di->irq);
|
||||
|
||||
@ -567,6 +585,17 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ab8500_fg_inst_curr_started() - check if fg conversion has started
|
||||
* @di: pointer to the ab8500_fg structure
|
||||
*
|
||||
* Returns 1 if conversion started, 0 if still waiting
|
||||
*/
|
||||
int ab8500_fg_inst_curr_started(struct ab8500_fg *di)
|
||||
{
|
||||
return completion_done(&di->ab8500_fg_started);
|
||||
}
|
||||
|
||||
/**
|
||||
* ab8500_fg_inst_curr_done() - check if fg conversion is done
|
||||
* @di: pointer to the ab8500_fg structure
|
||||
@ -595,13 +624,15 @@ int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res)
|
||||
int timeout;
|
||||
|
||||
if (!completion_done(&di->ab8500_fg_complete)) {
|
||||
timeout = wait_for_completion_timeout(&di->ab8500_fg_complete,
|
||||
timeout = wait_for_completion_timeout(
|
||||
&di->ab8500_fg_complete,
|
||||
INS_CURR_TIMEOUT);
|
||||
dev_dbg(di->dev, "Finalize time: %d ms\n",
|
||||
((INS_CURR_TIMEOUT - timeout) * 1000) / HZ);
|
||||
if (!timeout) {
|
||||
ret = -ETIME;
|
||||
disable_irq(di->irq);
|
||||
di->nbr_cceoc_irq_cnt = 0;
|
||||
dev_err(di->dev, "completion timed out [%d]\n",
|
||||
__LINE__);
|
||||
goto fail;
|
||||
@ -609,6 +640,7 @@ int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res)
|
||||
}
|
||||
|
||||
disable_irq(di->irq);
|
||||
di->nbr_cceoc_irq_cnt = 0;
|
||||
|
||||
ret = abx500_mask_and_set_register_interruptible(di->dev,
|
||||
AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
|
||||
@ -647,7 +679,7 @@ int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res)
|
||||
* 112.9nAh assumes 10mOhm, but fg_res is in 0.1mOhm
|
||||
*/
|
||||
val = (val * QLSB_NANO_AMP_HOURS_X10 * 36 * 4) /
|
||||
(1000 * di->bat->fg_res);
|
||||
(1000 * di->bm->fg_res);
|
||||
|
||||
if (di->turn_off_fg) {
|
||||
dev_dbg(di->dev, "%s Disable FG\n", __func__);
|
||||
@ -683,6 +715,7 @@ fail:
|
||||
int ab8500_fg_inst_curr_blocking(struct ab8500_fg *di)
|
||||
{
|
||||
int ret;
|
||||
int timeout;
|
||||
int res = 0;
|
||||
|
||||
ret = ab8500_fg_inst_curr_start(di);
|
||||
@ -691,13 +724,33 @@ int ab8500_fg_inst_curr_blocking(struct ab8500_fg *di)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Wait for CC to actually start */
|
||||
if (!completion_done(&di->ab8500_fg_started)) {
|
||||
timeout = wait_for_completion_timeout(
|
||||
&di->ab8500_fg_started,
|
||||
INS_CURR_TIMEOUT);
|
||||
dev_dbg(di->dev, "Start time: %d ms\n",
|
||||
((INS_CURR_TIMEOUT - timeout) * 1000) / HZ);
|
||||
if (!timeout) {
|
||||
ret = -ETIME;
|
||||
dev_err(di->dev, "completion timed out [%d]\n",
|
||||
__LINE__);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
ret = ab8500_fg_inst_curr_finalize(di, &res);
|
||||
if (ret) {
|
||||
dev_err(di->dev, "Failed to finalize fg_inst\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_dbg(di->dev, "%s instant current: %d", __func__, res);
|
||||
return res;
|
||||
fail:
|
||||
disable_irq(di->irq);
|
||||
mutex_unlock(&di->cc_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -750,19 +803,16 @@ static void ab8500_fg_acc_cur_work(struct work_struct *work)
|
||||
* 112.9nAh assumes 10mOhm, but fg_res is in 0.1mOhm
|
||||
*/
|
||||
di->accu_charge = (val * QLSB_NANO_AMP_HOURS_X10) /
|
||||
(100 * di->bat->fg_res);
|
||||
(100 * di->bm->fg_res);
|
||||
|
||||
/*
|
||||
* Convert to unit value in mA
|
||||
* Full scale input voltage is
|
||||
* 66.660mV => LSB = 66.660mV/(4096*res) = 1.627mA
|
||||
* Given a 250ms conversion cycle time the LSB corresponds
|
||||
* to 112.9 nAh. Convert to current by dividing by the conversion
|
||||
* by dividing by the conversion
|
||||
* time in hours (= samples / (3600 * 4)h)
|
||||
* 112.9nAh assumes 10mOhm, but fg_res is in 0.1mOhm
|
||||
* and multiply with 1000
|
||||
*/
|
||||
di->avg_curr = (val * QLSB_NANO_AMP_HOURS_X10 * 36) /
|
||||
(1000 * di->bat->fg_res * (di->fg_samples / 4));
|
||||
(1000 * di->bm->fg_res * (di->fg_samples / 4));
|
||||
|
||||
di->flags.conv_done = true;
|
||||
|
||||
@ -770,6 +820,8 @@ static void ab8500_fg_acc_cur_work(struct work_struct *work)
|
||||
|
||||
queue_work(di->fg_wq, &di->fg_work);
|
||||
|
||||
dev_dbg(di->dev, "fg_res: %d, fg_samples: %d, gasg: %d, accu_charge: %d \n",
|
||||
di->bm->fg_res, di->fg_samples, val, di->accu_charge);
|
||||
return;
|
||||
exit:
|
||||
dev_err(di->dev,
|
||||
@ -814,8 +866,8 @@ static int ab8500_fg_volt_to_capacity(struct ab8500_fg *di, int voltage)
|
||||
struct abx500_v_to_cap *tbl;
|
||||
int cap = 0;
|
||||
|
||||
tbl = di->bat->bat_type[di->bat->batt_id].v_to_cap_tbl,
|
||||
tbl_size = di->bat->bat_type[di->bat->batt_id].n_v_cap_tbl_elements;
|
||||
tbl = di->bm->bat_type[di->bm->batt_id].v_to_cap_tbl,
|
||||
tbl_size = di->bm->bat_type[di->bm->batt_id].n_v_cap_tbl_elements;
|
||||
|
||||
for (i = 0; i < tbl_size; ++i) {
|
||||
if (voltage > tbl[i].voltage)
|
||||
@ -866,8 +918,8 @@ static int ab8500_fg_battery_resistance(struct ab8500_fg *di)
|
||||
struct batres_vs_temp *tbl;
|
||||
int resist = 0;
|
||||
|
||||
tbl = di->bat->bat_type[di->bat->batt_id].batres_tbl;
|
||||
tbl_size = di->bat->bat_type[di->bat->batt_id].n_batres_tbl_elements;
|
||||
tbl = di->bm->bat_type[di->bm->batt_id].batres_tbl;
|
||||
tbl_size = di->bm->bat_type[di->bm->batt_id].n_batres_tbl_elements;
|
||||
|
||||
for (i = 0; i < tbl_size; ++i) {
|
||||
if (di->bat_temp / 10 > tbl[i].temp)
|
||||
@ -888,11 +940,11 @@ static int ab8500_fg_battery_resistance(struct ab8500_fg *di)
|
||||
|
||||
dev_dbg(di->dev, "%s Temp: %d battery internal resistance: %d"
|
||||
" fg resistance %d, total: %d (mOhm)\n",
|
||||
__func__, di->bat_temp, resist, di->bat->fg_res / 10,
|
||||
(di->bat->fg_res / 10) + resist);
|
||||
__func__, di->bat_temp, resist, di->bm->fg_res / 10,
|
||||
(di->bm->fg_res / 10) + resist);
|
||||
|
||||
/* fg_res variable is in 0.1mOhm */
|
||||
resist += di->bat->fg_res / 10;
|
||||
resist += di->bm->fg_res / 10;
|
||||
|
||||
return resist;
|
||||
}
|
||||
@ -915,7 +967,7 @@ static int ab8500_fg_load_comp_volt_to_capacity(struct ab8500_fg *di)
|
||||
do {
|
||||
vbat += ab8500_fg_bat_voltage(di);
|
||||
i++;
|
||||
msleep(5);
|
||||
usleep_range(5000, 6000);
|
||||
} while (!ab8500_fg_inst_curr_done(di));
|
||||
|
||||
ab8500_fg_inst_curr_finalize(di, &di->inst_curr);
|
||||
@ -1108,16 +1160,16 @@ static int ab8500_fg_capacity_level(struct ab8500_fg *di)
|
||||
{
|
||||
int ret, percent;
|
||||
|
||||
percent = di->bat_cap.permille / 10;
|
||||
percent = DIV_ROUND_CLOSEST(di->bat_cap.permille, 10);
|
||||
|
||||
if (percent <= di->bat->cap_levels->critical ||
|
||||
if (percent <= di->bm->cap_levels->critical ||
|
||||
di->flags.low_bat)
|
||||
ret = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
|
||||
else if (percent <= di->bat->cap_levels->low)
|
||||
else if (percent <= di->bm->cap_levels->low)
|
||||
ret = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
|
||||
else if (percent <= di->bat->cap_levels->normal)
|
||||
else if (percent <= di->bm->cap_levels->normal)
|
||||
ret = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
|
||||
else if (percent <= di->bat->cap_levels->high)
|
||||
else if (percent <= di->bm->cap_levels->high)
|
||||
ret = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
|
||||
else
|
||||
ret = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
|
||||
@ -1125,6 +1177,99 @@ static int ab8500_fg_capacity_level(struct ab8500_fg *di)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ab8500_fg_calculate_scaled_capacity() - Capacity scaling
|
||||
* @di: pointer to the ab8500_fg structure
|
||||
*
|
||||
* Calculates the capacity to be shown to upper layers. Scales the capacity
|
||||
* to have 100% as a reference from the actual capacity upon removal of charger
|
||||
* when charging is in maintenance mode.
|
||||
*/
|
||||
static int ab8500_fg_calculate_scaled_capacity(struct ab8500_fg *di)
|
||||
{
|
||||
struct ab8500_fg_cap_scaling *cs = &di->bat_cap.cap_scale;
|
||||
int capacity = di->bat_cap.prev_percent;
|
||||
|
||||
if (!cs->enable)
|
||||
return capacity;
|
||||
|
||||
/*
|
||||
* As long as we are in fully charge mode scale the capacity
|
||||
* to show 100%.
|
||||
*/
|
||||
if (di->flags.fully_charged) {
|
||||
cs->cap_to_scale[0] = 100;
|
||||
cs->cap_to_scale[1] =
|
||||
max(capacity, di->bm->fg_params->maint_thres);
|
||||
dev_dbg(di->dev, "Scale cap with %d/%d\n",
|
||||
cs->cap_to_scale[0], cs->cap_to_scale[1]);
|
||||
}
|
||||
|
||||
/* Calculates the scaled capacity. */
|
||||
if ((cs->cap_to_scale[0] != cs->cap_to_scale[1])
|
||||
&& (cs->cap_to_scale[1] > 0))
|
||||
capacity = min(100,
|
||||
DIV_ROUND_CLOSEST(di->bat_cap.prev_percent *
|
||||
cs->cap_to_scale[0],
|
||||
cs->cap_to_scale[1]));
|
||||
|
||||
if (di->flags.charging) {
|
||||
if (capacity < cs->disable_cap_level) {
|
||||
cs->disable_cap_level = capacity;
|
||||
dev_dbg(di->dev, "Cap to stop scale lowered %d%%\n",
|
||||
cs->disable_cap_level);
|
||||
} else if (!di->flags.fully_charged) {
|
||||
if (di->bat_cap.prev_percent >=
|
||||
cs->disable_cap_level) {
|
||||
dev_dbg(di->dev, "Disabling scaled capacity\n");
|
||||
cs->enable = false;
|
||||
capacity = di->bat_cap.prev_percent;
|
||||
} else {
|
||||
dev_dbg(di->dev,
|
||||
"Waiting in cap to level %d%%\n",
|
||||
cs->disable_cap_level);
|
||||
capacity = cs->disable_cap_level;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return capacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* ab8500_fg_update_cap_scalers() - Capacity scaling
|
||||
* @di: pointer to the ab8500_fg structure
|
||||
*
|
||||
* To be called when state change from charge<->discharge to update
|
||||
* the capacity scalers.
|
||||
*/
|
||||
static void ab8500_fg_update_cap_scalers(struct ab8500_fg *di)
|
||||
{
|
||||
struct ab8500_fg_cap_scaling *cs = &di->bat_cap.cap_scale;
|
||||
|
||||
if (!cs->enable)
|
||||
return;
|
||||
if (di->flags.charging) {
|
||||
di->bat_cap.cap_scale.disable_cap_level =
|
||||
di->bat_cap.cap_scale.scaled_cap;
|
||||
dev_dbg(di->dev, "Cap to stop scale at charge %d%%\n",
|
||||
di->bat_cap.cap_scale.disable_cap_level);
|
||||
} else {
|
||||
if (cs->scaled_cap != 100) {
|
||||
cs->cap_to_scale[0] = cs->scaled_cap;
|
||||
cs->cap_to_scale[1] = di->bat_cap.prev_percent;
|
||||
} else {
|
||||
cs->cap_to_scale[0] = 100;
|
||||
cs->cap_to_scale[1] =
|
||||
max(di->bat_cap.prev_percent,
|
||||
di->bm->fg_params->maint_thres);
|
||||
}
|
||||
|
||||
dev_dbg(di->dev, "Cap to scale at discharge %d/%d\n",
|
||||
cs->cap_to_scale[0], cs->cap_to_scale[1]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ab8500_fg_check_capacity_limits() - Check if capacity has changed
|
||||
* @di: pointer to the ab8500_fg structure
|
||||
@ -1136,6 +1281,7 @@ static int ab8500_fg_capacity_level(struct ab8500_fg *di)
|
||||
static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
|
||||
{
|
||||
bool changed = false;
|
||||
int percent = DIV_ROUND_CLOSEST(di->bat_cap.permille, 10);
|
||||
|
||||
di->bat_cap.level = ab8500_fg_capacity_level(di);
|
||||
|
||||
@ -1167,33 +1313,41 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
|
||||
dev_dbg(di->dev, "Battery low, set capacity to 0\n");
|
||||
di->bat_cap.prev_percent = 0;
|
||||
di->bat_cap.permille = 0;
|
||||
percent = 0;
|
||||
di->bat_cap.prev_mah = 0;
|
||||
di->bat_cap.mah = 0;
|
||||
changed = true;
|
||||
} else if (di->flags.fully_charged) {
|
||||
/*
|
||||
* We report 100% if algorithm reported fully charged
|
||||
* unless capacity drops too much
|
||||
* and show 100% during maintenance charging (scaling).
|
||||
*/
|
||||
if (di->flags.force_full) {
|
||||
di->bat_cap.prev_percent = di->bat_cap.permille / 10;
|
||||
di->bat_cap.prev_percent = percent;
|
||||
di->bat_cap.prev_mah = di->bat_cap.mah;
|
||||
} else if (!di->flags.force_full &&
|
||||
di->bat_cap.prev_percent !=
|
||||
(di->bat_cap.permille) / 10 &&
|
||||
(di->bat_cap.permille / 10) <
|
||||
di->bat->fg_params->maint_thres) {
|
||||
|
||||
changed = true;
|
||||
|
||||
if (!di->bat_cap.cap_scale.enable &&
|
||||
di->bm->capacity_scaling) {
|
||||
di->bat_cap.cap_scale.enable = true;
|
||||
di->bat_cap.cap_scale.cap_to_scale[0] = 100;
|
||||
di->bat_cap.cap_scale.cap_to_scale[1] =
|
||||
di->bat_cap.prev_percent;
|
||||
di->bat_cap.cap_scale.disable_cap_level = 100;
|
||||
}
|
||||
} else if (di->bat_cap.prev_percent != percent) {
|
||||
dev_dbg(di->dev,
|
||||
"battery reported full "
|
||||
"but capacity dropping: %d\n",
|
||||
di->bat_cap.permille / 10);
|
||||
di->bat_cap.prev_percent = di->bat_cap.permille / 10;
|
||||
percent);
|
||||
di->bat_cap.prev_percent = percent;
|
||||
di->bat_cap.prev_mah = di->bat_cap.mah;
|
||||
|
||||
changed = true;
|
||||
}
|
||||
} else if (di->bat_cap.prev_percent != di->bat_cap.permille / 10) {
|
||||
if (di->bat_cap.permille / 10 == 0) {
|
||||
} else if (di->bat_cap.prev_percent != percent) {
|
||||
if (percent == 0) {
|
||||
/*
|
||||
* We will not report 0% unless we've got
|
||||
* the LOW_BAT IRQ, no matter what the FG
|
||||
@ -1203,11 +1357,11 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
|
||||
di->bat_cap.permille = 1;
|
||||
di->bat_cap.prev_mah = 1;
|
||||
di->bat_cap.mah = 1;
|
||||
percent = 1;
|
||||
|
||||
changed = true;
|
||||
} else if (!(!di->flags.charging &&
|
||||
(di->bat_cap.permille / 10) >
|
||||
di->bat_cap.prev_percent) || init) {
|
||||
percent > di->bat_cap.prev_percent) || init) {
|
||||
/*
|
||||
* We do not allow reported capacity to go up
|
||||
* unless we're charging or if we're in init
|
||||
@ -1215,9 +1369,9 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
|
||||
dev_dbg(di->dev,
|
||||
"capacity changed from %d to %d (%d)\n",
|
||||
di->bat_cap.prev_percent,
|
||||
di->bat_cap.permille / 10,
|
||||
percent,
|
||||
di->bat_cap.permille);
|
||||
di->bat_cap.prev_percent = di->bat_cap.permille / 10;
|
||||
di->bat_cap.prev_percent = percent;
|
||||
di->bat_cap.prev_mah = di->bat_cap.mah;
|
||||
|
||||
changed = true;
|
||||
@ -1225,12 +1379,20 @@ static void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init)
|
||||
dev_dbg(di->dev, "capacity not allowed to go up since "
|
||||
"no charger is connected: %d to %d (%d)\n",
|
||||
di->bat_cap.prev_percent,
|
||||
di->bat_cap.permille / 10,
|
||||
percent,
|
||||
di->bat_cap.permille);
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
if (di->bm->capacity_scaling) {
|
||||
di->bat_cap.cap_scale.scaled_cap =
|
||||
ab8500_fg_calculate_scaled_capacity(di);
|
||||
|
||||
dev_info(di->dev, "capacity=%d (%d)\n",
|
||||
di->bat_cap.prev_percent,
|
||||
di->bat_cap.cap_scale.scaled_cap);
|
||||
}
|
||||
power_supply_changed(&di->fg_psy);
|
||||
if (di->flags.fully_charged && di->flags.force_full) {
|
||||
dev_dbg(di->dev, "Battery full, notifying.\n");
|
||||
@ -1284,7 +1446,7 @@ static void ab8500_fg_algorithm_charging(struct ab8500_fg *di)
|
||||
switch (di->charge_state) {
|
||||
case AB8500_FG_CHARGE_INIT:
|
||||
di->fg_samples = SEC_TO_SAMPLE(
|
||||
di->bat->fg_params->accu_charging);
|
||||
di->bm->fg_params->accu_charging);
|
||||
|
||||
ab8500_fg_coulomb_counter(di, true);
|
||||
ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_READOUT);
|
||||
@ -1296,7 +1458,7 @@ static void ab8500_fg_algorithm_charging(struct ab8500_fg *di)
|
||||
* Read the FG and calculate the new capacity
|
||||
*/
|
||||
mutex_lock(&di->cc_lock);
|
||||
if (!di->flags.conv_done) {
|
||||
if (!di->flags.conv_done && !di->flags.force_full) {
|
||||
/* Wasn't the CC IRQ that got us here */
|
||||
mutex_unlock(&di->cc_lock);
|
||||
dev_dbg(di->dev, "%s CC conv not done\n",
|
||||
@ -1346,8 +1508,8 @@ static bool check_sysfs_capacity(struct ab8500_fg *di)
|
||||
cap_permille = ab8500_fg_convert_mah_to_permille(di,
|
||||
di->bat_cap.user_mah);
|
||||
|
||||
lower = di->bat_cap.permille - di->bat->fg_params->user_cap_limit * 10;
|
||||
upper = di->bat_cap.permille + di->bat->fg_params->user_cap_limit * 10;
|
||||
lower = di->bat_cap.permille - di->bm->fg_params->user_cap_limit * 10;
|
||||
upper = di->bat_cap.permille + di->bm->fg_params->user_cap_limit * 10;
|
||||
|
||||
if (lower < 0)
|
||||
lower = 0;
|
||||
@ -1387,7 +1549,7 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
|
||||
case AB8500_FG_DISCHARGE_INIT:
|
||||
/* We use the FG IRQ to work on */
|
||||
di->init_cnt = 0;
|
||||
di->fg_samples = SEC_TO_SAMPLE(di->bat->fg_params->init_timer);
|
||||
di->fg_samples = SEC_TO_SAMPLE(di->bm->fg_params->init_timer);
|
||||
ab8500_fg_coulomb_counter(di, true);
|
||||
ab8500_fg_discharge_state_to(di,
|
||||
AB8500_FG_DISCHARGE_INITMEASURING);
|
||||
@ -1400,18 +1562,17 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
|
||||
* samples to get an initial capacity.
|
||||
* Then go to READOUT
|
||||
*/
|
||||
sleep_time = di->bat->fg_params->init_timer;
|
||||
sleep_time = di->bm->fg_params->init_timer;
|
||||
|
||||
/* Discard the first [x] seconds */
|
||||
if (di->init_cnt >
|
||||
di->bat->fg_params->init_discard_time) {
|
||||
if (di->init_cnt > di->bm->fg_params->init_discard_time) {
|
||||
ab8500_fg_calc_cap_discharge_voltage(di, true);
|
||||
|
||||
ab8500_fg_check_capacity_limits(di, true);
|
||||
}
|
||||
|
||||
di->init_cnt += sleep_time;
|
||||
if (di->init_cnt > di->bat->fg_params->init_total_time)
|
||||
if (di->init_cnt > di->bm->fg_params->init_total_time)
|
||||
ab8500_fg_discharge_state_to(di,
|
||||
AB8500_FG_DISCHARGE_READOUT_INIT);
|
||||
|
||||
@ -1426,7 +1587,7 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
|
||||
/* Intentional fallthrough */
|
||||
|
||||
case AB8500_FG_DISCHARGE_RECOVERY:
|
||||
sleep_time = di->bat->fg_params->recovery_sleep_timer;
|
||||
sleep_time = di->bm->fg_params->recovery_sleep_timer;
|
||||
|
||||
/*
|
||||
* We should check the power consumption
|
||||
@ -1438,9 +1599,9 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
|
||||
|
||||
if (ab8500_fg_is_low_curr(di, di->inst_curr)) {
|
||||
if (di->recovery_cnt >
|
||||
di->bat->fg_params->recovery_total_time) {
|
||||
di->bm->fg_params->recovery_total_time) {
|
||||
di->fg_samples = SEC_TO_SAMPLE(
|
||||
di->bat->fg_params->accu_high_curr);
|
||||
di->bm->fg_params->accu_high_curr);
|
||||
ab8500_fg_coulomb_counter(di, true);
|
||||
ab8500_fg_discharge_state_to(di,
|
||||
AB8500_FG_DISCHARGE_READOUT);
|
||||
@ -1453,7 +1614,7 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
|
||||
di->recovery_cnt += sleep_time;
|
||||
} else {
|
||||
di->fg_samples = SEC_TO_SAMPLE(
|
||||
di->bat->fg_params->accu_high_curr);
|
||||
di->bm->fg_params->accu_high_curr);
|
||||
ab8500_fg_coulomb_counter(di, true);
|
||||
ab8500_fg_discharge_state_to(di,
|
||||
AB8500_FG_DISCHARGE_READOUT);
|
||||
@ -1462,7 +1623,7 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
|
||||
|
||||
case AB8500_FG_DISCHARGE_READOUT_INIT:
|
||||
di->fg_samples = SEC_TO_SAMPLE(
|
||||
di->bat->fg_params->accu_high_curr);
|
||||
di->bm->fg_params->accu_high_curr);
|
||||
ab8500_fg_coulomb_counter(di, true);
|
||||
ab8500_fg_discharge_state_to(di,
|
||||
AB8500_FG_DISCHARGE_READOUT);
|
||||
@ -1480,7 +1641,7 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
|
||||
|
||||
if (di->recovery_needed) {
|
||||
ab8500_fg_discharge_state_to(di,
|
||||
AB8500_FG_DISCHARGE_RECOVERY);
|
||||
AB8500_FG_DISCHARGE_INIT_RECOVERY);
|
||||
|
||||
queue_delayed_work(di->fg_wq,
|
||||
&di->fg_periodic_work, 0);
|
||||
@ -1509,9 +1670,9 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
|
||||
}
|
||||
|
||||
di->high_curr_cnt +=
|
||||
di->bat->fg_params->accu_high_curr;
|
||||
di->bm->fg_params->accu_high_curr;
|
||||
if (di->high_curr_cnt >
|
||||
di->bat->fg_params->high_curr_time)
|
||||
di->bm->fg_params->high_curr_time)
|
||||
di->recovery_needed = true;
|
||||
|
||||
ab8500_fg_calc_cap_discharge_fg(di);
|
||||
@ -1523,12 +1684,10 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
|
||||
|
||||
case AB8500_FG_DISCHARGE_WAKEUP:
|
||||
ab8500_fg_coulomb_counter(di, true);
|
||||
di->inst_curr = ab8500_fg_inst_curr_blocking(di);
|
||||
|
||||
ab8500_fg_calc_cap_discharge_voltage(di, true);
|
||||
|
||||
di->fg_samples = SEC_TO_SAMPLE(
|
||||
di->bat->fg_params->accu_high_curr);
|
||||
di->bm->fg_params->accu_high_curr);
|
||||
ab8500_fg_coulomb_counter(di, true);
|
||||
ab8500_fg_discharge_state_to(di,
|
||||
AB8500_FG_DISCHARGE_READOUT);
|
||||
@ -1641,8 +1800,6 @@ static void ab8500_fg_periodic_work(struct work_struct *work)
|
||||
fg_periodic_work.work);
|
||||
|
||||
if (di->init_capacity) {
|
||||
/* A dummy read that will return 0 */
|
||||
di->inst_curr = ab8500_fg_inst_curr_blocking(di);
|
||||
/* Get an initial capacity calculation */
|
||||
ab8500_fg_calc_cap_discharge_voltage(di, true);
|
||||
ab8500_fg_check_capacity_limits(di, true);
|
||||
@ -1684,24 +1841,26 @@ static void ab8500_fg_check_hw_failure_work(struct work_struct *work)
|
||||
* If we have had a battery over-voltage situation,
|
||||
* check ovv-bit to see if it should be reset.
|
||||
*/
|
||||
if (di->flags.bat_ovv) {
|
||||
ret = abx500_get_register_interruptible(di->dev,
|
||||
AB8500_CHARGER, AB8500_CH_STAT_REG,
|
||||
®_value);
|
||||
if (ret < 0) {
|
||||
dev_err(di->dev, "%s ab8500 read failed\n", __func__);
|
||||
return;
|
||||
ret = abx500_get_register_interruptible(di->dev,
|
||||
AB8500_CHARGER, AB8500_CH_STAT_REG,
|
||||
®_value);
|
||||
if (ret < 0) {
|
||||
dev_err(di->dev, "%s ab8500 read failed\n", __func__);
|
||||
return;
|
||||
}
|
||||
if ((reg_value & BATT_OVV) == BATT_OVV) {
|
||||
if (!di->flags.bat_ovv) {
|
||||
dev_dbg(di->dev, "Battery OVV\n");
|
||||
di->flags.bat_ovv = true;
|
||||
power_supply_changed(&di->fg_psy);
|
||||
}
|
||||
if ((reg_value & BATT_OVV) != BATT_OVV) {
|
||||
/* Not yet recovered from ovv, reschedule this test */
|
||||
queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work,
|
||||
HZ);
|
||||
} else {
|
||||
dev_dbg(di->dev, "Battery recovered from OVV\n");
|
||||
di->flags.bat_ovv = false;
|
||||
power_supply_changed(&di->fg_psy);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Not yet recovered from ovv, reschedule this test */
|
||||
queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work,
|
||||
round_jiffies(HZ));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1721,26 +1880,30 @@ static void ab8500_fg_low_bat_work(struct work_struct *work)
|
||||
vbat = ab8500_fg_bat_voltage(di);
|
||||
|
||||
/* Check if LOW_BAT still fulfilled */
|
||||
if (vbat < di->bat->fg_params->lowbat_threshold) {
|
||||
di->flags.low_bat = true;
|
||||
dev_warn(di->dev, "Battery voltage still LOW\n");
|
||||
|
||||
/*
|
||||
* We need to re-schedule this check to be able to detect
|
||||
* if the voltage increases again during charging
|
||||
*/
|
||||
queue_delayed_work(di->fg_wq, &di->fg_low_bat_work,
|
||||
round_jiffies(LOW_BAT_CHECK_INTERVAL));
|
||||
if (vbat < di->bm->fg_params->lowbat_threshold) {
|
||||
/* Is it time to shut down? */
|
||||
if (di->low_bat_cnt < 1) {
|
||||
di->flags.low_bat = true;
|
||||
dev_warn(di->dev, "Shut down pending...\n");
|
||||
} else {
|
||||
/*
|
||||
* Else we need to re-schedule this check to be able to detect
|
||||
* if the voltage increases again during charging or
|
||||
* due to decreasing load.
|
||||
*/
|
||||
di->low_bat_cnt--;
|
||||
dev_warn(di->dev, "Battery voltage still LOW\n");
|
||||
queue_delayed_work(di->fg_wq, &di->fg_low_bat_work,
|
||||
round_jiffies(LOW_BAT_CHECK_INTERVAL));
|
||||
}
|
||||
} else {
|
||||
di->flags.low_bat = false;
|
||||
di->flags.low_bat_delay = false;
|
||||
di->low_bat_cnt = 10;
|
||||
dev_warn(di->dev, "Battery voltage OK again\n");
|
||||
}
|
||||
|
||||
/* This is needed to dispatch LOW_BAT */
|
||||
ab8500_fg_check_capacity_limits(di, false);
|
||||
|
||||
/* Set this flag to check if LOW_BAT IRQ still occurs */
|
||||
di->flags.low_bat_delay = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1779,8 +1942,8 @@ static int ab8500_fg_battok_init_hw_register(struct ab8500_fg *di)
|
||||
int ret;
|
||||
int new_val;
|
||||
|
||||
sel0 = di->bat->fg_params->battok_falling_th_sel0;
|
||||
sel1 = di->bat->fg_params->battok_raising_th_sel1;
|
||||
sel0 = di->bm->fg_params->battok_falling_th_sel0;
|
||||
sel1 = di->bm->fg_params->battok_raising_th_sel1;
|
||||
|
||||
cbp_sel0 = ab8500_fg_battok_calc(di, sel0);
|
||||
cbp_sel1 = ab8500_fg_battok_calc(di, sel1);
|
||||
@ -1828,7 +1991,13 @@ static void ab8500_fg_instant_work(struct work_struct *work)
|
||||
static irqreturn_t ab8500_fg_cc_data_end_handler(int irq, void *_di)
|
||||
{
|
||||
struct ab8500_fg *di = _di;
|
||||
complete(&di->ab8500_fg_complete);
|
||||
if (!di->nbr_cceoc_irq_cnt) {
|
||||
di->nbr_cceoc_irq_cnt++;
|
||||
complete(&di->ab8500_fg_started);
|
||||
} else {
|
||||
di->nbr_cceoc_irq_cnt = 0;
|
||||
complete(&di->ab8500_fg_complete);
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -1875,8 +2044,6 @@ static irqreturn_t ab8500_fg_batt_ovv_handler(int irq, void *_di)
|
||||
struct ab8500_fg *di = _di;
|
||||
|
||||
dev_dbg(di->dev, "Battery OVV\n");
|
||||
di->flags.bat_ovv = true;
|
||||
power_supply_changed(&di->fg_psy);
|
||||
|
||||
/* Schedule a new HW failure check */
|
||||
queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work, 0);
|
||||
@ -1895,6 +2062,7 @@ static irqreturn_t ab8500_fg_lowbatf_handler(int irq, void *_di)
|
||||
{
|
||||
struct ab8500_fg *di = _di;
|
||||
|
||||
/* Initiate handling in ab8500_fg_low_bat_work() if not already initiated. */
|
||||
if (!di->flags.low_bat_delay) {
|
||||
dev_warn(di->dev, "Battery voltage is below LOW threshold\n");
|
||||
di->flags.low_bat_delay = true;
|
||||
@ -1963,7 +2131,7 @@ static int ab8500_fg_get_property(struct power_supply *psy,
|
||||
di->bat_cap.max_mah);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_ENERGY_NOW:
|
||||
if (di->flags.batt_unknown && !di->bat->chg_unknown_bat &&
|
||||
if (di->flags.batt_unknown && !di->bm->chg_unknown_bat &&
|
||||
di->flags.batt_id_received)
|
||||
val->intval = ab8500_fg_convert_mah_to_uwh(di,
|
||||
di->bat_cap.max_mah);
|
||||
@ -1978,21 +2146,23 @@ static int ab8500_fg_get_property(struct power_supply *psy,
|
||||
val->intval = di->bat_cap.max_mah;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_NOW:
|
||||
if (di->flags.batt_unknown && !di->bat->chg_unknown_bat &&
|
||||
if (di->flags.batt_unknown && !di->bm->chg_unknown_bat &&
|
||||
di->flags.batt_id_received)
|
||||
val->intval = di->bat_cap.max_mah;
|
||||
else
|
||||
val->intval = di->bat_cap.prev_mah;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
if (di->flags.batt_unknown && !di->bat->chg_unknown_bat &&
|
||||
if (di->bm->capacity_scaling)
|
||||
val->intval = di->bat_cap.cap_scale.scaled_cap;
|
||||
else if (di->flags.batt_unknown && !di->bm->chg_unknown_bat &&
|
||||
di->flags.batt_id_received)
|
||||
val->intval = 100;
|
||||
else
|
||||
val->intval = di->bat_cap.prev_percent;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
|
||||
if (di->flags.batt_unknown && !di->bat->chg_unknown_bat &&
|
||||
if (di->flags.batt_unknown && !di->bm->chg_unknown_bat &&
|
||||
di->flags.batt_id_received)
|
||||
val->intval = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
|
||||
else
|
||||
@ -2049,6 +2219,8 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
|
||||
break;
|
||||
di->flags.charging = false;
|
||||
di->flags.fully_charged = false;
|
||||
if (di->bm->capacity_scaling)
|
||||
ab8500_fg_update_cap_scalers(di);
|
||||
queue_work(di->fg_wq, &di->fg_work);
|
||||
break;
|
||||
case POWER_SUPPLY_STATUS_FULL:
|
||||
@ -2061,10 +2233,13 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
|
||||
queue_work(di->fg_wq, &di->fg_work);
|
||||
break;
|
||||
case POWER_SUPPLY_STATUS_CHARGING:
|
||||
if (di->flags.charging)
|
||||
if (di->flags.charging &&
|
||||
!di->flags.fully_charged)
|
||||
break;
|
||||
di->flags.charging = true;
|
||||
di->flags.fully_charged = false;
|
||||
if (di->bm->capacity_scaling)
|
||||
ab8500_fg_update_cap_scalers(di);
|
||||
queue_work(di->fg_wq, &di->fg_work);
|
||||
break;
|
||||
};
|
||||
@ -2075,10 +2250,11 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
|
||||
case POWER_SUPPLY_PROP_TECHNOLOGY:
|
||||
switch (ext->type) {
|
||||
case POWER_SUPPLY_TYPE_BATTERY:
|
||||
if (!di->flags.batt_id_received) {
|
||||
if (!di->flags.batt_id_received &&
|
||||
di->bm->batt_id != BATTERY_UNKNOWN) {
|
||||
const struct abx500_battery_type *b;
|
||||
|
||||
b = &(di->bat->bat_type[di->bat->batt_id]);
|
||||
b = &(di->bm->bat_type[di->bm->batt_id]);
|
||||
|
||||
di->flags.batt_id_received = true;
|
||||
|
||||
@ -2104,8 +2280,8 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
|
||||
case POWER_SUPPLY_PROP_TEMP:
|
||||
switch (ext->type) {
|
||||
case POWER_SUPPLY_TYPE_BATTERY:
|
||||
if (di->flags.batt_id_received)
|
||||
di->bat_temp = ret.intval;
|
||||
if (di->flags.batt_id_received)
|
||||
di->bat_temp = ret.intval;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -2155,7 +2331,7 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di)
|
||||
AB8500_SYS_CTRL2_BLOCK,
|
||||
AB8500_LOW_BAT_REG,
|
||||
ab8500_volt_to_regval(
|
||||
di->bat->fg_params->lowbat_threshold) << 1 |
|
||||
di->bm->fg_params->lowbat_threshold) << 1 |
|
||||
LOW_BAT_ENABLE);
|
||||
if (ret) {
|
||||
dev_err(di->dev, "%s write failed\n", __func__);
|
||||
@ -2395,6 +2571,11 @@ static int ab8500_fg_suspend(struct platform_device *pdev,
|
||||
struct ab8500_fg *di = platform_get_drvdata(pdev);
|
||||
|
||||
flush_delayed_work(&di->fg_periodic_work);
|
||||
flush_work(&di->fg_work);
|
||||
flush_work(&di->fg_acc_cur_work);
|
||||
flush_delayed_work(&di->fg_reinit_work);
|
||||
flush_delayed_work(&di->fg_low_bat_work);
|
||||
flush_delayed_work(&di->fg_check_hw_failure_work);
|
||||
|
||||
/*
|
||||
* If the FG is enabled we will disable it before going to suspend
|
||||
@ -2448,6 +2629,7 @@ static char *supply_interface[] = {
|
||||
static int ab8500_fg_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct abx500_bm_data *plat = pdev->dev.platform_data;
|
||||
struct ab8500_fg *di;
|
||||
int i, irq;
|
||||
int ret = 0;
|
||||
@ -2457,21 +2639,19 @@ static int ab8500_fg_probe(struct platform_device *pdev)
|
||||
dev_err(&pdev->dev, "%s no mem for ab8500_fg\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
di->bat = pdev->mfd_cell->platform_data;
|
||||
if (!di->bat) {
|
||||
if (np) {
|
||||
ret = bmdevs_of_probe(&pdev->dev, np, &di->bat);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to get battery information\n");
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
dev_err(&pdev->dev, "missing dt node for ab8500_fg\n");
|
||||
return -EINVAL;
|
||||
|
||||
if (!plat) {
|
||||
dev_err(&pdev->dev, "no battery management data supplied\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
di->bm = plat;
|
||||
|
||||
if (np) {
|
||||
ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to get battery information\n");
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
dev_info(&pdev->dev, "falling back to legacy platform data\n");
|
||||
}
|
||||
|
||||
mutex_init(&di->cc_lock);
|
||||
@ -2491,11 +2671,11 @@ static int ab8500_fg_probe(struct platform_device *pdev)
|
||||
di->fg_psy.external_power_changed = ab8500_fg_external_power_changed;
|
||||
|
||||
di->bat_cap.max_mah_design = MILLI_TO_MICRO *
|
||||
di->bat->bat_type[di->bat->batt_id].charge_full_design;
|
||||
di->bm->bat_type[di->bm->batt_id].charge_full_design;
|
||||
|
||||
di->bat_cap.max_mah = di->bat_cap.max_mah_design;
|
||||
|
||||
di->vbat_nom = di->bat->bat_type[di->bat->batt_id].nominal_voltage;
|
||||
di->vbat_nom = di->bm->bat_type[di->bm->batt_id].nominal_voltage;
|
||||
|
||||
di->init_capacity = true;
|
||||
|
||||
@ -2531,6 +2711,12 @@ static int ab8500_fg_probe(struct platform_device *pdev)
|
||||
INIT_DEFERRABLE_WORK(&di->fg_check_hw_failure_work,
|
||||
ab8500_fg_check_hw_failure_work);
|
||||
|
||||
/* Reset battery low voltage flag */
|
||||
di->flags.low_bat = false;
|
||||
|
||||
/* Initialize low battery counter */
|
||||
di->low_bat_cnt = 10;
|
||||
|
||||
/* Initialize OVV, and other registers */
|
||||
ret = ab8500_fg_init_hw_registers(di);
|
||||
if (ret) {
|
||||
@ -2549,10 +2735,14 @@ static int ab8500_fg_probe(struct platform_device *pdev)
|
||||
goto free_inst_curr_wq;
|
||||
}
|
||||
|
||||
di->fg_samples = SEC_TO_SAMPLE(di->bat->fg_params->init_timer);
|
||||
di->fg_samples = SEC_TO_SAMPLE(di->bm->fg_params->init_timer);
|
||||
ab8500_fg_coulomb_counter(di, true);
|
||||
|
||||
/* Initialize completion used to notify completion of inst current */
|
||||
/*
|
||||
* Initialize completion used to notify completion and start
|
||||
* of inst current
|
||||
*/
|
||||
init_completion(&di->ab8500_fg_started);
|
||||
init_completion(&di->ab8500_fg_complete);
|
||||
|
||||
/* Register interrupts */
|
||||
@ -2572,6 +2762,7 @@ static int ab8500_fg_probe(struct platform_device *pdev)
|
||||
}
|
||||
di->irq = platform_get_irq_byname(pdev, "CCEOC");
|
||||
disable_irq(di->irq);
|
||||
di->nbr_cceoc_irq_cnt = 0;
|
||||
|
||||
platform_set_drvdata(pdev, di);
|
||||
|
||||
|
@ -33,9 +33,6 @@
|
||||
/* End-of-charge criteria counter */
|
||||
#define EOC_COND_CNT 10
|
||||
|
||||
/* Recharge criteria counter */
|
||||
#define RCH_COND_CNT 3
|
||||
|
||||
#define to_abx500_chargalg_device_info(x) container_of((x), \
|
||||
struct abx500_chargalg, chargalg_psy);
|
||||
|
||||
@ -196,7 +193,6 @@ enum maxim_ret {
|
||||
* @dev: pointer to the structure device
|
||||
* @charge_status: battery operating status
|
||||
* @eoc_cnt: counter used to determine end-of_charge
|
||||
* @rch_cnt: counter used to determine start of recharge
|
||||
* @maintenance_chg: indicate if maintenance charge is active
|
||||
* @t_hyst_norm temperature hysteresis when the temperature has been
|
||||
* over or under normal limits
|
||||
@ -207,7 +203,7 @@ enum maxim_ret {
|
||||
* @chg_info: information about connected charger types
|
||||
* @batt_data: data of the battery
|
||||
* @susp_status: current charger suspension status
|
||||
* @bat: pointer to the abx500_bm platform data
|
||||
* @bm: Platform specific battery management information
|
||||
* @chargalg_psy: structure that holds the battery properties exposed by
|
||||
* the charging algorithm
|
||||
* @events: structure for information about events triggered
|
||||
@ -223,7 +219,6 @@ struct abx500_chargalg {
|
||||
struct device *dev;
|
||||
int charge_status;
|
||||
int eoc_cnt;
|
||||
int rch_cnt;
|
||||
bool maintenance_chg;
|
||||
int t_hyst_norm;
|
||||
int t_hyst_lowhigh;
|
||||
@ -232,7 +227,7 @@ struct abx500_chargalg {
|
||||
struct abx500_chargalg_charger_info chg_info;
|
||||
struct abx500_chargalg_battery_data batt_data;
|
||||
struct abx500_chargalg_suspension_status susp_status;
|
||||
struct abx500_bm_data *bat;
|
||||
struct abx500_bm_data *bm;
|
||||
struct power_supply chargalg_psy;
|
||||
struct ux500_charger *ac_chg;
|
||||
struct ux500_charger *usb_chg;
|
||||
@ -367,13 +362,13 @@ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
|
||||
case AC_CHG:
|
||||
timer_expiration =
|
||||
round_jiffies(jiffies +
|
||||
(di->bat->main_safety_tmr_h * 3600 * HZ));
|
||||
(di->bm->main_safety_tmr_h * 3600 * HZ));
|
||||
break;
|
||||
|
||||
case USB_CHG:
|
||||
timer_expiration =
|
||||
round_jiffies(jiffies +
|
||||
(di->bat->usb_safety_tmr_h * 3600 * HZ));
|
||||
(di->bm->usb_safety_tmr_h * 3600 * HZ));
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -450,8 +445,18 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
|
||||
{
|
||||
/* Check if charger exists and kick watchdog if charging */
|
||||
if (di->ac_chg && di->ac_chg->ops.kick_wd &&
|
||||
di->chg_info.online_chg & AC_CHG)
|
||||
di->chg_info.online_chg & AC_CHG) {
|
||||
/*
|
||||
* If AB charger watchdog expired, pm2xxx charging
|
||||
* gets disabled. To be safe, kick both AB charger watchdog
|
||||
* and pm2xxx watchdog.
|
||||
*/
|
||||
if (di->ac_chg->external &&
|
||||
di->usb_chg && di->usb_chg->ops.kick_wd)
|
||||
di->usb_chg->ops.kick_wd(di->usb_chg);
|
||||
|
||||
return di->ac_chg->ops.kick_wd(di->ac_chg);
|
||||
}
|
||||
else if (di->usb_chg && di->usb_chg->ops.kick_wd &&
|
||||
di->chg_info.online_chg & USB_CHG)
|
||||
return di->usb_chg->ops.kick_wd(di->usb_chg);
|
||||
@ -608,6 +613,8 @@ static void abx500_chargalg_hold_charging(struct abx500_chargalg *di)
|
||||
static void abx500_chargalg_start_charging(struct abx500_chargalg *di,
|
||||
int vset, int iset)
|
||||
{
|
||||
bool start_chargalg_wd = true;
|
||||
|
||||
switch (di->chg_info.charger_type) {
|
||||
case AC_CHG:
|
||||
dev_dbg(di->dev,
|
||||
@ -625,8 +632,12 @@ static void abx500_chargalg_start_charging(struct abx500_chargalg *di,
|
||||
|
||||
default:
|
||||
dev_err(di->dev, "Unknown charger to charge from\n");
|
||||
start_chargalg_wd = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (start_chargalg_wd && !delayed_work_pending(&di->chargalg_wd_work))
|
||||
queue_delayed_work(di->chargalg_wq, &di->chargalg_wd_work, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -638,32 +649,32 @@ static void abx500_chargalg_start_charging(struct abx500_chargalg *di,
|
||||
*/
|
||||
static void abx500_chargalg_check_temp(struct abx500_chargalg *di)
|
||||
{
|
||||
if (di->batt_data.temp > (di->bat->temp_low + di->t_hyst_norm) &&
|
||||
di->batt_data.temp < (di->bat->temp_high - di->t_hyst_norm)) {
|
||||
if (di->batt_data.temp > (di->bm->temp_low + di->t_hyst_norm) &&
|
||||
di->batt_data.temp < (di->bm->temp_high - di->t_hyst_norm)) {
|
||||
/* Temp OK! */
|
||||
di->events.btemp_underover = false;
|
||||
di->events.btemp_lowhigh = false;
|
||||
di->t_hyst_norm = 0;
|
||||
di->t_hyst_lowhigh = 0;
|
||||
} else {
|
||||
if (((di->batt_data.temp >= di->bat->temp_high) &&
|
||||
if (((di->batt_data.temp >= di->bm->temp_high) &&
|
||||
(di->batt_data.temp <
|
||||
(di->bat->temp_over - di->t_hyst_lowhigh))) ||
|
||||
(di->bm->temp_over - di->t_hyst_lowhigh))) ||
|
||||
((di->batt_data.temp >
|
||||
(di->bat->temp_under + di->t_hyst_lowhigh)) &&
|
||||
(di->batt_data.temp <= di->bat->temp_low))) {
|
||||
(di->bm->temp_under + di->t_hyst_lowhigh)) &&
|
||||
(di->batt_data.temp <= di->bm->temp_low))) {
|
||||
/* TEMP minor!!!!! */
|
||||
di->events.btemp_underover = false;
|
||||
di->events.btemp_lowhigh = true;
|
||||
di->t_hyst_norm = di->bat->temp_hysteresis;
|
||||
di->t_hyst_norm = di->bm->temp_hysteresis;
|
||||
di->t_hyst_lowhigh = 0;
|
||||
} else if (di->batt_data.temp <= di->bat->temp_under ||
|
||||
di->batt_data.temp >= di->bat->temp_over) {
|
||||
} else if (di->batt_data.temp <= di->bm->temp_under ||
|
||||
di->batt_data.temp >= di->bm->temp_over) {
|
||||
/* TEMP major!!!!! */
|
||||
di->events.btemp_underover = true;
|
||||
di->events.btemp_lowhigh = false;
|
||||
di->t_hyst_norm = 0;
|
||||
di->t_hyst_lowhigh = di->bat->temp_hysteresis;
|
||||
di->t_hyst_lowhigh = di->bm->temp_hysteresis;
|
||||
} else {
|
||||
/* Within hysteresis */
|
||||
dev_dbg(di->dev, "Within hysteresis limit temp: %d "
|
||||
@ -682,12 +693,12 @@ static void abx500_chargalg_check_temp(struct abx500_chargalg *di)
|
||||
*/
|
||||
static void abx500_chargalg_check_charger_voltage(struct abx500_chargalg *di)
|
||||
{
|
||||
if (di->chg_info.usb_volt > di->bat->chg_params->usb_volt_max)
|
||||
if (di->chg_info.usb_volt > di->bm->chg_params->usb_volt_max)
|
||||
di->chg_info.usb_chg_ok = false;
|
||||
else
|
||||
di->chg_info.usb_chg_ok = true;
|
||||
|
||||
if (di->chg_info.ac_volt > di->bat->chg_params->ac_volt_max)
|
||||
if (di->chg_info.ac_volt > di->bm->chg_params->ac_volt_max)
|
||||
di->chg_info.ac_chg_ok = false;
|
||||
else
|
||||
di->chg_info.ac_chg_ok = true;
|
||||
@ -707,10 +718,10 @@ static void abx500_chargalg_end_of_charge(struct abx500_chargalg *di)
|
||||
if (di->charge_status == POWER_SUPPLY_STATUS_CHARGING &&
|
||||
di->charge_state == STATE_NORMAL &&
|
||||
!di->maintenance_chg && (di->batt_data.volt >=
|
||||
di->bat->bat_type[di->bat->batt_id].termination_vol ||
|
||||
di->bm->bat_type[di->bm->batt_id].termination_vol ||
|
||||
di->events.usb_cv_active || di->events.ac_cv_active) &&
|
||||
di->batt_data.avg_curr <
|
||||
di->bat->bat_type[di->bat->batt_id].termination_curr &&
|
||||
di->bm->bat_type[di->bm->batt_id].termination_curr &&
|
||||
di->batt_data.avg_curr > 0) {
|
||||
if (++di->eoc_cnt >= EOC_COND_CNT) {
|
||||
di->eoc_cnt = 0;
|
||||
@ -733,12 +744,12 @@ static void abx500_chargalg_end_of_charge(struct abx500_chargalg *di)
|
||||
static void init_maxim_chg_curr(struct abx500_chargalg *di)
|
||||
{
|
||||
di->ccm.original_iset =
|
||||
di->bat->bat_type[di->bat->batt_id].normal_cur_lvl;
|
||||
di->bm->bat_type[di->bm->batt_id].normal_cur_lvl;
|
||||
di->ccm.current_iset =
|
||||
di->bat->bat_type[di->bat->batt_id].normal_cur_lvl;
|
||||
di->ccm.test_delta_i = di->bat->maxi->charger_curr_step;
|
||||
di->ccm.max_current = di->bat->maxi->chg_curr;
|
||||
di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
|
||||
di->bm->bat_type[di->bm->batt_id].normal_cur_lvl;
|
||||
di->ccm.test_delta_i = di->bm->maxi->charger_curr_step;
|
||||
di->ccm.max_current = di->bm->maxi->chg_curr;
|
||||
di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
|
||||
di->ccm.level = 0;
|
||||
}
|
||||
|
||||
@ -755,7 +766,7 @@ static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di)
|
||||
{
|
||||
int delta_i;
|
||||
|
||||
if (!di->bat->maxi->ena_maxi)
|
||||
if (!di->bm->maxi->ena_maxi)
|
||||
return MAXIM_RET_NOACTION;
|
||||
|
||||
delta_i = di->ccm.original_iset - di->batt_data.inst_curr;
|
||||
@ -766,7 +777,7 @@ static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di)
|
||||
if (di->ccm.wait_cnt == 0) {
|
||||
dev_dbg(di->dev, "lowering current\n");
|
||||
di->ccm.wait_cnt++;
|
||||
di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
|
||||
di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
|
||||
di->ccm.max_current =
|
||||
di->ccm.current_iset - di->ccm.test_delta_i;
|
||||
di->ccm.current_iset = di->ccm.max_current;
|
||||
@ -791,7 +802,7 @@ static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di)
|
||||
if (di->ccm.current_iset == di->ccm.original_iset)
|
||||
return MAXIM_RET_NOACTION;
|
||||
|
||||
di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
|
||||
di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
|
||||
di->ccm.current_iset = di->ccm.original_iset;
|
||||
di->ccm.level = 0;
|
||||
|
||||
@ -803,7 +814,7 @@ static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di)
|
||||
di->ccm.max_current) {
|
||||
if (di->ccm.condition_cnt-- == 0) {
|
||||
/* Increse the iset with cco.test_delta_i */
|
||||
di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
|
||||
di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
|
||||
di->ccm.current_iset += di->ccm.test_delta_i;
|
||||
di->ccm.level++;
|
||||
dev_dbg(di->dev, " Maximization needed, increase"
|
||||
@ -818,7 +829,7 @@ static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di)
|
||||
return MAXIM_RET_NOACTION;
|
||||
}
|
||||
} else {
|
||||
di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
|
||||
di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
|
||||
return MAXIM_RET_NOACTION;
|
||||
}
|
||||
}
|
||||
@ -838,7 +849,7 @@ static void handle_maxim_chg_curr(struct abx500_chargalg *di)
|
||||
break;
|
||||
case MAXIM_RET_IBAT_TOO_HIGH:
|
||||
result = abx500_chargalg_update_chg_curr(di,
|
||||
di->bat->bat_type[di->bat->batt_id].normal_cur_lvl);
|
||||
di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
|
||||
if (result)
|
||||
dev_err(di->dev, "failed to set chg curr\n");
|
||||
break;
|
||||
@ -858,6 +869,7 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
|
||||
union power_supply_propval ret;
|
||||
int i, j;
|
||||
bool psy_found = false;
|
||||
bool capacity_updated = false;
|
||||
|
||||
psy = (struct power_supply *)data;
|
||||
ext = dev_get_drvdata(dev);
|
||||
@ -870,6 +882,16 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
|
||||
if (!psy_found)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If external is not registering 'POWER_SUPPLY_PROP_CAPACITY' to its
|
||||
* property because of handling that sysfs entry on its own, this is
|
||||
* the place to get the battery capacity.
|
||||
*/
|
||||
if (!ext->get_property(ext, POWER_SUPPLY_PROP_CAPACITY, &ret)) {
|
||||
di->batt_data.percent = ret.intval;
|
||||
capacity_updated = true;
|
||||
}
|
||||
|
||||
/* Go through all properties for the psy */
|
||||
for (j = 0; j < ext->num_properties; j++) {
|
||||
enum power_supply_property prop;
|
||||
@ -1154,7 +1176,8 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
|
||||
}
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
di->batt_data.percent = ret.intval;
|
||||
if (!capacity_updated)
|
||||
di->batt_data.percent = ret.intval;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -1210,7 +1233,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
|
||||
* this way
|
||||
*/
|
||||
if (!charger_status ||
|
||||
(di->events.batt_unknown && !di->bat->chg_unknown_bat)) {
|
||||
(di->events.batt_unknown && !di->bm->chg_unknown_bat)) {
|
||||
if (di->charge_state != STATE_HANDHELD) {
|
||||
di->events.safety_timer_expired = false;
|
||||
abx500_chargalg_state_to(di, STATE_HANDHELD_INIT);
|
||||
@ -1394,8 +1417,8 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
|
||||
|
||||
case STATE_NORMAL_INIT:
|
||||
abx500_chargalg_start_charging(di,
|
||||
di->bat->bat_type[di->bat->batt_id].normal_vol_lvl,
|
||||
di->bat->bat_type[di->bat->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);
|
||||
abx500_chargalg_state_to(di, STATE_NORMAL);
|
||||
abx500_chargalg_start_safety_timer(di);
|
||||
abx500_chargalg_stop_maintenance_timer(di);
|
||||
@ -1411,7 +1434,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
|
||||
handle_maxim_chg_curr(di);
|
||||
if (di->charge_status == POWER_SUPPLY_STATUS_FULL &&
|
||||
di->maintenance_chg) {
|
||||
if (di->bat->no_maintenance)
|
||||
if (di->bm->no_maintenance)
|
||||
abx500_chargalg_state_to(di,
|
||||
STATE_WAIT_FOR_RECHARGE_INIT);
|
||||
else
|
||||
@ -1424,28 +1447,25 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
|
||||
case STATE_WAIT_FOR_RECHARGE_INIT:
|
||||
abx500_chargalg_hold_charging(di);
|
||||
abx500_chargalg_state_to(di, STATE_WAIT_FOR_RECHARGE);
|
||||
di->rch_cnt = RCH_COND_CNT;
|
||||
/* Intentional fallthrough */
|
||||
|
||||
case STATE_WAIT_FOR_RECHARGE:
|
||||
if (di->batt_data.volt <=
|
||||
di->bat->bat_type[di->bat->batt_id].recharge_vol) {
|
||||
if (di->rch_cnt-- == 0)
|
||||
abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
|
||||
} else
|
||||
di->rch_cnt = RCH_COND_CNT;
|
||||
if (di->batt_data.percent <=
|
||||
di->bm->bat_type[di->bm->batt_id].
|
||||
recharge_cap)
|
||||
abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
|
||||
break;
|
||||
|
||||
case STATE_MAINTENANCE_A_INIT:
|
||||
abx500_chargalg_stop_safety_timer(di);
|
||||
abx500_chargalg_start_maintenance_timer(di,
|
||||
di->bat->bat_type[
|
||||
di->bat->batt_id].maint_a_chg_timer_h);
|
||||
di->bm->bat_type[
|
||||
di->bm->batt_id].maint_a_chg_timer_h);
|
||||
abx500_chargalg_start_charging(di,
|
||||
di->bat->bat_type[
|
||||
di->bat->batt_id].maint_a_vol_lvl,
|
||||
di->bat->bat_type[
|
||||
di->bat->batt_id].maint_a_cur_lvl);
|
||||
di->bm->bat_type[
|
||||
di->bm->batt_id].maint_a_vol_lvl,
|
||||
di->bm->bat_type[
|
||||
di->bm->batt_id].maint_a_cur_lvl);
|
||||
abx500_chargalg_state_to(di, STATE_MAINTENANCE_A);
|
||||
power_supply_changed(&di->chargalg_psy);
|
||||
/* Intentional fallthrough*/
|
||||
@ -1459,13 +1479,13 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
|
||||
|
||||
case STATE_MAINTENANCE_B_INIT:
|
||||
abx500_chargalg_start_maintenance_timer(di,
|
||||
di->bat->bat_type[
|
||||
di->bat->batt_id].maint_b_chg_timer_h);
|
||||
di->bm->bat_type[
|
||||
di->bm->batt_id].maint_b_chg_timer_h);
|
||||
abx500_chargalg_start_charging(di,
|
||||
di->bat->bat_type[
|
||||
di->bat->batt_id].maint_b_vol_lvl,
|
||||
di->bat->bat_type[
|
||||
di->bat->batt_id].maint_b_cur_lvl);
|
||||
di->bm->bat_type[
|
||||
di->bm->batt_id].maint_b_vol_lvl,
|
||||
di->bm->bat_type[
|
||||
di->bm->batt_id].maint_b_cur_lvl);
|
||||
abx500_chargalg_state_to(di, STATE_MAINTENANCE_B);
|
||||
power_supply_changed(&di->chargalg_psy);
|
||||
/* Intentional fallthrough*/
|
||||
@ -1479,10 +1499,10 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
|
||||
|
||||
case STATE_TEMP_LOWHIGH_INIT:
|
||||
abx500_chargalg_start_charging(di,
|
||||
di->bat->bat_type[
|
||||
di->bat->batt_id].low_high_vol_lvl,
|
||||
di->bat->bat_type[
|
||||
di->bat->batt_id].low_high_cur_lvl);
|
||||
di->bm->bat_type[
|
||||
di->bm->batt_id].low_high_vol_lvl,
|
||||
di->bm->bat_type[
|
||||
di->bm->batt_id].low_high_cur_lvl);
|
||||
abx500_chargalg_stop_maintenance_timer(di);
|
||||
di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
|
||||
abx500_chargalg_state_to(di, STATE_TEMP_LOWHIGH);
|
||||
@ -1543,11 +1563,11 @@ static void abx500_chargalg_periodic_work(struct work_struct *work)
|
||||
if (di->chg_info.conn_chg)
|
||||
queue_delayed_work(di->chargalg_wq,
|
||||
&di->chargalg_periodic_work,
|
||||
di->bat->interval_charging * HZ);
|
||||
di->bm->interval_charging * HZ);
|
||||
else
|
||||
queue_delayed_work(di->chargalg_wq,
|
||||
&di->chargalg_periodic_work,
|
||||
di->bat->interval_not_charging * HZ);
|
||||
di->bm->interval_not_charging * HZ);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1614,10 +1634,13 @@ static int abx500_chargalg_get_property(struct power_supply *psy,
|
||||
if (di->events.batt_ovv) {
|
||||
val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
|
||||
} else if (di->events.btemp_underover) {
|
||||
if (di->batt_data.temp <= di->bat->temp_under)
|
||||
if (di->batt_data.temp <= di->bm->temp_under)
|
||||
val->intval = POWER_SUPPLY_HEALTH_COLD;
|
||||
else
|
||||
val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
|
||||
} else if (di->charge_state == STATE_SAFETY_TIMER_EXPIRED ||
|
||||
di->charge_state == STATE_SAFETY_TIMER_EXPIRED_INIT) {
|
||||
val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
|
||||
} else {
|
||||
val->intval = POWER_SUPPLY_HEALTH_GOOD;
|
||||
}
|
||||
@ -1630,6 +1653,25 @@ static int abx500_chargalg_get_property(struct power_supply *psy,
|
||||
|
||||
/* Exposure to the sysfs interface */
|
||||
|
||||
/**
|
||||
* abx500_chargalg_sysfs_show() - sysfs show operations
|
||||
* @kobj: pointer to the struct kobject
|
||||
* @attr: pointer to the struct attribute
|
||||
* @buf: buffer that holds the parameter to send to userspace
|
||||
*
|
||||
* Returns a buffer to be displayed in user space
|
||||
*/
|
||||
static ssize_t abx500_chargalg_sysfs_show(struct kobject *kobj,
|
||||
struct attribute *attr, char *buf)
|
||||
{
|
||||
struct abx500_chargalg *di = container_of(kobj,
|
||||
struct abx500_chargalg, chargalg_kobject);
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
di->susp_status.ac_suspended &&
|
||||
di->susp_status.usb_suspended);
|
||||
}
|
||||
|
||||
/**
|
||||
* abx500_chargalg_sysfs_charger() - sysfs store operations
|
||||
* @kobj: pointer to the struct kobject
|
||||
@ -1698,7 +1740,7 @@ static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj,
|
||||
static struct attribute abx500_chargalg_en_charger = \
|
||||
{
|
||||
.name = "chargalg",
|
||||
.mode = S_IWUGO,
|
||||
.mode = S_IRUGO | S_IWUSR,
|
||||
};
|
||||
|
||||
static struct attribute *abx500_chargalg_chg[] = {
|
||||
@ -1707,6 +1749,7 @@ static struct attribute *abx500_chargalg_chg[] = {
|
||||
};
|
||||
|
||||
static const struct sysfs_ops abx500_chargalg_sysfs_ops = {
|
||||
.show = abx500_chargalg_sysfs_show,
|
||||
.store = abx500_chargalg_sysfs_charger,
|
||||
};
|
||||
|
||||
@ -1806,6 +1849,7 @@ static char *supply_interface[] = {
|
||||
static int abx500_chargalg_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct abx500_bm_data *plat = pdev->dev.platform_data;
|
||||
struct abx500_chargalg *di;
|
||||
int ret = 0;
|
||||
|
||||
@ -1814,21 +1858,19 @@ static int abx500_chargalg_probe(struct platform_device *pdev)
|
||||
dev_err(&pdev->dev, "%s no mem for ab8500_chargalg\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
di->bat = pdev->mfd_cell->platform_data;
|
||||
if (!di->bat) {
|
||||
if (np) {
|
||||
ret = bmdevs_of_probe(&pdev->dev, np, &di->bat);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to get battery information\n");
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
dev_err(&pdev->dev, "missing dt node for ab8500_chargalg\n");
|
||||
return -EINVAL;
|
||||
|
||||
if (!plat) {
|
||||
dev_err(&pdev->dev, "no battery management data supplied\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
di->bm = plat;
|
||||
|
||||
if (np) {
|
||||
ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to get battery information\n");
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
dev_info(&pdev->dev, "falling back to legacy platform data\n");
|
||||
}
|
||||
|
||||
/* get device struct */
|
||||
|
@ -28,7 +28,6 @@
|
||||
* http://www.ti.com/product/bq24155
|
||||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/param.h>
|
||||
@ -734,12 +733,10 @@ static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode)
|
||||
int charger = 0;
|
||||
int boost = 0;
|
||||
|
||||
if (mode == BQ2415X_MODE_HOST_CHARGER ||
|
||||
mode == BQ2415X_MODE_DEDICATED_CHARGER)
|
||||
charger = 1;
|
||||
|
||||
if (mode == BQ2415X_MODE_BOOST)
|
||||
boost = 1;
|
||||
else if (mode != BQ2415X_MODE_OFF)
|
||||
charger = 1;
|
||||
|
||||
if (!charger)
|
||||
ret = bq2415x_exec_command(bq, BQ2415X_CHARGER_DISABLE);
|
||||
@ -751,6 +748,10 @@ static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode)
|
||||
return ret;
|
||||
|
||||
switch (mode) {
|
||||
case BQ2415X_MODE_OFF:
|
||||
dev_dbg(bq->dev, "changing mode to: Offline\n");
|
||||
ret = bq2415x_set_current_limit(bq, 100);
|
||||
break;
|
||||
case BQ2415X_MODE_NONE:
|
||||
dev_dbg(bq->dev, "changing mode to: N/A\n");
|
||||
ret = bq2415x_set_current_limit(bq, 100);
|
||||
@ -843,7 +844,7 @@ static void bq2415x_timer_error(struct bq2415x_device *bq, const char *msg)
|
||||
dev_err(bq->dev, "%s\n", msg);
|
||||
if (bq->automode > 0)
|
||||
bq->automode = 0;
|
||||
bq2415x_set_mode(bq, BQ2415X_MODE_NONE);
|
||||
bq2415x_set_mode(bq, BQ2415X_MODE_OFF);
|
||||
bq2415x_set_autotimer(bq, 0);
|
||||
}
|
||||
|
||||
@ -1136,6 +1137,10 @@ static ssize_t bq2415x_sysfs_set_mode(struct device *dev,
|
||||
return -ENOSYS;
|
||||
bq->automode = 1;
|
||||
mode = bq->reported_mode;
|
||||
} else if (strncmp(buf, "off", 3) == 0) {
|
||||
if (bq->automode > 0)
|
||||
bq->automode = 0;
|
||||
mode = BQ2415X_MODE_OFF;
|
||||
} else if (strncmp(buf, "none", 4) == 0) {
|
||||
if (bq->automode > 0)
|
||||
bq->automode = 0;
|
||||
@ -1183,6 +1188,9 @@ static ssize_t bq2415x_sysfs_show_mode(struct device *dev,
|
||||
ret += sprintf(buf+ret, "auto (");
|
||||
|
||||
switch (bq->mode) {
|
||||
case BQ2415X_MODE_OFF:
|
||||
ret += sprintf(buf+ret, "off");
|
||||
break;
|
||||
case BQ2415X_MODE_NONE:
|
||||
ret += sprintf(buf+ret, "none");
|
||||
break;
|
||||
@ -1217,6 +1225,8 @@ static ssize_t bq2415x_sysfs_show_reported_mode(struct device *dev,
|
||||
return -EINVAL;
|
||||
|
||||
switch (bq->reported_mode) {
|
||||
case BQ2415X_MODE_OFF:
|
||||
return sprintf(buf, "off\n");
|
||||
case BQ2415X_MODE_NONE:
|
||||
return sprintf(buf, "none\n");
|
||||
case BQ2415X_MODE_HOST_CHARGER:
|
||||
@ -1523,7 +1533,7 @@ static int bq2415x_probe(struct i2c_client *client,
|
||||
goto error_1;
|
||||
}
|
||||
|
||||
bq = kzalloc(sizeof(*bq), GFP_KERNEL);
|
||||
bq = devm_kzalloc(&client->dev, sizeof(*bq), GFP_KERNEL);
|
||||
if (!bq) {
|
||||
dev_err(&client->dev, "failed to allocate device data\n");
|
||||
ret = -ENOMEM;
|
||||
@ -1536,8 +1546,8 @@ static int bq2415x_probe(struct i2c_client *client,
|
||||
bq->dev = &client->dev;
|
||||
bq->chip = id->driver_data;
|
||||
bq->name = name;
|
||||
bq->mode = BQ2415X_MODE_NONE;
|
||||
bq->reported_mode = BQ2415X_MODE_NONE;
|
||||
bq->mode = BQ2415X_MODE_OFF;
|
||||
bq->reported_mode = BQ2415X_MODE_OFF;
|
||||
bq->autotimer = 0;
|
||||
bq->automode = 0;
|
||||
|
||||
@ -1549,19 +1559,19 @@ static int bq2415x_probe(struct i2c_client *client,
|
||||
ret = bq2415x_power_supply_init(bq);
|
||||
if (ret) {
|
||||
dev_err(bq->dev, "failed to register power supply: %d\n", ret);
|
||||
goto error_3;
|
||||
goto error_2;
|
||||
}
|
||||
|
||||
ret = bq2415x_sysfs_init(bq);
|
||||
if (ret) {
|
||||
dev_err(bq->dev, "failed to create sysfs entries: %d\n", ret);
|
||||
goto error_4;
|
||||
goto error_3;
|
||||
}
|
||||
|
||||
ret = bq2415x_set_defaults(bq);
|
||||
if (ret) {
|
||||
dev_err(bq->dev, "failed to set default values: %d\n", ret);
|
||||
goto error_5;
|
||||
goto error_4;
|
||||
}
|
||||
|
||||
if (bq->init_data.set_mode_hook) {
|
||||
@ -1585,12 +1595,10 @@ static int bq2415x_probe(struct i2c_client *client,
|
||||
dev_info(bq->dev, "driver registered\n");
|
||||
return 0;
|
||||
|
||||
error_5:
|
||||
bq2415x_sysfs_exit(bq);
|
||||
error_4:
|
||||
bq2415x_power_supply_exit(bq);
|
||||
bq2415x_sysfs_exit(bq);
|
||||
error_3:
|
||||
kfree(bq);
|
||||
bq2415x_power_supply_exit(bq);
|
||||
error_2:
|
||||
kfree(name);
|
||||
error_1:
|
||||
@ -1622,7 +1630,6 @@ static int bq2415x_remove(struct i2c_client *client)
|
||||
dev_info(bq->dev, "driver unregistered\n");
|
||||
|
||||
kfree(bq->name);
|
||||
kfree(bq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1652,18 +1659,7 @@ static struct i2c_driver bq2415x_driver = {
|
||||
.remove = bq2415x_remove,
|
||||
.id_table = bq2415x_i2c_id_table,
|
||||
};
|
||||
|
||||
static int __init bq2415x_init(void)
|
||||
{
|
||||
return i2c_add_driver(&bq2415x_driver);
|
||||
}
|
||||
module_init(bq2415x_init);
|
||||
|
||||
static void __exit bq2415x_exit(void)
|
||||
{
|
||||
i2c_del_driver(&bq2415x_driver);
|
||||
}
|
||||
module_exit(bq2415x_exit);
|
||||
module_i2c_driver(bq2415x_driver);
|
||||
|
||||
MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
|
||||
MODULE_DESCRIPTION("bq2415x charger driver");
|
||||
|
@ -299,7 +299,7 @@ static int bq27x00_battery_read_energy(struct bq27x00_device_info *di)
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the battery temperature in tenths of degree Celsius
|
||||
* Return the battery temperature in tenths of degree Kelvin
|
||||
* Or < 0 if something fails.
|
||||
*/
|
||||
static int bq27x00_battery_read_temperature(struct bq27x00_device_info *di)
|
||||
@ -312,10 +312,8 @@ static int bq27x00_battery_read_temperature(struct bq27x00_device_info *di)
|
||||
return temp;
|
||||
}
|
||||
|
||||
if (bq27xxx_is_chip_version_higher(di))
|
||||
temp -= 2731;
|
||||
else
|
||||
temp = ((temp * 5) - 5463) / 2;
|
||||
if (!bq27xxx_is_chip_version_higher(di))
|
||||
temp = 5 * temp / 2;
|
||||
|
||||
return temp;
|
||||
}
|
||||
@ -448,7 +446,6 @@ static void bq27x00_update(struct bq27x00_device_info *di)
|
||||
cache.temperature = bq27x00_battery_read_temperature(di);
|
||||
if (!is_bq27425)
|
||||
cache.cycle_count = bq27x00_battery_read_cyct(di);
|
||||
cache.cycle_count = bq27x00_battery_read_cyct(di);
|
||||
cache.power_avg =
|
||||
bq27x00_battery_read_pwr_avg(di, BQ27x00_POWER_AVG);
|
||||
|
||||
@ -642,6 +639,8 @@ static int bq27x00_battery_get_property(struct power_supply *psy,
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TEMP:
|
||||
ret = bq27x00_simple_value(di->cache.temperature, val);
|
||||
if (ret == 0)
|
||||
val->intval -= 2731;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
|
||||
ret = bq27x00_simple_value(di->cache.time_to_empty, val);
|
||||
@ -696,7 +695,6 @@ static int bq27x00_powersupply_init(struct bq27x00_device_info *di)
|
||||
int ret;
|
||||
|
||||
di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
di->chip = BQ27425;
|
||||
if (di->chip == BQ27425) {
|
||||
di->bat.properties = bq27425_battery_props;
|
||||
di->bat.num_properties = ARRAY_SIZE(bq27425_battery_props);
|
||||
|
@ -669,15 +669,21 @@ static void _setup_polling(struct work_struct *work)
|
||||
WARN(cm_wq == NULL, "charger-manager: workqueue not initialized"
|
||||
". try it later. %s\n", __func__);
|
||||
|
||||
/*
|
||||
* Use mod_delayed_work() iff the next polling interval should
|
||||
* occur before the currently scheduled one. If @cm_monitor_work
|
||||
* isn't active, the end result is the same, so no need to worry
|
||||
* about stale @next_polling.
|
||||
*/
|
||||
_next_polling = jiffies + polling_jiffy;
|
||||
|
||||
if (!delayed_work_pending(&cm_monitor_work) ||
|
||||
(delayed_work_pending(&cm_monitor_work) &&
|
||||
time_after(next_polling, _next_polling))) {
|
||||
next_polling = jiffies + polling_jiffy;
|
||||
if (time_before(_next_polling, next_polling)) {
|
||||
mod_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy);
|
||||
next_polling = _next_polling;
|
||||
} else {
|
||||
if (queue_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy))
|
||||
next_polling = _next_polling;
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&cm_list_mtx);
|
||||
}
|
||||
@ -751,8 +757,7 @@ static void misc_event_handler(struct charger_manager *cm,
|
||||
if (cm_suspended)
|
||||
device_set_wakeup_capable(cm->dev, true);
|
||||
|
||||
if (!delayed_work_pending(&cm_monitor_work) &&
|
||||
is_polling_required(cm) && cm->desc->polling_interval_ms)
|
||||
if (is_polling_required(cm) && cm->desc->polling_interval_ms)
|
||||
schedule_work(&setup_polling);
|
||||
uevent_notify(cm, default_event_names[type]);
|
||||
}
|
||||
@ -1170,8 +1175,7 @@ static int charger_extcon_notifier(struct notifier_block *self,
|
||||
* when charger cable is attached.
|
||||
*/
|
||||
if (cable->attached && is_polling_required(cable->cm)) {
|
||||
if (work_pending(&setup_polling))
|
||||
cancel_work_sync(&setup_polling);
|
||||
cancel_work_sync(&setup_polling);
|
||||
schedule_work(&setup_polling);
|
||||
}
|
||||
|
||||
@ -1215,6 +1219,55 @@ static int charger_extcon_init(struct charger_manager *cm,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* charger_manager_register_extcon - Register extcon device to recevie state
|
||||
* of charger cable.
|
||||
* @cm: the Charger Manager representing the battery.
|
||||
*
|
||||
* This function support EXTCON(External Connector) subsystem to detect the
|
||||
* state of charger cables for enabling or disabling charger(regulator) and
|
||||
* select the charger cable for charging among a number of external cable
|
||||
* according to policy of H/W board.
|
||||
*/
|
||||
static int charger_manager_register_extcon(struct charger_manager *cm)
|
||||
{
|
||||
struct charger_desc *desc = cm->desc;
|
||||
struct charger_regulator *charger;
|
||||
int ret = 0;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
for (i = 0; i < desc->num_charger_regulators; i++) {
|
||||
charger = &desc->charger_regulators[i];
|
||||
|
||||
charger->consumer = regulator_get(cm->dev,
|
||||
charger->regulator_name);
|
||||
if (charger->consumer == NULL) {
|
||||
dev_err(cm->dev, "Cannot find charger(%s)n",
|
||||
charger->regulator_name);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
charger->cm = cm;
|
||||
|
||||
for (j = 0; j < charger->num_cables; j++) {
|
||||
struct charger_cable *cable = &charger->cables[j];
|
||||
|
||||
ret = charger_extcon_init(cm, cable);
|
||||
if (ret < 0) {
|
||||
dev_err(cm->dev, "Cannot initialize charger(%s)n",
|
||||
charger->regulator_name);
|
||||
goto err;
|
||||
}
|
||||
cable->charger = charger;
|
||||
cable->cm = cm;
|
||||
}
|
||||
}
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* help function of sysfs node to control charger(regulator) */
|
||||
static ssize_t charger_name_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
@ -1274,7 +1327,7 @@ static ssize_t charger_externally_control_store(struct device *dev,
|
||||
|
||||
for (i = 0; i < desc->num_charger_regulators; i++) {
|
||||
if (&desc->charger_regulators[i] != charger &&
|
||||
!desc->charger_regulators[i].externally_control) {
|
||||
!desc->charger_regulators[i].externally_control) {
|
||||
/*
|
||||
* At least, one charger is controlled by
|
||||
* charger-manager
|
||||
@ -1303,13 +1356,107 @@ static ssize_t charger_externally_control_store(struct device *dev,
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* charger_manager_register_sysfs - Register sysfs entry for each charger
|
||||
* @cm: the Charger Manager representing the battery.
|
||||
*
|
||||
* This function add sysfs entry for charger(regulator) to control charger from
|
||||
* user-space. If some development board use one more chargers for charging
|
||||
* but only need one charger on specific case which is dependent on user
|
||||
* scenario or hardware restrictions, the user enter 1 or 0(zero) to '/sys/
|
||||
* class/power_supply/battery/charger.[index]/externally_control'. For example,
|
||||
* if user enter 1 to 'sys/class/power_supply/battery/charger.[index]/
|
||||
* externally_control, this charger isn't controlled from charger-manager and
|
||||
* always stay off state of regulator.
|
||||
*/
|
||||
static int charger_manager_register_sysfs(struct charger_manager *cm)
|
||||
{
|
||||
struct charger_desc *desc = cm->desc;
|
||||
struct charger_regulator *charger;
|
||||
int chargers_externally_control = 1;
|
||||
char buf[11];
|
||||
char *str;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
/* Create sysfs entry to control charger(regulator) */
|
||||
for (i = 0; i < desc->num_charger_regulators; i++) {
|
||||
charger = &desc->charger_regulators[i];
|
||||
|
||||
snprintf(buf, 10, "charger.%d", i);
|
||||
str = kzalloc(sizeof(char) * (strlen(buf) + 1), GFP_KERNEL);
|
||||
if (!str) {
|
||||
dev_err(cm->dev, "Cannot allocate memory: %s\n",
|
||||
charger->regulator_name);
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
strcpy(str, buf);
|
||||
|
||||
charger->attrs[0] = &charger->attr_name.attr;
|
||||
charger->attrs[1] = &charger->attr_state.attr;
|
||||
charger->attrs[2] = &charger->attr_externally_control.attr;
|
||||
charger->attrs[3] = NULL;
|
||||
charger->attr_g.name = str;
|
||||
charger->attr_g.attrs = charger->attrs;
|
||||
|
||||
sysfs_attr_init(&charger->attr_name.attr);
|
||||
charger->attr_name.attr.name = "name";
|
||||
charger->attr_name.attr.mode = 0444;
|
||||
charger->attr_name.show = charger_name_show;
|
||||
|
||||
sysfs_attr_init(&charger->attr_state.attr);
|
||||
charger->attr_state.attr.name = "state";
|
||||
charger->attr_state.attr.mode = 0444;
|
||||
charger->attr_state.show = charger_state_show;
|
||||
|
||||
sysfs_attr_init(&charger->attr_externally_control.attr);
|
||||
charger->attr_externally_control.attr.name
|
||||
= "externally_control";
|
||||
charger->attr_externally_control.attr.mode = 0644;
|
||||
charger->attr_externally_control.show
|
||||
= charger_externally_control_show;
|
||||
charger->attr_externally_control.store
|
||||
= charger_externally_control_store;
|
||||
|
||||
if (!desc->charger_regulators[i].externally_control ||
|
||||
!chargers_externally_control)
|
||||
chargers_externally_control = 0;
|
||||
|
||||
dev_info(cm->dev, "'%s' regulator's externally_control"
|
||||
"is %d\n", charger->regulator_name,
|
||||
charger->externally_control);
|
||||
|
||||
ret = sysfs_create_group(&cm->charger_psy.dev->kobj,
|
||||
&charger->attr_g);
|
||||
if (ret < 0) {
|
||||
dev_err(cm->dev, "Cannot create sysfs entry"
|
||||
"of %s regulator\n",
|
||||
charger->regulator_name);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (chargers_externally_control) {
|
||||
dev_err(cm->dev, "Cannot register regulator because "
|
||||
"charger-manager must need at least "
|
||||
"one charger for charging battery\n");
|
||||
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int charger_manager_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct charger_desc *desc = dev_get_platdata(&pdev->dev);
|
||||
struct charger_manager *cm;
|
||||
int ret = 0, i = 0;
|
||||
int j = 0;
|
||||
int chargers_externally_control = 1;
|
||||
union power_supply_propval val;
|
||||
|
||||
if (g_desc && !rtc_dev && g_desc->rtc_name) {
|
||||
@ -1440,11 +1587,10 @@ static int charger_manager_probe(struct platform_device *pdev)
|
||||
|
||||
memcpy(&cm->charger_psy, &psy_default, sizeof(psy_default));
|
||||
|
||||
if (!desc->psy_name) {
|
||||
if (!desc->psy_name)
|
||||
strncpy(cm->psy_name_buf, psy_default.name, PSY_NAME_MAX);
|
||||
} else {
|
||||
else
|
||||
strncpy(cm->psy_name_buf, desc->psy_name, PSY_NAME_MAX);
|
||||
}
|
||||
cm->charger_psy.name = cm->psy_name_buf;
|
||||
|
||||
/* Allocate for psy properties because they may vary */
|
||||
@ -1496,105 +1642,19 @@ static int charger_manager_probe(struct platform_device *pdev)
|
||||
goto err_register;
|
||||
}
|
||||
|
||||
for (i = 0 ; i < desc->num_charger_regulators ; i++) {
|
||||
struct charger_regulator *charger
|
||||
= &desc->charger_regulators[i];
|
||||
char buf[11];
|
||||
char *str;
|
||||
|
||||
charger->consumer = regulator_get(&pdev->dev,
|
||||
charger->regulator_name);
|
||||
if (charger->consumer == NULL) {
|
||||
dev_err(&pdev->dev, "Cannot find charger(%s)n",
|
||||
charger->regulator_name);
|
||||
ret = -EINVAL;
|
||||
goto err_chg_get;
|
||||
}
|
||||
charger->cm = cm;
|
||||
|
||||
for (j = 0 ; j < charger->num_cables ; j++) {
|
||||
struct charger_cable *cable = &charger->cables[j];
|
||||
|
||||
ret = charger_extcon_init(cm, cable);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Cannot find charger(%s)n",
|
||||
charger->regulator_name);
|
||||
goto err_extcon;
|
||||
}
|
||||
cable->charger = charger;
|
||||
cable->cm = cm;
|
||||
}
|
||||
|
||||
/* Create sysfs entry to control charger(regulator) */
|
||||
snprintf(buf, 10, "charger.%d", i);
|
||||
str = kzalloc(sizeof(char) * (strlen(buf) + 1), GFP_KERNEL);
|
||||
if (!str) {
|
||||
for (i--; i >= 0; i--) {
|
||||
charger = &desc->charger_regulators[i];
|
||||
kfree(charger->attr_g.name);
|
||||
}
|
||||
ret = -ENOMEM;
|
||||
|
||||
goto err_extcon;
|
||||
}
|
||||
strcpy(str, buf);
|
||||
|
||||
charger->attrs[0] = &charger->attr_name.attr;
|
||||
charger->attrs[1] = &charger->attr_state.attr;
|
||||
charger->attrs[2] = &charger->attr_externally_control.attr;
|
||||
charger->attrs[3] = NULL;
|
||||
charger->attr_g.name = str;
|
||||
charger->attr_g.attrs = charger->attrs;
|
||||
|
||||
sysfs_attr_init(&charger->attr_name.attr);
|
||||
charger->attr_name.attr.name = "name";
|
||||
charger->attr_name.attr.mode = 0444;
|
||||
charger->attr_name.show = charger_name_show;
|
||||
|
||||
sysfs_attr_init(&charger->attr_state.attr);
|
||||
charger->attr_state.attr.name = "state";
|
||||
charger->attr_state.attr.mode = 0444;
|
||||
charger->attr_state.show = charger_state_show;
|
||||
|
||||
sysfs_attr_init(&charger->attr_externally_control.attr);
|
||||
charger->attr_externally_control.attr.name
|
||||
= "externally_control";
|
||||
charger->attr_externally_control.attr.mode = 0644;
|
||||
charger->attr_externally_control.show
|
||||
= charger_externally_control_show;
|
||||
charger->attr_externally_control.store
|
||||
= charger_externally_control_store;
|
||||
|
||||
if (!desc->charger_regulators[i].externally_control ||
|
||||
!chargers_externally_control) {
|
||||
chargers_externally_control = 0;
|
||||
}
|
||||
dev_info(&pdev->dev, "'%s' regulator's externally_control"
|
||||
"is %d\n", charger->regulator_name,
|
||||
charger->externally_control);
|
||||
|
||||
ret = sysfs_create_group(&cm->charger_psy.dev->kobj,
|
||||
&charger->attr_g);
|
||||
if (ret < 0) {
|
||||
dev_info(&pdev->dev, "Cannot create sysfs entry"
|
||||
"of %s regulator\n",
|
||||
charger->regulator_name);
|
||||
}
|
||||
/* Register extcon device for charger cable */
|
||||
ret = charger_manager_register_extcon(cm);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Cannot initialize extcon device\n");
|
||||
goto err_reg_extcon;
|
||||
}
|
||||
|
||||
if (chargers_externally_control) {
|
||||
dev_err(&pdev->dev, "Cannot register regulator because "
|
||||
"charger-manager must need at least "
|
||||
"one charger for charging battery\n");
|
||||
|
||||
ret = -EINVAL;
|
||||
goto err_chg_enable;
|
||||
}
|
||||
|
||||
ret = try_charger_enable(cm, true);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Cannot enable charger regulators\n");
|
||||
goto err_chg_enable;
|
||||
/* Register sysfs entry for charger(regulator) */
|
||||
ret = charger_manager_register_sysfs(cm);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"Cannot initialize sysfs entry of regulator\n");
|
||||
goto err_reg_sysfs;
|
||||
}
|
||||
|
||||
/* Add to the list */
|
||||
@ -1613,27 +1673,28 @@ static int charger_manager_probe(struct platform_device *pdev)
|
||||
|
||||
return 0;
|
||||
|
||||
err_chg_enable:
|
||||
err_reg_sysfs:
|
||||
for (i = 0; i < desc->num_charger_regulators; i++) {
|
||||
struct charger_regulator *charger;
|
||||
|
||||
charger = &desc->charger_regulators[i];
|
||||
sysfs_remove_group(&cm->charger_psy.dev->kobj,
|
||||
&charger->attr_g);
|
||||
|
||||
kfree(charger->attr_g.name);
|
||||
}
|
||||
err_extcon:
|
||||
for (i = 0 ; i < desc->num_charger_regulators ; i++) {
|
||||
struct charger_regulator *charger
|
||||
= &desc->charger_regulators[i];
|
||||
for (j = 0 ; j < charger->num_cables ; j++) {
|
||||
err_reg_extcon:
|
||||
for (i = 0; i < desc->num_charger_regulators; i++) {
|
||||
struct charger_regulator *charger;
|
||||
|
||||
charger = &desc->charger_regulators[i];
|
||||
for (j = 0; j < charger->num_cables; j++) {
|
||||
struct charger_cable *cable = &charger->cables[j];
|
||||
extcon_unregister_interest(&cable->extcon_dev);
|
||||
}
|
||||
}
|
||||
err_chg_get:
|
||||
for (i = 0 ; i < desc->num_charger_regulators ; i++)
|
||||
|
||||
regulator_put(desc->charger_regulators[i].consumer);
|
||||
}
|
||||
|
||||
power_supply_unregister(&cm->charger_psy);
|
||||
err_register:
|
||||
@ -1661,10 +1722,8 @@ static int charger_manager_remove(struct platform_device *pdev)
|
||||
list_del(&cm->entry);
|
||||
mutex_unlock(&cm_list_mtx);
|
||||
|
||||
if (work_pending(&setup_polling))
|
||||
cancel_work_sync(&setup_polling);
|
||||
if (delayed_work_pending(&cm_monitor_work))
|
||||
cancel_delayed_work_sync(&cm_monitor_work);
|
||||
cancel_work_sync(&setup_polling);
|
||||
cancel_delayed_work_sync(&cm_monitor_work);
|
||||
|
||||
for (i = 0 ; i < desc->num_charger_regulators ; i++) {
|
||||
struct charger_regulator *charger
|
||||
@ -1733,8 +1792,7 @@ static int cm_suspend_prepare(struct device *dev)
|
||||
cm_suspended = true;
|
||||
}
|
||||
|
||||
if (delayed_work_pending(&cm->fullbatt_vchk_work))
|
||||
cancel_delayed_work(&cm->fullbatt_vchk_work);
|
||||
cancel_delayed_work(&cm->fullbatt_vchk_work);
|
||||
cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm);
|
||||
cm->status_save_batt = is_batt_present(cm);
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/notifier.h>
|
||||
|
||||
#define DA9030_FAULT_LOG 0x0a
|
||||
#define DA9030_FAULT_LOG_OVER_TEMP (1 << 7)
|
||||
|
@ -337,7 +337,7 @@ static unsigned char da9052_determine_vc_tbl_index(unsigned char adc_temp)
|
||||
if (adc_temp > vc_tbl_ref[DA9052_VC_TBL_REF_SZ - 1])
|
||||
return DA9052_VC_TBL_REF_SZ - 1;
|
||||
|
||||
for (i = 0; i < DA9052_VC_TBL_REF_SZ; i++) {
|
||||
for (i = 0; i < DA9052_VC_TBL_REF_SZ - 1; i++) {
|
||||
if ((adc_temp > vc_tbl_ref[i]) &&
|
||||
(adc_temp <= DA9052_MEAN(vc_tbl_ref[i], vc_tbl_ref[i + 1])))
|
||||
return i;
|
||||
|
@ -7,6 +7,8 @@
|
||||
*
|
||||
* DS2786 added by Yulia Vilensky <vilensky@compulab.co.il>
|
||||
*
|
||||
* UEvent sending added by Evgeny Romanov <romanov@neurosoft.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
@ -19,6 +21,7 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/swab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/slab.h>
|
||||
@ -40,6 +43,8 @@
|
||||
|
||||
#define DS2786_CURRENT_UNITS 25
|
||||
|
||||
#define DS278x_DELAY 1000
|
||||
|
||||
struct ds278x_info;
|
||||
|
||||
struct ds278x_battery_ops {
|
||||
@ -54,8 +59,11 @@ struct ds278x_info {
|
||||
struct i2c_client *client;
|
||||
struct power_supply battery;
|
||||
struct ds278x_battery_ops *ops;
|
||||
struct delayed_work bat_work;
|
||||
int id;
|
||||
int rsns;
|
||||
int capacity;
|
||||
int status; /* State Of Charge */
|
||||
};
|
||||
|
||||
static DEFINE_IDR(battery_id);
|
||||
@ -220,6 +228,8 @@ static int ds278x_get_status(struct ds278x_info *info, int *status)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
info->capacity = capacity;
|
||||
|
||||
if (capacity == 100)
|
||||
*status = POWER_SUPPLY_STATUS_FULL;
|
||||
else if (current_uA == 0)
|
||||
@ -267,6 +277,27 @@ static int ds278x_battery_get_property(struct power_supply *psy,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ds278x_bat_update(struct ds278x_info *info)
|
||||
{
|
||||
int old_status = info->status;
|
||||
int old_capacity = info->capacity;
|
||||
|
||||
ds278x_get_status(info, &info->status);
|
||||
|
||||
if ((old_status != info->status) || (old_capacity != info->capacity))
|
||||
power_supply_changed(&info->battery);
|
||||
}
|
||||
|
||||
static void ds278x_bat_work(struct work_struct *work)
|
||||
{
|
||||
struct ds278x_info *info;
|
||||
|
||||
info = container_of(work, struct ds278x_info, bat_work.work);
|
||||
ds278x_bat_update(info);
|
||||
|
||||
schedule_delayed_work(&info->bat_work, DS278x_DELAY);
|
||||
}
|
||||
|
||||
static enum power_supply_property ds278x_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
@ -295,10 +326,39 @@ static int ds278x_battery_remove(struct i2c_client *client)
|
||||
idr_remove(&battery_id, info->id);
|
||||
mutex_unlock(&battery_lock);
|
||||
|
||||
cancel_delayed_work(&info->bat_work);
|
||||
|
||||
kfree(info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int ds278x_suspend(struct i2c_client *client,
|
||||
pm_message_t state)
|
||||
{
|
||||
struct ds278x_info *info = i2c_get_clientdata(client);
|
||||
|
||||
cancel_delayed_work(&info->bat_work);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ds278x_resume(struct i2c_client *client)
|
||||
{
|
||||
struct ds278x_info *info = i2c_get_clientdata(client);
|
||||
|
||||
schedule_delayed_work(&info->bat_work, DS278x_DELAY);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define ds278x_suspend NULL
|
||||
#define ds278x_resume NULL
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
|
||||
enum ds278x_num_id {
|
||||
DS2782 = 0,
|
||||
DS2786,
|
||||
@ -368,10 +428,17 @@ static int ds278x_battery_probe(struct i2c_client *client,
|
||||
info->ops = &ds278x_ops[id->driver_data];
|
||||
ds278x_power_supply_init(&info->battery);
|
||||
|
||||
info->capacity = 100;
|
||||
info->status = POWER_SUPPLY_STATUS_FULL;
|
||||
|
||||
INIT_DELAYED_WORK(&info->bat_work, ds278x_bat_work);
|
||||
|
||||
ret = power_supply_register(&client->dev, &info->battery);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to register battery\n");
|
||||
goto fail_register;
|
||||
} else {
|
||||
schedule_delayed_work(&info->bat_work, DS278x_DELAY);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -401,6 +468,8 @@ static struct i2c_driver ds278x_battery_driver = {
|
||||
},
|
||||
.probe = ds278x_battery_probe,
|
||||
.remove = ds278x_battery_remove,
|
||||
.suspend = ds278x_suspend,
|
||||
.resume = ds278x_resume,
|
||||
.id_table = ds278x_id,
|
||||
};
|
||||
module_i2c_driver(ds278x_battery_driver);
|
||||
|
@ -263,9 +263,6 @@ static int gab_probe(struct platform_device *pdev)
|
||||
psy->external_power_changed = gab_ext_power_changed;
|
||||
adc_bat->pdata = pdata;
|
||||
|
||||
/* calculate the total number of channels */
|
||||
chan = ARRAY_SIZE(gab_chan_name);
|
||||
|
||||
/*
|
||||
* copying the static properties and allocating extra memory for holding
|
||||
* the extra configurable properties received from platform data.
|
||||
@ -291,6 +288,7 @@ static int gab_probe(struct platform_device *pdev)
|
||||
gab_chan_name[chan]);
|
||||
if (IS_ERR(adc_bat->channel[chan])) {
|
||||
ret = PTR_ERR(adc_bat->channel[chan]);
|
||||
adc_bat->channel[chan] = NULL;
|
||||
} else {
|
||||
/* copying properties for supported channels only */
|
||||
memcpy(properties + sizeof(*(psy->properties)) * index,
|
||||
@ -344,8 +342,10 @@ err_gpio:
|
||||
gpio_req_fail:
|
||||
power_supply_unregister(psy);
|
||||
err_reg_fail:
|
||||
for (chan = 0; ARRAY_SIZE(gab_chan_name); chan++)
|
||||
iio_channel_release(adc_bat->channel[chan]);
|
||||
for (chan = 0; chan < ARRAY_SIZE(gab_chan_name); chan++) {
|
||||
if (adc_bat->channel[chan])
|
||||
iio_channel_release(adc_bat->channel[chan]);
|
||||
}
|
||||
second_mem_fail:
|
||||
kfree(psy->properties);
|
||||
first_mem_fail:
|
||||
@ -365,8 +365,10 @@ static int gab_remove(struct platform_device *pdev)
|
||||
gpio_free(pdata->gpio_charge_finished);
|
||||
}
|
||||
|
||||
for (chan = 0; ARRAY_SIZE(gab_chan_name); chan++)
|
||||
iio_channel_release(adc_bat->channel[chan]);
|
||||
for (chan = 0; chan < ARRAY_SIZE(gab_chan_name); chan++) {
|
||||
if (adc_bat->channel[chan])
|
||||
iio_channel_release(adc_bat->channel[chan]);
|
||||
}
|
||||
|
||||
kfree(adc_bat->psy.properties);
|
||||
cancel_delayed_work(&adc_bat->bat_work);
|
||||
|
236
drivers/power/goldfish_battery.c
Normal file
236
drivers/power/goldfish_battery.c
Normal file
@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Power supply driver for the goldfish emulator
|
||||
*
|
||||
* Copyright (C) 2008 Google, Inc.
|
||||
* Copyright (C) 2012 Intel, Inc.
|
||||
* Copyright (C) 2013 Intel, Inc.
|
||||
* Author: Mike Lockwood <lockwood@android.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
struct goldfish_battery_data {
|
||||
void __iomem *reg_base;
|
||||
int irq;
|
||||
spinlock_t lock;
|
||||
|
||||
struct power_supply battery;
|
||||
struct power_supply ac;
|
||||
};
|
||||
|
||||
#define GOLDFISH_BATTERY_READ(data, addr) \
|
||||
(readl(data->reg_base + addr))
|
||||
#define GOLDFISH_BATTERY_WRITE(data, addr, x) \
|
||||
(writel(x, data->reg_base + addr))
|
||||
|
||||
/*
|
||||
* Temporary variable used between goldfish_battery_probe() and
|
||||
* goldfish_battery_open().
|
||||
*/
|
||||
static struct goldfish_battery_data *battery_data;
|
||||
|
||||
enum {
|
||||
/* status register */
|
||||
BATTERY_INT_STATUS = 0x00,
|
||||
/* set this to enable IRQ */
|
||||
BATTERY_INT_ENABLE = 0x04,
|
||||
|
||||
BATTERY_AC_ONLINE = 0x08,
|
||||
BATTERY_STATUS = 0x0C,
|
||||
BATTERY_HEALTH = 0x10,
|
||||
BATTERY_PRESENT = 0x14,
|
||||
BATTERY_CAPACITY = 0x18,
|
||||
|
||||
BATTERY_STATUS_CHANGED = 1U << 0,
|
||||
AC_STATUS_CHANGED = 1U << 1,
|
||||
BATTERY_INT_MASK = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED,
|
||||
};
|
||||
|
||||
|
||||
static int goldfish_ac_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct goldfish_battery_data *data = container_of(psy,
|
||||
struct goldfish_battery_data, ac);
|
||||
int ret = 0;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_AC_ONLINE);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int goldfish_battery_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct goldfish_battery_data *data = container_of(psy,
|
||||
struct goldfish_battery_data, battery);
|
||||
int ret = 0;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_STATUS);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_HEALTH:
|
||||
val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_HEALTH);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_PRESENT);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TECHNOLOGY:
|
||||
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CAPACITY);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum power_supply_property goldfish_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
};
|
||||
|
||||
static enum power_supply_property goldfish_ac_props[] = {
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
};
|
||||
|
||||
static irqreturn_t goldfish_battery_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
unsigned long irq_flags;
|
||||
struct goldfish_battery_data *data = dev_id;
|
||||
uint32_t status;
|
||||
|
||||
spin_lock_irqsave(&data->lock, irq_flags);
|
||||
|
||||
/* read status flags, which will clear the interrupt */
|
||||
status = GOLDFISH_BATTERY_READ(data, BATTERY_INT_STATUS);
|
||||
status &= BATTERY_INT_MASK;
|
||||
|
||||
if (status & BATTERY_STATUS_CHANGED)
|
||||
power_supply_changed(&data->battery);
|
||||
if (status & AC_STATUS_CHANGED)
|
||||
power_supply_changed(&data->ac);
|
||||
|
||||
spin_unlock_irqrestore(&data->lock, irq_flags);
|
||||
return status ? IRQ_HANDLED : IRQ_NONE;
|
||||
}
|
||||
|
||||
|
||||
static int goldfish_battery_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct resource *r;
|
||||
struct goldfish_battery_data *data;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&data->lock);
|
||||
|
||||
data->battery.properties = goldfish_battery_props;
|
||||
data->battery.num_properties = ARRAY_SIZE(goldfish_battery_props);
|
||||
data->battery.get_property = goldfish_battery_get_property;
|
||||
data->battery.name = "battery";
|
||||
data->battery.type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
|
||||
data->ac.properties = goldfish_ac_props;
|
||||
data->ac.num_properties = ARRAY_SIZE(goldfish_ac_props);
|
||||
data->ac.get_property = goldfish_ac_get_property;
|
||||
data->ac.name = "ac";
|
||||
data->ac.type = POWER_SUPPLY_TYPE_MAINS;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (r == NULL) {
|
||||
dev_err(&pdev->dev, "platform_get_resource failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
data->reg_base = devm_ioremap(&pdev->dev, r->start, r->end - r->start + 1);
|
||||
if (data->reg_base == NULL) {
|
||||
dev_err(&pdev->dev, "unable to remap MMIO\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
data->irq = platform_get_irq(pdev, 0);
|
||||
if (data->irq < 0) {
|
||||
dev_err(&pdev->dev, "platform_get_irq failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, data->irq, goldfish_battery_interrupt,
|
||||
IRQF_SHARED, pdev->name, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = power_supply_register(&pdev->dev, &data->ac);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = power_supply_register(&pdev->dev, &data->battery);
|
||||
if (ret) {
|
||||
power_supply_unregister(&data->ac);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
battery_data = data;
|
||||
|
||||
GOLDFISH_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int goldfish_battery_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct goldfish_battery_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
power_supply_unregister(&data->battery);
|
||||
power_supply_unregister(&data->ac);
|
||||
battery_data = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver goldfish_battery_device = {
|
||||
.probe = goldfish_battery_probe,
|
||||
.remove = goldfish_battery_remove,
|
||||
.driver = {
|
||||
.name = "goldfish-battery"
|
||||
}
|
||||
};
|
||||
module_platform_driver(goldfish_battery_device);
|
||||
|
||||
MODULE_AUTHOR("Mike Lockwood lockwood@android.com");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Battery driver for the Goldfish emulator");
|
@ -367,28 +367,28 @@ static int lp8727_battery_get_property(struct power_supply *psy,
|
||||
return -EINVAL;
|
||||
|
||||
if (pdata->get_batt_present)
|
||||
val->intval = pchg->pdata->get_batt_present();
|
||||
val->intval = pdata->get_batt_present();
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
if (!pdata)
|
||||
return -EINVAL;
|
||||
|
||||
if (pdata->get_batt_level)
|
||||
val->intval = pchg->pdata->get_batt_level();
|
||||
val->intval = pdata->get_batt_level();
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
if (!pdata)
|
||||
return -EINVAL;
|
||||
|
||||
if (pdata->get_batt_capacity)
|
||||
val->intval = pchg->pdata->get_batt_capacity();
|
||||
val->intval = pdata->get_batt_capacity();
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TEMP:
|
||||
if (!pdata)
|
||||
return -EINVAL;
|
||||
|
||||
if (pdata->get_batt_temp)
|
||||
val->intval = pchg->pdata->get_batt_temp();
|
||||
val->intval = pdata->get_batt_temp();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -367,7 +367,8 @@ static inline bool lp8788_is_valid_charger_register(u8 addr)
|
||||
return addr >= LP8788_CHG_START && addr <= LP8788_CHG_END;
|
||||
}
|
||||
|
||||
static int lp8788_update_charger_params(struct lp8788_charger *pchg)
|
||||
static int lp8788_update_charger_params(struct platform_device *pdev,
|
||||
struct lp8788_charger *pchg)
|
||||
{
|
||||
struct lp8788 *lp = pchg->lp;
|
||||
struct lp8788_charger_platform_data *pdata = pchg->pdata;
|
||||
@ -376,7 +377,7 @@ static int lp8788_update_charger_params(struct lp8788_charger *pchg)
|
||||
int ret;
|
||||
|
||||
if (!pdata || !pdata->chg_params) {
|
||||
dev_info(lp->dev, "skip updating charger parameters\n");
|
||||
dev_info(&pdev->dev, "skip updating charger parameters\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -537,7 +538,6 @@ err_free_irq:
|
||||
static int lp8788_irq_register(struct platform_device *pdev,
|
||||
struct lp8788_charger *pchg)
|
||||
{
|
||||
struct lp8788 *lp = pchg->lp;
|
||||
const char *name[] = {
|
||||
LP8788_CHG_IRQ, LP8788_PRSW_IRQ, LP8788_BATT_IRQ
|
||||
};
|
||||
@ -550,13 +550,13 @@ static int lp8788_irq_register(struct platform_device *pdev,
|
||||
for (i = 0; i < ARRAY_SIZE(name); i++) {
|
||||
ret = lp8788_set_irqs(pdev, pchg, name[i]);
|
||||
if (ret) {
|
||||
dev_warn(lp->dev, "irq setup failed: %s\n", name[i]);
|
||||
dev_warn(&pdev->dev, "irq setup failed: %s\n", name[i]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (pchg->num_irqs > LP8788_MAX_CHG_IRQS) {
|
||||
dev_err(lp->dev, "invalid total number of irqs: %d\n",
|
||||
dev_err(&pdev->dev, "invalid total number of irqs: %d\n",
|
||||
pchg->num_irqs);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -690,9 +690,10 @@ static int lp8788_charger_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
|
||||
struct lp8788_charger *pchg;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
pchg = devm_kzalloc(lp->dev, sizeof(struct lp8788_charger), GFP_KERNEL);
|
||||
pchg = devm_kzalloc(dev, sizeof(struct lp8788_charger), GFP_KERNEL);
|
||||
if (!pchg)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -700,7 +701,7 @@ static int lp8788_charger_probe(struct platform_device *pdev)
|
||||
pchg->pdata = lp->pdata ? lp->pdata->chg_pdata : NULL;
|
||||
platform_set_drvdata(pdev, pchg);
|
||||
|
||||
ret = lp8788_update_charger_params(pchg);
|
||||
ret = lp8788_update_charger_params(pdev, pchg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -718,7 +719,7 @@ static int lp8788_charger_probe(struct platform_device *pdev)
|
||||
|
||||
ret = lp8788_irq_register(pdev, pchg);
|
||||
if (ret)
|
||||
dev_warn(lp->dev, "failed to register charger irq: %d\n", ret);
|
||||
dev_warn(dev, "failed to register charger irq: %d\n", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ static int max17040_probe(struct i2c_client *client,
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
|
||||
return -EIO;
|
||||
|
||||
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
|
||||
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -225,7 +225,6 @@ static int max17040_probe(struct i2c_client *client,
|
||||
ret = power_supply_register(&client->dev, &chip->battery);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed: power supply register\n");
|
||||
kfree(chip);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -244,7 +243,6 @@ static int max17040_remove(struct i2c_client *client)
|
||||
|
||||
power_supply_unregister(&chip->battery);
|
||||
cancel_delayed_work(&chip->work);
|
||||
kfree(chip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
1088
drivers/power/pm2301_charger.c
Normal file
1088
drivers/power/pm2301_charger.c
Normal file
File diff suppressed because it is too large
Load Diff
513
drivers/power/pm2301_charger.h
Normal file
513
drivers/power/pm2301_charger.h
Normal file
@ -0,0 +1,513 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2012
|
||||
*
|
||||
* PM2301 power supply interface
|
||||
*
|
||||
* License terms: GNU General Public License (GPL), version 2
|
||||
*/
|
||||
|
||||
#ifndef PM2301_CHARGER_H
|
||||
#define PM2301_CHARGER_H
|
||||
|
||||
#define MAIN_WDOG_ENA 0x01
|
||||
#define MAIN_WDOG_KICK 0x02
|
||||
#define MAIN_WDOG_DIS 0x00
|
||||
#define CHARG_WD_KICK 0x01
|
||||
#define MAIN_CH_ENA 0x01
|
||||
#define MAIN_CH_NO_OVERSHOOT_ENA_N 0x02
|
||||
#define MAIN_CH_DET 0x01
|
||||
#define MAIN_CH_CV_ON 0x04
|
||||
#define OTP_ENABLE_WD 0x01
|
||||
|
||||
#define MAIN_CH_INPUT_CURR_SHIFT 4
|
||||
|
||||
#define LED_INDICATOR_PWM_ENA 0x01
|
||||
#define LED_INDICATOR_PWM_DIS 0x00
|
||||
#define LED_IND_CUR_5MA 0x04
|
||||
#define LED_INDICATOR_PWM_DUTY_252_256 0xBF
|
||||
|
||||
/* HW failure constants */
|
||||
#define MAIN_CH_TH_PROT 0x02
|
||||
#define MAIN_CH_NOK 0x01
|
||||
|
||||
/* Watchdog timeout constant */
|
||||
#define WD_TIMER 0x30 /* 4min */
|
||||
#define WD_KICK_INTERVAL (30 * HZ)
|
||||
|
||||
#define PM2XXX_NUM_INT_REG 0x6
|
||||
|
||||
/* Constant voltage/current */
|
||||
#define PM2XXX_CONST_CURR 0x0
|
||||
#define PM2XXX_CONST_VOLT 0x1
|
||||
|
||||
/* Lowest charger voltage is 3.39V -> 0x4E */
|
||||
#define LOW_VOLT_REG 0x4E
|
||||
|
||||
#define PM2XXX_BATT_CTRL_REG1 0x00
|
||||
#define PM2XXX_BATT_CTRL_REG2 0x01
|
||||
#define PM2XXX_BATT_CTRL_REG3 0x02
|
||||
#define PM2XXX_BATT_CTRL_REG4 0x03
|
||||
#define PM2XXX_BATT_CTRL_REG5 0x04
|
||||
#define PM2XXX_BATT_CTRL_REG6 0x05
|
||||
#define PM2XXX_BATT_CTRL_REG7 0x06
|
||||
#define PM2XXX_BATT_CTRL_REG8 0x07
|
||||
#define PM2XXX_NTC_CTRL_REG1 0x08
|
||||
#define PM2XXX_NTC_CTRL_REG2 0x09
|
||||
#define PM2XXX_BATT_CTRL_REG9 0x0A
|
||||
#define PM2XXX_BATT_STAT_REG1 0x0B
|
||||
#define PM2XXX_INP_VOLT_VPWR2 0x11
|
||||
#define PM2XXX_INP_DROP_VPWR2 0x13
|
||||
#define PM2XXX_INP_VOLT_VPWR1 0x15
|
||||
#define PM2XXX_INP_DROP_VPWR1 0x17
|
||||
#define PM2XXX_INP_MODE_VPWR 0x18
|
||||
#define PM2XXX_BATT_WD_KICK 0x70
|
||||
#define PM2XXX_DEV_VER_STAT 0x0C
|
||||
#define PM2XXX_THERM_WARN_CTRL_REG 0x20
|
||||
#define PM2XXX_BATT_DISC_REG 0x21
|
||||
#define PM2XXX_BATT_LOW_LEV_COMP_REG 0x22
|
||||
#define PM2XXX_BATT_LOW_LEV_VAL_REG 0x23
|
||||
#define PM2XXX_I2C_PAD_CTRL_REG 0x24
|
||||
#define PM2XXX_SW_CTRL_REG 0x26
|
||||
#define PM2XXX_LED_CTRL_REG 0x28
|
||||
|
||||
#define PM2XXX_REG_INT1 0x40
|
||||
#define PM2XXX_MASK_REG_INT1 0x50
|
||||
#define PM2XXX_SRCE_REG_INT1 0x60
|
||||
#define PM2XXX_REG_INT2 0x41
|
||||
#define PM2XXX_MASK_REG_INT2 0x51
|
||||
#define PM2XXX_SRCE_REG_INT2 0x61
|
||||
#define PM2XXX_REG_INT3 0x42
|
||||
#define PM2XXX_MASK_REG_INT3 0x52
|
||||
#define PM2XXX_SRCE_REG_INT3 0x62
|
||||
#define PM2XXX_REG_INT4 0x43
|
||||
#define PM2XXX_MASK_REG_INT4 0x53
|
||||
#define PM2XXX_SRCE_REG_INT4 0x63
|
||||
#define PM2XXX_REG_INT5 0x44
|
||||
#define PM2XXX_MASK_REG_INT5 0x54
|
||||
#define PM2XXX_SRCE_REG_INT5 0x64
|
||||
#define PM2XXX_REG_INT6 0x45
|
||||
#define PM2XXX_MASK_REG_INT6 0x55
|
||||
#define PM2XXX_SRCE_REG_INT6 0x65
|
||||
|
||||
#define VPWR_OVV 0x0
|
||||
#define VSYSTEM_OVV 0x1
|
||||
|
||||
/* control Reg 1 */
|
||||
#define PM2XXX_CH_RESUME_EN 0x1
|
||||
#define PM2XXX_CH_RESUME_DIS 0x0
|
||||
|
||||
/* control Reg 2 */
|
||||
#define PM2XXX_CH_AUTO_RESUME_EN 0X2
|
||||
#define PM2XXX_CH_AUTO_RESUME_DIS 0X0
|
||||
#define PM2XXX_CHARGER_ENA 0x4
|
||||
#define PM2XXX_CHARGER_DIS 0x0
|
||||
|
||||
/* control Reg 3 */
|
||||
#define PM2XXX_CH_WD_CC_PHASE_OFF 0x0
|
||||
#define PM2XXX_CH_WD_CC_PHASE_5MIN 0x1
|
||||
#define PM2XXX_CH_WD_CC_PHASE_10MIN 0x2
|
||||
#define PM2XXX_CH_WD_CC_PHASE_30MIN 0x3
|
||||
#define PM2XXX_CH_WD_CC_PHASE_60MIN 0x4
|
||||
#define PM2XXX_CH_WD_CC_PHASE_120MIN 0x5
|
||||
#define PM2XXX_CH_WD_CC_PHASE_240MIN 0x6
|
||||
#define PM2XXX_CH_WD_CC_PHASE_360MIN 0x7
|
||||
|
||||
#define PM2XXX_CH_WD_CV_PHASE_OFF (0x0<<3)
|
||||
#define PM2XXX_CH_WD_CV_PHASE_5MIN (0x1<<3)
|
||||
#define PM2XXX_CH_WD_CV_PHASE_10MIN (0x2<<3)
|
||||
#define PM2XXX_CH_WD_CV_PHASE_30MIN (0x3<<3)
|
||||
#define PM2XXX_CH_WD_CV_PHASE_60MIN (0x4<<3)
|
||||
#define PM2XXX_CH_WD_CV_PHASE_120MIN (0x5<<3)
|
||||
#define PM2XXX_CH_WD_CV_PHASE_240MIN (0x6<<3)
|
||||
#define PM2XXX_CH_WD_CV_PHASE_360MIN (0x7<<3)
|
||||
|
||||
/* control Reg 4 */
|
||||
#define PM2XXX_CH_WD_PRECH_PHASE_OFF 0x0
|
||||
#define PM2XXX_CH_WD_PRECH_PHASE_1MIN 0x1
|
||||
#define PM2XXX_CH_WD_PRECH_PHASE_5MIN 0x2
|
||||
#define PM2XXX_CH_WD_PRECH_PHASE_10MIN 0x3
|
||||
#define PM2XXX_CH_WD_PRECH_PHASE_30MIN 0x4
|
||||
#define PM2XXX_CH_WD_PRECH_PHASE_60MIN 0x5
|
||||
#define PM2XXX_CH_WD_PRECH_PHASE_120MIN 0x6
|
||||
#define PM2XXX_CH_WD_PRECH_PHASE_240MIN 0x7
|
||||
|
||||
/* control Reg 5 */
|
||||
#define PM2XXX_CH_WD_AUTO_TIMEOUT_NONE 0x0
|
||||
#define PM2XXX_CH_WD_AUTO_TIMEOUT_20MIN 0x1
|
||||
|
||||
/* control Reg 6 */
|
||||
#define PM2XXX_DIR_CH_CC_CURRENT_MASK 0x0F
|
||||
#define PM2XXX_DIR_CH_CC_CURRENT_200MA 0x0
|
||||
#define PM2XXX_DIR_CH_CC_CURRENT_400MA 0x2
|
||||
#define PM2XXX_DIR_CH_CC_CURRENT_600MA 0x3
|
||||
#define PM2XXX_DIR_CH_CC_CURRENT_800MA 0x4
|
||||
#define PM2XXX_DIR_CH_CC_CURRENT_1000MA 0x5
|
||||
#define PM2XXX_DIR_CH_CC_CURRENT_1200MA 0x6
|
||||
#define PM2XXX_DIR_CH_CC_CURRENT_1400MA 0x7
|
||||
#define PM2XXX_DIR_CH_CC_CURRENT_1600MA 0x8
|
||||
#define PM2XXX_DIR_CH_CC_CURRENT_1800MA 0x9
|
||||
#define PM2XXX_DIR_CH_CC_CURRENT_2000MA 0xA
|
||||
#define PM2XXX_DIR_CH_CC_CURRENT_2200MA 0xB
|
||||
#define PM2XXX_DIR_CH_CC_CURRENT_2400MA 0xC
|
||||
#define PM2XXX_DIR_CH_CC_CURRENT_2600MA 0xD
|
||||
#define PM2XXX_DIR_CH_CC_CURRENT_2800MA 0xE
|
||||
#define PM2XXX_DIR_CH_CC_CURRENT_3000MA 0xF
|
||||
|
||||
#define PM2XXX_CH_PRECH_CURRENT_MASK 0x30
|
||||
#define PM2XXX_CH_PRECH_CURRENT_25MA (0x0<<4)
|
||||
#define PM2XXX_CH_PRECH_CURRENT_50MA (0x1<<4)
|
||||
#define PM2XXX_CH_PRECH_CURRENT_75MA (0x2<<4)
|
||||
#define PM2XXX_CH_PRECH_CURRENT_100MA (0x3<<4)
|
||||
|
||||
#define PM2XXX_CH_EOC_CURRENT_MASK 0xC0
|
||||
#define PM2XXX_CH_EOC_CURRENT_100MA (0x0<<6)
|
||||
#define PM2XXX_CH_EOC_CURRENT_150MA (0x1<<6)
|
||||
#define PM2XXX_CH_EOC_CURRENT_300MA (0x2<<6)
|
||||
#define PM2XXX_CH_EOC_CURRENT_400MA (0x3<<6)
|
||||
|
||||
/* control Reg 7 */
|
||||
#define PM2XXX_CH_PRECH_VOL_2_5 0x0
|
||||
#define PM2XXX_CH_PRECH_VOL_2_7 0x1
|
||||
#define PM2XXX_CH_PRECH_VOL_2_9 0x2
|
||||
#define PM2XXX_CH_PRECH_VOL_3_1 0x3
|
||||
|
||||
#define PM2XXX_CH_VRESUME_VOL_3_2 (0x0<<2)
|
||||
#define PM2XXX_CH_VRESUME_VOL_3_4 (0x1<<2)
|
||||
#define PM2XXX_CH_VRESUME_VOL_3_6 (0x2<<2)
|
||||
#define PM2XXX_CH_VRESUME_VOL_3_8 (0x3<<2)
|
||||
|
||||
/* control Reg 8 */
|
||||
#define PM2XXX_CH_VOLT_MASK 0x3F
|
||||
#define PM2XXX_CH_VOLT_3_5 0x0
|
||||
#define PM2XXX_CH_VOLT_3_5225 0x1
|
||||
#define PM2XXX_CH_VOLT_3_6 0x4
|
||||
#define PM2XXX_CH_VOLT_3_7 0x8
|
||||
#define PM2XXX_CH_VOLT_4_0 0x14
|
||||
#define PM2XXX_CH_VOLT_4_175 0x1B
|
||||
#define PM2XXX_CH_VOLT_4_2 0x1C
|
||||
#define PM2XXX_CH_VOLT_4_275 0x1F
|
||||
#define PM2XXX_CH_VOLT_4_3 0x20
|
||||
|
||||
/*NTC control register 1*/
|
||||
#define PM2XXX_BTEMP_HIGH_TH_45 0x0
|
||||
#define PM2XXX_BTEMP_HIGH_TH_50 0x1
|
||||
#define PM2XXX_BTEMP_HIGH_TH_55 0x2
|
||||
#define PM2XXX_BTEMP_HIGH_TH_60 0x3
|
||||
#define PM2XXX_BTEMP_HIGH_TH_65 0x4
|
||||
|
||||
#define PM2XXX_BTEMP_LOW_TH_N5 (0x0<<3)
|
||||
#define PM2XXX_BTEMP_LOW_TH_0 (0x1<<3)
|
||||
#define PM2XXX_BTEMP_LOW_TH_5 (0x2<<3)
|
||||
#define PM2XXX_BTEMP_LOW_TH_10 (0x3<<3)
|
||||
|
||||
/*NTC control register 2*/
|
||||
#define PM2XXX_NTC_BETA_COEFF_3477 0x0
|
||||
#define PM2XXX_NTC_BETA_COEFF_3964 0x1
|
||||
|
||||
#define PM2XXX_NTC_RES_10K (0x0<<2)
|
||||
#define PM2XXX_NTC_RES_47K (0x1<<2)
|
||||
#define PM2XXX_NTC_RES_100K (0x2<<2)
|
||||
#define PM2XXX_NTC_RES_NO_NTC (0x3<<2)
|
||||
|
||||
/* control Reg 9 */
|
||||
#define PM2XXX_CH_CC_MODEDROP_EN 1
|
||||
#define PM2XXX_CH_CC_MODEDROP_DIS 0
|
||||
|
||||
#define PM2XXX_CH_CC_REDUCED_CURRENT_100MA (0x0<<1)
|
||||
#define PM2XXX_CH_CC_REDUCED_CURRENT_200MA (0x1<<1)
|
||||
#define PM2XXX_CH_CC_REDUCED_CURRENT_400MA (0x2<<1)
|
||||
#define PM2XXX_CH_CC_REDUCED_CURRENT_IDENT (0x3<<1)
|
||||
|
||||
#define PM2XXX_CHARCHING_INFO_DIS (0<<3)
|
||||
#define PM2XXX_CHARCHING_INFO_EN (1<<3)
|
||||
|
||||
#define PM2XXX_CH_150MV_DROP_300MV (0<<4)
|
||||
#define PM2XXX_CH_150MV_DROP_150MV (1<<4)
|
||||
|
||||
|
||||
/* charger status register */
|
||||
#define PM2XXX_CHG_STATUS_OFF 0x0
|
||||
#define PM2XXX_CHG_STATUS_ON 0x1
|
||||
#define PM2XXX_CHG_STATUS_FULL 0x2
|
||||
#define PM2XXX_CHG_STATUS_ERR 0x3
|
||||
#define PM2XXX_CHG_STATUS_WAIT 0x4
|
||||
#define PM2XXX_CHG_STATUS_NOBAT 0x5
|
||||
|
||||
/* Input charger voltage VPWR2 */
|
||||
#define PM2XXX_VPWR2_OVV_6_0 0x0
|
||||
#define PM2XXX_VPWR2_OVV_6_3 0x1
|
||||
#define PM2XXX_VPWR2_OVV_10 0x2
|
||||
#define PM2XXX_VPWR2_OVV_NONE 0x3
|
||||
|
||||
/* Input charger drop VPWR2 */
|
||||
#define PM2XXX_VPWR2_HW_OPT_EN (0x1<<4)
|
||||
#define PM2XXX_VPWR2_HW_OPT_DIS (0x0<<4)
|
||||
|
||||
#define PM2XXX_VPWR2_VALID_EN (0x1<<3)
|
||||
#define PM2XXX_VPWR2_VALID_DIS (0x0<<3)
|
||||
|
||||
#define PM2XXX_VPWR2_DROP_EN (0x1<<2)
|
||||
#define PM2XXX_VPWR2_DROP_DIS (0x0<<2)
|
||||
|
||||
/* Input charger voltage VPWR1 */
|
||||
#define PM2XXX_VPWR1_OVV_6_0 0x0
|
||||
#define PM2XXX_VPWR1_OVV_6_3 0x1
|
||||
#define PM2XXX_VPWR1_OVV_10 0x2
|
||||
#define PM2XXX_VPWR1_OVV_NONE 0x3
|
||||
|
||||
/* Input charger drop VPWR1 */
|
||||
#define PM2XXX_VPWR1_HW_OPT_EN (0x1<<4)
|
||||
#define PM2XXX_VPWR1_HW_OPT_DIS (0x0<<4)
|
||||
|
||||
#define PM2XXX_VPWR1_VALID_EN (0x1<<3)
|
||||
#define PM2XXX_VPWR1_VALID_DIS (0x0<<3)
|
||||
|
||||
#define PM2XXX_VPWR1_DROP_EN (0x1<<2)
|
||||
#define PM2XXX_VPWR1_DROP_DIS (0x0<<2)
|
||||
|
||||
/* Battery low level comparator control register */
|
||||
#define PM2XXX_VBAT_LOW_MONITORING_DIS 0x0
|
||||
#define PM2XXX_VBAT_LOW_MONITORING_ENA 0x1
|
||||
|
||||
/* Battery low level value control register */
|
||||
#define PM2XXX_VBAT_LOW_LEVEL_2_3 0x0
|
||||
#define PM2XXX_VBAT_LOW_LEVEL_2_4 0x1
|
||||
#define PM2XXX_VBAT_LOW_LEVEL_2_5 0x2
|
||||
#define PM2XXX_VBAT_LOW_LEVEL_2_6 0x3
|
||||
#define PM2XXX_VBAT_LOW_LEVEL_2_7 0x4
|
||||
#define PM2XXX_VBAT_LOW_LEVEL_2_8 0x5
|
||||
#define PM2XXX_VBAT_LOW_LEVEL_2_9 0x6
|
||||
#define PM2XXX_VBAT_LOW_LEVEL_3_0 0x7
|
||||
#define PM2XXX_VBAT_LOW_LEVEL_3_1 0x8
|
||||
#define PM2XXX_VBAT_LOW_LEVEL_3_2 0x9
|
||||
#define PM2XXX_VBAT_LOW_LEVEL_3_3 0xA
|
||||
#define PM2XXX_VBAT_LOW_LEVEL_3_4 0xB
|
||||
#define PM2XXX_VBAT_LOW_LEVEL_3_5 0xC
|
||||
#define PM2XXX_VBAT_LOW_LEVEL_3_6 0xD
|
||||
#define PM2XXX_VBAT_LOW_LEVEL_3_7 0xE
|
||||
#define PM2XXX_VBAT_LOW_LEVEL_3_8 0xF
|
||||
#define PM2XXX_VBAT_LOW_LEVEL_3_9 0x10
|
||||
#define PM2XXX_VBAT_LOW_LEVEL_4_0 0x11
|
||||
#define PM2XXX_VBAT_LOW_LEVEL_4_1 0x12
|
||||
#define PM2XXX_VBAT_LOW_LEVEL_4_2 0x13
|
||||
|
||||
/* SW CTRL */
|
||||
#define PM2XXX_SWCTRL_HW 0x0
|
||||
#define PM2XXX_SWCTRL_SW 0x1
|
||||
|
||||
|
||||
/* LED Driver Control */
|
||||
#define PM2XXX_LED_CURRENT_MASK 0x0C
|
||||
#define PM2XXX_LED_CURRENT_2_5MA (0X0<<2)
|
||||
#define PM2XXX_LED_CURRENT_1MA (0X1<<2)
|
||||
#define PM2XXX_LED_CURRENT_5MA (0X2<<2)
|
||||
#define PM2XXX_LED_CURRENT_10MA (0X3<<2)
|
||||
|
||||
#define PM2XXX_LED_SELECT_MASK 0x02
|
||||
#define PM2XXX_LED_SELECT_EN (0X0<<1)
|
||||
#define PM2XXX_LED_SELECT_DIS (0X1<<1)
|
||||
|
||||
#define PM2XXX_ANTI_OVERSHOOT_MASK 0x01
|
||||
#define PM2XXX_ANTI_OVERSHOOT_DIS 0X0
|
||||
#define PM2XXX_ANTI_OVERSHOOT_EN 0X1
|
||||
|
||||
enum pm2xxx_reg_int1 {
|
||||
PM2XXX_INT1_ITVBATDISCONNECT = 0x02,
|
||||
PM2XXX_INT1_ITVBATLOWR = 0x04,
|
||||
PM2XXX_INT1_ITVBATLOWF = 0x08,
|
||||
};
|
||||
|
||||
enum pm2xxx_mask_reg_int1 {
|
||||
PM2XXX_INT1_M_ITVBATDISCONNECT = 0x02,
|
||||
PM2XXX_INT1_M_ITVBATLOWR = 0x04,
|
||||
PM2XXX_INT1_M_ITVBATLOWF = 0x08,
|
||||
};
|
||||
|
||||
enum pm2xxx_source_reg_int1 {
|
||||
PM2XXX_INT1_S_ITVBATDISCONNECT = 0x02,
|
||||
PM2XXX_INT1_S_ITVBATLOWR = 0x04,
|
||||
PM2XXX_INT1_S_ITVBATLOWF = 0x08,
|
||||
};
|
||||
|
||||
enum pm2xxx_reg_int2 {
|
||||
PM2XXX_INT2_ITVPWR2PLUG = 0x01,
|
||||
PM2XXX_INT2_ITVPWR2UNPLUG = 0x02,
|
||||
PM2XXX_INT2_ITVPWR1PLUG = 0x04,
|
||||
PM2XXX_INT2_ITVPWR1UNPLUG = 0x08,
|
||||
};
|
||||
|
||||
enum pm2xxx_mask_reg_int2 {
|
||||
PM2XXX_INT2_M_ITVPWR2PLUG = 0x01,
|
||||
PM2XXX_INT2_M_ITVPWR2UNPLUG = 0x02,
|
||||
PM2XXX_INT2_M_ITVPWR1PLUG = 0x04,
|
||||
PM2XXX_INT2_M_ITVPWR1UNPLUG = 0x08,
|
||||
};
|
||||
|
||||
enum pm2xxx_source_reg_int2 {
|
||||
PM2XXX_INT2_S_ITVPWR2PLUG = 0x03,
|
||||
PM2XXX_INT2_S_ITVPWR1PLUG = 0x0c,
|
||||
};
|
||||
|
||||
enum pm2xxx_reg_int3 {
|
||||
PM2XXX_INT3_ITCHPRECHARGEWD = 0x01,
|
||||
PM2XXX_INT3_ITCHCCWD = 0x02,
|
||||
PM2XXX_INT3_ITCHCVWD = 0x04,
|
||||
PM2XXX_INT3_ITAUTOTIMEOUTWD = 0x08,
|
||||
};
|
||||
|
||||
enum pm2xxx_mask_reg_int3 {
|
||||
PM2XXX_INT3_M_ITCHPRECHARGEWD = 0x01,
|
||||
PM2XXX_INT3_M_ITCHCCWD = 0x02,
|
||||
PM2XXX_INT3_M_ITCHCVWD = 0x04,
|
||||
PM2XXX_INT3_M_ITAUTOTIMEOUTWD = 0x08,
|
||||
};
|
||||
|
||||
enum pm2xxx_source_reg_int3 {
|
||||
PM2XXX_INT3_S_ITCHPRECHARGEWD = 0x01,
|
||||
PM2XXX_INT3_S_ITCHCCWD = 0x02,
|
||||
PM2XXX_INT3_S_ITCHCVWD = 0x04,
|
||||
PM2XXX_INT3_S_ITAUTOTIMEOUTWD = 0x08,
|
||||
};
|
||||
|
||||
enum pm2xxx_reg_int4 {
|
||||
PM2XXX_INT4_ITBATTEMPCOLD = 0x01,
|
||||
PM2XXX_INT4_ITBATTEMPHOT = 0x02,
|
||||
PM2XXX_INT4_ITVPWR2OVV = 0x04,
|
||||
PM2XXX_INT4_ITVPWR1OVV = 0x08,
|
||||
PM2XXX_INT4_ITCHARGINGON = 0x10,
|
||||
PM2XXX_INT4_ITVRESUME = 0x20,
|
||||
PM2XXX_INT4_ITBATTFULL = 0x40,
|
||||
PM2XXX_INT4_ITCVPHASE = 0x80,
|
||||
};
|
||||
|
||||
enum pm2xxx_mask_reg_int4 {
|
||||
PM2XXX_INT4_M_ITBATTEMPCOLD = 0x01,
|
||||
PM2XXX_INT4_M_ITBATTEMPHOT = 0x02,
|
||||
PM2XXX_INT4_M_ITVPWR2OVV = 0x04,
|
||||
PM2XXX_INT4_M_ITVPWR1OVV = 0x08,
|
||||
PM2XXX_INT4_M_ITCHARGINGON = 0x10,
|
||||
PM2XXX_INT4_M_ITVRESUME = 0x20,
|
||||
PM2XXX_INT4_M_ITBATTFULL = 0x40,
|
||||
PM2XXX_INT4_M_ITCVPHASE = 0x80,
|
||||
};
|
||||
|
||||
enum pm2xxx_source_reg_int4 {
|
||||
PM2XXX_INT4_S_ITBATTEMPCOLD = 0x01,
|
||||
PM2XXX_INT4_S_ITBATTEMPHOT = 0x02,
|
||||
PM2XXX_INT4_S_ITVPWR2OVV = 0x04,
|
||||
PM2XXX_INT4_S_ITVPWR1OVV = 0x08,
|
||||
PM2XXX_INT4_S_ITCHARGINGON = 0x10,
|
||||
PM2XXX_INT4_S_ITVRESUME = 0x20,
|
||||
PM2XXX_INT4_S_ITBATTFULL = 0x40,
|
||||
PM2XXX_INT4_S_ITCVPHASE = 0x80,
|
||||
};
|
||||
|
||||
enum pm2xxx_reg_int5 {
|
||||
PM2XXX_INT5_ITTHERMALSHUTDOWNRISE = 0x01,
|
||||
PM2XXX_INT5_ITTHERMALSHUTDOWNFALL = 0x02,
|
||||
PM2XXX_INT5_ITTHERMALWARNINGRISE = 0x04,
|
||||
PM2XXX_INT5_ITTHERMALWARNINGFALL = 0x08,
|
||||
PM2XXX_INT5_ITVSYSTEMOVV = 0x10,
|
||||
};
|
||||
|
||||
enum pm2xxx_mask_reg_int5 {
|
||||
PM2XXX_INT5_M_ITTHERMALSHUTDOWNRISE = 0x01,
|
||||
PM2XXX_INT5_M_ITTHERMALSHUTDOWNFALL = 0x02,
|
||||
PM2XXX_INT5_M_ITTHERMALWARNINGRISE = 0x04,
|
||||
PM2XXX_INT5_M_ITTHERMALWARNINGFALL = 0x08,
|
||||
PM2XXX_INT5_M_ITVSYSTEMOVV = 0x10,
|
||||
};
|
||||
|
||||
enum pm2xxx_source_reg_int5 {
|
||||
PM2XXX_INT5_S_ITTHERMALSHUTDOWNRISE = 0x01,
|
||||
PM2XXX_INT5_S_ITTHERMALSHUTDOWNFALL = 0x02,
|
||||
PM2XXX_INT5_S_ITTHERMALWARNINGRISE = 0x04,
|
||||
PM2XXX_INT5_S_ITTHERMALWARNINGFALL = 0x08,
|
||||
PM2XXX_INT5_S_ITVSYSTEMOVV = 0x10,
|
||||
};
|
||||
|
||||
enum pm2xxx_reg_int6 {
|
||||
PM2XXX_INT6_ITVPWR2DROP = 0x01,
|
||||
PM2XXX_INT6_ITVPWR1DROP = 0x02,
|
||||
PM2XXX_INT6_ITVPWR2VALIDRISE = 0x04,
|
||||
PM2XXX_INT6_ITVPWR2VALIDFALL = 0x08,
|
||||
PM2XXX_INT6_ITVPWR1VALIDRISE = 0x10,
|
||||
PM2XXX_INT6_ITVPWR1VALIDFALL = 0x20,
|
||||
};
|
||||
|
||||
enum pm2xxx_mask_reg_int6 {
|
||||
PM2XXX_INT6_M_ITVPWR2DROP = 0x01,
|
||||
PM2XXX_INT6_M_ITVPWR1DROP = 0x02,
|
||||
PM2XXX_INT6_M_ITVPWR2VALIDRISE = 0x04,
|
||||
PM2XXX_INT6_M_ITVPWR2VALIDFALL = 0x08,
|
||||
PM2XXX_INT6_M_ITVPWR1VALIDRISE = 0x10,
|
||||
PM2XXX_INT6_M_ITVPWR1VALIDFALL = 0x20,
|
||||
};
|
||||
|
||||
enum pm2xxx_source_reg_int6 {
|
||||
PM2XXX_INT6_S_ITVPWR2DROP = 0x01,
|
||||
PM2XXX_INT6_S_ITVPWR1DROP = 0x02,
|
||||
PM2XXX_INT6_S_ITVPWR2VALIDRISE = 0x04,
|
||||
PM2XXX_INT6_S_ITVPWR2VALIDFALL = 0x08,
|
||||
PM2XXX_INT6_S_ITVPWR1VALIDRISE = 0x10,
|
||||
PM2XXX_INT6_S_ITVPWR1VALIDFALL = 0x20,
|
||||
};
|
||||
|
||||
struct pm2xxx_charger_info {
|
||||
int charger_connected;
|
||||
int charger_online;
|
||||
int cv_active;
|
||||
bool wd_expired;
|
||||
};
|
||||
|
||||
struct pm2xxx_charger_event_flags {
|
||||
bool mainextchnotok;
|
||||
bool main_thermal_prot;
|
||||
bool ovv;
|
||||
bool chgwdexp;
|
||||
};
|
||||
|
||||
struct pm2xxx_interrupts {
|
||||
u8 reg[PM2XXX_NUM_INT_REG];
|
||||
int (*handler[PM2XXX_NUM_INT_REG])(void *, int);
|
||||
};
|
||||
|
||||
struct pm2xxx_config {
|
||||
struct i2c_client *pm2xxx_i2c;
|
||||
struct i2c_device_id *pm2xxx_id;
|
||||
};
|
||||
|
||||
struct pm2xxx_irq {
|
||||
char *name;
|
||||
irqreturn_t (*isr)(int irq, void *data);
|
||||
};
|
||||
|
||||
struct pm2xxx_charger {
|
||||
struct device *dev;
|
||||
u8 chip_id;
|
||||
bool vddadc_en_ac;
|
||||
struct pm2xxx_config config;
|
||||
bool ac_conn;
|
||||
unsigned int gpio_irq;
|
||||
int vbat;
|
||||
int old_vbat;
|
||||
int failure_case;
|
||||
int failure_input_ovv;
|
||||
unsigned int lpn_pin;
|
||||
struct pm2xxx_interrupts *pm2_int;
|
||||
struct ab8500_gpadc *gpadc;
|
||||
struct regulator *regu;
|
||||
struct pm2xxx_bm_data *bat;
|
||||
struct mutex lock;
|
||||
struct ab8500 *parent;
|
||||
struct pm2xxx_charger_info ac;
|
||||
struct pm2xxx_charger_platform_data *pdata;
|
||||
struct workqueue_struct *charger_wq;
|
||||
struct delayed_work check_vbat_work;
|
||||
struct work_struct ac_work;
|
||||
struct work_struct check_main_thermal_prot_work;
|
||||
struct ux500_charger ac_chg;
|
||||
struct pm2xxx_charger_event_flags flags;
|
||||
};
|
||||
|
||||
#endif /* PM2301_CHARGER_H */
|
@ -55,7 +55,8 @@ static ssize_t power_supply_show_property(struct device *dev,
|
||||
};
|
||||
static char *health_text[] = {
|
||||
"Unknown", "Good", "Overheat", "Dead", "Over voltage",
|
||||
"Unspecified failure", "Cold",
|
||||
"Unspecified failure", "Cold", "Watchdog timer expire",
|
||||
"Safety timer expire"
|
||||
};
|
||||
static char *technology_text[] = {
|
||||
"Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd",
|
||||
|
@ -13,3 +13,20 @@ config POWER_RESET_GPIO
|
||||
This driver supports turning off your board via a GPIO line.
|
||||
If your board needs a GPIO high/low to power down, say Y and
|
||||
create a binding in your devicetree.
|
||||
|
||||
config POWER_RESET_QNAP
|
||||
bool "QNAP power-off driver"
|
||||
depends on OF_GPIO && POWER_RESET && PLAT_ORION
|
||||
help
|
||||
This driver supports turning off QNAP NAS devices by sending
|
||||
commands to the microcontroller which controls the main power.
|
||||
|
||||
Say Y if you have a QNAP NAS.
|
||||
|
||||
config POWER_RESET_RESTART
|
||||
bool "Restart power-off driver"
|
||||
depends on ARM
|
||||
help
|
||||
Some boards don't actually have the ability to power off.
|
||||
Instead they restart, and u-boot holds the SoC until the
|
||||
user presses a key. u-boot then boots into Linux.
|
||||
|
@ -1 +1,3 @@
|
||||
obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o
|
116
drivers/power/reset/qnap-poweroff.c
Normal file
116
drivers/power/reset/qnap-poweroff.c
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* QNAP Turbo NAS Board power off
|
||||
*
|
||||
* Copyright (C) 2012 Andrew Lunn <andrew@lunn.ch>
|
||||
*
|
||||
* Based on the code from:
|
||||
*
|
||||
* Copyright (C) 2009 Martin Michlmayr <tbm@cyrius.com>
|
||||
* Copyright (C) 2008 Byron Bradley <byron.bbradley@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/serial_reg.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#define UART1_REG(x) (base + ((UART_##x) << 2))
|
||||
|
||||
static void __iomem *base;
|
||||
static unsigned long tclk;
|
||||
|
||||
static void qnap_power_off(void)
|
||||
{
|
||||
/* 19200 baud divisor */
|
||||
const unsigned divisor = ((tclk + (8 * 19200)) / (16 * 19200));
|
||||
|
||||
pr_err("%s: triggering power-off...\n", __func__);
|
||||
|
||||
/* hijack UART1 and reset into sane state (19200,8n1) */
|
||||
writel(0x83, UART1_REG(LCR));
|
||||
writel(divisor & 0xff, UART1_REG(DLL));
|
||||
writel((divisor >> 8) & 0xff, UART1_REG(DLM));
|
||||
writel(0x03, UART1_REG(LCR));
|
||||
writel(0x00, UART1_REG(IER));
|
||||
writel(0x00, UART1_REG(FCR));
|
||||
writel(0x00, UART1_REG(MCR));
|
||||
|
||||
/* send the power-off command 'A' to PIC */
|
||||
writel('A', UART1_REG(TX));
|
||||
}
|
||||
|
||||
static int qnap_power_off_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct clk *clk;
|
||||
char symname[KSYM_NAME_LEN];
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Missing resource");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
||||
if (!base) {
|
||||
dev_err(&pdev->dev, "Unable to map resource");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* We need to know tclk in order to calculate the UART divisor */
|
||||
clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "Clk missing");
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
tclk = clk_get_rate(clk);
|
||||
|
||||
/* Check that nothing else has already setup a handler */
|
||||
if (pm_power_off) {
|
||||
lookup_symbol_name((ulong)pm_power_off, symname);
|
||||
dev_err(&pdev->dev,
|
||||
"pm_power_off already claimed %p %s",
|
||||
pm_power_off, symname);
|
||||
return -EBUSY;
|
||||
}
|
||||
pm_power_off = qnap_power_off;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qnap_power_off_remove(struct platform_device *pdev)
|
||||
{
|
||||
pm_power_off = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qnap_power_off_of_match_table[] = {
|
||||
{ .compatible = "qnap,power-off", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qnap_power_off_of_match_table);
|
||||
|
||||
static struct platform_driver qnap_power_off_driver = {
|
||||
.probe = qnap_power_off_probe,
|
||||
.remove = qnap_power_off_remove,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "qnap_power_off",
|
||||
.of_match_table = of_match_ptr(qnap_power_off_of_match_table),
|
||||
},
|
||||
};
|
||||
module_platform_driver(qnap_power_off_driver);
|
||||
|
||||
MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");
|
||||
MODULE_DESCRIPTION("QNAP Power off driver");
|
||||
MODULE_LICENSE("GPL v2");
|
65
drivers/power/reset/restart-poweroff.c
Normal file
65
drivers/power/reset/restart-poweroff.c
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Power off by restarting and let u-boot keep hold of the machine
|
||||
* until the user presses a button for example.
|
||||
*
|
||||
* Andrew Lunn <andrew@lunn.ch>
|
||||
*
|
||||
* Copyright (C) 2012 Andrew Lunn
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/module.h>
|
||||
#include <asm/system_misc.h>
|
||||
|
||||
static void restart_poweroff_do_poweroff(void)
|
||||
{
|
||||
arm_pm_restart('h', NULL);
|
||||
}
|
||||
|
||||
static int restart_poweroff_probe(struct platform_device *pdev)
|
||||
{
|
||||
/* If a pm_power_off function has already been added, leave it alone */
|
||||
if (pm_power_off != NULL) {
|
||||
dev_err(&pdev->dev,
|
||||
"pm_power_off function already registered");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
pm_power_off = &restart_poweroff_do_poweroff;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int restart_poweroff_remove(struct platform_device *pdev)
|
||||
{
|
||||
if (pm_power_off == &restart_poweroff_do_poweroff)
|
||||
pm_power_off = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_restart_poweroff_match[] = {
|
||||
{ .compatible = "restart-poweroff", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver restart_poweroff_driver = {
|
||||
.probe = restart_poweroff_probe,
|
||||
.remove = restart_poweroff_remove,
|
||||
.driver = {
|
||||
.name = "poweroff-restart",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_restart_poweroff_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(restart_poweroff_driver);
|
||||
|
||||
MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch");
|
||||
MODULE_DESCRIPTION("restart poweroff driver");
|
||||
MODULE_LICENSE("GPLv2");
|
||||
MODULE_ALIAS("platform:poweroff-restart");
|
@ -131,7 +131,7 @@ struct abx500_maxim_parameters {
|
||||
* @nominal_voltage: Nominal voltage of the battery in mV
|
||||
* @termination_vol: max voltage upto which battery can be charged
|
||||
* @termination_curr battery charging termination current in mA
|
||||
* @recharge_vol battery voltage limit that will trigger a new
|
||||
* @recharge_cap battery capacity limit that will trigger a new
|
||||
* full charging cycle in the case where maintenan-
|
||||
* -ce charging has been disabled
|
||||
* @normal_cur_lvl: charger current in normal state in mA
|
||||
@ -160,7 +160,7 @@ struct abx500_battery_type {
|
||||
int nominal_voltage;
|
||||
int termination_vol;
|
||||
int termination_curr;
|
||||
int recharge_vol;
|
||||
int recharge_cap;
|
||||
int normal_cur_lvl;
|
||||
int normal_vol_lvl;
|
||||
int maint_a_cur_lvl;
|
||||
@ -224,6 +224,7 @@ struct abx500_bm_charger_parameters {
|
||||
* @bkup_bat_v voltage which we charge the backup battery with
|
||||
* @bkup_bat_i current which we charge the backup battery with
|
||||
* @no_maintenance indicates that maintenance charging is disabled
|
||||
* @capacity_scaling indicates whether capacity scaling is to be used
|
||||
* @abx500_adc_therm placement of thermistor, batctrl or battemp adc
|
||||
* @chg_unknown_bat flag to enable charging of unknown batteries
|
||||
* @enable_overshoot flag to enable VBAT overshoot control
|
||||
@ -253,7 +254,11 @@ struct abx500_bm_data {
|
||||
int usb_safety_tmr_h;
|
||||
int bkup_bat_v;
|
||||
int bkup_bat_i;
|
||||
bool autopower_cfg;
|
||||
bool ac_enabled;
|
||||
bool usb_enabled;
|
||||
bool no_maintenance;
|
||||
bool capacity_scaling;
|
||||
bool chg_unknown_bat;
|
||||
bool enable_overshoot;
|
||||
bool auto_trig;
|
||||
@ -277,9 +282,9 @@ enum {
|
||||
NTC_INTERNAL,
|
||||
};
|
||||
|
||||
int bmdevs_of_probe(struct device *dev,
|
||||
struct device_node *np,
|
||||
struct abx500_bm_data **battery);
|
||||
int ab8500_bm_of_probe(struct device *dev,
|
||||
struct device_node *np,
|
||||
struct abx500_bm_data *bm);
|
||||
|
||||
int abx500_set_register_interruptible(struct device *dev, u8 bank, u8 reg,
|
||||
u8 value);
|
||||
|
@ -23,6 +23,7 @@
|
||||
* Bank : 0x5
|
||||
*/
|
||||
#define AB8500_USB_LINE_STAT_REG 0x80
|
||||
#define AB8500_USB_LINK1_STAT_REG 0x94
|
||||
|
||||
/*
|
||||
* Charger / status register offfsets
|
||||
@ -225,6 +226,8 @@
|
||||
/* BatCtrl Current Source Constants */
|
||||
#define BAT_CTRL_7U_ENA 0x01
|
||||
#define BAT_CTRL_20U_ENA 0x02
|
||||
#define BAT_CTRL_18U_ENA 0x01
|
||||
#define BAT_CTRL_16U_ENA 0x02
|
||||
#define BAT_CTRL_CMP_ENA 0x04
|
||||
#define FORCE_BAT_CTRL_CMP_HIGH 0x08
|
||||
#define BAT_CTRL_PULL_UP_ENA 0x10
|
||||
@ -355,6 +358,7 @@ struct ab8500_bm_charger_parameters {
|
||||
* @bkup_bat_v voltage which we charge the backup battery with
|
||||
* @bkup_bat_i current which we charge the backup battery with
|
||||
* @no_maintenance indicates that maintenance charging is disabled
|
||||
* @capacity_scaling indicates whether capacity scaling is to be used
|
||||
* @adc_therm placement of thermistor, batctrl or battemp adc
|
||||
* @chg_unknown_bat flag to enable charging of unknown batteries
|
||||
* @enable_overshoot flag to enable VBAT overshoot control
|
||||
@ -383,6 +387,7 @@ struct ab8500_bm_data {
|
||||
int bkup_bat_v;
|
||||
int bkup_bat_i;
|
||||
bool no_maintenance;
|
||||
bool capacity_scaling;
|
||||
bool chg_unknown_bat;
|
||||
bool enable_overshoot;
|
||||
enum abx500_adc_therm adc_therm;
|
||||
@ -399,26 +404,6 @@ struct ab8500_bm_data {
|
||||
const struct ab8500_fg_parameters *fg_params;
|
||||
};
|
||||
|
||||
struct ab8500_charger_platform_data {
|
||||
char **supplied_to;
|
||||
size_t num_supplicants;
|
||||
bool autopower_cfg;
|
||||
};
|
||||
|
||||
struct ab8500_btemp_platform_data {
|
||||
char **supplied_to;
|
||||
size_t num_supplicants;
|
||||
};
|
||||
|
||||
struct ab8500_fg_platform_data {
|
||||
char **supplied_to;
|
||||
size_t num_supplicants;
|
||||
};
|
||||
|
||||
struct ab8500_chargalg_platform_data {
|
||||
char **supplied_to;
|
||||
size_t num_supplicants;
|
||||
};
|
||||
struct ab8500_btemp;
|
||||
struct ab8500_gpadc;
|
||||
struct ab8500_fg;
|
||||
@ -434,20 +419,10 @@ struct ab8500_fg *ab8500_fg_get(void);
|
||||
int ab8500_fg_inst_curr_blocking(struct ab8500_fg *dev);
|
||||
int ab8500_fg_inst_curr_start(struct ab8500_fg *di);
|
||||
int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res);
|
||||
int ab8500_fg_inst_curr_started(struct ab8500_fg *di);
|
||||
int ab8500_fg_inst_curr_done(struct ab8500_fg *di);
|
||||
|
||||
#else
|
||||
static struct abx500_bm_data ab8500_bm_data;
|
||||
|
||||
static inline int ab8500_fg_inst_curr_start(struct ab8500_fg *di)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* _AB8500_BM_H */
|
||||
|
@ -27,12 +27,17 @@ struct ux500_charger_ops {
|
||||
* @ops ux500 charger operations
|
||||
* @max_out_volt maximum output charger voltage in mV
|
||||
* @max_out_curr maximum output charger current in mA
|
||||
* @enabled indicates if this charger is used or not
|
||||
* @external external charger unit (pm2xxx)
|
||||
*/
|
||||
struct ux500_charger {
|
||||
struct power_supply psy;
|
||||
struct ux500_charger_ops ops;
|
||||
int max_out_volt;
|
||||
int max_out_curr;
|
||||
int wdt_refresh;
|
||||
bool enabled;
|
||||
bool external;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
61
include/linux/pm2301_charger.h
Normal file
61
include/linux/pm2301_charger.h
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* PM2301 charger driver.
|
||||
*
|
||||
* Copyright (C) 2012 ST Ericsson Corporation
|
||||
*
|
||||
* Contact: Olivier LAUNAY (olivier.launay@stericsson.com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_PM2301_H
|
||||
#define __LINUX_PM2301_H
|
||||
|
||||
/**
|
||||
* struct pm2xxx_bm_charger_parameters - Charger specific parameters
|
||||
* @ac_volt_max: maximum allowed AC charger voltage in mV
|
||||
* @ac_curr_max: maximum allowed AC charger current in mA
|
||||
*/
|
||||
struct pm2xxx_bm_charger_parameters {
|
||||
int ac_volt_max;
|
||||
int ac_curr_max;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pm2xxx_bm_data - pm2xxx battery management data
|
||||
* @enable_overshoot flag to enable VBAT overshoot control
|
||||
* @chg_params charger parameters
|
||||
*/
|
||||
struct pm2xxx_bm_data {
|
||||
bool enable_overshoot;
|
||||
const struct pm2xxx_bm_charger_parameters *chg_params;
|
||||
};
|
||||
|
||||
struct pm2xxx_charger_platform_data {
|
||||
char **supplied_to;
|
||||
size_t num_supplicants;
|
||||
int i2c_bus;
|
||||
const char *label;
|
||||
int irq_number;
|
||||
unsigned int lpn_gpio;
|
||||
int irq_type;
|
||||
};
|
||||
|
||||
struct pm2xxx_platform_data {
|
||||
struct pm2xxx_charger_platform_data *wall_charger;
|
||||
struct pm2xxx_bm_data *battery;
|
||||
};
|
||||
|
||||
#endif /* __LINUX_PM2301_H */
|
@ -75,7 +75,8 @@
|
||||
|
||||
/* Supported modes with maximal current limit */
|
||||
enum bq2415x_mode {
|
||||
BQ2415X_MODE_NONE, /* unknown or no charger (100mA) */
|
||||
BQ2415X_MODE_OFF, /* offline mode (charger disabled) */
|
||||
BQ2415X_MODE_NONE, /* unknown charger (100mA) */
|
||||
BQ2415X_MODE_HOST_CHARGER, /* usb host/hub charger (500mA) */
|
||||
BQ2415X_MODE_DEDICATED_CHARGER, /* dedicated charger (unlimited) */
|
||||
BQ2415X_MODE_BOOST, /* boost mode (charging disabled) */
|
||||
|
@ -54,6 +54,8 @@ enum {
|
||||
POWER_SUPPLY_HEALTH_OVERVOLTAGE,
|
||||
POWER_SUPPLY_HEALTH_UNSPEC_FAILURE,
|
||||
POWER_SUPPLY_HEALTH_COLD,
|
||||
POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE,
|
||||
POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE,
|
||||
};
|
||||
|
||||
enum {
|
||||
|
Loading…
Reference in New Issue
Block a user