提交 5a120391 编写于 作者: L Linus Torvalds

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..
* QNAP Power Off
QNAP NAS devices have a microcontroller controlling the main power
supply. This microcontroller is connected to UART1 of the Kirkwood and
Orion5x SoCs. Sending the charactor 'A', at 19200 baud, tells the
microcontroller to turn the power off. This driver adds a handler to
pm_power_off which is called to turn the power off.
Required Properties:
- compatible: Should be "qnap,power-off"
- reg: Address and length of the register set for UART1
- clocks: tclk clock
* Restart Power Off
Buffalo Linkstation LS-XHL and LS-CHLv2, and other devices power off
by restarting and letting u-boot keep hold of the machine until the
user presses a button.
Required Properties:
- compatible: Should be "restart-poweroff"
...@@ -7612,6 +7612,22 @@ F: Documentation/backlight/lp855x-driver.txt ...@@ -7612,6 +7612,22 @@ F: Documentation/backlight/lp855x-driver.txt
F: drivers/video/backlight/lp855x_bl.c F: drivers/video/backlight/lp855x_bl.c
F: include/linux/platform_data/lp855x.h F: include/linux/platform_data/lp855x.h
TI LP8727 CHARGER DRIVER
M: Milo Kim <milo.kim@ti.com>
S: Maintained
F: drivers/power/lp8727_charger.c
F: include/linux/platform_data/lp8727.h
TI LP8788 MFD DRIVER
M: Milo Kim <milo.kim@ti.com>
S: Maintained
F: drivers/iio/adc/lp8788_adc.c
F: drivers/leds/leds-lp8788.c
F: drivers/mfd/lp8788*.c
F: drivers/power/lp8788-charger.c
F: drivers/regulator/lp8788-*.c
F: include/linux/mfd/lp8788*.h
TI TWL4030 SERIES SOC CODEC DRIVER TI TWL4030 SERIES SOC CODEC DRIVER
M: Peter Ujfalusi <peter.ujfalusi@ti.com> M: Peter Ujfalusi <peter.ujfalusi@ti.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: alsa-devel@alsa-project.org (moderated for non-subscribers)
......
...@@ -750,6 +750,12 @@ static struct resource ab8500_charger_resources[] = { ...@@ -750,6 +750,12 @@ static struct resource ab8500_charger_resources[] = {
.end = AB8500_INT_CH_WD_EXP, .end = AB8500_INT_CH_WD_EXP,
.flags = IORESOURCE_IRQ, .flags = IORESOURCE_IRQ,
}, },
{
.name = "VBUS_CH_DROP_END",
.start = AB8500_INT_VBUS_CH_DROP_END,
.end = AB8500_INT_VBUS_CH_DROP_END,
.flags = IORESOURCE_IRQ,
},
}; };
static struct resource ab8500_btemp_resources[] = { static struct resource ab8500_btemp_resources[] = {
...@@ -1012,40 +1018,32 @@ static struct mfd_cell ab8500_bm_devs[] = { ...@@ -1012,40 +1018,32 @@ static struct mfd_cell ab8500_bm_devs[] = {
.of_compatible = "stericsson,ab8500-charger", .of_compatible = "stericsson,ab8500-charger",
.num_resources = ARRAY_SIZE(ab8500_charger_resources), .num_resources = ARRAY_SIZE(ab8500_charger_resources),
.resources = ab8500_charger_resources, .resources = ab8500_charger_resources,
#ifndef CONFIG_OF
.platform_data = &ab8500_bm_data, .platform_data = &ab8500_bm_data,
.pdata_size = sizeof(ab8500_bm_data), .pdata_size = sizeof(ab8500_bm_data),
#endif
}, },
{ {
.name = "ab8500-btemp", .name = "ab8500-btemp",
.of_compatible = "stericsson,ab8500-btemp", .of_compatible = "stericsson,ab8500-btemp",
.num_resources = ARRAY_SIZE(ab8500_btemp_resources), .num_resources = ARRAY_SIZE(ab8500_btemp_resources),
.resources = ab8500_btemp_resources, .resources = ab8500_btemp_resources,
#ifndef CONFIG_OF
.platform_data = &ab8500_bm_data, .platform_data = &ab8500_bm_data,
.pdata_size = sizeof(ab8500_bm_data), .pdata_size = sizeof(ab8500_bm_data),
#endif
}, },
{ {
.name = "ab8500-fg", .name = "ab8500-fg",
.of_compatible = "stericsson,ab8500-fg", .of_compatible = "stericsson,ab8500-fg",
.num_resources = ARRAY_SIZE(ab8500_fg_resources), .num_resources = ARRAY_SIZE(ab8500_fg_resources),
.resources = ab8500_fg_resources, .resources = ab8500_fg_resources,
#ifndef CONFIG_OF
.platform_data = &ab8500_bm_data, .platform_data = &ab8500_bm_data,
.pdata_size = sizeof(ab8500_bm_data), .pdata_size = sizeof(ab8500_bm_data),
#endif
}, },
{ {
.name = "ab8500-chargalg", .name = "ab8500-chargalg",
.of_compatible = "stericsson,ab8500-chargalg", .of_compatible = "stericsson,ab8500-chargalg",
.num_resources = ARRAY_SIZE(ab8500_chargalg_resources), .num_resources = ARRAY_SIZE(ab8500_chargalg_resources),
.resources = ab8500_chargalg_resources, .resources = ab8500_chargalg_resources,
#ifndef CONFIG_OF
.platform_data = &ab8500_bm_data, .platform_data = &ab8500_bm_data,
.pdata_size = sizeof(ab8500_bm_data), .pdata_size = sizeof(ab8500_bm_data),
#endif
}, },
}; };
......
...@@ -915,15 +915,13 @@ static int pm860x_battery_probe(struct platform_device *pdev) ...@@ -915,15 +915,13 @@ static int pm860x_battery_probe(struct platform_device *pdev)
info->irq_cc = platform_get_irq(pdev, 0); info->irq_cc = platform_get_irq(pdev, 0);
if (info->irq_cc <= 0) { if (info->irq_cc <= 0) {
dev_err(&pdev->dev, "No IRQ resource!\n"); dev_err(&pdev->dev, "No IRQ resource!\n");
ret = -EINVAL; return -EINVAL;
goto out;
} }
info->irq_batt = platform_get_irq(pdev, 1); info->irq_batt = platform_get_irq(pdev, 1);
if (info->irq_batt <= 0) { if (info->irq_batt <= 0) {
dev_err(&pdev->dev, "No IRQ resource!\n"); dev_err(&pdev->dev, "No IRQ resource!\n");
ret = -EINVAL; return -EINVAL;
goto out;
} }
info->chip = chip; info->chip = chip;
...@@ -957,7 +955,7 @@ static int pm860x_battery_probe(struct platform_device *pdev) ...@@ -957,7 +955,7 @@ static int pm860x_battery_probe(struct platform_device *pdev)
ret = power_supply_register(&pdev->dev, &info->battery); ret = power_supply_register(&pdev->dev, &info->battery);
if (ret) if (ret)
goto out; return ret;
info->battery.dev->parent = &pdev->dev; info->battery.dev->parent = &pdev->dev;
ret = request_threaded_irq(info->irq_cc, NULL, ret = request_threaded_irq(info->irq_cc, NULL,
...@@ -984,8 +982,6 @@ static int pm860x_battery_probe(struct platform_device *pdev) ...@@ -984,8 +982,6 @@ static int pm860x_battery_probe(struct platform_device *pdev)
free_irq(info->irq_cc, info); free_irq(info->irq_cc, info);
out_reg: out_reg:
power_supply_unregister(&info->battery); power_supply_unregister(&info->battery);
out:
kfree(info);
return ret; return ret;
} }
...@@ -993,10 +989,9 @@ static int pm860x_battery_remove(struct platform_device *pdev) ...@@ -993,10 +989,9 @@ static int pm860x_battery_remove(struct platform_device *pdev)
{ {
struct pm860x_battery_info *info = platform_get_drvdata(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_batt, info);
free_irq(info->irq_cc, info); free_irq(info->irq_cc, info);
kfree(info); power_supply_unregister(&info->battery);
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
return 0; return 0;
} }
......
...@@ -346,6 +346,20 @@ config AB8500_BM ...@@ -346,6 +346,20 @@ config AB8500_BM
help help
Say Y to include support for AB8500 battery management. 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" source "drivers/power/reset/Kconfig"
endif # POWER_SUPPLY endif # POWER_SUPPLY
......
...@@ -20,6 +20,7 @@ obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o ...@@ -20,6 +20,7 @@ obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o
obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o
obj-$(CONFIG_BATTERY_DS2782) += ds2782_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_PMU) += pmu_battery.o
obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o
obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o
...@@ -38,7 +39,7 @@ obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o ...@@ -38,7 +39,7 @@ obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o
obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o
obj-$(CONFIG_BATTERY_RX51) += rx51_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_ISP1704) += isp1704_charger.o
obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o
obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o
...@@ -46,6 +47,7 @@ obj-$(CONFIG_CHARGER_LP8727) += lp8727_charger.o ...@@ -46,6 +47,7 @@ obj-$(CONFIG_CHARGER_LP8727) += lp8727_charger.o
obj-$(CONFIG_CHARGER_LP8788) += lp8788-charger.o obj-$(CONFIG_CHARGER_LP8788) += lp8788-charger.o
obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o
obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.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_MAX8997) += max8997_charger.o
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o
......
...@@ -182,206 +182,206 @@ static struct batres_vs_temp temp_to_batres_tbl_9100[] = { ...@@ -182,206 +182,206 @@ static struct batres_vs_temp temp_to_batres_tbl_9100[] = {
}; };
static struct abx500_battery_type bat_type_thermistor[] = { static struct abx500_battery_type bat_type_thermistor[] = {
[BATTERY_UNKNOWN] = { [BATTERY_UNKNOWN] = {
/* First element always represent the UNKNOWN battery */ /* First element always represent the UNKNOWN battery */
.name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN, .name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
.resis_high = 0, .resis_high = 0,
.resis_low = 0, .resis_low = 0,
.battery_resistance = 300, .battery_resistance = 300,
.charge_full_design = 612, .charge_full_design = 612,
.nominal_voltage = 3700, .nominal_voltage = 3700,
.termination_vol = 4050, .termination_vol = 4050,
.termination_curr = 200, .termination_curr = 200,
.recharge_vol = 3990, .recharge_cap = 95,
.normal_cur_lvl = 400, .normal_cur_lvl = 400,
.normal_vol_lvl = 4100, .normal_vol_lvl = 4100,
.maint_a_cur_lvl = 400, .maint_a_cur_lvl = 400,
.maint_a_vol_lvl = 4050, .maint_a_vol_lvl = 4050,
.maint_a_chg_timer_h = 60, .maint_a_chg_timer_h = 60,
.maint_b_cur_lvl = 400, .maint_b_cur_lvl = 400,
.maint_b_vol_lvl = 4000, .maint_b_vol_lvl = 4000,
.maint_b_chg_timer_h = 200, .maint_b_chg_timer_h = 200,
.low_high_cur_lvl = 300, .low_high_cur_lvl = 300,
.low_high_vol_lvl = 4000, .low_high_vol_lvl = 4000,
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl), .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
.r_to_t_tbl = temp_tbl, .r_to_t_tbl = temp_tbl,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl), .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
.v_to_cap_tbl = cap_tbl, .v_to_cap_tbl = cap_tbl,
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor), .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
.batres_tbl = temp_to_batres_tbl_thermistor, .batres_tbl = temp_to_batres_tbl_thermistor,
}, },
{ {
.name = POWER_SUPPLY_TECHNOLOGY_LIPO, .name = POWER_SUPPLY_TECHNOLOGY_LIPO,
.resis_high = 53407, .resis_high = 53407,
.resis_low = 12500, .resis_low = 12500,
.battery_resistance = 300, .battery_resistance = 300,
.charge_full_design = 900, .charge_full_design = 900,
.nominal_voltage = 3600, .nominal_voltage = 3600,
.termination_vol = 4150, .termination_vol = 4150,
.termination_curr = 80, .termination_curr = 80,
.recharge_vol = 4130, .recharge_cap = 95,
.normal_cur_lvl = 700, .normal_cur_lvl = 700,
.normal_vol_lvl = 4200, .normal_vol_lvl = 4200,
.maint_a_cur_lvl = 600, .maint_a_cur_lvl = 600,
.maint_a_vol_lvl = 4150, .maint_a_vol_lvl = 4150,
.maint_a_chg_timer_h = 60, .maint_a_chg_timer_h = 60,
.maint_b_cur_lvl = 600, .maint_b_cur_lvl = 600,
.maint_b_vol_lvl = 4100, .maint_b_vol_lvl = 4100,
.maint_b_chg_timer_h = 200, .maint_b_chg_timer_h = 200,
.low_high_cur_lvl = 300, .low_high_cur_lvl = 300,
.low_high_vol_lvl = 4000, .low_high_vol_lvl = 4000,
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_A_thermistor), .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_A_thermistor),
.r_to_t_tbl = temp_tbl_A_thermistor, .r_to_t_tbl = temp_tbl_A_thermistor,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_A_thermistor), .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_A_thermistor),
.v_to_cap_tbl = cap_tbl_A_thermistor, .v_to_cap_tbl = cap_tbl_A_thermistor,
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor), .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
.batres_tbl = temp_to_batres_tbl_thermistor, .batres_tbl = temp_to_batres_tbl_thermistor,
}, },
{ {
.name = POWER_SUPPLY_TECHNOLOGY_LIPO, .name = POWER_SUPPLY_TECHNOLOGY_LIPO,
.resis_high = 200000, .resis_high = 200000,
.resis_low = 82869, .resis_low = 82869,
.battery_resistance = 300, .battery_resistance = 300,
.charge_full_design = 900, .charge_full_design = 900,
.nominal_voltage = 3600, .nominal_voltage = 3600,
.termination_vol = 4150, .termination_vol = 4150,
.termination_curr = 80, .termination_curr = 80,
.recharge_vol = 4130, .recharge_cap = 95,
.normal_cur_lvl = 700, .normal_cur_lvl = 700,
.normal_vol_lvl = 4200, .normal_vol_lvl = 4200,
.maint_a_cur_lvl = 600, .maint_a_cur_lvl = 600,
.maint_a_vol_lvl = 4150, .maint_a_vol_lvl = 4150,
.maint_a_chg_timer_h = 60, .maint_a_chg_timer_h = 60,
.maint_b_cur_lvl = 600, .maint_b_cur_lvl = 600,
.maint_b_vol_lvl = 4100, .maint_b_vol_lvl = 4100,
.maint_b_chg_timer_h = 200, .maint_b_chg_timer_h = 200,
.low_high_cur_lvl = 300, .low_high_cur_lvl = 300,
.low_high_vol_lvl = 4000, .low_high_vol_lvl = 4000,
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_B_thermistor), .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_B_thermistor),
.r_to_t_tbl = temp_tbl_B_thermistor, .r_to_t_tbl = temp_tbl_B_thermistor,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_B_thermistor), .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_B_thermistor),
.v_to_cap_tbl = cap_tbl_B_thermistor, .v_to_cap_tbl = cap_tbl_B_thermistor,
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor), .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
.batres_tbl = temp_to_batres_tbl_thermistor, .batres_tbl = temp_to_batres_tbl_thermistor,
}, },
}; };
static struct abx500_battery_type bat_type_ext_thermistor[] = { static struct abx500_battery_type bat_type_ext_thermistor[] = {
[BATTERY_UNKNOWN] = { [BATTERY_UNKNOWN] = {
/* First element always represent the UNKNOWN battery */ /* First element always represent the UNKNOWN battery */
.name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN, .name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
.resis_high = 0, .resis_high = 0,
.resis_low = 0, .resis_low = 0,
.battery_resistance = 300, .battery_resistance = 300,
.charge_full_design = 612, .charge_full_design = 612,
.nominal_voltage = 3700, .nominal_voltage = 3700,
.termination_vol = 4050, .termination_vol = 4050,
.termination_curr = 200, .termination_curr = 200,
.recharge_vol = 3990, .recharge_cap = 95,
.normal_cur_lvl = 400, .normal_cur_lvl = 400,
.normal_vol_lvl = 4100, .normal_vol_lvl = 4100,
.maint_a_cur_lvl = 400, .maint_a_cur_lvl = 400,
.maint_a_vol_lvl = 4050, .maint_a_vol_lvl = 4050,
.maint_a_chg_timer_h = 60, .maint_a_chg_timer_h = 60,
.maint_b_cur_lvl = 400, .maint_b_cur_lvl = 400,
.maint_b_vol_lvl = 4000, .maint_b_vol_lvl = 4000,
.maint_b_chg_timer_h = 200, .maint_b_chg_timer_h = 200,
.low_high_cur_lvl = 300, .low_high_cur_lvl = 300,
.low_high_vol_lvl = 4000, .low_high_vol_lvl = 4000,
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl), .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
.r_to_t_tbl = temp_tbl, .r_to_t_tbl = temp_tbl,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl), .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
.v_to_cap_tbl = cap_tbl, .v_to_cap_tbl = cap_tbl,
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor), .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
.batres_tbl = 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 * 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 * its temperature. The temperature in this case is measure with a NTC placed
* near the battery but on the PCB. * near the battery but on the PCB.
*/ */
{ {
.name = POWER_SUPPLY_TECHNOLOGY_LIPO, .name = POWER_SUPPLY_TECHNOLOGY_LIPO,
.resis_high = 76000, .resis_high = 76000,
.resis_low = 53000, .resis_low = 53000,
.battery_resistance = 300, .battery_resistance = 300,
.charge_full_design = 900, .charge_full_design = 900,
.nominal_voltage = 3700, .nominal_voltage = 3700,
.termination_vol = 4150, .termination_vol = 4150,
.termination_curr = 100, .termination_curr = 100,
.recharge_vol = 4130, .recharge_cap = 95,
.normal_cur_lvl = 700, .normal_cur_lvl = 700,
.normal_vol_lvl = 4200, .normal_vol_lvl = 4200,
.maint_a_cur_lvl = 600, .maint_a_cur_lvl = 600,
.maint_a_vol_lvl = 4150, .maint_a_vol_lvl = 4150,
.maint_a_chg_timer_h = 60, .maint_a_chg_timer_h = 60,
.maint_b_cur_lvl = 600, .maint_b_cur_lvl = 600,
.maint_b_vol_lvl = 4100, .maint_b_vol_lvl = 4100,
.maint_b_chg_timer_h = 200, .maint_b_chg_timer_h = 200,
.low_high_cur_lvl = 300, .low_high_cur_lvl = 300,
.low_high_vol_lvl = 4000, .low_high_vol_lvl = 4000,
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl), .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
.r_to_t_tbl = temp_tbl, .r_to_t_tbl = temp_tbl,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl), .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
.v_to_cap_tbl = cap_tbl, .v_to_cap_tbl = cap_tbl,
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor), .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
.batres_tbl = temp_to_batres_tbl_thermistor, .batres_tbl = temp_to_batres_tbl_thermistor,
}, },
{ {
.name = POWER_SUPPLY_TECHNOLOGY_LION, .name = POWER_SUPPLY_TECHNOLOGY_LION,
.resis_high = 30000, .resis_high = 30000,
.resis_low = 10000, .resis_low = 10000,
.battery_resistance = 300, .battery_resistance = 300,
.charge_full_design = 950, .charge_full_design = 950,
.nominal_voltage = 3700, .nominal_voltage = 3700,
.termination_vol = 4150, .termination_vol = 4150,
.termination_curr = 100, .termination_curr = 100,
.recharge_vol = 4130, .recharge_cap = 95,
.normal_cur_lvl = 700, .normal_cur_lvl = 700,
.normal_vol_lvl = 4200, .normal_vol_lvl = 4200,
.maint_a_cur_lvl = 600, .maint_a_cur_lvl = 600,
.maint_a_vol_lvl = 4150, .maint_a_vol_lvl = 4150,
.maint_a_chg_timer_h = 60, .maint_a_chg_timer_h = 60,
.maint_b_cur_lvl = 600, .maint_b_cur_lvl = 600,
.maint_b_vol_lvl = 4100, .maint_b_vol_lvl = 4100,
.maint_b_chg_timer_h = 200, .maint_b_chg_timer_h = 200,
.low_high_cur_lvl = 300, .low_high_cur_lvl = 300,
.low_high_vol_lvl = 4000, .low_high_vol_lvl = 4000,
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl), .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
.r_to_t_tbl = temp_tbl, .r_to_t_tbl = temp_tbl,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl), .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
.v_to_cap_tbl = cap_tbl, .v_to_cap_tbl = cap_tbl,
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor), .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
.batres_tbl = temp_to_batres_tbl_thermistor, .batres_tbl = temp_to_batres_tbl_thermistor,
}, },
{ {
.name = POWER_SUPPLY_TECHNOLOGY_LION, .name = POWER_SUPPLY_TECHNOLOGY_LION,
.resis_high = 95000, .resis_high = 95000,
.resis_low = 76001, .resis_low = 76001,
.battery_resistance = 300, .battery_resistance = 300,
.charge_full_design = 950, .charge_full_design = 950,
.nominal_voltage = 3700, .nominal_voltage = 3700,
.termination_vol = 4150, .termination_vol = 4150,
.termination_curr = 100, .termination_curr = 100,
.recharge_vol = 4130, .recharge_cap = 95,
.normal_cur_lvl = 700, .normal_cur_lvl = 700,
.normal_vol_lvl = 4200, .normal_vol_lvl = 4200,
.maint_a_cur_lvl = 600, .maint_a_cur_lvl = 600,
.maint_a_vol_lvl = 4150, .maint_a_vol_lvl = 4150,
.maint_a_chg_timer_h = 60, .maint_a_chg_timer_h = 60,
.maint_b_cur_lvl = 600, .maint_b_cur_lvl = 600,
.maint_b_vol_lvl = 4100, .maint_b_vol_lvl = 4100,
.maint_b_chg_timer_h = 200, .maint_b_chg_timer_h = 200,
.low_high_cur_lvl = 300, .low_high_cur_lvl = 300,
.low_high_vol_lvl = 4000, .low_high_vol_lvl = 4000,
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl), .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
.r_to_t_tbl = temp_tbl, .r_to_t_tbl = temp_tbl,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl), .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
.v_to_cap_tbl = cap_tbl, .v_to_cap_tbl = cap_tbl,
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor), .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
.batres_tbl = temp_to_batres_tbl_thermistor, .batres_tbl = temp_to_batres_tbl_thermistor,
}, },
}; };
static const struct abx500_bm_capacity_levels cap_levels = { static const struct abx500_bm_capacity_levels cap_levels = {
...@@ -405,8 +405,8 @@ static const struct abx500_fg_parameters fg = { ...@@ -405,8 +405,8 @@ static const struct abx500_fg_parameters fg = {
.lowbat_threshold = 3100, .lowbat_threshold = 3100,
.battok_falling_th_sel0 = 2860, .battok_falling_th_sel0 = 2860,
.battok_raising_th_sel1 = 2860, .battok_raising_th_sel1 = 2860,
.maint_thres = 95,
.user_cap_limit = 15, .user_cap_limit = 15,
.maint_thres = 97,
}; };
static const struct abx500_maxim_parameters maxi_params = { static const struct abx500_maxim_parameters maxi_params = {
...@@ -424,96 +424,84 @@ static const struct abx500_bm_charger_parameters chg = { ...@@ -424,96 +424,84 @@ static const struct abx500_bm_charger_parameters chg = {
}; };
struct abx500_bm_data ab8500_bm_data = { struct abx500_bm_data ab8500_bm_data = {
.temp_under = 3, .temp_under = 3,
.temp_low = 8, .temp_low = 8,
.temp_high = 43, .temp_high = 43,
.temp_over = 48, .temp_over = 48,
.main_safety_tmr_h = 4, .main_safety_tmr_h = 4,
.temp_interval_chg = 20, .temp_interval_chg = 20,
.temp_interval_nochg = 120, .temp_interval_nochg = 120,
.usb_safety_tmr_h = 4, .usb_safety_tmr_h = 4,
.bkup_bat_v = BUP_VCH_SEL_2P6V, .bkup_bat_v = BUP_VCH_SEL_2P6V,
.bkup_bat_i = BUP_ICH_SEL_150UA, .bkup_bat_i = BUP_ICH_SEL_150UA,
.no_maintenance = false, .no_maintenance = false,
.adc_therm = ABx500_ADC_THERM_BATCTRL, .capacity_scaling = false,
.chg_unknown_bat = false, .adc_therm = ABx500_ADC_THERM_BATCTRL,
.enable_overshoot = false, .chg_unknown_bat = false,
.fg_res = 100, .enable_overshoot = false,
.cap_levels = &cap_levels, .fg_res = 100,
.bat_type = bat_type_thermistor, .cap_levels = &cap_levels,
.n_btypes = 3, .bat_type = bat_type_thermistor,
.batt_id = 0, .n_btypes = 3,
.interval_charging = 5, .batt_id = 0,
.interval_not_charging = 120, .interval_charging = 5,
.temp_hysteresis = 3, .interval_not_charging = 120,
.gnd_lift_resistance = 34, .temp_hysteresis = 3,
.maxi = &maxi_params, .gnd_lift_resistance = 34,
.chg_params = &chg, .maxi = &maxi_params,
.fg_params = &fg, .chg_params = &chg,
.fg_params = &fg,
}; };
int bmdevs_of_probe(struct device *dev, struct device_node *np, int ab8500_bm_of_probe(struct device *dev,
struct abx500_bm_data **battery) struct device_node *np,
struct abx500_bm_data *bm)
{ {
struct abx500_battery_type *btype; struct batres_vs_temp *tmp_batres_tbl;
struct device_node *np_bat_supply; struct device_node *battery_node;
struct abx500_bm_data *bat;
const char *btech; const char *btech;
char bat_tech[8]; int i;
int i, thermistor;
*battery = &ab8500_bm_data;
/* get phandle to 'battery-info' node */ /* get phandle to 'battery-info' node */
np_bat_supply = of_parse_phandle(np, "battery", 0); battery_node = of_parse_phandle(np, "battery", 0);
if (!np_bat_supply) { if (!battery_node) {
dev_err(dev, "missing property battery\n"); dev_err(dev, "battery node or reference missing\n");
return -EINVAL; return -EINVAL;
} }
if (of_property_read_bool(np_bat_supply,
"thermistor-on-batctrl"))
thermistor = NTC_INTERNAL;
else
thermistor = NTC_EXTERNAL;
bat = *battery; btech = of_get_property(battery_node, "stericsson,battery-type", NULL);
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);
if (!btech) { if (!btech) {
dev_warn(dev, "missing property battery-name/type\n"); dev_warn(dev, "missing property battery-name/type\n");
strcpy(bat_tech, "UNKNOWN"); return -EINVAL;
} else {
strcpy(bat_tech, btech);
} }
if (strncmp(bat_tech, "LION", 4) == 0) { if (strncmp(btech, "LION", 4) == 0) {
bat->no_maintenance = true; bm->no_maintenance = true;
bat->chg_unknown_bat = true; bm->chg_unknown_bat = true;
bat->bat_type[BATTERY_UNKNOWN].charge_full_design = 2600; bm->bat_type[BATTERY_UNKNOWN].charge_full_design = 2600;
bat->bat_type[BATTERY_UNKNOWN].termination_vol = 4150; bm->bat_type[BATTERY_UNKNOWN].termination_vol = 4150;
bat->bat_type[BATTERY_UNKNOWN].recharge_vol = 4130; bm->bat_type[BATTERY_UNKNOWN].recharge_cap = 95;
bat->bat_type[BATTERY_UNKNOWN].normal_cur_lvl = 520; bm->bat_type[BATTERY_UNKNOWN].normal_cur_lvl = 520;
bat->bat_type[BATTERY_UNKNOWN].normal_vol_lvl = 4200; bm->bat_type[BATTERY_UNKNOWN].normal_vol_lvl = 4200;
} }
/* select the battery resolution table */
for (i = 0; i < bat->n_btypes; ++i) { if (of_property_read_bool(battery_node, "thermistor-on-batctrl")) {
btype = (bat->bat_type + i); if (strncmp(btech, "LION", 4) == 0)
if (thermistor == NTC_EXTERNAL) { tmp_batres_tbl = temp_to_batres_tbl_9100;
btype->batres_tbl = else
temp_to_batres_tbl_ext_thermistor; tmp_batres_tbl = temp_to_batres_tbl_thermistor;
} else if (strncmp(bat_tech, "LION", 4) == 0) { } else {
btype->batres_tbl = bm->n_btypes = 4;
temp_to_batres_tbl_9100; bm->bat_type = bat_type_ext_thermistor;
} else { bm->adc_therm = ABx500_ADC_THERM_BATTEMP;
btype->batres_tbl = tmp_batres_tbl = temp_to_batres_tbl_ext_thermistor;
temp_to_batres_tbl_thermistor;
}
} }
of_node_put(np_bat_supply);
/* select the battery resolution table */
for (i = 0; i < bm->n_btypes; ++i)
bm->bat_type[i].batres_tbl = tmp_batres_tbl;
of_node_put(battery_node);
return 0; return 0;
} }
...@@ -39,6 +39,9 @@ ...@@ -39,6 +39,9 @@
#define BTEMP_BATCTRL_CURR_SRC_7UA 7 #define BTEMP_BATCTRL_CURR_SRC_7UA 7
#define BTEMP_BATCTRL_CURR_SRC_20UA 20 #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), \ #define to_ab8500_btemp_device_info(x) container_of((x), \
struct ab8500_btemp, btemp_psy); struct ab8500_btemp, btemp_psy);
...@@ -78,12 +81,13 @@ struct ab8500_btemp_ranges { ...@@ -78,12 +81,13 @@ struct ab8500_btemp_ranges {
* @parent: Pointer to the struct ab8500 * @parent: Pointer to the struct ab8500
* @gpadc: Pointer to the struct gpadc * @gpadc: Pointer to the struct gpadc
* @fg: Pointer to the struct fg * @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 * @btemp_psy: Structure for BTEMP specific battery properties
* @events: Structure for information about events triggered * @events: Structure for information about events triggered
* @btemp_ranges: Battery temperature range structure * @btemp_ranges: Battery temperature range structure
* @btemp_wq: Work queue for measuring the temperature periodically * @btemp_wq: Work queue for measuring the temperature periodically
* @btemp_periodic_work: Work for measuring the temperature periodically * @btemp_periodic_work: Work for measuring the temperature periodically
* @initialized: True if battery id read.
*/ */
struct ab8500_btemp { struct ab8500_btemp {
struct device *dev; struct device *dev;
...@@ -94,12 +98,13 @@ struct ab8500_btemp { ...@@ -94,12 +98,13 @@ struct ab8500_btemp {
struct ab8500 *parent; struct ab8500 *parent;
struct ab8500_gpadc *gpadc; struct ab8500_gpadc *gpadc;
struct ab8500_fg *fg; struct ab8500_fg *fg;
struct abx500_bm_data *bat; struct abx500_bm_data *bm;
struct power_supply btemp_psy; struct power_supply btemp_psy;
struct ab8500_btemp_events events; struct ab8500_btemp_events events;
struct ab8500_btemp_ranges btemp_ranges; struct ab8500_btemp_ranges btemp_ranges;
struct workqueue_struct *btemp_wq; struct workqueue_struct *btemp_wq;
struct delayed_work btemp_periodic_work; struct delayed_work btemp_periodic_work;
bool initialized;
}; };
/* BTEMP power supply properties */ /* BTEMP power supply properties */
...@@ -147,13 +152,13 @@ static int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di, ...@@ -147,13 +152,13 @@ static int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di,
return (450000 * (v_batctrl)) / (1800 - v_batctrl); 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 * If the battery has internal NTC, we use the current
* source to calculate the resistance, 7uA or 20uA * source to calculate the resistance, 7uA or 20uA
*/ */
rbs = (v_batctrl * 1000 rbs = (v_batctrl * 1000
- di->bat->gnd_lift_resistance * inst_curr) - di->bm->gnd_lift_resistance * inst_curr)
/ di->curr_source; / di->curr_source;
} else { } else {
/* /*
...@@ -209,11 +214,19 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di, ...@@ -209,11 +214,19 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
return 0; return 0;
/* Only do this for batteries with internal NTC */ /* Only do this for batteries with internal NTC */
if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL && enable) { if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && enable) {
if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_7UA)
curr = BAT_CTRL_7U_ENA; if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
else if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_16UA)
curr = BAT_CTRL_20U_ENA; 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); 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, ...@@ -241,14 +254,25 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
__func__); __func__);
goto disable_curr_source; 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"); dev_dbg(di->dev, "Disable BATCTRL curr source\n");
/* Write 0 to the curr bits */ if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
ret = abx500_mask_and_set_register_interruptible(di->dev, /* Write 0 to the curr bits */
AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE, ret = abx500_mask_and_set_register_interruptible(
BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA, di->dev,
~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA)); 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) { if (ret) {
dev_err(di->dev, "%s failed disabling current source\n", dev_err(di->dev, "%s failed disabling current source\n",
__func__); __func__);
...@@ -290,11 +314,20 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di, ...@@ -290,11 +314,20 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
* if we got an error above * if we got an error above
*/ */
disable_curr_source: disable_curr_source:
/* Write 0 to the curr bits */ if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
ret = abx500_mask_and_set_register_interruptible(di->dev, /* 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, AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE,
BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA, BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA,
~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA)); ~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA));
}
if (ret) { if (ret) {
dev_err(di->dev, "%s failed disabling current source\n", dev_err(di->dev, "%s failed disabling current source\n",
__func__); __func__);
...@@ -372,13 +405,10 @@ static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di) ...@@ -372,13 +405,10 @@ static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di)
return ret; return ret;
} }
/* do {
* Since there is no interrupt when current measurement is done, msleep(20);
* loop for over 250ms (250ms is one sample conversion time } while (!ab8500_fg_inst_curr_started(di->fg));
* 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.
*/
i = 0; i = 0;
do { do {
...@@ -457,9 +487,9 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di) ...@@ -457,9 +487,9 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
int rbat, rntc, vntc; int rbat, rntc, vntc;
u8 id; 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) { id != BATTERY_UNKNOWN) {
rbat = ab8500_btemp_get_batctrl_res(di); rbat = ab8500_btemp_get_batctrl_res(di);
...@@ -474,8 +504,8 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di) ...@@ -474,8 +504,8 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
} }
temp = ab8500_btemp_res_to_temp(di, temp = ab8500_btemp_res_to_temp(di,
di->bat->bat_type[id].r_to_t_tbl, di->bm->bat_type[id].r_to_t_tbl,
di->bat->bat_type[id].n_temp_tbl_elements, rbat); di->bm->bat_type[id].n_temp_tbl_elements, rbat);
} else { } else {
vntc = ab8500_gpadc_convert(di->gpadc, BTEMP_BALL); vntc = ab8500_gpadc_convert(di->gpadc, BTEMP_BALL);
if (vntc < 0) { if (vntc < 0) {
...@@ -491,8 +521,8 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di) ...@@ -491,8 +521,8 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
rntc = 230000 * vntc / (VTVOUT_V - vntc); rntc = 230000 * vntc / (VTVOUT_V - vntc);
temp = ab8500_btemp_res_to_temp(di, temp = ab8500_btemp_res_to_temp(di,
di->bat->bat_type[id].r_to_t_tbl, di->bm->bat_type[id].r_to_t_tbl,
di->bat->bat_type[id].n_temp_tbl_elements, rntc); di->bm->bat_type[id].n_temp_tbl_elements, rntc);
prev = temp; prev = temp;
} }
dev_dbg(di->dev, "Battery temperature is %d\n", temp); dev_dbg(di->dev, "Battery temperature is %d\n", temp);
...@@ -511,9 +541,12 @@ static int ab8500_btemp_id(struct ab8500_btemp *di) ...@@ -511,9 +541,12 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
{ {
int res; int res;
u8 i; 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->bm->batt_id = BATTERY_UNKNOWN;
di->bat->batt_id = BATTERY_UNKNOWN;
res = ab8500_btemp_get_batctrl_res(di); res = ab8500_btemp_get_batctrl_res(di);
if (res < 0) { if (res < 0) {
...@@ -522,23 +555,23 @@ static int ab8500_btemp_id(struct ab8500_btemp *di) ...@@ -522,23 +555,23 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
} }
/* BATTERY_UNKNOWN is defined on position 0, skip it! */ /* BATTERY_UNKNOWN is defined on position 0, skip it! */
for (i = BATTERY_UNKNOWN + 1; i < di->bat->n_btypes; i++) { for (i = BATTERY_UNKNOWN + 1; i < di->bm->n_btypes; i++) {
if ((res <= di->bat->bat_type[i].resis_high) && if ((res <= di->bm->bat_type[i].resis_high) &&
(res >= di->bat->bat_type[i].resis_low)) { (res >= di->bm->bat_type[i].resis_low)) {
dev_dbg(di->dev, "Battery detected on %s" dev_dbg(di->dev, "Battery detected on %s"
" low %d < res %d < high: %d" " low %d < res %d < high: %d"
" index: %d\n", " index: %d\n",
di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL ? di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL ?
"BATCTRL" : "BATTEMP", "BATCTRL" : "BATTEMP",
di->bat->bat_type[i].resis_low, res, di->bm->bat_type[i].resis_low, res,
di->bat->bat_type[i].resis_high, i); di->bm->bat_type[i].resis_high, i);
di->bat->batt_id = i; di->bm->batt_id = i;
break; break;
} }
} }
if (di->bat->batt_id == BATTERY_UNKNOWN) { if (di->bm->batt_id == BATTERY_UNKNOWN) {
dev_warn(di->dev, "Battery identified as unknown" dev_warn(di->dev, "Battery identified as unknown"
", resistance %d Ohm\n", res); ", resistance %d Ohm\n", res);
return -ENXIO; return -ENXIO;
...@@ -548,13 +581,18 @@ static int ab8500_btemp_id(struct ab8500_btemp *di) ...@@ -548,13 +581,18 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
* We only have to change current source if the * We only have to change current source if the
* detected type is Type 1, else we use the 7uA source * detected type is Type 1, else we use the 7uA source
*/ */
if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL && if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL &&
di->bat->batt_id == 1) { di->bm->batt_id == 1) {
dev_dbg(di->dev, "Set BATCTRL current source to 20uA\n"); if (is_ab9540(di->parent) || is_ab8505(di->parent)) {
di->curr_source = BTEMP_BATCTRL_CURR_SRC_20UA; 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) ...@@ -569,6 +607,13 @@ static void ab8500_btemp_periodic_work(struct work_struct *work)
struct ab8500_btemp *di = container_of(work, struct ab8500_btemp *di = container_of(work,
struct ab8500_btemp, btemp_periodic_work.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); di->bat_temp = ab8500_btemp_measure_temp(di);
if (di->bat_temp != di->prev_bat_temp) { if (di->bat_temp != di->prev_bat_temp) {
...@@ -577,9 +622,9 @@ static void ab8500_btemp_periodic_work(struct work_struct *work) ...@@ -577,9 +622,9 @@ static void ab8500_btemp_periodic_work(struct work_struct *work)
} }
if (di->events.ac_conn || di->events.usb_conn) if (di->events.ac_conn || di->events.usb_conn)
interval = di->bat->temp_interval_chg; interval = di->bm->temp_interval_chg;
else else
interval = di->bat->temp_interval_nochg; interval = di->bm->temp_interval_nochg;
/* Schedule a new measurement */ /* Schedule a new measurement */
queue_delayed_work(di->btemp_wq, queue_delayed_work(di->btemp_wq,
...@@ -616,9 +661,9 @@ static irqreturn_t ab8500_btemp_templow_handler(int irq, void *_di) ...@@ -616,9 +661,9 @@ static irqreturn_t ab8500_btemp_templow_handler(int irq, void *_di)
{ {
struct ab8500_btemp *di = _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" 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 { } else {
dev_crit(di->dev, "Battery temperature lower than -10deg c\n"); 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) ...@@ -732,30 +777,30 @@ static int ab8500_btemp_get_temp(struct ab8500_btemp *di)
int temp = 0; 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 * and prior versions
*/ */
if (is_ab8500_2p0_or_earlier(di->parent)) { if (is_ab8500_3p3_or_earlier(di->parent)) {
temp = di->bat_temp * 10; temp = di->bat_temp * 10;
} else { } else {
if (di->events.btemp_low) { if (di->events.btemp_low) {
if (temp > di->btemp_ranges.btemp_low_limit) if (temp > di->btemp_ranges.btemp_low_limit)
temp = di->btemp_ranges.btemp_low_limit; temp = di->btemp_ranges.btemp_low_limit * 10;
else else
temp = di->bat_temp * 10; temp = di->bat_temp * 10;
} else if (di->events.btemp_high) { } else if (di->events.btemp_high) {
if (temp < di->btemp_ranges.btemp_high_limit) if (temp < di->btemp_ranges.btemp_high_limit)
temp = di->btemp_ranges.btemp_high_limit; temp = di->btemp_ranges.btemp_high_limit * 10;
else else
temp = di->bat_temp * 10; temp = di->bat_temp * 10;
} else if (di->events.btemp_lowmed) { } else if (di->events.btemp_lowmed) {
if (temp > di->btemp_ranges.btemp_med_limit) if (temp > di->btemp_ranges.btemp_med_limit)
temp = di->btemp_ranges.btemp_med_limit; temp = di->btemp_ranges.btemp_med_limit * 10;
else else
temp = di->bat_temp * 10; temp = di->bat_temp * 10;
} else if (di->events.btemp_medhigh) { } else if (di->events.btemp_medhigh) {
if (temp < di->btemp_ranges.btemp_med_limit) if (temp < di->btemp_ranges.btemp_med_limit)
temp = di->btemp_ranges.btemp_med_limit; temp = di->btemp_ranges.btemp_med_limit * 10;
else else
temp = di->bat_temp * 10; temp = di->bat_temp * 10;
} else } else
...@@ -806,7 +851,7 @@ static int ab8500_btemp_get_property(struct power_supply *psy, ...@@ -806,7 +851,7 @@ static int ab8500_btemp_get_property(struct power_supply *psy,
val->intval = 1; val->intval = 1;
break; break;
case POWER_SUPPLY_PROP_TECHNOLOGY: 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; break;
case POWER_SUPPLY_PROP_TEMP: case POWER_SUPPLY_PROP_TEMP:
val->intval = ab8500_btemp_get_temp(di); val->intval = ab8500_btemp_get_temp(di);
...@@ -967,6 +1012,7 @@ static char *supply_interface[] = { ...@@ -967,6 +1012,7 @@ static char *supply_interface[] = {
static int ab8500_btemp_probe(struct platform_device *pdev) static int ab8500_btemp_probe(struct platform_device *pdev)
{ {
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
struct abx500_bm_data *plat = pdev->dev.platform_data;
struct ab8500_btemp *di; struct ab8500_btemp *di;
int irq, i, ret = 0; int irq, i, ret = 0;
u8 val; u8 val;
...@@ -976,21 +1022,19 @@ static int ab8500_btemp_probe(struct platform_device *pdev) ...@@ -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__); dev_err(&pdev->dev, "%s no mem for ab8500_btemp\n", __func__);
return -ENOMEM; return -ENOMEM;
} }
di->bat = pdev->mfd_cell->platform_data;
if (!di->bat) { if (!plat) {
if (np) { dev_err(&pdev->dev, "no battery management data supplied\n");
ret = bmdevs_of_probe(&pdev->dev, np, &di->bat); return -EINVAL;
if (ret) { }
dev_err(&pdev->dev, di->bm = plat;
"failed to get battery information\n");
return ret; if (np) {
} ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm);
} else { if (ret) {
dev_err(&pdev->dev, "missing dt node for ab8500_btemp\n"); dev_err(&pdev->dev, "failed to get battery information\n");
return -EINVAL; return ret;
} }
} else {
dev_info(&pdev->dev, "falling back to legacy platform data\n");
} }
/* get parent data */ /* get parent data */
...@@ -998,6 +1042,8 @@ static int ab8500_btemp_probe(struct platform_device *pdev) ...@@ -998,6 +1042,8 @@ static int ab8500_btemp_probe(struct platform_device *pdev)
di->parent = dev_get_drvdata(pdev->dev.parent); di->parent = dev_get_drvdata(pdev->dev.parent);
di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
di->initialized = false;
/* BTEMP supply */ /* BTEMP supply */
di->btemp_psy.name = "ab8500_btemp"; di->btemp_psy.name = "ab8500_btemp";
di->btemp_psy.type = POWER_SUPPLY_TYPE_BATTERY; di->btemp_psy.type = POWER_SUPPLY_TYPE_BATTERY;
...@@ -1022,10 +1068,6 @@ static int ab8500_btemp_probe(struct platform_device *pdev) ...@@ -1022,10 +1068,6 @@ static int ab8500_btemp_probe(struct platform_device *pdev)
INIT_DEFERRABLE_WORK(&di->btemp_periodic_work, INIT_DEFERRABLE_WORK(&di->btemp_periodic_work,
ab8500_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 */ /* Set BTEMP thermal limits. Low and Med are fixed */
di->btemp_ranges.btemp_low_limit = BTEMP_THERMAL_LOW_LIMIT; di->btemp_ranges.btemp_low_limit = BTEMP_THERMAL_LOW_LIMIT;
di->btemp_ranges.btemp_med_limit = BTEMP_THERMAL_MED_LIMIT; di->btemp_ranges.btemp_med_limit = BTEMP_THERMAL_MED_LIMIT;
...@@ -1123,7 +1165,7 @@ static void __exit ab8500_btemp_exit(void) ...@@ -1123,7 +1165,7 @@ static void __exit ab8500_btemp_exit(void)
platform_driver_unregister(&ab8500_btemp_driver); platform_driver_unregister(&ab8500_btemp_driver);
} }
subsys_initcall_sync(ab8500_btemp_init); device_initcall(ab8500_btemp_init);
module_exit(ab8500_btemp_exit); module_exit(ab8500_btemp_exit);
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
......
此差异已折叠。
此差异已折叠。
...@@ -33,9 +33,6 @@ ...@@ -33,9 +33,6 @@
/* End-of-charge criteria counter */ /* End-of-charge criteria counter */
#define EOC_COND_CNT 10 #define EOC_COND_CNT 10
/* Recharge criteria counter */
#define RCH_COND_CNT 3
#define to_abx500_chargalg_device_info(x) container_of((x), \ #define to_abx500_chargalg_device_info(x) container_of((x), \
struct abx500_chargalg, chargalg_psy); struct abx500_chargalg, chargalg_psy);
...@@ -196,7 +193,6 @@ enum maxim_ret { ...@@ -196,7 +193,6 @@ enum maxim_ret {
* @dev: pointer to the structure device * @dev: pointer to the structure device
* @charge_status: battery operating status * @charge_status: battery operating status
* @eoc_cnt: counter used to determine end-of_charge * @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 * @maintenance_chg: indicate if maintenance charge is active
* @t_hyst_norm temperature hysteresis when the temperature has been * @t_hyst_norm temperature hysteresis when the temperature has been
* over or under normal limits * over or under normal limits
...@@ -207,7 +203,7 @@ enum maxim_ret { ...@@ -207,7 +203,7 @@ enum maxim_ret {
* @chg_info: information about connected charger types * @chg_info: information about connected charger types
* @batt_data: data of the battery * @batt_data: data of the battery
* @susp_status: current charger suspension status * @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 * @chargalg_psy: structure that holds the battery properties exposed by
* the charging algorithm * the charging algorithm
* @events: structure for information about events triggered * @events: structure for information about events triggered
...@@ -223,7 +219,6 @@ struct abx500_chargalg { ...@@ -223,7 +219,6 @@ struct abx500_chargalg {
struct device *dev; struct device *dev;
int charge_status; int charge_status;
int eoc_cnt; int eoc_cnt;
int rch_cnt;
bool maintenance_chg; bool maintenance_chg;
int t_hyst_norm; int t_hyst_norm;
int t_hyst_lowhigh; int t_hyst_lowhigh;
...@@ -232,7 +227,7 @@ struct abx500_chargalg { ...@@ -232,7 +227,7 @@ struct abx500_chargalg {
struct abx500_chargalg_charger_info chg_info; struct abx500_chargalg_charger_info chg_info;
struct abx500_chargalg_battery_data batt_data; struct abx500_chargalg_battery_data batt_data;
struct abx500_chargalg_suspension_status susp_status; struct abx500_chargalg_suspension_status susp_status;
struct abx500_bm_data *bat; struct abx500_bm_data *bm;
struct power_supply chargalg_psy; struct power_supply chargalg_psy;
struct ux500_charger *ac_chg; struct ux500_charger *ac_chg;
struct ux500_charger *usb_chg; struct ux500_charger *usb_chg;
...@@ -367,13 +362,13 @@ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di) ...@@ -367,13 +362,13 @@ static void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di)
case AC_CHG: case AC_CHG:
timer_expiration = timer_expiration =
round_jiffies(jiffies + round_jiffies(jiffies +
(di->bat->main_safety_tmr_h * 3600 * HZ)); (di->bm->main_safety_tmr_h * 3600 * HZ));
break; break;
case USB_CHG: case USB_CHG:
timer_expiration = timer_expiration =
round_jiffies(jiffies + round_jiffies(jiffies +
(di->bat->usb_safety_tmr_h * 3600 * HZ)); (di->bm->usb_safety_tmr_h * 3600 * HZ));
break; break;
default: default:
...@@ -450,8 +445,18 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di) ...@@ -450,8 +445,18 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
{ {
/* Check if charger exists and kick watchdog if charging */ /* Check if charger exists and kick watchdog if charging */
if (di->ac_chg && di->ac_chg->ops.kick_wd && 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); return di->ac_chg->ops.kick_wd(di->ac_chg);
}
else if (di->usb_chg && di->usb_chg->ops.kick_wd && else if (di->usb_chg && di->usb_chg->ops.kick_wd &&
di->chg_info.online_chg & USB_CHG) di->chg_info.online_chg & USB_CHG)
return di->usb_chg->ops.kick_wd(di->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) ...@@ -608,6 +613,8 @@ static void abx500_chargalg_hold_charging(struct abx500_chargalg *di)
static void abx500_chargalg_start_charging(struct abx500_chargalg *di, static void abx500_chargalg_start_charging(struct abx500_chargalg *di,
int vset, int iset) int vset, int iset)
{ {
bool start_chargalg_wd = true;
switch (di->chg_info.charger_type) { switch (di->chg_info.charger_type) {
case AC_CHG: case AC_CHG:
dev_dbg(di->dev, dev_dbg(di->dev,
...@@ -625,8 +632,12 @@ static void abx500_chargalg_start_charging(struct abx500_chargalg *di, ...@@ -625,8 +632,12 @@ static void abx500_chargalg_start_charging(struct abx500_chargalg *di,
default: default:
dev_err(di->dev, "Unknown charger to charge from\n"); dev_err(di->dev, "Unknown charger to charge from\n");
start_chargalg_wd = false;
break; 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, ...@@ -638,32 +649,32 @@ static void abx500_chargalg_start_charging(struct abx500_chargalg *di,
*/ */
static void abx500_chargalg_check_temp(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) && if (di->batt_data.temp > (di->bm->temp_low + di->t_hyst_norm) &&
di->batt_data.temp < (di->bat->temp_high - di->t_hyst_norm)) { di->batt_data.temp < (di->bm->temp_high - di->t_hyst_norm)) {
/* Temp OK! */ /* Temp OK! */
di->events.btemp_underover = false; di->events.btemp_underover = false;
di->events.btemp_lowhigh = false; di->events.btemp_lowhigh = false;
di->t_hyst_norm = 0; di->t_hyst_norm = 0;
di->t_hyst_lowhigh = 0; di->t_hyst_lowhigh = 0;
} else { } else {
if (((di->batt_data.temp >= di->bat->temp_high) && if (((di->batt_data.temp >= di->bm->temp_high) &&
(di->batt_data.temp < (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->batt_data.temp >
(di->bat->temp_under + di->t_hyst_lowhigh)) && (di->bm->temp_under + di->t_hyst_lowhigh)) &&
(di->batt_data.temp <= di->bat->temp_low))) { (di->batt_data.temp <= di->bm->temp_low))) {
/* TEMP minor!!!!! */ /* TEMP minor!!!!! */
di->events.btemp_underover = false; di->events.btemp_underover = false;
di->events.btemp_lowhigh = true; 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; di->t_hyst_lowhigh = 0;
} else if (di->batt_data.temp <= di->bat->temp_under || } else if (di->batt_data.temp <= di->bm->temp_under ||
di->batt_data.temp >= di->bat->temp_over) { di->batt_data.temp >= di->bm->temp_over) {
/* TEMP major!!!!! */ /* TEMP major!!!!! */
di->events.btemp_underover = true; di->events.btemp_underover = true;
di->events.btemp_lowhigh = false; di->events.btemp_lowhigh = false;
di->t_hyst_norm = 0; di->t_hyst_norm = 0;
di->t_hyst_lowhigh = di->bat->temp_hysteresis; di->t_hyst_lowhigh = di->bm->temp_hysteresis;
} else { } else {
/* Within hysteresis */ /* Within hysteresis */
dev_dbg(di->dev, "Within hysteresis limit temp: %d " dev_dbg(di->dev, "Within hysteresis limit temp: %d "
...@@ -682,12 +693,12 @@ static void abx500_chargalg_check_temp(struct abx500_chargalg *di) ...@@ -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) 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; di->chg_info.usb_chg_ok = false;
else else
di->chg_info.usb_chg_ok = true; 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; di->chg_info.ac_chg_ok = false;
else else
di->chg_info.ac_chg_ok = true; di->chg_info.ac_chg_ok = true;
...@@ -707,10 +718,10 @@ static void abx500_chargalg_end_of_charge(struct abx500_chargalg *di) ...@@ -707,10 +718,10 @@ static void abx500_chargalg_end_of_charge(struct abx500_chargalg *di)
if (di->charge_status == POWER_SUPPLY_STATUS_CHARGING && if (di->charge_status == POWER_SUPPLY_STATUS_CHARGING &&
di->charge_state == STATE_NORMAL && di->charge_state == STATE_NORMAL &&
!di->maintenance_chg && (di->batt_data.volt >= !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->events.usb_cv_active || di->events.ac_cv_active) &&
di->batt_data.avg_curr < 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) { di->batt_data.avg_curr > 0) {
if (++di->eoc_cnt >= EOC_COND_CNT) { if (++di->eoc_cnt >= EOC_COND_CNT) {
di->eoc_cnt = 0; di->eoc_cnt = 0;
...@@ -733,12 +744,12 @@ static void abx500_chargalg_end_of_charge(struct abx500_chargalg *di) ...@@ -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) static void init_maxim_chg_curr(struct abx500_chargalg *di)
{ {
di->ccm.original_iset = 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->ccm.current_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.test_delta_i = di->bat->maxi->charger_curr_step; di->ccm.test_delta_i = di->bm->maxi->charger_curr_step;
di->ccm.max_current = di->bat->maxi->chg_curr; di->ccm.max_current = di->bm->maxi->chg_curr;
di->ccm.condition_cnt = di->bat->maxi->wait_cycles; di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
di->ccm.level = 0; di->ccm.level = 0;
} }
...@@ -755,7 +766,7 @@ static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di) ...@@ -755,7 +766,7 @@ static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di)
{ {
int delta_i; int delta_i;
if (!di->bat->maxi->ena_maxi) if (!di->bm->maxi->ena_maxi)
return MAXIM_RET_NOACTION; return MAXIM_RET_NOACTION;
delta_i = di->ccm.original_iset - di->batt_data.inst_curr; 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) ...@@ -766,7 +777,7 @@ static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di)
if (di->ccm.wait_cnt == 0) { if (di->ccm.wait_cnt == 0) {
dev_dbg(di->dev, "lowering current\n"); dev_dbg(di->dev, "lowering current\n");
di->ccm.wait_cnt++; 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.max_current =
di->ccm.current_iset - di->ccm.test_delta_i; di->ccm.current_iset - di->ccm.test_delta_i;
di->ccm.current_iset = di->ccm.max_current; 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) ...@@ -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) if (di->ccm.current_iset == di->ccm.original_iset)
return MAXIM_RET_NOACTION; 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.current_iset = di->ccm.original_iset;
di->ccm.level = 0; di->ccm.level = 0;
...@@ -803,7 +814,7 @@ static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di) ...@@ -803,7 +814,7 @@ static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di)
di->ccm.max_current) { di->ccm.max_current) {
if (di->ccm.condition_cnt-- == 0) { if (di->ccm.condition_cnt-- == 0) {
/* Increse the iset with cco.test_delta_i */ /* 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.current_iset += di->ccm.test_delta_i;
di->ccm.level++; di->ccm.level++;
dev_dbg(di->dev, " Maximization needed, increase" dev_dbg(di->dev, " Maximization needed, increase"
...@@ -818,7 +829,7 @@ static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di) ...@@ -818,7 +829,7 @@ static enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di)
return MAXIM_RET_NOACTION; return MAXIM_RET_NOACTION;
} }
} else { } else {
di->ccm.condition_cnt = di->bat->maxi->wait_cycles; di->ccm.condition_cnt = di->bm->maxi->wait_cycles;
return MAXIM_RET_NOACTION; return MAXIM_RET_NOACTION;
} }
} }
...@@ -838,7 +849,7 @@ static void handle_maxim_chg_curr(struct abx500_chargalg *di) ...@@ -838,7 +849,7 @@ static void handle_maxim_chg_curr(struct abx500_chargalg *di)
break; break;
case MAXIM_RET_IBAT_TOO_HIGH: case MAXIM_RET_IBAT_TOO_HIGH:
result = abx500_chargalg_update_chg_curr(di, 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) if (result)
dev_err(di->dev, "failed to set chg curr\n"); dev_err(di->dev, "failed to set chg curr\n");
break; break;
...@@ -858,6 +869,7 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data) ...@@ -858,6 +869,7 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
union power_supply_propval ret; union power_supply_propval ret;
int i, j; int i, j;
bool psy_found = false; bool psy_found = false;
bool capacity_updated = false;
psy = (struct power_supply *)data; psy = (struct power_supply *)data;
ext = dev_get_drvdata(dev); ext = dev_get_drvdata(dev);
...@@ -870,6 +882,16 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data) ...@@ -870,6 +882,16 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
if (!psy_found) if (!psy_found)
return 0; 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 */ /* Go through all properties for the psy */
for (j = 0; j < ext->num_properties; j++) { for (j = 0; j < ext->num_properties; j++) {
enum power_supply_property prop; enum power_supply_property prop;
...@@ -1154,7 +1176,8 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data) ...@@ -1154,7 +1176,8 @@ static int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data)
} }
break; break;
case POWER_SUPPLY_PROP_CAPACITY: case POWER_SUPPLY_PROP_CAPACITY:
di->batt_data.percent = ret.intval; if (!capacity_updated)
di->batt_data.percent = ret.intval;
break; break;
default: default:
break; break;
...@@ -1210,7 +1233,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) ...@@ -1210,7 +1233,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
* this way * this way
*/ */
if (!charger_status || 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) { if (di->charge_state != STATE_HANDHELD) {
di->events.safety_timer_expired = false; di->events.safety_timer_expired = false;
abx500_chargalg_state_to(di, STATE_HANDHELD_INIT); abx500_chargalg_state_to(di, STATE_HANDHELD_INIT);
...@@ -1394,8 +1417,8 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) ...@@ -1394,8 +1417,8 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
case STATE_NORMAL_INIT: case STATE_NORMAL_INIT:
abx500_chargalg_start_charging(di, abx500_chargalg_start_charging(di,
di->bat->bat_type[di->bat->batt_id].normal_vol_lvl, di->bm->bat_type[di->bm->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_cur_lvl);
abx500_chargalg_state_to(di, STATE_NORMAL); abx500_chargalg_state_to(di, STATE_NORMAL);
abx500_chargalg_start_safety_timer(di); abx500_chargalg_start_safety_timer(di);
abx500_chargalg_stop_maintenance_timer(di); abx500_chargalg_stop_maintenance_timer(di);
...@@ -1411,7 +1434,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) ...@@ -1411,7 +1434,7 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
handle_maxim_chg_curr(di); handle_maxim_chg_curr(di);
if (di->charge_status == POWER_SUPPLY_STATUS_FULL && if (di->charge_status == POWER_SUPPLY_STATUS_FULL &&
di->maintenance_chg) { di->maintenance_chg) {
if (di->bat->no_maintenance) if (di->bm->no_maintenance)
abx500_chargalg_state_to(di, abx500_chargalg_state_to(di,
STATE_WAIT_FOR_RECHARGE_INIT); STATE_WAIT_FOR_RECHARGE_INIT);
else else
...@@ -1424,28 +1447,25 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) ...@@ -1424,28 +1447,25 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
case STATE_WAIT_FOR_RECHARGE_INIT: case STATE_WAIT_FOR_RECHARGE_INIT:
abx500_chargalg_hold_charging(di); abx500_chargalg_hold_charging(di);
abx500_chargalg_state_to(di, STATE_WAIT_FOR_RECHARGE); abx500_chargalg_state_to(di, STATE_WAIT_FOR_RECHARGE);
di->rch_cnt = RCH_COND_CNT;
/* Intentional fallthrough */ /* Intentional fallthrough */
case STATE_WAIT_FOR_RECHARGE: case STATE_WAIT_FOR_RECHARGE:
if (di->batt_data.volt <= if (di->batt_data.percent <=
di->bat->bat_type[di->bat->batt_id].recharge_vol) { di->bm->bat_type[di->bm->batt_id].
if (di->rch_cnt-- == 0) recharge_cap)
abx500_chargalg_state_to(di, STATE_NORMAL_INIT); abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
} else
di->rch_cnt = RCH_COND_CNT;
break; break;
case STATE_MAINTENANCE_A_INIT: case STATE_MAINTENANCE_A_INIT:
abx500_chargalg_stop_safety_timer(di); abx500_chargalg_stop_safety_timer(di);
abx500_chargalg_start_maintenance_timer(di, abx500_chargalg_start_maintenance_timer(di,
di->bat->bat_type[ di->bm->bat_type[
di->bat->batt_id].maint_a_chg_timer_h); di->bm->batt_id].maint_a_chg_timer_h);
abx500_chargalg_start_charging(di, abx500_chargalg_start_charging(di,
di->bat->bat_type[ di->bm->bat_type[
di->bat->batt_id].maint_a_vol_lvl, di->bm->batt_id].maint_a_vol_lvl,
di->bat->bat_type[ di->bm->bat_type[
di->bat->batt_id].maint_a_cur_lvl); di->bm->batt_id].maint_a_cur_lvl);
abx500_chargalg_state_to(di, STATE_MAINTENANCE_A); abx500_chargalg_state_to(di, STATE_MAINTENANCE_A);
power_supply_changed(&di->chargalg_psy); power_supply_changed(&di->chargalg_psy);
/* Intentional fallthrough*/ /* Intentional fallthrough*/
...@@ -1459,13 +1479,13 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) ...@@ -1459,13 +1479,13 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
case STATE_MAINTENANCE_B_INIT: case STATE_MAINTENANCE_B_INIT:
abx500_chargalg_start_maintenance_timer(di, abx500_chargalg_start_maintenance_timer(di,
di->bat->bat_type[ di->bm->bat_type[
di->bat->batt_id].maint_b_chg_timer_h); di->bm->batt_id].maint_b_chg_timer_h);
abx500_chargalg_start_charging(di, abx500_chargalg_start_charging(di,
di->bat->bat_type[ di->bm->bat_type[
di->bat->batt_id].maint_b_vol_lvl, di->bm->batt_id].maint_b_vol_lvl,
di->bat->bat_type[ di->bm->bat_type[
di->bat->batt_id].maint_b_cur_lvl); di->bm->batt_id].maint_b_cur_lvl);
abx500_chargalg_state_to(di, STATE_MAINTENANCE_B); abx500_chargalg_state_to(di, STATE_MAINTENANCE_B);
power_supply_changed(&di->chargalg_psy); power_supply_changed(&di->chargalg_psy);
/* Intentional fallthrough*/ /* Intentional fallthrough*/
...@@ -1479,10 +1499,10 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) ...@@ -1479,10 +1499,10 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
case STATE_TEMP_LOWHIGH_INIT: case STATE_TEMP_LOWHIGH_INIT:
abx500_chargalg_start_charging(di, abx500_chargalg_start_charging(di,
di->bat->bat_type[ di->bm->bat_type[
di->bat->batt_id].low_high_vol_lvl, di->bm->batt_id].low_high_vol_lvl,
di->bat->bat_type[ di->bm->bat_type[
di->bat->batt_id].low_high_cur_lvl); di->bm->batt_id].low_high_cur_lvl);
abx500_chargalg_stop_maintenance_timer(di); abx500_chargalg_stop_maintenance_timer(di);
di->charge_status = POWER_SUPPLY_STATUS_CHARGING; di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
abx500_chargalg_state_to(di, STATE_TEMP_LOWHIGH); abx500_chargalg_state_to(di, STATE_TEMP_LOWHIGH);
...@@ -1543,11 +1563,11 @@ static void abx500_chargalg_periodic_work(struct work_struct *work) ...@@ -1543,11 +1563,11 @@ static void abx500_chargalg_periodic_work(struct work_struct *work)
if (di->chg_info.conn_chg) if (di->chg_info.conn_chg)
queue_delayed_work(di->chargalg_wq, queue_delayed_work(di->chargalg_wq,
&di->chargalg_periodic_work, &di->chargalg_periodic_work,
di->bat->interval_charging * HZ); di->bm->interval_charging * HZ);
else else
queue_delayed_work(di->chargalg_wq, queue_delayed_work(di->chargalg_wq,
&di->chargalg_periodic_work, &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, ...@@ -1614,10 +1634,13 @@ static int abx500_chargalg_get_property(struct power_supply *psy,
if (di->events.batt_ovv) { if (di->events.batt_ovv) {
val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
} else if (di->events.btemp_underover) { } 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; val->intval = POWER_SUPPLY_HEALTH_COLD;
else else
val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; 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 { } else {
val->intval = POWER_SUPPLY_HEALTH_GOOD; val->intval = POWER_SUPPLY_HEALTH_GOOD;
} }
...@@ -1630,6 +1653,25 @@ static int abx500_chargalg_get_property(struct power_supply *psy, ...@@ -1630,6 +1653,25 @@ static int abx500_chargalg_get_property(struct power_supply *psy,
/* Exposure to the sysfs interface */ /* 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 * abx500_chargalg_sysfs_charger() - sysfs store operations
* @kobj: pointer to the struct kobject * @kobj: pointer to the struct kobject
...@@ -1698,7 +1740,7 @@ static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj, ...@@ -1698,7 +1740,7 @@ static ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj,
static struct attribute abx500_chargalg_en_charger = \ static struct attribute abx500_chargalg_en_charger = \
{ {
.name = "chargalg", .name = "chargalg",
.mode = S_IWUGO, .mode = S_IRUGO | S_IWUSR,
}; };
static struct attribute *abx500_chargalg_chg[] = { static struct attribute *abx500_chargalg_chg[] = {
...@@ -1707,6 +1749,7 @@ 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 = { static const struct sysfs_ops abx500_chargalg_sysfs_ops = {
.show = abx500_chargalg_sysfs_show,
.store = abx500_chargalg_sysfs_charger, .store = abx500_chargalg_sysfs_charger,
}; };
...@@ -1806,6 +1849,7 @@ static char *supply_interface[] = { ...@@ -1806,6 +1849,7 @@ static char *supply_interface[] = {
static int abx500_chargalg_probe(struct platform_device *pdev) static int abx500_chargalg_probe(struct platform_device *pdev)
{ {
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
struct abx500_bm_data *plat = pdev->dev.platform_data;
struct abx500_chargalg *di; struct abx500_chargalg *di;
int ret = 0; int ret = 0;
...@@ -1814,21 +1858,19 @@ static int abx500_chargalg_probe(struct platform_device *pdev) ...@@ -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__); dev_err(&pdev->dev, "%s no mem for ab8500_chargalg\n", __func__);
return -ENOMEM; return -ENOMEM;
} }
di->bat = pdev->mfd_cell->platform_data;
if (!di->bat) { if (!plat) {
if (np) { dev_err(&pdev->dev, "no battery management data supplied\n");
ret = bmdevs_of_probe(&pdev->dev, np, &di->bat); return -EINVAL;
if (ret) { }
dev_err(&pdev->dev, di->bm = plat;
"failed to get battery information\n");
return ret; if (np) {
} ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm);
} else { if (ret) {
dev_err(&pdev->dev, "missing dt node for ab8500_chargalg\n"); dev_err(&pdev->dev, "failed to get battery information\n");
return -EINVAL; return ret;
} }
} else {
dev_info(&pdev->dev, "falling back to legacy platform data\n");
} }
/* get device struct */ /* get device struct */
......
...@@ -28,7 +28,6 @@ ...@@ -28,7 +28,6 @@
* http://www.ti.com/product/bq24155 * http://www.ti.com/product/bq24155
*/ */
#include <linux/version.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/param.h> #include <linux/param.h>
...@@ -734,12 +733,10 @@ static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode) ...@@ -734,12 +733,10 @@ static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode)
int charger = 0; int charger = 0;
int boost = 0; int boost = 0;
if (mode == BQ2415X_MODE_HOST_CHARGER ||
mode == BQ2415X_MODE_DEDICATED_CHARGER)
charger = 1;
if (mode == BQ2415X_MODE_BOOST) if (mode == BQ2415X_MODE_BOOST)
boost = 1; boost = 1;
else if (mode != BQ2415X_MODE_OFF)
charger = 1;
if (!charger) if (!charger)
ret = bq2415x_exec_command(bq, BQ2415X_CHARGER_DISABLE); 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) ...@@ -751,6 +748,10 @@ static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode)
return ret; return ret;
switch (mode) { 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: case BQ2415X_MODE_NONE:
dev_dbg(bq->dev, "changing mode to: N/A\n"); dev_dbg(bq->dev, "changing mode to: N/A\n");
ret = bq2415x_set_current_limit(bq, 100); ret = bq2415x_set_current_limit(bq, 100);
...@@ -843,7 +844,7 @@ static void bq2415x_timer_error(struct bq2415x_device *bq, const char *msg) ...@@ -843,7 +844,7 @@ static void bq2415x_timer_error(struct bq2415x_device *bq, const char *msg)
dev_err(bq->dev, "%s\n", msg); dev_err(bq->dev, "%s\n", msg);
if (bq->automode > 0) if (bq->automode > 0)
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); bq2415x_set_autotimer(bq, 0);
} }
...@@ -1136,6 +1137,10 @@ static ssize_t bq2415x_sysfs_set_mode(struct device *dev, ...@@ -1136,6 +1137,10 @@ static ssize_t bq2415x_sysfs_set_mode(struct device *dev,
return -ENOSYS; return -ENOSYS;
bq->automode = 1; bq->automode = 1;
mode = bq->reported_mode; 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) { } else if (strncmp(buf, "none", 4) == 0) {
if (bq->automode > 0) if (bq->automode > 0)
bq->automode = 0; bq->automode = 0;
...@@ -1183,6 +1188,9 @@ static ssize_t bq2415x_sysfs_show_mode(struct device *dev, ...@@ -1183,6 +1188,9 @@ static ssize_t bq2415x_sysfs_show_mode(struct device *dev,
ret += sprintf(buf+ret, "auto ("); ret += sprintf(buf+ret, "auto (");
switch (bq->mode) { switch (bq->mode) {
case BQ2415X_MODE_OFF:
ret += sprintf(buf+ret, "off");
break;
case BQ2415X_MODE_NONE: case BQ2415X_MODE_NONE:
ret += sprintf(buf+ret, "none"); ret += sprintf(buf+ret, "none");
break; break;
...@@ -1217,6 +1225,8 @@ static ssize_t bq2415x_sysfs_show_reported_mode(struct device *dev, ...@@ -1217,6 +1225,8 @@ static ssize_t bq2415x_sysfs_show_reported_mode(struct device *dev,
return -EINVAL; return -EINVAL;
switch (bq->reported_mode) { switch (bq->reported_mode) {
case BQ2415X_MODE_OFF:
return sprintf(buf, "off\n");
case BQ2415X_MODE_NONE: case BQ2415X_MODE_NONE:
return sprintf(buf, "none\n"); return sprintf(buf, "none\n");
case BQ2415X_MODE_HOST_CHARGER: case BQ2415X_MODE_HOST_CHARGER:
...@@ -1523,7 +1533,7 @@ static int bq2415x_probe(struct i2c_client *client, ...@@ -1523,7 +1533,7 @@ static int bq2415x_probe(struct i2c_client *client,
goto error_1; goto error_1;
} }
bq = kzalloc(sizeof(*bq), GFP_KERNEL); bq = devm_kzalloc(&client->dev, sizeof(*bq), GFP_KERNEL);
if (!bq) { if (!bq) {
dev_err(&client->dev, "failed to allocate device data\n"); dev_err(&client->dev, "failed to allocate device data\n");
ret = -ENOMEM; ret = -ENOMEM;
...@@ -1536,8 +1546,8 @@ static int bq2415x_probe(struct i2c_client *client, ...@@ -1536,8 +1546,8 @@ static int bq2415x_probe(struct i2c_client *client,
bq->dev = &client->dev; bq->dev = &client->dev;
bq->chip = id->driver_data; bq->chip = id->driver_data;
bq->name = name; bq->name = name;
bq->mode = BQ2415X_MODE_NONE; bq->mode = BQ2415X_MODE_OFF;
bq->reported_mode = BQ2415X_MODE_NONE; bq->reported_mode = BQ2415X_MODE_OFF;
bq->autotimer = 0; bq->autotimer = 0;
bq->automode = 0; bq->automode = 0;
...@@ -1549,19 +1559,19 @@ static int bq2415x_probe(struct i2c_client *client, ...@@ -1549,19 +1559,19 @@ static int bq2415x_probe(struct i2c_client *client,
ret = bq2415x_power_supply_init(bq); ret = bq2415x_power_supply_init(bq);
if (ret) { if (ret) {
dev_err(bq->dev, "failed to register power supply: %d\n", ret); dev_err(bq->dev, "failed to register power supply: %d\n", ret);
goto error_3; goto error_2;
} }
ret = bq2415x_sysfs_init(bq); ret = bq2415x_sysfs_init(bq);
if (ret) { if (ret) {
dev_err(bq->dev, "failed to create sysfs entries: %d\n", ret); dev_err(bq->dev, "failed to create sysfs entries: %d\n", ret);
goto error_4; goto error_3;
} }
ret = bq2415x_set_defaults(bq); ret = bq2415x_set_defaults(bq);
if (ret) { if (ret) {
dev_err(bq->dev, "failed to set default values: %d\n", 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) { if (bq->init_data.set_mode_hook) {
...@@ -1585,12 +1595,10 @@ static int bq2415x_probe(struct i2c_client *client, ...@@ -1585,12 +1595,10 @@ static int bq2415x_probe(struct i2c_client *client,
dev_info(bq->dev, "driver registered\n"); dev_info(bq->dev, "driver registered\n");
return 0; return 0;
error_5:
bq2415x_sysfs_exit(bq);
error_4: error_4:
bq2415x_power_supply_exit(bq); bq2415x_sysfs_exit(bq);
error_3: error_3:
kfree(bq); bq2415x_power_supply_exit(bq);
error_2: error_2:
kfree(name); kfree(name);
error_1: error_1:
...@@ -1622,7 +1630,6 @@ static int bq2415x_remove(struct i2c_client *client) ...@@ -1622,7 +1630,6 @@ static int bq2415x_remove(struct i2c_client *client)
dev_info(bq->dev, "driver unregistered\n"); dev_info(bq->dev, "driver unregistered\n");
kfree(bq->name); kfree(bq->name);
kfree(bq);
return 0; return 0;
} }
...@@ -1652,18 +1659,7 @@ static struct i2c_driver bq2415x_driver = { ...@@ -1652,18 +1659,7 @@ static struct i2c_driver bq2415x_driver = {
.remove = bq2415x_remove, .remove = bq2415x_remove,
.id_table = bq2415x_i2c_id_table, .id_table = bq2415x_i2c_id_table,
}; };
module_i2c_driver(bq2415x_driver);
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_AUTHOR("Pali Rohár <pali.rohar@gmail.com>"); MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
MODULE_DESCRIPTION("bq2415x charger driver"); MODULE_DESCRIPTION("bq2415x charger driver");
......
...@@ -299,7 +299,7 @@ static int bq27x00_battery_read_energy(struct bq27x00_device_info *di) ...@@ -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. * Or < 0 if something fails.
*/ */
static int bq27x00_battery_read_temperature(struct bq27x00_device_info *di) 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) ...@@ -312,10 +312,8 @@ static int bq27x00_battery_read_temperature(struct bq27x00_device_info *di)
return temp; return temp;
} }
if (bq27xxx_is_chip_version_higher(di)) if (!bq27xxx_is_chip_version_higher(di))
temp -= 2731; temp = 5 * temp / 2;
else
temp = ((temp * 5) - 5463) / 2;
return temp; return temp;
} }
...@@ -448,7 +446,6 @@ static void bq27x00_update(struct bq27x00_device_info *di) ...@@ -448,7 +446,6 @@ static void bq27x00_update(struct bq27x00_device_info *di)
cache.temperature = bq27x00_battery_read_temperature(di); cache.temperature = bq27x00_battery_read_temperature(di);
if (!is_bq27425) if (!is_bq27425)
cache.cycle_count = bq27x00_battery_read_cyct(di); cache.cycle_count = bq27x00_battery_read_cyct(di);
cache.cycle_count = bq27x00_battery_read_cyct(di);
cache.power_avg = cache.power_avg =
bq27x00_battery_read_pwr_avg(di, BQ27x00_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, ...@@ -642,6 +639,8 @@ static int bq27x00_battery_get_property(struct power_supply *psy,
break; break;
case POWER_SUPPLY_PROP_TEMP: case POWER_SUPPLY_PROP_TEMP:
ret = bq27x00_simple_value(di->cache.temperature, val); ret = bq27x00_simple_value(di->cache.temperature, val);
if (ret == 0)
val->intval -= 2731;
break; break;
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
ret = bq27x00_simple_value(di->cache.time_to_empty, val); ret = bq27x00_simple_value(di->cache.time_to_empty, val);
...@@ -696,7 +695,6 @@ static int bq27x00_powersupply_init(struct bq27x00_device_info *di) ...@@ -696,7 +695,6 @@ static int bq27x00_powersupply_init(struct bq27x00_device_info *di)
int ret; int ret;
di->bat.type = POWER_SUPPLY_TYPE_BATTERY; di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
di->chip = BQ27425;
if (di->chip == BQ27425) { if (di->chip == BQ27425) {
di->bat.properties = bq27425_battery_props; di->bat.properties = bq27425_battery_props;
di->bat.num_properties = ARRAY_SIZE(bq27425_battery_props); di->bat.num_properties = ARRAY_SIZE(bq27425_battery_props);
......
...@@ -669,15 +669,21 @@ static void _setup_polling(struct work_struct *work) ...@@ -669,15 +669,21 @@ static void _setup_polling(struct work_struct *work)
WARN(cm_wq == NULL, "charger-manager: workqueue not initialized" WARN(cm_wq == NULL, "charger-manager: workqueue not initialized"
". try it later. %s\n", __func__); ". 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; _next_polling = jiffies + polling_jiffy;
if (!delayed_work_pending(&cm_monitor_work) || if (time_before(_next_polling, next_polling)) {
(delayed_work_pending(&cm_monitor_work) &&
time_after(next_polling, _next_polling))) {
next_polling = jiffies + polling_jiffy;
mod_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy); 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: out:
mutex_unlock(&cm_list_mtx); mutex_unlock(&cm_list_mtx);
} }
...@@ -751,8 +757,7 @@ static void misc_event_handler(struct charger_manager *cm, ...@@ -751,8 +757,7 @@ static void misc_event_handler(struct charger_manager *cm,
if (cm_suspended) if (cm_suspended)
device_set_wakeup_capable(cm->dev, true); device_set_wakeup_capable(cm->dev, true);
if (!delayed_work_pending(&cm_monitor_work) && if (is_polling_required(cm) && cm->desc->polling_interval_ms)
is_polling_required(cm) && cm->desc->polling_interval_ms)
schedule_work(&setup_polling); schedule_work(&setup_polling);
uevent_notify(cm, default_event_names[type]); uevent_notify(cm, default_event_names[type]);
} }
...@@ -1170,8 +1175,7 @@ static int charger_extcon_notifier(struct notifier_block *self, ...@@ -1170,8 +1175,7 @@ static int charger_extcon_notifier(struct notifier_block *self,
* when charger cable is attached. * when charger cable is attached.
*/ */
if (cable->attached && is_polling_required(cable->cm)) { 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); schedule_work(&setup_polling);
} }
...@@ -1215,6 +1219,55 @@ static int charger_extcon_init(struct charger_manager *cm, ...@@ -1215,6 +1219,55 @@ static int charger_extcon_init(struct charger_manager *cm,
return ret; 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) */ /* help function of sysfs node to control charger(regulator) */
static ssize_t charger_name_show(struct device *dev, static ssize_t charger_name_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
...@@ -1274,7 +1327,7 @@ static ssize_t charger_externally_control_store(struct device *dev, ...@@ -1274,7 +1327,7 @@ static ssize_t charger_externally_control_store(struct device *dev,
for (i = 0; i < desc->num_charger_regulators; i++) { for (i = 0; i < desc->num_charger_regulators; i++) {
if (&desc->charger_regulators[i] != charger && 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 * At least, one charger is controlled by
* charger-manager * charger-manager
...@@ -1303,13 +1356,107 @@ static ssize_t charger_externally_control_store(struct device *dev, ...@@ -1303,13 +1356,107 @@ static ssize_t charger_externally_control_store(struct device *dev,
return count; 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) static int charger_manager_probe(struct platform_device *pdev)
{ {
struct charger_desc *desc = dev_get_platdata(&pdev->dev); struct charger_desc *desc = dev_get_platdata(&pdev->dev);
struct charger_manager *cm; struct charger_manager *cm;
int ret = 0, i = 0; int ret = 0, i = 0;
int j = 0; int j = 0;
int chargers_externally_control = 1;
union power_supply_propval val; union power_supply_propval val;
if (g_desc && !rtc_dev && g_desc->rtc_name) { if (g_desc && !rtc_dev && g_desc->rtc_name) {
...@@ -1440,11 +1587,10 @@ static int charger_manager_probe(struct platform_device *pdev) ...@@ -1440,11 +1587,10 @@ static int charger_manager_probe(struct platform_device *pdev)
memcpy(&cm->charger_psy, &psy_default, sizeof(psy_default)); 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); strncpy(cm->psy_name_buf, psy_default.name, PSY_NAME_MAX);
} else { else
strncpy(cm->psy_name_buf, desc->psy_name, PSY_NAME_MAX); strncpy(cm->psy_name_buf, desc->psy_name, PSY_NAME_MAX);
}
cm->charger_psy.name = cm->psy_name_buf; cm->charger_psy.name = cm->psy_name_buf;
/* Allocate for psy properties because they may vary */ /* Allocate for psy properties because they may vary */
...@@ -1496,105 +1642,19 @@ static int charger_manager_probe(struct platform_device *pdev) ...@@ -1496,105 +1642,19 @@ static int charger_manager_probe(struct platform_device *pdev)
goto err_register; goto err_register;
} }
for (i = 0 ; i < desc->num_charger_regulators ; i++) { /* Register extcon device for charger cable */
struct charger_regulator *charger ret = charger_manager_register_extcon(cm);
= &desc->charger_regulators[i]; if (ret < 0) {
char buf[11]; dev_err(&pdev->dev, "Cannot initialize extcon device\n");
char *str; goto err_reg_extcon;
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);
}
}
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); /* Register sysfs entry for charger(regulator) */
if (ret) { ret = charger_manager_register_sysfs(cm);
dev_err(&pdev->dev, "Cannot enable charger regulators\n"); if (ret < 0) {
goto err_chg_enable; dev_err(&pdev->dev,
"Cannot initialize sysfs entry of regulator\n");
goto err_reg_sysfs;
} }
/* Add to the list */ /* Add to the list */
...@@ -1613,27 +1673,28 @@ static int charger_manager_probe(struct platform_device *pdev) ...@@ -1613,27 +1673,28 @@ static int charger_manager_probe(struct platform_device *pdev)
return 0; return 0;
err_chg_enable: err_reg_sysfs:
for (i = 0; i < desc->num_charger_regulators; i++) { for (i = 0; i < desc->num_charger_regulators; i++) {
struct charger_regulator *charger; struct charger_regulator *charger;
charger = &desc->charger_regulators[i]; charger = &desc->charger_regulators[i];
sysfs_remove_group(&cm->charger_psy.dev->kobj, sysfs_remove_group(&cm->charger_psy.dev->kobj,
&charger->attr_g); &charger->attr_g);
kfree(charger->attr_g.name); kfree(charger->attr_g.name);
} }
err_extcon: err_reg_extcon:
for (i = 0 ; i < desc->num_charger_regulators ; i++) { for (i = 0; i < desc->num_charger_regulators; i++) {
struct charger_regulator *charger struct charger_regulator *charger;
= &desc->charger_regulators[i];
for (j = 0 ; j < charger->num_cables ; j++) { charger = &desc->charger_regulators[i];
for (j = 0; j < charger->num_cables; j++) {
struct charger_cable *cable = &charger->cables[j]; struct charger_cable *cable = &charger->cables[j];
extcon_unregister_interest(&cable->extcon_dev); 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); regulator_put(desc->charger_regulators[i].consumer);
}
power_supply_unregister(&cm->charger_psy); power_supply_unregister(&cm->charger_psy);
err_register: err_register:
...@@ -1661,10 +1722,8 @@ static int charger_manager_remove(struct platform_device *pdev) ...@@ -1661,10 +1722,8 @@ static int charger_manager_remove(struct platform_device *pdev)
list_del(&cm->entry); list_del(&cm->entry);
mutex_unlock(&cm_list_mtx); mutex_unlock(&cm_list_mtx);
if (work_pending(&setup_polling)) cancel_work_sync(&setup_polling);
cancel_work_sync(&setup_polling); cancel_delayed_work_sync(&cm_monitor_work);
if (delayed_work_pending(&cm_monitor_work))
cancel_delayed_work_sync(&cm_monitor_work);
for (i = 0 ; i < desc->num_charger_regulators ; i++) { for (i = 0 ; i < desc->num_charger_regulators ; i++) {
struct charger_regulator *charger struct charger_regulator *charger
...@@ -1733,8 +1792,7 @@ static int cm_suspend_prepare(struct device *dev) ...@@ -1733,8 +1792,7 @@ static int cm_suspend_prepare(struct device *dev)
cm_suspended = true; 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_ext_pwr_inserted = is_ext_pwr_online(cm);
cm->status_save_batt = is_batt_present(cm); cm->status_save_batt = is_batt_present(cm);
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/notifier.h>
#define DA9030_FAULT_LOG 0x0a #define DA9030_FAULT_LOG 0x0a
#define DA9030_FAULT_LOG_OVER_TEMP (1 << 7) #define DA9030_FAULT_LOG_OVER_TEMP (1 << 7)
......
...@@ -337,7 +337,7 @@ static unsigned char da9052_determine_vc_tbl_index(unsigned char adc_temp) ...@@ -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]) if (adc_temp > vc_tbl_ref[DA9052_VC_TBL_REF_SZ - 1])
return 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]) && if ((adc_temp > vc_tbl_ref[i]) &&
(adc_temp <= DA9052_MEAN(vc_tbl_ref[i], vc_tbl_ref[i + 1]))) (adc_temp <= DA9052_MEAN(vc_tbl_ref[i], vc_tbl_ref[i + 1])))
return i; return i;
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
* *
* DS2786 added by Yulia Vilensky <vilensky@compulab.co.il> * 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 * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
...@@ -19,6 +21,7 @@ ...@@ -19,6 +21,7 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/swab.h> #include <linux/swab.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/idr.h> #include <linux/idr.h>
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -40,6 +43,8 @@ ...@@ -40,6 +43,8 @@
#define DS2786_CURRENT_UNITS 25 #define DS2786_CURRENT_UNITS 25
#define DS278x_DELAY 1000
struct ds278x_info; struct ds278x_info;
struct ds278x_battery_ops { struct ds278x_battery_ops {
...@@ -54,8 +59,11 @@ struct ds278x_info { ...@@ -54,8 +59,11 @@ struct ds278x_info {
struct i2c_client *client; struct i2c_client *client;
struct power_supply battery; struct power_supply battery;
struct ds278x_battery_ops *ops; struct ds278x_battery_ops *ops;
struct delayed_work bat_work;
int id; int id;
int rsns; int rsns;
int capacity;
int status; /* State Of Charge */
}; };
static DEFINE_IDR(battery_id); static DEFINE_IDR(battery_id);
...@@ -220,6 +228,8 @@ static int ds278x_get_status(struct ds278x_info *info, int *status) ...@@ -220,6 +228,8 @@ static int ds278x_get_status(struct ds278x_info *info, int *status)
if (err) if (err)
return err; return err;
info->capacity = capacity;
if (capacity == 100) if (capacity == 100)
*status = POWER_SUPPLY_STATUS_FULL; *status = POWER_SUPPLY_STATUS_FULL;
else if (current_uA == 0) else if (current_uA == 0)
...@@ -267,6 +277,27 @@ static int ds278x_battery_get_property(struct power_supply *psy, ...@@ -267,6 +277,27 @@ static int ds278x_battery_get_property(struct power_supply *psy,
return ret; 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[] = { static enum power_supply_property ds278x_battery_props[] = {
POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CAPACITY,
...@@ -295,10 +326,39 @@ static int ds278x_battery_remove(struct i2c_client *client) ...@@ -295,10 +326,39 @@ static int ds278x_battery_remove(struct i2c_client *client)
idr_remove(&battery_id, info->id); idr_remove(&battery_id, info->id);
mutex_unlock(&battery_lock); mutex_unlock(&battery_lock);
cancel_delayed_work(&info->bat_work);
kfree(info); kfree(info);
return 0; 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 { enum ds278x_num_id {
DS2782 = 0, DS2782 = 0,
DS2786, DS2786,
...@@ -368,10 +428,17 @@ static int ds278x_battery_probe(struct i2c_client *client, ...@@ -368,10 +428,17 @@ static int ds278x_battery_probe(struct i2c_client *client,
info->ops = &ds278x_ops[id->driver_data]; info->ops = &ds278x_ops[id->driver_data];
ds278x_power_supply_init(&info->battery); 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); ret = power_supply_register(&client->dev, &info->battery);
if (ret) { if (ret) {
dev_err(&client->dev, "failed to register battery\n"); dev_err(&client->dev, "failed to register battery\n");
goto fail_register; goto fail_register;
} else {
schedule_delayed_work(&info->bat_work, DS278x_DELAY);
} }
return 0; return 0;
...@@ -401,6 +468,8 @@ static struct i2c_driver ds278x_battery_driver = { ...@@ -401,6 +468,8 @@ static struct i2c_driver ds278x_battery_driver = {
}, },
.probe = ds278x_battery_probe, .probe = ds278x_battery_probe,
.remove = ds278x_battery_remove, .remove = ds278x_battery_remove,
.suspend = ds278x_suspend,
.resume = ds278x_resume,
.id_table = ds278x_id, .id_table = ds278x_id,
}; };
module_i2c_driver(ds278x_battery_driver); module_i2c_driver(ds278x_battery_driver);
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
...@@ -55,7 +55,8 @@ static ssize_t power_supply_show_property(struct device *dev, ...@@ -55,7 +55,8 @@ static ssize_t power_supply_show_property(struct device *dev,
}; };
static char *health_text[] = { static char *health_text[] = {
"Unknown", "Good", "Overheat", "Dead", "Over voltage", "Unknown", "Good", "Overheat", "Dead", "Over voltage",
"Unspecified failure", "Cold", "Unspecified failure", "Cold", "Watchdog timer expire",
"Safety timer expire"
}; };
static char *technology_text[] = { static char *technology_text[] = {
"Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd", "Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd",
......
此差异已折叠。
obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o
obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册