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
36 changed files with 4174 additions and 1000 deletions

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