- 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:
Linus Torvalds 2013-02-20 10:19:07 -08:00
commit 5a1203914a
36 changed files with 4174 additions and 1000 deletions

View File

@ -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

View File

@ -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"

View File

@ -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)

View File

@ -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
},
};

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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, &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,
&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,
&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);

View File

@ -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 */

View File

@ -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");

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -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);

View 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");

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

File diff suppressed because it is too large Load Diff

View 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 */

View File

@ -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",

View File

@ -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.

View File

@ -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

View 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");

View 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");

View File

@ -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);

View File

@ -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 */

View File

@ -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

View 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 */

View File

@ -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) */

View File

@ -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 {