Commit 7313264b authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-v3.8-merged' of git://git.infradead.org/battery-2.6

Pull battery subsystem updates from Anton Vorontsov:
 "Highlights:

   - Two new drivers from Pali Rohár and N900 hackers: rx51_battery and
     bq2415x_charger.  The drivers are a part of a solution to replace
     the proprietary Nokia BME stack

   - Power supply core now registers devices with a thermal cooling
     subsystem, so we can now automatically throttle charging.  Thanks
     to Ramakrishna Pallala!

   - Device tree support for ab8500 and max8925_power drivers

   - Random fixups and enhancements for a bunch of drivers."

* tag 'for-v3.8-merged' of git://git.infradead.org/battery-2.6: (22 commits)
  max8925_power: Add support for device-tree initialization
  ab8500: Add devicetree support for chargalg
  ab8500: Add devicetree support for charger
  ab8500: Add devicetree support for btemp
  ab8500: Add devicetree support for fuelgauge
  twl4030_charger: Change TWL4030_MODULE_* ids to TWL_MODULE_*
  jz4740-battery: Use devm_request_and_ioremap
  jz4740-battery: Use devm_kzalloc
  bq27x00_battery: Fixup nominal available capacity reporting
  bq2415x_charger: Fix style issues
  bq2415x_charger: Add Kconfig/Makefile entries
  power_supply: Add bq2415x charger driver
  power_supply: Add new Nokia RX-51 (N900) power supply battery driver
  max17042_battery: Fix missing verify_model_lock() return value check
  ds2782_battery: Fix signedness bug in ds278x_read_reg16()
  lp8788-charger: Fix ADC channel names
  lp8788-charger: Fix wrong ADC conversion
  lp8788-charger: Use consumer device name on setting IIO channels
  power_supply: Register power supply for thermal cooling device
  power_supply: Add support for CHARGE_CONTROL_* attributes
  ...
parents d8c532c4 76d8a23b
...@@ -24,7 +24,32 @@ ab8500-bm : : : Battery Manager ...@@ -24,7 +24,32 @@ ab8500-bm : : : Battery Manager
ab8500-btemp : : : Battery Temperature ab8500-btemp : : : Battery Temperature
ab8500-charger : : : Battery Charger ab8500-charger : : : Battery Charger
ab8500-codec : : : Audio Codec ab8500-codec : : : Audio Codec
ab8500-fg : : : Fuel Gauge ab8500-fg : : vddadc : Fuel Gauge
: NCONV_ACCU : : Accumulate N Sample Conversion
: BATT_OVV : : Battery Over Voltage
: LOW_BAT_F : : LOW threshold battery voltage
: CC_INT_CALIB : : Coulomb Counter Internal Calibration
: CCEOC : : Coulomb Counter End of Conversion
ab8500-btemp : : vtvout : Battery Temperature
: BAT_CTRL_INDB : : Battery Removal Indicator
: BTEMP_LOW : : Btemp < BtempLow, if battery temperature is lower than -10°C
: BTEMP_LOW_MEDIUM : : BtempLow < Btemp < BtempMedium,if battery temperature is between -10 and 0°C
: BTEMP_MEDIUM_HIGH : : BtempMedium < Btemp < BtempHigh,if battery temperature is between 0°C and“MaxTemp
: BTEMP_HIGH : : Btemp > BtempHigh, if battery temperature is higher than “MaxTemp
ab8500-charger : : vddadc : Charger interface
: MAIN_CH_UNPLUG_DET : : main charger unplug detection management (not in 8505)
: MAIN_CHARGE_PLUG_DET : : main charger plug detection management (not in 8505)
: MAIN_EXT_CH_NOT_OK : : main charger not OK
: MAIN_CH_TH_PROT_R : : Die temp is above main charger
: MAIN_CH_TH_PROT_F : : Die temp is below main charger
: VBUS_DET_F : : VBUS falling detected
: VBUS_DET_R : : VBUS rising detected
: USB_LINK_STATUS : : USB link status has changed
: USB_CH_TH_PROT_R : : Die temp is above usb charger
: USB_CH_TH_PROT_F : : Die temp is below usb charger
: USB_CHARGER_NOT_OKR : : allowed USB charger not ok detection
: VBUS_OVV : : Overvoltage on Vbus ball detected (USB charge is stopped)
: CH_WD_EXP : : Charger watchdog detected
ab8500-gpadc : HW_CONV_END : vddadc : Analogue to Digital Converter ab8500-gpadc : HW_CONV_END : vddadc : Analogue to Digital Converter
SW_CONV_END : : SW_CONV_END : :
ab8500-gpio : : : GPIO Controller ab8500-gpio : : : GPIO Controller
......
=== AB8500 Battery Temperature Monitor Driver ===
The properties below describes the node for btemp driver.
Required Properties:
- compatible = Shall be: "stericsson,ab8500-btemp"
- battery = Shall be battery specific information
Example:
ab8500_btemp {
compatible = "stericsson,ab8500-btemp";
battery = <&ab8500_battery>;
};
For information on battery specific node, Ref:
Documentation/devicetree/bindings/power_supply/ab8500/fg.txt
=== AB8500 Charging Algorithm Driver ===
The properties below describes the node for chargalg driver.
Required Properties:
- compatible = Shall be: "stericsson,ab8500-chargalg"
- battery = Shall be battery specific information
Example:
ab8500_chargalg {
compatible = "stericsson,ab8500-chargalg";
battery = <&ab8500_battery>;
};
For information on battery specific node, Ref:
Documentation/devicetree/bindings/power_supply/ab8500/fg.txt
=== AB8500 Charger Driver ===
Required Properties:
- compatible = Shall be "stericsson,ab8500-charger"
- battery = Shall be battery specific information
Example:
ab8500_charger {
compatible = "stericsson,ab8500-charger";
battery = <&ab8500_battery>;
};
- vddadc-supply: Supply for USB and Main charger
Example:
ab8500-charger {
vddadc-supply = <&ab8500_ldo_tvout_reg>;
}
- autopower_cfg:
Boolean value depicting the presence of 'automatic poweron after powerloss'
Example:
ab8500-charger {
autopower_cfg;
};
For information on battery specific node, Ref:
Documentation/devicetree/bindings/power_supply/ab8500/fg.txt
=== AB8500 Fuel Gauge Driver ===
AB8500 is a mixed signal multimedia and power management
device comprising: power and energy-management-module,
wall-charger, usb-charger, audio codec, general purpose adc,
tvout, clock management and sim card interface.
Fuelgauge support is part of energy-management-modules, other
components of this module are:
main-charger, usb-combo-charger and battery-temperature-monitoring.
The properties below describes the node for fuelgauge driver.
Required Properties:
- compatible = This shall be: "stericsson,ab8500-fg"
- battery = Shall be battery specific information
Example:
ab8500_fg {
compatible = "stericsson,ab8500-fg";
battery = <&ab8500_battery>;
};
dependent node:
ab8500_battery: ab8500_battery {
};
This node will provide information on 'thermistor interface' and
'battery technology type' used.
Properties of this node are:
thermistor-on-batctrl:
A boolean value indicating thermistor interface to battery
Note:
'btemp' and 'batctrl' are the pins interfaced for battery temperature
measurement, 'btemp' signal is used when NTC(negative temperature
coefficient) resister is interfaced external to battery whereas
'batctrl' pin is used when NTC resister is internal to battery.
Example:
ab8500_battery: ab8500_battery {
thermistor-on-batctrl;
};
indicates: NTC resister is internal to battery, 'batctrl' is used
for thermal measurement.
The absence of property 'thermal-on-batctrl' indicates
NTC resister is external to battery and 'btemp' signal is used
for thermal measurement.
battery-type:
This shall be the battery manufacturing technology type,
allowed types are:
"UNKNOWN" "NiMH" "LION" "LIPO" "LiFe" "NiCd" "LiMn"
Example:
ab8500_battery: ab8500_battery {
stericsson,battery-type = "LIPO";
}
...@@ -123,6 +123,9 @@ CONSTANT_CHARGE_VOLTAGE - constant charge voltage programmed by charger. ...@@ -123,6 +123,9 @@ CONSTANT_CHARGE_VOLTAGE - constant charge voltage programmed by charger.
CONSTANT_CHARGE_VOLTAGE_MAX - maximum charge voltage supported by the CONSTANT_CHARGE_VOLTAGE_MAX - maximum charge voltage supported by the
power supply object. power supply object.
CHARGE_CONTROL_LIMIT - current charge control limit setting
CHARGE_CONTROL_LIMIT_MAX - maximum charge control limit setting
ENERGY_FULL, ENERGY_EMPTY - same as above but for energy. ENERGY_FULL, ENERGY_EMPTY - same as above but for energy.
CAPACITY - capacity in percents. CAPACITY - capacity in percents.
......
...@@ -340,7 +340,33 @@ ab8500-gpadc { ...@@ -340,7 +340,33 @@ ab8500-gpadc {
vddadc-supply = <&ab8500_ldo_tvout_reg>; vddadc-supply = <&ab8500_ldo_tvout_reg>;
}; };
ab8500-usb { ab8500_battery: ab8500_battery {
stericsson,battery-type = "LIPO";
thermistor-on-batctrl;
};
ab8500_fg {
compatible = "stericsson,ab8500-fg";
battery = <&ab8500_battery>;
};
ab8500_btemp {
compatible = "stericsson,ab8500-btemp";
battery = <&ab8500_battery>;
};
ab8500_charger {
compatible = "stericsson,ab8500-charger";
battery = <&ab8500_battery>;
vddadc-supply = <&ab8500_ldo_tvout_reg>;
};
ab8500_chargalg {
compatible = "stericsson,ab8500-chargalg";
battery = <&ab8500_battery>;
};
ab8500_usb {
compatible = "stericsson,ab8500-usb"; compatible = "stericsson,ab8500-usb";
interrupts = < 90 0x4 interrupts = < 90 0x4
96 0x4 96 0x4
......
...@@ -1036,23 +1036,43 @@ static struct mfd_cell abx500_common_devs[] = { ...@@ -1036,23 +1036,43 @@ static struct mfd_cell abx500_common_devs[] = {
static struct mfd_cell ab8500_bm_devs[] = { static struct mfd_cell ab8500_bm_devs[] = {
{ {
.name = "ab8500-charger", .name = "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,
.pdata_size = sizeof(ab8500_bm_data),
#endif
}, },
{ {
.name = "ab8500-btemp", .name = "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,
.pdata_size = sizeof(ab8500_bm_data),
#endif
}, },
{ {
.name = "ab8500-fg", .name = "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,
.pdata_size = sizeof(ab8500_bm_data),
#endif
}, },
{ {
.name = "ab8500-chargalg", .name = "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,
.pdata_size = sizeof(ab8500_bm_data),
#endif
}, },
}; };
......
...@@ -245,6 +245,13 @@ config BATTERY_INTEL_MID ...@@ -245,6 +245,13 @@ config BATTERY_INTEL_MID
Say Y here to enable the battery driver on Intel MID Say Y here to enable the battery driver on Intel MID
platforms. platforms.
config BATTERY_RX51
tristate "Nokia RX-51 (N900) battery driver"
depends on TWL4030_MADC
help
Say Y here to enable support for battery information on Nokia
RX-51, also known as N900 tablet.
config CHARGER_ISP1704 config CHARGER_ISP1704
tristate "ISP1704 USB Charger Detection" tristate "ISP1704 USB Charger Detection"
depends on USB_OTG_UTILS depends on USB_OTG_UTILS
...@@ -315,6 +322,16 @@ config CHARGER_MAX8998 ...@@ -315,6 +322,16 @@ config CHARGER_MAX8998
Say Y to enable support for the battery charger control sysfs and Say Y to enable support for the battery charger control sysfs and
platform data of MAX8998/LP3974 PMICs. platform data of MAX8998/LP3974 PMICs.
config CHARGER_BQ2415X
tristate "TI BQ2415x battery charger driver"
depends on I2C
help
Say Y to enable support for the TI BQ2415x battery charger
PMICs.
You'll need this driver to charge batteries on e.g. Nokia
RX-51/N900.
config CHARGER_SMB347 config CHARGER_SMB347
tristate "Summit Microelectronics SMB347 Battery Charger" tristate "Summit Microelectronics SMB347 Battery Charger"
depends on I2C depends on I2C
...@@ -329,13 +346,6 @@ config AB8500_BM ...@@ -329,13 +346,6 @@ config AB8500_BM
help help
Say Y to include support for AB8500 battery management. Say Y to include support for AB8500 battery management.
config AB8500_BATTERY_THERM_ON_BATCTRL
bool "Thermistor connected on BATCTRL ADC"
depends on AB8500_BM
help
Say Y to enable battery temperature measurements using
thermistor connected on BATCTRL ADC.
source "drivers/power/reset/Kconfig" source "drivers/power/reset/Kconfig"
endif # POWER_SUPPLY endif # POWER_SUPPLY
......
...@@ -37,7 +37,8 @@ obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o ...@@ -37,7 +37,8 @@ obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o 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_AB8500_BM) += ab8500_charger.o ab8500_btemp.o ab8500_fg.o abx500_chargalg.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_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
...@@ -47,6 +48,7 @@ obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o ...@@ -47,6 +48,7 @@ obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o
obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.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_POWER_AVS) += avs/ obj-$(CONFIG_POWER_AVS) += avs/
obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o
obj-$(CONFIG_POWER_RESET) += reset/ obj-$(CONFIG_POWER_RESET) += reset/
#include <linux/export.h>
#include <linux/power_supply.h>
#include <linux/of.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/mfd/abx500/ab8500-bm.h>
/*
* These are the defined batteries that uses a NTC and ID resistor placed
* inside of the battery pack.
* Note that the res_to_temp table must be strictly sorted by falling resistance
* values to work.
*/
static struct abx500_res_to_temp temp_tbl_A_thermistor[] = {
{-5, 53407},
{ 0, 48594},
{ 5, 43804},
{10, 39188},
{15, 34870},
{20, 30933},
{25, 27422},
{30, 24347},
{35, 21694},
{40, 19431},
{45, 17517},
{50, 15908},
{55, 14561},
{60, 13437},
{65, 12500},
};
static struct abx500_res_to_temp temp_tbl_B_thermistor[] = {
{-5, 200000},
{ 0, 159024},
{ 5, 151921},
{10, 144300},
{15, 136424},
{20, 128565},
{25, 120978},
{30, 113875},
{35, 107397},
{40, 101629},
{45, 96592},
{50, 92253},
{55, 88569},
{60, 85461},
{65, 82869},
};
static struct abx500_v_to_cap cap_tbl_A_thermistor[] = {
{4171, 100},
{4114, 95},
{4009, 83},
{3947, 74},
{3907, 67},
{3863, 59},
{3830, 56},
{3813, 53},
{3791, 46},
{3771, 33},
{3754, 25},
{3735, 20},
{3717, 17},
{3681, 13},
{3664, 8},
{3651, 6},
{3635, 5},
{3560, 3},
{3408, 1},
{3247, 0},
};
static struct abx500_v_to_cap cap_tbl_B_thermistor[] = {
{4161, 100},
{4124, 98},
{4044, 90},
{4003, 85},
{3966, 80},
{3933, 75},
{3888, 67},
{3849, 60},
{3813, 55},
{3787, 47},
{3772, 30},
{3751, 25},
{3718, 20},
{3681, 16},
{3660, 14},
{3589, 10},
{3546, 7},
{3495, 4},
{3404, 2},
{3250, 0},
};
static struct abx500_v_to_cap cap_tbl[] = {
{4186, 100},
{4163, 99},
{4114, 95},
{4068, 90},
{3990, 80},
{3926, 70},
{3898, 65},
{3866, 60},
{3833, 55},
{3812, 50},
{3787, 40},
{3768, 30},
{3747, 25},
{3730, 20},
{3705, 15},
{3699, 14},
{3684, 12},
{3672, 9},
{3657, 7},
{3638, 6},
{3556, 4},
{3424, 2},
{3317, 1},
{3094, 0},
};
/*
* Note that the res_to_temp table must be strictly sorted by falling
* resistance values to work.
*/
static struct abx500_res_to_temp temp_tbl[] = {
{-5, 214834},
{ 0, 162943},
{ 5, 124820},
{10, 96520},
{15, 75306},
{20, 59254},
{25, 47000},
{30, 37566},
{35, 30245},
{40, 24520},
{45, 20010},
{50, 16432},
{55, 13576},
{60, 11280},
{65, 9425},
};
/*
* Note that the batres_vs_temp table must be strictly sorted by falling
* temperature values to work.
*/
static struct batres_vs_temp temp_to_batres_tbl_thermistor[] = {
{ 40, 120},
{ 30, 135},
{ 20, 165},
{ 10, 230},
{ 00, 325},
{-10, 445},
{-20, 595},
};
/*
* Note that the batres_vs_temp table must be strictly sorted by falling
* temperature values to work.
*/
static struct batres_vs_temp temp_to_batres_tbl_ext_thermistor[] = {
{ 60, 300},
{ 30, 300},
{ 20, 300},
{ 10, 300},
{ 00, 300},
{-10, 300},
{-20, 300},
};
/* battery resistance table for LI ION 9100 battery */
static struct batres_vs_temp temp_to_batres_tbl_9100[] = {
{ 60, 180},
{ 30, 180},
{ 20, 180},
{ 10, 180},
{ 00, 180},
{-10, 180},
{-20, 180},
};
static struct abx500_battery_type bat_type_thermistor[] = {
[BATTERY_UNKNOWN] = {
/* First element always represent the UNKNOWN battery */
.name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
.resis_high = 0,
.resis_low = 0,
.battery_resistance = 300,
.charge_full_design = 612,
.nominal_voltage = 3700,
.termination_vol = 4050,
.termination_curr = 200,
.recharge_vol = 3990,
.normal_cur_lvl = 400,
.normal_vol_lvl = 4100,
.maint_a_cur_lvl = 400,
.maint_a_vol_lvl = 4050,
.maint_a_chg_timer_h = 60,
.maint_b_cur_lvl = 400,
.maint_b_vol_lvl = 4000,
.maint_b_chg_timer_h = 200,
.low_high_cur_lvl = 300,
.low_high_vol_lvl = 4000,
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
.r_to_t_tbl = temp_tbl,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
.v_to_cap_tbl = cap_tbl,
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
.batres_tbl = temp_to_batres_tbl_thermistor,
},
{
.name = POWER_SUPPLY_TECHNOLOGY_LIPO,
.resis_high = 53407,
.resis_low = 12500,
.battery_resistance = 300,
.charge_full_design = 900,
.nominal_voltage = 3600,
.termination_vol = 4150,
.termination_curr = 80,
.recharge_vol = 4130,
.normal_cur_lvl = 700,
.normal_vol_lvl = 4200,
.maint_a_cur_lvl = 600,
.maint_a_vol_lvl = 4150,
.maint_a_chg_timer_h = 60,
.maint_b_cur_lvl = 600,
.maint_b_vol_lvl = 4100,
.maint_b_chg_timer_h = 200,
.low_high_cur_lvl = 300,
.low_high_vol_lvl = 4000,
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_A_thermistor),
.r_to_t_tbl = temp_tbl_A_thermistor,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_A_thermistor),
.v_to_cap_tbl = cap_tbl_A_thermistor,
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
.batres_tbl = temp_to_batres_tbl_thermistor,
},
{
.name = POWER_SUPPLY_TECHNOLOGY_LIPO,
.resis_high = 200000,
.resis_low = 82869,
.battery_resistance = 300,
.charge_full_design = 900,
.nominal_voltage = 3600,
.termination_vol = 4150,
.termination_curr = 80,
.recharge_vol = 4130,
.normal_cur_lvl = 700,
.normal_vol_lvl = 4200,
.maint_a_cur_lvl = 600,
.maint_a_vol_lvl = 4150,
.maint_a_chg_timer_h = 60,
.maint_b_cur_lvl = 600,
.maint_b_vol_lvl = 4100,
.maint_b_chg_timer_h = 200,
.low_high_cur_lvl = 300,
.low_high_vol_lvl = 4000,
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_B_thermistor),
.r_to_t_tbl = temp_tbl_B_thermistor,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_B_thermistor),
.v_to_cap_tbl = cap_tbl_B_thermistor,
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
.batres_tbl = temp_to_batres_tbl_thermistor,
},
};
static struct abx500_battery_type bat_type_ext_thermistor[] = {
[BATTERY_UNKNOWN] = {
/* First element always represent the UNKNOWN battery */
.name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
.resis_high = 0,
.resis_low = 0,
.battery_resistance = 300,
.charge_full_design = 612,
.nominal_voltage = 3700,
.termination_vol = 4050,
.termination_curr = 200,
.recharge_vol = 3990,
.normal_cur_lvl = 400,
.normal_vol_lvl = 4100,
.maint_a_cur_lvl = 400,
.maint_a_vol_lvl = 4050,
.maint_a_chg_timer_h = 60,
.maint_b_cur_lvl = 400,
.maint_b_vol_lvl = 4000,
.maint_b_chg_timer_h = 200,
.low_high_cur_lvl = 300,
.low_high_vol_lvl = 4000,
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
.r_to_t_tbl = temp_tbl,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
.v_to_cap_tbl = cap_tbl,
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
.batres_tbl = temp_to_batres_tbl_thermistor,
},
/*
* These are the batteries that doesn't have an internal NTC resistor to measure
* its temperature. The temperature in this case is measure with a NTC placed
* near the battery but on the PCB.
*/
{
.name = POWER_SUPPLY_TECHNOLOGY_LIPO,
.resis_high = 76000,
.resis_low = 53000,
.battery_resistance = 300,
.charge_full_design = 900,
.nominal_voltage = 3700,
.termination_vol = 4150,
.termination_curr = 100,
.recharge_vol = 4130,
.normal_cur_lvl = 700,
.normal_vol_lvl = 4200,
.maint_a_cur_lvl = 600,
.maint_a_vol_lvl = 4150,
.maint_a_chg_timer_h = 60,
.maint_b_cur_lvl = 600,
.maint_b_vol_lvl = 4100,
.maint_b_chg_timer_h = 200,
.low_high_cur_lvl = 300,
.low_high_vol_lvl = 4000,
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
.r_to_t_tbl = temp_tbl,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
.v_to_cap_tbl = cap_tbl,
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
.batres_tbl = temp_to_batres_tbl_thermistor,
},
{
.name = POWER_SUPPLY_TECHNOLOGY_LION,
.resis_high = 30000,
.resis_low = 10000,
.battery_resistance = 300,
.charge_full_design = 950,
.nominal_voltage = 3700,
.termination_vol = 4150,
.termination_curr = 100,
.recharge_vol = 4130,
.normal_cur_lvl = 700,
.normal_vol_lvl = 4200,
.maint_a_cur_lvl = 600,
.maint_a_vol_lvl = 4150,
.maint_a_chg_timer_h = 60,
.maint_b_cur_lvl = 600,
.maint_b_vol_lvl = 4100,
.maint_b_chg_timer_h = 200,
.low_high_cur_lvl = 300,
.low_high_vol_lvl = 4000,
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
.r_to_t_tbl = temp_tbl,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
.v_to_cap_tbl = cap_tbl,
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
.batres_tbl = temp_to_batres_tbl_thermistor,
},
{
.name = POWER_SUPPLY_TECHNOLOGY_LION,
.resis_high = 95000,
.resis_low = 76001,
.battery_resistance = 300,
.charge_full_design = 950,
.nominal_voltage = 3700,
.termination_vol = 4150,
.termination_curr = 100,
.recharge_vol = 4130,
.normal_cur_lvl = 700,
.normal_vol_lvl = 4200,
.maint_a_cur_lvl = 600,
.maint_a_vol_lvl = 4150,
.maint_a_chg_timer_h = 60,
.maint_b_cur_lvl = 600,
.maint_b_vol_lvl = 4100,
.maint_b_chg_timer_h = 200,
.low_high_cur_lvl = 300,
.low_high_vol_lvl = 4000,
.n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
.r_to_t_tbl = temp_tbl,
.n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
.v_to_cap_tbl = cap_tbl,
.n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor),
.batres_tbl = temp_to_batres_tbl_thermistor,
},
};
static const struct abx500_bm_capacity_levels cap_levels = {
.critical = 2,
.low = 10,
.normal = 70,
.high = 95,
.full = 100,
};
static const struct abx500_fg_parameters fg = {
.recovery_sleep_timer = 10,
.recovery_total_time = 100,
.init_timer = 1,
.init_discard_time = 5,
.init_total_time = 40,
.high_curr_time = 60,
.accu_charging = 30,
.accu_high_curr = 30,
.high_curr_threshold = 50,
.lowbat_threshold = 3100,
.battok_falling_th_sel0 = 2860,
.battok_raising_th_sel1 = 2860,
.user_cap_limit = 15,
.maint_thres = 97,
};
static const struct abx500_maxim_parameters maxi_params = {
.ena_maxi = true,
.chg_curr = 910,
.wait_cycles = 10,
.charger_curr_step = 100,
};
static const struct abx500_bm_charger_parameters chg = {
.usb_volt_max = 5500,
.usb_curr_max = 1500,
.ac_volt_max = 7500,
.ac_curr_max = 1500,
};
struct abx500_bm_data ab8500_bm_data = {
.temp_under = 3,
.temp_low = 8,
.temp_high = 43,
.temp_over = 48,
.main_safety_tmr_h = 4,
.temp_interval_chg = 20,
.temp_interval_nochg = 120,
.usb_safety_tmr_h = 4,
.bkup_bat_v = BUP_VCH_SEL_2P6V,
.bkup_bat_i = BUP_ICH_SEL_150UA,
.no_maintenance = false,
.adc_therm = ABx500_ADC_THERM_BATCTRL,
.chg_unknown_bat = false,
.enable_overshoot = false,
.fg_res = 100,
.cap_levels = &cap_levels,
.bat_type = bat_type_thermistor,
.n_btypes = 3,
.batt_id = 0,
.interval_charging = 5,
.interval_not_charging = 120,
.temp_hysteresis = 3,
.gnd_lift_resistance = 34,
.maxi = &maxi_params,
.chg_params = &chg,
.fg_params = &fg,
};
int __devinit
bmdevs_of_probe(struct device *dev,
struct device_node *np,
struct abx500_bm_data **battery)
{
struct abx500_battery_type *btype;
struct device_node *np_bat_supply;
struct abx500_bm_data *bat;
const char *btech;
char bat_tech[8];
int i, thermistor;
*battery = &ab8500_bm_data;
/* get phandle to 'battery-info' node */
np_bat_supply = of_parse_phandle(np, "battery", 0);
if (!np_bat_supply) {
dev_err(dev, "missing property battery\n");
return -EINVAL;
}
if (of_property_read_bool(np_bat_supply,
"thermistor-on-batctrl"))
thermistor = NTC_INTERNAL;
else
thermistor = NTC_EXTERNAL;
bat = *battery;
if (thermistor == NTC_EXTERNAL) {
bat->n_btypes = 4;
bat->bat_type = bat_type_ext_thermistor;
bat->adc_therm = ABx500_ADC_THERM_BATTEMP;
}
btech = of_get_property(np_bat_supply,
"stericsson,battery-type", NULL);
if (!btech) {
dev_warn(dev, "missing property battery-name/type\n");
strcpy(bat_tech, "UNKNOWN");
} else {
strcpy(bat_tech, btech);
}
if (strncmp(bat_tech, "LION", 4) == 0) {
bat->no_maintenance = true;
bat->chg_unknown_bat = true;
bat->bat_type[BATTERY_UNKNOWN].charge_full_design = 2600;
bat->bat_type[BATTERY_UNKNOWN].termination_vol = 4150;
bat->bat_type[BATTERY_UNKNOWN].recharge_vol = 4130;
bat->bat_type[BATTERY_UNKNOWN].normal_cur_lvl = 520;
bat->bat_type[BATTERY_UNKNOWN].normal_vol_lvl = 4200;
}
/* select the battery resolution table */
for (i = 0; i < bat->n_btypes; ++i) {
btype = (bat->bat_type + i);
if (thermistor == NTC_EXTERNAL) {
btype->batres_tbl =
temp_to_batres_tbl_ext_thermistor;
} else if (strncmp(bat_tech, "LION", 4) == 0) {
btype->batres_tbl =
temp_to_batres_tbl_9100;
} else {
btype->batres_tbl =
temp_to_batres_tbl_thermistor;
}
}
of_node_put(np_bat_supply);
return 0;
}
...@@ -20,11 +20,13 @@ ...@@ -20,11 +20,13 @@
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/mfd/abx500/ab8500.h> #include <linux/jiffies.h>
#include <linux/of.h>
#include <linux/mfd/core.h>
#include <linux/mfd/abx500.h> #include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/mfd/abx500/ab8500-bm.h> #include <linux/mfd/abx500/ab8500-bm.h>
#include <linux/mfd/abx500/ab8500-gpadc.h> #include <linux/mfd/abx500/ab8500-gpadc.h>
#include <linux/jiffies.h>
#define VTVOUT_V 1800 #define VTVOUT_V 1800
...@@ -76,7 +78,6 @@ struct ab8500_btemp_ranges { ...@@ -76,7 +78,6 @@ 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
* @pdata: Pointer to the abx500_btemp platform data
* @bat: Pointer to the abx500_bm platform data * @bat: Pointer to the abx500_bm platform data
* @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
...@@ -93,7 +94,6 @@ struct ab8500_btemp { ...@@ -93,7 +94,6 @@ 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_btemp_platform_data *pdata;
struct abx500_bm_data *bat; struct abx500_bm_data *bat;
struct power_supply btemp_psy; struct power_supply btemp_psy;
struct ab8500_btemp_events events; struct ab8500_btemp_events events;
...@@ -955,56 +955,57 @@ static int ab8500_btemp_remove(struct platform_device *pdev) ...@@ -955,56 +955,57 @@ static int ab8500_btemp_remove(struct platform_device *pdev)
flush_scheduled_work(); flush_scheduled_work();
power_supply_unregister(&di->btemp_psy); power_supply_unregister(&di->btemp_psy);
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
kfree(di);
return 0; return 0;
} }
static char *supply_interface[] = {
"ab8500_chargalg",
"ab8500_fg",
};
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 ab8500_btemp *di;
int irq, i, ret = 0; int irq, i, ret = 0;
u8 val; u8 val;
struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data;
struct ab8500_btemp *di;
if (!plat_data) {
dev_err(&pdev->dev, "No platform data\n");
return -EINVAL;
}
di = kzalloc(sizeof(*di), GFP_KERNEL); di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
if (!di) if (!di) {
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 (np) {
ret = bmdevs_of_probe(&pdev->dev, np, &di->bat);
if (ret) {
dev_err(&pdev->dev,
"failed to get battery information\n");
return ret;
}
} else {
dev_err(&pdev->dev, "missing dt node for ab8500_btemp\n");
return -EINVAL;
}
} else {
dev_info(&pdev->dev, "falling back to legacy platform data\n");
}
/* get parent data */ /* get parent data */
di->dev = &pdev->dev; di->dev = &pdev->dev;
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");
/* get btemp specific platform data */
di->pdata = plat_data->btemp;
if (!di->pdata) {
dev_err(di->dev, "no btemp platform data supplied\n");
ret = -EINVAL;
goto free_device_info;
}
/* get battery specific platform data */
di->bat = plat_data->battery;
if (!di->bat) {
dev_err(di->dev, "no battery platform data supplied\n");
ret = -EINVAL;
goto free_device_info;
}
/* 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;
di->btemp_psy.properties = ab8500_btemp_props; di->btemp_psy.properties = ab8500_btemp_props;
di->btemp_psy.num_properties = ARRAY_SIZE(ab8500_btemp_props); di->btemp_psy.num_properties = ARRAY_SIZE(ab8500_btemp_props);
di->btemp_psy.get_property = ab8500_btemp_get_property; di->btemp_psy.get_property = ab8500_btemp_get_property;
di->btemp_psy.supplied_to = di->pdata->supplied_to; di->btemp_psy.supplied_to = supply_interface;
di->btemp_psy.num_supplicants = di->pdata->num_supplicants; di->btemp_psy.num_supplicants = ARRAY_SIZE(supply_interface);
di->btemp_psy.external_power_changed = di->btemp_psy.external_power_changed =
ab8500_btemp_external_power_changed; ab8500_btemp_external_power_changed;
...@@ -1014,8 +1015,7 @@ static int ab8500_btemp_probe(struct platform_device *pdev) ...@@ -1014,8 +1015,7 @@ static int ab8500_btemp_probe(struct platform_device *pdev)
create_singlethread_workqueue("ab8500_btemp_wq"); create_singlethread_workqueue("ab8500_btemp_wq");
if (di->btemp_wq == NULL) { if (di->btemp_wq == NULL) {
dev_err(di->dev, "failed to create work queue\n"); dev_err(di->dev, "failed to create work queue\n");
ret = -ENOMEM; return -ENOMEM;
goto free_device_info;
} }
/* Init work for measuring temperature periodically */ /* Init work for measuring temperature periodically */
...@@ -1093,12 +1093,14 @@ static int ab8500_btemp_probe(struct platform_device *pdev) ...@@ -1093,12 +1093,14 @@ static int ab8500_btemp_probe(struct platform_device *pdev)
} }
free_btemp_wq: free_btemp_wq:
destroy_workqueue(di->btemp_wq); destroy_workqueue(di->btemp_wq);
free_device_info:
kfree(di);
return ret; return ret;
} }
static const struct of_device_id ab8500_btemp_match[] = {
{ .compatible = "stericsson,ab8500-btemp", },
{ },
};
static struct platform_driver ab8500_btemp_driver = { static struct platform_driver ab8500_btemp_driver = {
.probe = ab8500_btemp_probe, .probe = ab8500_btemp_probe,
.remove = ab8500_btemp_remove, .remove = ab8500_btemp_remove,
...@@ -1107,6 +1109,7 @@ static struct platform_driver ab8500_btemp_driver = { ...@@ -1107,6 +1109,7 @@ static struct platform_driver ab8500_btemp_driver = {
.driver = { .driver = {
.name = "ab8500-btemp", .name = "ab8500-btemp",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = ab8500_btemp_match,
}, },
}; };
......
...@@ -23,6 +23,8 @@ ...@@ -23,6 +23,8 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/kobject.h> #include <linux/kobject.h>
#include <linux/of.h>
#include <linux/mfd/core.h>
#include <linux/mfd/abx500/ab8500.h> #include <linux/mfd/abx500/ab8500.h>
#include <linux/mfd/abx500.h> #include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500-bm.h> #include <linux/mfd/abx500/ab8500-bm.h>
...@@ -181,9 +183,9 @@ struct ab8500_charger_usb_state { ...@@ -181,9 +183,9 @@ struct ab8500_charger_usb_state {
* @vbat Battery voltage * @vbat Battery voltage
* @old_vbat Previously measured battery voltage * @old_vbat Previously measured battery voltage
* @autopower Indicate if we should have automatic pwron after pwrloss * @autopower Indicate if we should have automatic pwron after pwrloss
* @autopower_cfg platform specific power config support for "pwron after pwrloss"
* @parent: Pointer to the struct ab8500 * @parent: Pointer to the struct ab8500
* @gpadc: Pointer to the struct gpadc * @gpadc: Pointer to the struct gpadc
* @pdata: Pointer to the abx500_charger platform data
* @bat: Pointer to the abx500_bm platform data * @bat: Pointer to the abx500_bm platform data
* @flags: Structure for information about events triggered * @flags: Structure for information about events triggered
* @usb_state: Structure for usb stack information * @usb_state: Structure for usb stack information
...@@ -218,9 +220,9 @@ struct ab8500_charger { ...@@ -218,9 +220,9 @@ struct ab8500_charger {
int vbat; int vbat;
int old_vbat; int old_vbat;
bool autopower; bool autopower;
bool autopower_cfg;
struct ab8500 *parent; struct ab8500 *parent;
struct ab8500_gpadc *gpadc; struct ab8500_gpadc *gpadc;
struct abx500_charger_platform_data *pdata;
struct abx500_bm_data *bat; struct abx500_bm_data *bat;
struct ab8500_charger_event_flags flags; struct ab8500_charger_event_flags flags;
struct ab8500_charger_usb_state usb_state; struct ab8500_charger_usb_state usb_state;
...@@ -322,7 +324,7 @@ static void ab8500_power_loss_handling(struct ab8500_charger *di) ...@@ -322,7 +324,7 @@ static void ab8500_power_loss_handling(struct ab8500_charger *di)
static void ab8500_power_supply_changed(struct ab8500_charger *di, static void ab8500_power_supply_changed(struct ab8500_charger *di,
struct power_supply *psy) struct power_supply *psy)
{ {
if (di->pdata->autopower_cfg) { if (di->autopower_cfg) {
if (!di->usb.charger_connected && if (!di->usb.charger_connected &&
!di->ac.charger_connected && !di->ac.charger_connected &&
di->autopower) { di->autopower) {
...@@ -2526,25 +2528,45 @@ static int ab8500_charger_remove(struct platform_device *pdev) ...@@ -2526,25 +2528,45 @@ static int ab8500_charger_remove(struct platform_device *pdev)
power_supply_unregister(&di->usb_chg.psy); power_supply_unregister(&di->usb_chg.psy);
power_supply_unregister(&di->ac_chg.psy); power_supply_unregister(&di->ac_chg.psy);
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
kfree(di);
return 0; return 0;
} }
static char *supply_interface[] = {
"ab8500_chargalg",
"ab8500_fg",
"ab8500_btemp",
};
static int ab8500_charger_probe(struct platform_device *pdev) static int ab8500_charger_probe(struct platform_device *pdev)
{ {
int irq, i, charger_status, ret = 0; struct device_node *np = pdev->dev.of_node;
struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data;
struct ab8500_charger *di; struct ab8500_charger *di;
int irq, i, charger_status, ret = 0;
if (!plat_data) { di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
dev_err(&pdev->dev, "No platform data\n"); if (!di) {
return -EINVAL; dev_err(&pdev->dev, "%s no mem for ab8500_charger\n", __func__);
}
di = kzalloc(sizeof(*di), GFP_KERNEL);
if (!di)
return -ENOMEM; return -ENOMEM;
}
di->bat = pdev->mfd_cell->platform_data;
if (!di->bat) {
if (np) {
ret = bmdevs_of_probe(&pdev->dev, np, &di->bat);
if (ret) {
dev_err(&pdev->dev,
"failed to get battery information\n");
return ret;
}
di->autopower_cfg = of_property_read_bool(np, "autopower_cfg");
} else {
dev_err(&pdev->dev, "missing dt node for ab8500_charger\n");
return -EINVAL;
}
} else {
dev_info(&pdev->dev, "falling back to legacy platform data\n");
di->autopower_cfg = false;
}
/* get parent data */ /* get parent data */
di->dev = &pdev->dev; di->dev = &pdev->dev;
...@@ -2554,22 +2576,6 @@ static int ab8500_charger_probe(struct platform_device *pdev) ...@@ -2554,22 +2576,6 @@ static int ab8500_charger_probe(struct platform_device *pdev)
/* initialize lock */ /* initialize lock */
spin_lock_init(&di->usb_state.usb_lock); spin_lock_init(&di->usb_state.usb_lock);
/* get charger specific platform data */
di->pdata = plat_data->charger;
if (!di->pdata) {
dev_err(di->dev, "no charger platform data supplied\n");
ret = -EINVAL;
goto free_device_info;
}
/* get battery specific platform data */
di->bat = plat_data->battery;
if (!di->bat) {
dev_err(di->dev, "no battery platform data supplied\n");
ret = -EINVAL;
goto free_device_info;
}
di->autopower = false; di->autopower = false;
/* AC supply */ /* AC supply */
...@@ -2579,8 +2585,8 @@ static int ab8500_charger_probe(struct platform_device *pdev) ...@@ -2579,8 +2585,8 @@ static int ab8500_charger_probe(struct platform_device *pdev)
di->ac_chg.psy.properties = ab8500_charger_ac_props; di->ac_chg.psy.properties = ab8500_charger_ac_props;
di->ac_chg.psy.num_properties = ARRAY_SIZE(ab8500_charger_ac_props); di->ac_chg.psy.num_properties = ARRAY_SIZE(ab8500_charger_ac_props);
di->ac_chg.psy.get_property = ab8500_charger_ac_get_property; di->ac_chg.psy.get_property = ab8500_charger_ac_get_property;
di->ac_chg.psy.supplied_to = di->pdata->supplied_to; di->ac_chg.psy.supplied_to = supply_interface;
di->ac_chg.psy.num_supplicants = di->pdata->num_supplicants; di->ac_chg.psy.num_supplicants = ARRAY_SIZE(supply_interface),
/* ux500_charger sub-class */ /* ux500_charger sub-class */
di->ac_chg.ops.enable = &ab8500_charger_ac_en; di->ac_chg.ops.enable = &ab8500_charger_ac_en;
di->ac_chg.ops.kick_wd = &ab8500_charger_watchdog_kick; di->ac_chg.ops.kick_wd = &ab8500_charger_watchdog_kick;
...@@ -2597,8 +2603,8 @@ static int ab8500_charger_probe(struct platform_device *pdev) ...@@ -2597,8 +2603,8 @@ static int ab8500_charger_probe(struct platform_device *pdev)
di->usb_chg.psy.properties = ab8500_charger_usb_props; di->usb_chg.psy.properties = ab8500_charger_usb_props;
di->usb_chg.psy.num_properties = ARRAY_SIZE(ab8500_charger_usb_props); di->usb_chg.psy.num_properties = ARRAY_SIZE(ab8500_charger_usb_props);
di->usb_chg.psy.get_property = ab8500_charger_usb_get_property; di->usb_chg.psy.get_property = ab8500_charger_usb_get_property;
di->usb_chg.psy.supplied_to = di->pdata->supplied_to; di->usb_chg.psy.supplied_to = supply_interface;
di->usb_chg.psy.num_supplicants = di->pdata->num_supplicants; di->usb_chg.psy.num_supplicants = ARRAY_SIZE(supply_interface),
/* ux500_charger sub-class */ /* ux500_charger sub-class */
di->usb_chg.ops.enable = &ab8500_charger_usb_en; di->usb_chg.ops.enable = &ab8500_charger_usb_en;
di->usb_chg.ops.kick_wd = &ab8500_charger_watchdog_kick; di->usb_chg.ops.kick_wd = &ab8500_charger_watchdog_kick;
...@@ -2614,8 +2620,7 @@ static int ab8500_charger_probe(struct platform_device *pdev) ...@@ -2614,8 +2620,7 @@ static int ab8500_charger_probe(struct platform_device *pdev)
create_singlethread_workqueue("ab8500_charger_wq"); create_singlethread_workqueue("ab8500_charger_wq");
if (di->charger_wq == NULL) { if (di->charger_wq == NULL) {
dev_err(di->dev, "failed to create work queue\n"); dev_err(di->dev, "failed to create work queue\n");
ret = -ENOMEM; return -ENOMEM;
goto free_device_info;
} }
/* Init work for HW failure check */ /* Init work for HW failure check */
...@@ -2757,12 +2762,14 @@ static int ab8500_charger_probe(struct platform_device *pdev) ...@@ -2757,12 +2762,14 @@ static int ab8500_charger_probe(struct platform_device *pdev)
regulator_put(di->regu); regulator_put(di->regu);
free_charger_wq: free_charger_wq:
destroy_workqueue(di->charger_wq); destroy_workqueue(di->charger_wq);
free_device_info:
kfree(di);
return ret; return ret;
} }
static const struct of_device_id ab8500_charger_match[] = {
{ .compatible = "stericsson,ab8500-charger", },
{ },
};
static struct platform_driver ab8500_charger_driver = { static struct platform_driver ab8500_charger_driver = {
.probe = ab8500_charger_probe, .probe = ab8500_charger_probe,
.remove = ab8500_charger_remove, .remove = ab8500_charger_remove,
...@@ -2771,6 +2778,7 @@ static struct platform_driver ab8500_charger_driver = { ...@@ -2771,6 +2778,7 @@ static struct platform_driver ab8500_charger_driver = {
.driver = { .driver = {
.name = "ab8500-charger", .name = "ab8500-charger",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = ab8500_charger_match,
}, },
}; };
......
...@@ -22,15 +22,16 @@ ...@@ -22,15 +22,16 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/kobject.h> #include <linux/kobject.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/mfd/abx500.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mfd/abx500/ab8500-bm.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/mfd/abx500/ab8500-gpadc.h>
#include <linux/mfd/abx500.h>
#include <linux/time.h> #include <linux/time.h>
#include <linux/of.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/mfd/core.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/mfd/abx500/ab8500-bm.h>
#include <linux/mfd/abx500/ab8500-gpadc.h>
#define MILLI_TO_MICRO 1000 #define MILLI_TO_MICRO 1000
#define FG_LSB_IN_MA 1627 #define FG_LSB_IN_MA 1627
...@@ -172,7 +173,6 @@ struct inst_curr_result_list { ...@@ -172,7 +173,6 @@ struct inst_curr_result_list {
* @avg_cap: Average capacity filter * @avg_cap: Average capacity filter
* @parent: Pointer to the struct ab8500 * @parent: Pointer to the struct ab8500
* @gpadc: Pointer to the struct gpadc * @gpadc: Pointer to the struct gpadc
* @pdata: Pointer to the abx500_fg platform data
* @bat: Pointer to the abx500_bm platform data * @bat: Pointer to the abx500_bm platform data
* @fg_psy: Structure that holds the FG specific battery properties * @fg_psy: Structure that holds the FG specific battery properties
* @fg_wq: Work queue for running the FG algorithm * @fg_wq: Work queue for running the FG algorithm
...@@ -212,7 +212,6 @@ struct ab8500_fg { ...@@ -212,7 +212,6 @@ struct ab8500_fg {
struct ab8500_fg_avg_cap avg_cap; struct ab8500_fg_avg_cap avg_cap;
struct ab8500 *parent; struct ab8500 *parent;
struct ab8500_gpadc *gpadc; struct ab8500_gpadc *gpadc;
struct abx500_fg_platform_data *pdata;
struct abx500_bm_data *bat; struct abx500_bm_data *bat;
struct power_supply fg_psy; struct power_supply fg_psy;
struct workqueue_struct *fg_wq; struct workqueue_struct *fg_wq;
...@@ -2429,7 +2428,6 @@ static int ab8500_fg_remove(struct platform_device *pdev) ...@@ -2429,7 +2428,6 @@ static int ab8500_fg_remove(struct platform_device *pdev)
flush_scheduled_work(); flush_scheduled_work();
power_supply_unregister(&di->fg_psy); power_supply_unregister(&di->fg_psy);
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
kfree(di);
return ret; return ret;
} }
...@@ -2442,21 +2440,39 @@ static struct ab8500_fg_interrupts ab8500_fg_irq[] = { ...@@ -2442,21 +2440,39 @@ static struct ab8500_fg_interrupts ab8500_fg_irq[] = {
{"CCEOC", ab8500_fg_cc_data_end_handler}, {"CCEOC", ab8500_fg_cc_data_end_handler},
}; };
static char *supply_interface[] = {
"ab8500_chargalg",
"ab8500_usb",
};
static int ab8500_fg_probe(struct platform_device *pdev) static int ab8500_fg_probe(struct platform_device *pdev)
{ {
struct device_node *np = pdev->dev.of_node;
struct ab8500_fg *di;
int i, irq; int i, irq;
int ret = 0; int ret = 0;
struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data;
struct ab8500_fg *di;
if (!plat_data) {
dev_err(&pdev->dev, "No platform data\n");
return -EINVAL;
}
di = kzalloc(sizeof(*di), GFP_KERNEL); di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
if (!di) if (!di) {
dev_err(&pdev->dev, "%s no mem for ab8500_fg\n", __func__);
return -ENOMEM; return -ENOMEM;
}
di->bat = pdev->mfd_cell->platform_data;
if (!di->bat) {
if (np) {
ret = bmdevs_of_probe(&pdev->dev, np, &di->bat);
if (ret) {
dev_err(&pdev->dev,
"failed to get battery information\n");
return ret;
}
} else {
dev_err(&pdev->dev, "missing dt node for ab8500_fg\n");
return -EINVAL;
}
} else {
dev_info(&pdev->dev, "falling back to legacy platform data\n");
}
mutex_init(&di->cc_lock); mutex_init(&di->cc_lock);
...@@ -2465,29 +2481,13 @@ static int ab8500_fg_probe(struct platform_device *pdev) ...@@ -2465,29 +2481,13 @@ static int ab8500_fg_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");
/* get fg specific platform data */
di->pdata = plat_data->fg;
if (!di->pdata) {
dev_err(di->dev, "no fg platform data supplied\n");
ret = -EINVAL;
goto free_device_info;
}
/* get battery specific platform data */
di->bat = plat_data->battery;
if (!di->bat) {
dev_err(di->dev, "no battery platform data supplied\n");
ret = -EINVAL;
goto free_device_info;
}
di->fg_psy.name = "ab8500_fg"; di->fg_psy.name = "ab8500_fg";
di->fg_psy.type = POWER_SUPPLY_TYPE_BATTERY; di->fg_psy.type = POWER_SUPPLY_TYPE_BATTERY;
di->fg_psy.properties = ab8500_fg_props; di->fg_psy.properties = ab8500_fg_props;
di->fg_psy.num_properties = ARRAY_SIZE(ab8500_fg_props); di->fg_psy.num_properties = ARRAY_SIZE(ab8500_fg_props);
di->fg_psy.get_property = ab8500_fg_get_property; di->fg_psy.get_property = ab8500_fg_get_property;
di->fg_psy.supplied_to = di->pdata->supplied_to; di->fg_psy.supplied_to = supply_interface;
di->fg_psy.num_supplicants = di->pdata->num_supplicants; di->fg_psy.num_supplicants = ARRAY_SIZE(supply_interface),
di->fg_psy.external_power_changed = ab8500_fg_external_power_changed; di->fg_psy.external_power_changed = ab8500_fg_external_power_changed;
di->bat_cap.max_mah_design = MILLI_TO_MICRO * di->bat_cap.max_mah_design = MILLI_TO_MICRO *
...@@ -2506,8 +2506,7 @@ static int ab8500_fg_probe(struct platform_device *pdev) ...@@ -2506,8 +2506,7 @@ static int ab8500_fg_probe(struct platform_device *pdev)
di->fg_wq = create_singlethread_workqueue("ab8500_fg_wq"); di->fg_wq = create_singlethread_workqueue("ab8500_fg_wq");
if (di->fg_wq == NULL) { if (di->fg_wq == NULL) {
dev_err(di->dev, "failed to create work queue\n"); dev_err(di->dev, "failed to create work queue\n");
ret = -ENOMEM; return -ENOMEM;
goto free_device_info;
} }
/* Init work for running the fg algorithm instantly */ /* Init work for running the fg algorithm instantly */
...@@ -2606,12 +2605,14 @@ static int ab8500_fg_probe(struct platform_device *pdev) ...@@ -2606,12 +2605,14 @@ static int ab8500_fg_probe(struct platform_device *pdev)
} }
free_inst_curr_wq: free_inst_curr_wq:
destroy_workqueue(di->fg_wq); destroy_workqueue(di->fg_wq);
free_device_info:
kfree(di);
return ret; return ret;
} }
static const struct of_device_id ab8500_fg_match[] = {
{ .compatible = "stericsson,ab8500-fg", },
{ },
};
static struct platform_driver ab8500_fg_driver = { static struct platform_driver ab8500_fg_driver = {
.probe = ab8500_fg_probe, .probe = ab8500_fg_probe,
.remove = ab8500_fg_remove, .remove = ab8500_fg_remove,
...@@ -2620,6 +2621,7 @@ static struct platform_driver ab8500_fg_driver = { ...@@ -2620,6 +2621,7 @@ static struct platform_driver ab8500_fg_driver = {
.driver = { .driver = {
.name = "ab8500-fg", .name = "ab8500-fg",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = ab8500_fg_match,
}, },
}; };
......
...@@ -21,6 +21,8 @@ ...@@ -21,6 +21,8 @@
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/kobject.h> #include <linux/kobject.h>
#include <linux/of.h>
#include <linux/mfd/core.h>
#include <linux/mfd/abx500.h> #include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ux500_chargalg.h> #include <linux/mfd/abx500/ux500_chargalg.h>
#include <linux/mfd/abx500/ab8500-bm.h> #include <linux/mfd/abx500/ab8500-bm.h>
...@@ -205,7 +207,6 @@ enum maxim_ret { ...@@ -205,7 +207,6 @@ 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
* @pdata: pointer to the abx500_chargalg platform data
* @bat: pointer to the abx500_bm platform data * @bat: pointer to the abx500_bm platform data
* @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
...@@ -231,7 +232,6 @@ struct abx500_chargalg { ...@@ -231,7 +232,6 @@ 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_chargalg_platform_data *pdata;
struct abx500_bm_data *bat; struct abx500_bm_data *bat;
struct power_supply chargalg_psy; struct power_supply chargalg_psy;
struct ux500_charger *ac_chg; struct ux500_charger *ac_chg;
...@@ -1795,36 +1795,53 @@ static int abx500_chargalg_remove(struct platform_device *pdev) ...@@ -1795,36 +1795,53 @@ static int abx500_chargalg_remove(struct platform_device *pdev)
flush_scheduled_work(); flush_scheduled_work();
power_supply_unregister(&di->chargalg_psy); power_supply_unregister(&di->chargalg_psy);
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
kfree(di);
return 0; return 0;
} }
static char *supply_interface[] = {
"ab8500_fg",
};
static int abx500_chargalg_probe(struct platform_device *pdev) static int abx500_chargalg_probe(struct platform_device *pdev)
{ {
struct abx500_bm_plat_data *plat_data; struct device_node *np = pdev->dev.of_node;
struct abx500_chargalg *di;
int ret = 0; int ret = 0;
struct abx500_chargalg *di = di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
kzalloc(sizeof(struct abx500_chargalg), GFP_KERNEL); if (!di) {
if (!di) 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 (np) {
ret = bmdevs_of_probe(&pdev->dev, np, &di->bat);
if (ret) {
dev_err(&pdev->dev,
"failed to get battery information\n");
return ret;
}
} else {
dev_err(&pdev->dev, "missing dt node for ab8500_chargalg\n");
return -EINVAL;
}
} else {
dev_info(&pdev->dev, "falling back to legacy platform data\n");
}
/* get device struct */ /* get device struct */
di->dev = &pdev->dev; di->dev = &pdev->dev;
plat_data = pdev->dev.platform_data;
di->pdata = plat_data->chargalg;
di->bat = plat_data->battery;
/* chargalg supply */ /* chargalg supply */
di->chargalg_psy.name = "abx500_chargalg"; di->chargalg_psy.name = "abx500_chargalg";
di->chargalg_psy.type = POWER_SUPPLY_TYPE_BATTERY; di->chargalg_psy.type = POWER_SUPPLY_TYPE_BATTERY;
di->chargalg_psy.properties = abx500_chargalg_props; di->chargalg_psy.properties = abx500_chargalg_props;
di->chargalg_psy.num_properties = ARRAY_SIZE(abx500_chargalg_props); di->chargalg_psy.num_properties = ARRAY_SIZE(abx500_chargalg_props);
di->chargalg_psy.get_property = abx500_chargalg_get_property; di->chargalg_psy.get_property = abx500_chargalg_get_property;
di->chargalg_psy.supplied_to = di->pdata->supplied_to; di->chargalg_psy.supplied_to = supply_interface;
di->chargalg_psy.num_supplicants = di->pdata->num_supplicants; di->chargalg_psy.num_supplicants = ARRAY_SIZE(supply_interface),
di->chargalg_psy.external_power_changed = di->chargalg_psy.external_power_changed =
abx500_chargalg_external_power_changed; abx500_chargalg_external_power_changed;
...@@ -1844,7 +1861,7 @@ static int abx500_chargalg_probe(struct platform_device *pdev) ...@@ -1844,7 +1861,7 @@ static int abx500_chargalg_probe(struct platform_device *pdev)
create_singlethread_workqueue("abx500_chargalg_wq"); create_singlethread_workqueue("abx500_chargalg_wq");
if (di->chargalg_wq == NULL) { if (di->chargalg_wq == NULL) {
dev_err(di->dev, "failed to create work queue\n"); dev_err(di->dev, "failed to create work queue\n");
goto free_device_info; return -ENOMEM;
} }
/* Init work for chargalg */ /* Init work for chargalg */
...@@ -1885,20 +1902,23 @@ static int abx500_chargalg_probe(struct platform_device *pdev) ...@@ -1885,20 +1902,23 @@ static int abx500_chargalg_probe(struct platform_device *pdev)
power_supply_unregister(&di->chargalg_psy); power_supply_unregister(&di->chargalg_psy);
free_chargalg_wq: free_chargalg_wq:
destroy_workqueue(di->chargalg_wq); destroy_workqueue(di->chargalg_wq);
free_device_info:
kfree(di);
return ret; return ret;
} }
static const struct of_device_id ab8500_chargalg_match[] = {
{ .compatible = "stericsson,ab8500-chargalg", },
{ },
};
static struct platform_driver abx500_chargalg_driver = { static struct platform_driver abx500_chargalg_driver = {
.probe = abx500_chargalg_probe, .probe = abx500_chargalg_probe,
.remove = abx500_chargalg_remove, .remove = abx500_chargalg_remove,
.suspend = abx500_chargalg_suspend, .suspend = abx500_chargalg_suspend,
.resume = abx500_chargalg_resume, .resume = abx500_chargalg_resume,
.driver = { .driver = {
.name = "abx500-chargalg", .name = "ab8500-chargalg",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = ab8500_chargalg_match,
}, },
}; };
......
/*
* bq2415x charger driver
*
* Copyright (C) 2011-2012 Pali Rohár <pali.rohar@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
* Datasheets:
* http://www.ti.com/product/bq24150
* http://www.ti.com/product/bq24150a
* http://www.ti.com/product/bq24152
* http://www.ti.com/product/bq24153
* http://www.ti.com/product/bq24153a
* http://www.ti.com/product/bq24155
*/
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/param.h>
#include <linux/err.h>
#include <linux/workqueue.h>
#include <linux/sysfs.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/idr.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/power/bq2415x_charger.h>
/* timeout for resetting chip timer */
#define BQ2415X_TIMER_TIMEOUT 10
#define BQ2415X_REG_STATUS 0x00
#define BQ2415X_REG_CONTROL 0x01
#define BQ2415X_REG_VOLTAGE 0x02
#define BQ2415X_REG_VENDER 0x03
#define BQ2415X_REG_CURRENT 0x04
/* reset state for all registers */
#define BQ2415X_RESET_STATUS BIT(6)
#define BQ2415X_RESET_CONTROL (BIT(4)|BIT(5))
#define BQ2415X_RESET_VOLTAGE (BIT(1)|BIT(3))
#define BQ2415X_RESET_CURRENT (BIT(0)|BIT(3)|BIT(7))
/* status register */
#define BQ2415X_BIT_TMR_RST 7
#define BQ2415X_BIT_OTG 7
#define BQ2415X_BIT_EN_STAT 6
#define BQ2415X_MASK_STAT (BIT(4)|BIT(5))
#define BQ2415X_SHIFT_STAT 4
#define BQ2415X_BIT_BOOST 3
#define BQ2415X_MASK_FAULT (BIT(0)|BIT(1)|BIT(2))
#define BQ2415X_SHIFT_FAULT 0
/* control register */
#define BQ2415X_MASK_LIMIT (BIT(6)|BIT(7))
#define BQ2415X_SHIFT_LIMIT 6
#define BQ2415X_MASK_VLOWV (BIT(4)|BIT(5))
#define BQ2415X_SHIFT_VLOWV 4
#define BQ2415X_BIT_TE 3
#define BQ2415X_BIT_CE 2
#define BQ2415X_BIT_HZ_MODE 1
#define BQ2415X_BIT_OPA_MODE 0
/* voltage register */
#define BQ2415X_MASK_VO (BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7))
#define BQ2415X_SHIFT_VO 2
#define BQ2415X_BIT_OTG_PL 1
#define BQ2415X_BIT_OTG_EN 0
/* vender register */
#define BQ2415X_MASK_VENDER (BIT(5)|BIT(6)|BIT(7))
#define BQ2415X_SHIFT_VENDER 5
#define BQ2415X_MASK_PN (BIT(3)|BIT(4))
#define BQ2415X_SHIFT_PN 3
#define BQ2415X_MASK_REVISION (BIT(0)|BIT(1)|BIT(2))
#define BQ2415X_SHIFT_REVISION 0
/* current register */
#define BQ2415X_MASK_RESET BIT(7)
#define BQ2415X_MASK_VI_CHRG (BIT(4)|BIT(5)|BIT(6))
#define BQ2415X_SHIFT_VI_CHRG 4
/* N/A BIT(3) */
#define BQ2415X_MASK_VI_TERM (BIT(0)|BIT(1)|BIT(2))
#define BQ2415X_SHIFT_VI_TERM 0
enum bq2415x_command {
BQ2415X_TIMER_RESET,
BQ2415X_OTG_STATUS,
BQ2415X_STAT_PIN_STATUS,
BQ2415X_STAT_PIN_ENABLE,
BQ2415X_STAT_PIN_DISABLE,
BQ2415X_CHARGE_STATUS,
BQ2415X_BOOST_STATUS,
BQ2415X_FAULT_STATUS,
BQ2415X_CHARGE_TERMINATION_STATUS,
BQ2415X_CHARGE_TERMINATION_ENABLE,
BQ2415X_CHARGE_TERMINATION_DISABLE,
BQ2415X_CHARGER_STATUS,
BQ2415X_CHARGER_ENABLE,
BQ2415X_CHARGER_DISABLE,
BQ2415X_HIGH_IMPEDANCE_STATUS,
BQ2415X_HIGH_IMPEDANCE_ENABLE,
BQ2415X_HIGH_IMPEDANCE_DISABLE,
BQ2415X_BOOST_MODE_STATUS,
BQ2415X_BOOST_MODE_ENABLE,
BQ2415X_BOOST_MODE_DISABLE,
BQ2415X_OTG_LEVEL,
BQ2415X_OTG_ACTIVATE_HIGH,
BQ2415X_OTG_ACTIVATE_LOW,
BQ2415X_OTG_PIN_STATUS,
BQ2415X_OTG_PIN_ENABLE,
BQ2415X_OTG_PIN_DISABLE,
BQ2415X_VENDER_CODE,
BQ2415X_PART_NUMBER,
BQ2415X_REVISION,
};
enum bq2415x_chip {
BQUNKNOWN,
BQ24150,
BQ24150A,
BQ24151,
BQ24151A,
BQ24152,
BQ24153,
BQ24153A,
BQ24155,
BQ24156,
BQ24156A,
BQ24158,
};
static char *bq2415x_chip_name[] = {
"unknown",
"bq24150",
"bq24150a",
"bq24151",
"bq24151a",
"bq24152",
"bq24153",
"bq24153a",
"bq24155",
"bq24156",
"bq24156a",
"bq24158",
};
struct bq2415x_device {
struct device *dev;
struct bq2415x_platform_data init_data;
struct power_supply charger;
struct delayed_work work;
enum bq2415x_mode reported_mode;/* mode reported by hook function */
enum bq2415x_mode mode; /* current configured mode */
enum bq2415x_chip chip;
const char *timer_error;
char *model;
char *name;
int autotimer; /* 1 - if driver automatically reset timer, 0 - not */
int automode; /* 1 - enabled, 0 - disabled; -1 - not supported */
int id;
};
/* each registered chip must have unique id */
static DEFINE_IDR(bq2415x_id);
static DEFINE_MUTEX(bq2415x_id_mutex);
static DEFINE_MUTEX(bq2415x_timer_mutex);
static DEFINE_MUTEX(bq2415x_i2c_mutex);
/**** i2c read functions ****/
/* read value from register */
static int bq2415x_i2c_read(struct bq2415x_device *bq, u8 reg)
{
struct i2c_client *client = to_i2c_client(bq->dev);
struct i2c_msg msg[2];
u8 val;
int ret;
if (!client->adapter)
return -ENODEV;
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].buf = &reg;
msg[0].len = sizeof(reg);
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].buf = &val;
msg[1].len = sizeof(val);
mutex_lock(&bq2415x_i2c_mutex);
ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
mutex_unlock(&bq2415x_i2c_mutex);
if (ret < 0)
return ret;
return val;
}
/* read value from register, apply mask and right shift it */
static int bq2415x_i2c_read_mask(struct bq2415x_device *bq, u8 reg,
u8 mask, u8 shift)
{
int ret;
if (shift > 8)
return -EINVAL;
ret = bq2415x_i2c_read(bq, reg);
if (ret < 0)
return ret;
return (ret & mask) >> shift;
}
/* read value from register and return one specified bit */
static int bq2415x_i2c_read_bit(struct bq2415x_device *bq, u8 reg, u8 bit)
{
if (bit > 8)
return -EINVAL;
return bq2415x_i2c_read_mask(bq, reg, BIT(bit), bit);
}
/**** i2c write functions ****/
/* write value to register */
static int bq2415x_i2c_write(struct bq2415x_device *bq, u8 reg, u8 val)
{
struct i2c_client *client = to_i2c_client(bq->dev);
struct i2c_msg msg[1];
u8 data[2];
int ret;
data[0] = reg;
data[1] = val;
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].buf = data;
msg[0].len = ARRAY_SIZE(data);
mutex_lock(&bq2415x_i2c_mutex);
ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
mutex_unlock(&bq2415x_i2c_mutex);
/* i2c_transfer returns number of messages transferred */
if (ret < 0)
return ret;
else if (ret != 1)
return -EIO;
return 0;
}
/* read value from register, change it with mask left shifted and write back */
static int bq2415x_i2c_write_mask(struct bq2415x_device *bq, u8 reg, u8 val,
u8 mask, u8 shift)
{
int ret;
if (shift > 8)
return -EINVAL;
ret = bq2415x_i2c_read(bq, reg);
if (ret < 0)
return ret;
ret &= ~mask;
ret |= val << shift;
return bq2415x_i2c_write(bq, reg, ret);
}
/* change only one bit in register */
static int bq2415x_i2c_write_bit(struct bq2415x_device *bq, u8 reg,
bool val, u8 bit)
{
if (bit > 8)
return -EINVAL;
return bq2415x_i2c_write_mask(bq, reg, val, BIT(bit), bit);
}
/**** global functions ****/
/* exec command function */
static int bq2415x_exec_command(struct bq2415x_device *bq,
enum bq2415x_command command)
{
int ret;
switch (command) {
case BQ2415X_TIMER_RESET:
return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS,
1, BQ2415X_BIT_TMR_RST);
case BQ2415X_OTG_STATUS:
return bq2415x_i2c_read_bit(bq, BQ2415X_REG_STATUS,
BQ2415X_BIT_OTG);
case BQ2415X_STAT_PIN_STATUS:
return bq2415x_i2c_read_bit(bq, BQ2415X_REG_STATUS,
BQ2415X_BIT_EN_STAT);
case BQ2415X_STAT_PIN_ENABLE:
return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS, 1,
BQ2415X_BIT_EN_STAT);
case BQ2415X_STAT_PIN_DISABLE:
return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS, 0,
BQ2415X_BIT_EN_STAT);
case BQ2415X_CHARGE_STATUS:
return bq2415x_i2c_read_mask(bq, BQ2415X_REG_STATUS,
BQ2415X_MASK_STAT, BQ2415X_SHIFT_STAT);
case BQ2415X_BOOST_STATUS:
return bq2415x_i2c_read_bit(bq, BQ2415X_REG_STATUS,
BQ2415X_BIT_BOOST);
case BQ2415X_FAULT_STATUS:
return bq2415x_i2c_read_mask(bq, BQ2415X_REG_STATUS,
BQ2415X_MASK_FAULT, BQ2415X_SHIFT_FAULT);
case BQ2415X_CHARGE_TERMINATION_STATUS:
return bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL,
BQ2415X_BIT_TE);
case BQ2415X_CHARGE_TERMINATION_ENABLE:
return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
1, BQ2415X_BIT_TE);
case BQ2415X_CHARGE_TERMINATION_DISABLE:
return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
0, BQ2415X_BIT_TE);
case BQ2415X_CHARGER_STATUS:
ret = bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL,
BQ2415X_BIT_CE);
if (ret < 0)
return ret;
else
return ret > 0 ? 0 : 1;
case BQ2415X_CHARGER_ENABLE:
return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
0, BQ2415X_BIT_CE);
case BQ2415X_CHARGER_DISABLE:
return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
1, BQ2415X_BIT_CE);
case BQ2415X_HIGH_IMPEDANCE_STATUS:
return bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL,
BQ2415X_BIT_HZ_MODE);
case BQ2415X_HIGH_IMPEDANCE_ENABLE:
return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
1, BQ2415X_BIT_HZ_MODE);
case BQ2415X_HIGH_IMPEDANCE_DISABLE:
return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
0, BQ2415X_BIT_HZ_MODE);
case BQ2415X_BOOST_MODE_STATUS:
return bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL,
BQ2415X_BIT_OPA_MODE);
case BQ2415X_BOOST_MODE_ENABLE:
return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
1, BQ2415X_BIT_OPA_MODE);
case BQ2415X_BOOST_MODE_DISABLE:
return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
0, BQ2415X_BIT_OPA_MODE);
case BQ2415X_OTG_LEVEL:
return bq2415x_i2c_read_bit(bq, BQ2415X_REG_VOLTAGE,
BQ2415X_BIT_OTG_PL);
case BQ2415X_OTG_ACTIVATE_HIGH:
return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE,
1, BQ2415X_BIT_OTG_PL);
case BQ2415X_OTG_ACTIVATE_LOW:
return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE,
0, BQ2415X_BIT_OTG_PL);
case BQ2415X_OTG_PIN_STATUS:
return bq2415x_i2c_read_bit(bq, BQ2415X_REG_VOLTAGE,
BQ2415X_BIT_OTG_EN);
case BQ2415X_OTG_PIN_ENABLE:
return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE,
1, BQ2415X_BIT_OTG_EN);
case BQ2415X_OTG_PIN_DISABLE:
return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE,
0, BQ2415X_BIT_OTG_EN);
case BQ2415X_VENDER_CODE:
return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER,
BQ2415X_MASK_VENDER, BQ2415X_SHIFT_VENDER);
case BQ2415X_PART_NUMBER:
return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER,
BQ2415X_MASK_PN, BQ2415X_SHIFT_PN);
case BQ2415X_REVISION:
return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER,
BQ2415X_MASK_REVISION, BQ2415X_SHIFT_REVISION);
}
return -EINVAL;
}
/* detect chip type */
static enum bq2415x_chip bq2415x_detect_chip(struct bq2415x_device *bq)
{
struct i2c_client *client = to_i2c_client(bq->dev);
int ret = bq2415x_exec_command(bq, BQ2415X_PART_NUMBER);
if (ret < 0)
return ret;
switch (client->addr) {
case 0x6b:
switch (ret) {
case 0:
if (bq->chip == BQ24151A)
return bq->chip;
else
return BQ24151;
case 1:
if (bq->chip == BQ24150A ||
bq->chip == BQ24152 ||
bq->chip == BQ24155)
return bq->chip;
else
return BQ24150;
case 2:
if (bq->chip == BQ24153A)
return bq->chip;
else
return BQ24153;
default:
return BQUNKNOWN;
}
break;
case 0x6a:
switch (ret) {
case 0:
if (bq->chip == BQ24156A)
return bq->chip;
else
return BQ24156;
case 2:
return BQ24158;
default:
return BQUNKNOWN;
}
break;
}
return BQUNKNOWN;
}
/* detect chip revision */
static int bq2415x_detect_revision(struct bq2415x_device *bq)
{
int ret = bq2415x_exec_command(bq, BQ2415X_REVISION);
int chip = bq2415x_detect_chip(bq);
if (ret < 0 || chip < 0)
return -1;
switch (chip) {
case BQ24150:
case BQ24150A:
case BQ24151:
case BQ24151A:
case BQ24152:
if (ret >= 0 && ret <= 3)
return ret;
else
return -1;
case BQ24153:
case BQ24153A:
case BQ24156:
case BQ24156A:
case BQ24158:
if (ret == 3)
return 0;
else if (ret == 1)
return 1;
else
return -1;
case BQ24155:
if (ret == 3)
return 3;
else
return -1;
case BQUNKNOWN:
return -1;
}
return -1;
}
/* return chip vender code */
static int bq2415x_get_vender_code(struct bq2415x_device *bq)
{
int ret;
ret = bq2415x_exec_command(bq, BQ2415X_VENDER_CODE);
if (ret < 0)
return 0;
/* convert to binary */
return (ret & 0x1) +
((ret >> 1) & 0x1) * 10 +
((ret >> 2) & 0x1) * 100;
}
/* reset all chip registers to default state */
static void bq2415x_reset_chip(struct bq2415x_device *bq)
{
bq2415x_i2c_write(bq, BQ2415X_REG_CURRENT, BQ2415X_RESET_CURRENT);
bq2415x_i2c_write(bq, BQ2415X_REG_VOLTAGE, BQ2415X_RESET_VOLTAGE);
bq2415x_i2c_write(bq, BQ2415X_REG_CONTROL, BQ2415X_RESET_CONTROL);
bq2415x_i2c_write(bq, BQ2415X_REG_STATUS, BQ2415X_RESET_STATUS);
bq->timer_error = NULL;
}
/**** properties functions ****/
/* set current limit in mA */
static int bq2415x_set_current_limit(struct bq2415x_device *bq, int mA)
{
int val;
if (mA <= 100)
val = 0;
else if (mA <= 500)
val = 1;
else if (mA <= 800)
val = 2;
else
val = 3;
return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CONTROL, val,
BQ2415X_MASK_LIMIT, BQ2415X_SHIFT_LIMIT);
}
/* get current limit in mA */
static int bq2415x_get_current_limit(struct bq2415x_device *bq)
{
int ret;
ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CONTROL,
BQ2415X_MASK_LIMIT, BQ2415X_SHIFT_LIMIT);
if (ret < 0)
return ret;
else if (ret == 0)
return 100;
else if (ret == 1)
return 500;
else if (ret == 2)
return 800;
else if (ret == 3)
return 1800;
return -EINVAL;
}
/* set weak battery voltage in mV */
static int bq2415x_set_weak_battery_voltage(struct bq2415x_device *bq, int mV)
{
int val;
/* round to 100mV */
if (mV <= 3400 + 50)
val = 0;
else if (mV <= 3500 + 50)
val = 1;
else if (mV <= 3600 + 50)
val = 2;
else
val = 3;
return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CONTROL, val,
BQ2415X_MASK_VLOWV, BQ2415X_SHIFT_VLOWV);
}
/* get weak battery voltage in mV */
static int bq2415x_get_weak_battery_voltage(struct bq2415x_device *bq)
{
int ret;
ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CONTROL,
BQ2415X_MASK_VLOWV, BQ2415X_SHIFT_VLOWV);
if (ret < 0)
return ret;
return 100 * (34 + ret);
}
/* set battery regulation voltage in mV */
static int bq2415x_set_battery_regulation_voltage(struct bq2415x_device *bq,
int mV)
{
int val = (mV/10 - 350) / 2;
if (val < 0)
val = 0;
else if (val > 94) /* FIXME: Max is 94 or 122 ? Set max value ? */
return -EINVAL;
return bq2415x_i2c_write_mask(bq, BQ2415X_REG_VOLTAGE, val,
BQ2415X_MASK_VO, BQ2415X_SHIFT_VO);
}
/* get battery regulation voltage in mV */
static int bq2415x_get_battery_regulation_voltage(struct bq2415x_device *bq)
{
int ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_VOLTAGE,
BQ2415X_MASK_VO, BQ2415X_SHIFT_VO);
if (ret < 0)
return ret;
return 10 * (350 + 2*ret);
}
/* set charge current in mA (platform data must provide resistor sense) */
static int bq2415x_set_charge_current(struct bq2415x_device *bq, int mA)
{
int val;
if (bq->init_data.resistor_sense <= 0)
return -ENOSYS;
val = (mA * bq->init_data.resistor_sense - 37400) / 6800;
if (val < 0)
val = 0;
else if (val > 7)
val = 7;
return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CURRENT, val,
BQ2415X_MASK_VI_CHRG | BQ2415X_MASK_RESET,
BQ2415X_SHIFT_VI_CHRG);
}
/* get charge current in mA (platform data must provide resistor sense) */
static int bq2415x_get_charge_current(struct bq2415x_device *bq)
{
int ret;
if (bq->init_data.resistor_sense <= 0)
return -ENOSYS;
ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CURRENT,
BQ2415X_MASK_VI_CHRG, BQ2415X_SHIFT_VI_CHRG);
if (ret < 0)
return ret;
return (37400 + 6800*ret) / bq->init_data.resistor_sense;
}
/* set termination current in mA (platform data must provide resistor sense) */
static int bq2415x_set_termination_current(struct bq2415x_device *bq, int mA)
{
int val;
if (bq->init_data.resistor_sense <= 0)
return -ENOSYS;
val = (mA * bq->init_data.resistor_sense - 3400) / 3400;
if (val < 0)
val = 0;
else if (val > 7)
val = 7;
return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CURRENT, val,
BQ2415X_MASK_VI_TERM | BQ2415X_MASK_RESET,
BQ2415X_SHIFT_VI_TERM);
}
/* get termination current in mA (platform data must provide resistor sense) */
static int bq2415x_get_termination_current(struct bq2415x_device *bq)
{
int ret;
if (bq->init_data.resistor_sense <= 0)
return -ENOSYS;
ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CURRENT,
BQ2415X_MASK_VI_TERM, BQ2415X_SHIFT_VI_TERM);
if (ret < 0)
return ret;
return (3400 + 3400*ret) / bq->init_data.resistor_sense;
}
/* set default value of property */
#define bq2415x_set_default_value(bq, prop) \
do { \
int ret = 0; \
if (bq->init_data.prop != -1) \
ret = bq2415x_set_##prop(bq, bq->init_data.prop); \
if (ret < 0) \
return ret; \
} while (0)
/* set default values of all properties */
static int bq2415x_set_defaults(struct bq2415x_device *bq)
{
bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_DISABLE);
bq2415x_exec_command(bq, BQ2415X_CHARGER_DISABLE);
bq2415x_exec_command(bq, BQ2415X_CHARGE_TERMINATION_DISABLE);
bq2415x_set_default_value(bq, current_limit);
bq2415x_set_default_value(bq, weak_battery_voltage);
bq2415x_set_default_value(bq, battery_regulation_voltage);
if (bq->init_data.resistor_sense > 0) {
bq2415x_set_default_value(bq, charge_current);
bq2415x_set_default_value(bq, termination_current);
bq2415x_exec_command(bq, BQ2415X_CHARGE_TERMINATION_ENABLE);
}
bq2415x_exec_command(bq, BQ2415X_CHARGER_ENABLE);
return 0;
}
/**** charger mode functions ****/
/* set charger mode */
static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode)
{
int ret = 0;
int charger = 0;
int boost = 0;
if (mode == BQ2415X_MODE_HOST_CHARGER ||
mode == BQ2415X_MODE_DEDICATED_CHARGER)
charger = 1;
if (mode == BQ2415X_MODE_BOOST)
boost = 1;
if (!charger)
ret = bq2415x_exec_command(bq, BQ2415X_CHARGER_DISABLE);
if (!boost)
ret = bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_DISABLE);
if (ret < 0)
return ret;
switch (mode) {
case BQ2415X_MODE_NONE:
dev_dbg(bq->dev, "changing mode to: N/A\n");
ret = bq2415x_set_current_limit(bq, 100);
break;
case BQ2415X_MODE_HOST_CHARGER:
dev_dbg(bq->dev, "changing mode to: Host/HUB charger\n");
ret = bq2415x_set_current_limit(bq, 500);
break;
case BQ2415X_MODE_DEDICATED_CHARGER:
dev_dbg(bq->dev, "changing mode to: Dedicated charger\n");
ret = bq2415x_set_current_limit(bq, 1800);
break;
case BQ2415X_MODE_BOOST: /* Boost mode */
dev_dbg(bq->dev, "changing mode to: Boost\n");
ret = bq2415x_set_current_limit(bq, 100);
break;
}
if (ret < 0)
return ret;
if (charger)
ret = bq2415x_exec_command(bq, BQ2415X_CHARGER_ENABLE);
else if (boost)
ret = bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_ENABLE);
if (ret < 0)
return ret;
bq2415x_set_default_value(bq, weak_battery_voltage);
bq2415x_set_default_value(bq, battery_regulation_voltage);
bq->mode = mode;
sysfs_notify(&bq->charger.dev->kobj, NULL, "mode");
return 0;
}
/* hook function called by other driver which set reported mode */
static void bq2415x_hook_function(enum bq2415x_mode mode, void *data)
{
struct bq2415x_device *bq = data;
if (!bq)
return;
dev_dbg(bq->dev, "hook function was called\n");
bq->reported_mode = mode;
/* if automode is not enabled do not tell about reported_mode */
if (bq->automode < 1)
return;
sysfs_notify(&bq->charger.dev->kobj, NULL, "reported_mode");
bq2415x_set_mode(bq, bq->reported_mode);
}
/**** timer functions ****/
/* enable/disable auto resetting chip timer */
static void bq2415x_set_autotimer(struct bq2415x_device *bq, int state)
{
mutex_lock(&bq2415x_timer_mutex);
if (bq->autotimer == state) {
mutex_unlock(&bq2415x_timer_mutex);
return;
}
bq->autotimer = state;
if (state) {
schedule_delayed_work(&bq->work, BQ2415X_TIMER_TIMEOUT * HZ);
bq2415x_exec_command(bq, BQ2415X_TIMER_RESET);
bq->timer_error = NULL;
} else {
cancel_delayed_work_sync(&bq->work);
}
mutex_unlock(&bq2415x_timer_mutex);
}
/* called by bq2415x_timer_work on timer error */
static void bq2415x_timer_error(struct bq2415x_device *bq, const char *msg)
{
bq->timer_error = msg;
sysfs_notify(&bq->charger.dev->kobj, NULL, "timer");
dev_err(bq->dev, "%s\n", msg);
if (bq->automode > 0)
bq->automode = 0;
bq2415x_set_mode(bq, BQ2415X_MODE_NONE);
bq2415x_set_autotimer(bq, 0);
}
/* delayed work function for auto resetting chip timer */
static void bq2415x_timer_work(struct work_struct *work)
{
struct bq2415x_device *bq = container_of(work, struct bq2415x_device,
work.work);
int ret;
int error;
int boost;
if (!bq->autotimer)
return;
ret = bq2415x_exec_command(bq, BQ2415X_TIMER_RESET);
if (ret < 0) {
bq2415x_timer_error(bq, "Resetting timer failed");
return;
}
boost = bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_STATUS);
if (boost < 0) {
bq2415x_timer_error(bq, "Unknown error");
return;
}
error = bq2415x_exec_command(bq, BQ2415X_FAULT_STATUS);
if (error < 0) {
bq2415x_timer_error(bq, "Unknown error");
return;
}
if (boost) {
switch (error) {
/* Non fatal errors, chip is OK */
case 0: /* No error */
break;
case 6: /* Timer expired */
dev_err(bq->dev, "Timer expired\n");
break;
case 3: /* Battery voltage too low */
dev_err(bq->dev, "Battery voltage to low\n");
break;
/* Fatal errors, disable and reset chip */
case 1: /* Overvoltage protection (chip fried) */
bq2415x_timer_error(bq,
"Overvoltage protection (chip fried)");
return;
case 2: /* Overload */
bq2415x_timer_error(bq, "Overload");
return;
case 4: /* Battery overvoltage protection */
bq2415x_timer_error(bq,
"Battery overvoltage protection");
return;
case 5: /* Thermal shutdown (too hot) */
bq2415x_timer_error(bq,
"Thermal shutdown (too hot)");
return;
case 7: /* N/A */
bq2415x_timer_error(bq, "Unknown error");
return;
}
} else {
switch (error) {
/* Non fatal errors, chip is OK */
case 0: /* No error */
break;
case 2: /* Sleep mode */
dev_err(bq->dev, "Sleep mode\n");
break;
case 3: /* Poor input source */
dev_err(bq->dev, "Poor input source\n");
break;
case 6: /* Timer expired */
dev_err(bq->dev, "Timer expired\n");
break;
case 7: /* No battery */
dev_err(bq->dev, "No battery\n");
break;
/* Fatal errors, disable and reset chip */
case 1: /* Overvoltage protection (chip fried) */
bq2415x_timer_error(bq,
"Overvoltage protection (chip fried)");
return;
case 4: /* Battery overvoltage protection */
bq2415x_timer_error(bq,
"Battery overvoltage protection");
return;
case 5: /* Thermal shutdown (too hot) */
bq2415x_timer_error(bq,
"Thermal shutdown (too hot)");
return;
}
}
schedule_delayed_work(&bq->work, BQ2415X_TIMER_TIMEOUT * HZ);
}
/**** power supply interface code ****/
static enum power_supply_property bq2415x_power_supply_props[] = {
/* TODO: maybe add more power supply properties */
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_MODEL_NAME,
};
static int bq2415x_power_supply_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
charger);
int ret;
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
ret = bq2415x_exec_command(bq, BQ2415X_CHARGE_STATUS);
if (ret < 0)
return ret;
else if (ret == 0) /* Ready */
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
else if (ret == 1) /* Charge in progress */
val->intval = POWER_SUPPLY_STATUS_CHARGING;
else if (ret == 2) /* Charge done */
val->intval = POWER_SUPPLY_STATUS_FULL;
else
val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = bq->model;
break;
default:
return -EINVAL;
}
return 0;
}
static int bq2415x_power_supply_init(struct bq2415x_device *bq)
{
int ret;
int chip;
char revstr[8];
bq->charger.name = bq->name;
bq->charger.type = POWER_SUPPLY_TYPE_USB;
bq->charger.properties = bq2415x_power_supply_props;
bq->charger.num_properties = ARRAY_SIZE(bq2415x_power_supply_props);
bq->charger.get_property = bq2415x_power_supply_get_property;
ret = bq2415x_detect_chip(bq);
if (ret < 0)
chip = BQUNKNOWN;
else
chip = ret;
ret = bq2415x_detect_revision(bq);
if (ret < 0)
strcpy(revstr, "unknown");
else
sprintf(revstr, "1.%d", ret);
bq->model = kasprintf(GFP_KERNEL,
"chip %s, revision %s, vender code %.3d",
bq2415x_chip_name[chip], revstr,
bq2415x_get_vender_code(bq));
if (!bq->model) {
dev_err(bq->dev, "failed to allocate model name\n");
return -ENOMEM;
}
ret = power_supply_register(bq->dev, &bq->charger);
if (ret) {
kfree(bq->model);
return ret;
}
return 0;
}
static void bq2415x_power_supply_exit(struct bq2415x_device *bq)
{
bq->autotimer = 0;
if (bq->automode > 0)
bq->automode = 0;
cancel_delayed_work_sync(&bq->work);
power_supply_unregister(&bq->charger);
kfree(bq->model);
}
/**** additional sysfs entries for power supply interface ****/
/* show *_status entries */
static ssize_t bq2415x_sysfs_show_status(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
charger);
enum bq2415x_command command;
int ret;
if (strcmp(attr->attr.name, "otg_status") == 0)
command = BQ2415X_OTG_STATUS;
else if (strcmp(attr->attr.name, "charge_status") == 0)
command = BQ2415X_CHARGE_STATUS;
else if (strcmp(attr->attr.name, "boost_status") == 0)
command = BQ2415X_BOOST_STATUS;
else if (strcmp(attr->attr.name, "fault_status") == 0)
command = BQ2415X_FAULT_STATUS;
else
return -EINVAL;
ret = bq2415x_exec_command(bq, command);
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", ret);
}
/*
* set timer entry:
* auto - enable auto mode
* off - disable auto mode
* (other values) - reset chip timer
*/
static ssize_t bq2415x_sysfs_set_timer(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
charger);
int ret = 0;
if (strncmp(buf, "auto", 4) == 0)
bq2415x_set_autotimer(bq, 1);
else if (strncmp(buf, "off", 3) == 0)
bq2415x_set_autotimer(bq, 0);
else
ret = bq2415x_exec_command(bq, BQ2415X_TIMER_RESET);
if (ret < 0)
return ret;
return count;
}
/* show timer entry (auto or off) */
static ssize_t bq2415x_sysfs_show_timer(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
charger);
if (bq->timer_error)
return sprintf(buf, "%s\n", bq->timer_error);
if (bq->autotimer)
return sprintf(buf, "auto\n");
return sprintf(buf, "off\n");
}
/*
* set mode entry:
* auto - if automode is supported, enable it and set mode to reported
* none - disable charger and boost mode
* host - charging mode for host/hub chargers (current limit 500mA)
* dedicated - charging mode for dedicated chargers (unlimited current limit)
* boost - disable charger and enable boost mode
*/
static ssize_t bq2415x_sysfs_set_mode(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
charger);
enum bq2415x_mode mode;
int ret = 0;
if (strncmp(buf, "auto", 4) == 0) {
if (bq->automode < 0)
return -ENOSYS;
bq->automode = 1;
mode = bq->reported_mode;
} else if (strncmp(buf, "none", 4) == 0) {
if (bq->automode > 0)
bq->automode = 0;
mode = BQ2415X_MODE_NONE;
} else if (strncmp(buf, "host", 4) == 0) {
if (bq->automode > 0)
bq->automode = 0;
mode = BQ2415X_MODE_HOST_CHARGER;
} else if (strncmp(buf, "dedicated", 9) == 0) {
if (bq->automode > 0)
bq->automode = 0;
mode = BQ2415X_MODE_DEDICATED_CHARGER;
} else if (strncmp(buf, "boost", 5) == 0) {
if (bq->automode > 0)
bq->automode = 0;
mode = BQ2415X_MODE_BOOST;
} else if (strncmp(buf, "reset", 5) == 0) {
bq2415x_reset_chip(bq);
bq2415x_set_defaults(bq);
if (bq->automode <= 0)
return count;
bq->automode = 1;
mode = bq->reported_mode;
} else {
return -EINVAL;
}
ret = bq2415x_set_mode(bq, mode);
if (ret < 0)
return ret;
return count;
}
/* show mode entry (auto, none, host, dedicated or boost) */
static ssize_t bq2415x_sysfs_show_mode(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
charger);
ssize_t ret = 0;
if (bq->automode > 0)
ret += sprintf(buf+ret, "auto (");
switch (bq->mode) {
case BQ2415X_MODE_NONE:
ret += sprintf(buf+ret, "none");
break;
case BQ2415X_MODE_HOST_CHARGER:
ret += sprintf(buf+ret, "host");
break;
case BQ2415X_MODE_DEDICATED_CHARGER:
ret += sprintf(buf+ret, "dedicated");
break;
case BQ2415X_MODE_BOOST:
ret += sprintf(buf+ret, "boost");
break;
}
if (bq->automode > 0)
ret += sprintf(buf+ret, ")");
ret += sprintf(buf+ret, "\n");
return ret;
}
/* show reported_mode entry (none, host, dedicated or boost) */
static ssize_t bq2415x_sysfs_show_reported_mode(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
charger);
if (bq->automode < 0)
return -EINVAL;
switch (bq->reported_mode) {
case BQ2415X_MODE_NONE:
return sprintf(buf, "none\n");
case BQ2415X_MODE_HOST_CHARGER:
return sprintf(buf, "host\n");
case BQ2415X_MODE_DEDICATED_CHARGER:
return sprintf(buf, "dedicated\n");
case BQ2415X_MODE_BOOST:
return sprintf(buf, "boost\n");
}
return -EINVAL;
}
/* directly set raw value to chip register, format: 'register value' */
static ssize_t bq2415x_sysfs_set_registers(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
charger);
ssize_t ret = 0;
unsigned int reg;
unsigned int val;
if (sscanf(buf, "%x %x", &reg, &val) != 2)
return -EINVAL;
if (reg > 4 || val > 255)
return -EINVAL;
ret = bq2415x_i2c_write(bq, reg, val);
if (ret < 0)
return ret;
return count;
}
/* print value of chip register, format: 'register=value' */
static ssize_t bq2415x_sysfs_print_reg(struct bq2415x_device *bq,
u8 reg,
char *buf)
{
int ret = bq2415x_i2c_read(bq, reg);
if (ret < 0)
return sprintf(buf, "%#.2x=error %d\n", reg, ret);
return sprintf(buf, "%#.2x=%#.2x\n", reg, ret);
}
/* show all raw values of chip register, format per line: 'register=value' */
static ssize_t bq2415x_sysfs_show_registers(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
charger);
ssize_t ret = 0;
ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_STATUS, buf+ret);
ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_CONTROL, buf+ret);
ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_VOLTAGE, buf+ret);
ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_VENDER, buf+ret);
ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_CURRENT, buf+ret);
return ret;
}
/* set current and voltage limit entries (in mA or mV) */
static ssize_t bq2415x_sysfs_set_limit(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
charger);
long val;
int ret;
if (kstrtol(buf, 10, &val) < 0)
return -EINVAL;
if (strcmp(attr->attr.name, "current_limit") == 0)
ret = bq2415x_set_current_limit(bq, val);
else if (strcmp(attr->attr.name, "weak_battery_voltage") == 0)
ret = bq2415x_set_weak_battery_voltage(bq, val);
else if (strcmp(attr->attr.name, "battery_regulation_voltage") == 0)
ret = bq2415x_set_battery_regulation_voltage(bq, val);
else if (strcmp(attr->attr.name, "charge_current") == 0)
ret = bq2415x_set_charge_current(bq, val);
else if (strcmp(attr->attr.name, "termination_current") == 0)
ret = bq2415x_set_termination_current(bq, val);
else
return -EINVAL;
if (ret < 0)
return ret;
return count;
}
/* show current and voltage limit entries (in mA or mV) */
static ssize_t bq2415x_sysfs_show_limit(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
charger);
int ret;
if (strcmp(attr->attr.name, "current_limit") == 0)
ret = bq2415x_get_current_limit(bq);
else if (strcmp(attr->attr.name, "weak_battery_voltage") == 0)
ret = bq2415x_get_weak_battery_voltage(bq);
else if (strcmp(attr->attr.name, "battery_regulation_voltage") == 0)
ret = bq2415x_get_battery_regulation_voltage(bq);
else if (strcmp(attr->attr.name, "charge_current") == 0)
ret = bq2415x_get_charge_current(bq);
else if (strcmp(attr->attr.name, "termination_current") == 0)
ret = bq2415x_get_termination_current(bq);
else
return -EINVAL;
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", ret);
}
/* set *_enable entries */
static ssize_t bq2415x_sysfs_set_enable(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
charger);
enum bq2415x_command command;
long val;
int ret;
if (kstrtol(buf, 10, &val) < 0)
return -EINVAL;
if (strcmp(attr->attr.name, "charge_termination_enable") == 0)
command = val ? BQ2415X_CHARGE_TERMINATION_ENABLE :
BQ2415X_CHARGE_TERMINATION_DISABLE;
else if (strcmp(attr->attr.name, "high_impedance_enable") == 0)
command = val ? BQ2415X_HIGH_IMPEDANCE_ENABLE :
BQ2415X_HIGH_IMPEDANCE_DISABLE;
else if (strcmp(attr->attr.name, "otg_pin_enable") == 0)
command = val ? BQ2415X_OTG_PIN_ENABLE :
BQ2415X_OTG_PIN_DISABLE;
else if (strcmp(attr->attr.name, "stat_pin_enable") == 0)
command = val ? BQ2415X_STAT_PIN_ENABLE :
BQ2415X_STAT_PIN_DISABLE;
else
return -EINVAL;
ret = bq2415x_exec_command(bq, command);
if (ret < 0)
return ret;
return count;
}
/* show *_enable entries */
static ssize_t bq2415x_sysfs_show_enable(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct power_supply *psy = dev_get_drvdata(dev);
struct bq2415x_device *bq = container_of(psy, struct bq2415x_device,
charger);
enum bq2415x_command command;
int ret;
if (strcmp(attr->attr.name, "charge_termination_enable") == 0)
command = BQ2415X_CHARGE_TERMINATION_STATUS;
else if (strcmp(attr->attr.name, "high_impedance_enable") == 0)
command = BQ2415X_HIGH_IMPEDANCE_STATUS;
else if (strcmp(attr->attr.name, "otg_pin_enable") == 0)
command = BQ2415X_OTG_PIN_STATUS;
else if (strcmp(attr->attr.name, "stat_pin_enable") == 0)
command = BQ2415X_STAT_PIN_STATUS;
else
return -EINVAL;
ret = bq2415x_exec_command(bq, command);
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", ret);
}
static DEVICE_ATTR(current_limit, S_IWUSR | S_IRUGO,
bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
static DEVICE_ATTR(weak_battery_voltage, S_IWUSR | S_IRUGO,
bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
static DEVICE_ATTR(battery_regulation_voltage, S_IWUSR | S_IRUGO,
bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
static DEVICE_ATTR(charge_current, S_IWUSR | S_IRUGO,
bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
static DEVICE_ATTR(termination_current, S_IWUSR | S_IRUGO,
bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
static DEVICE_ATTR(charge_termination_enable, S_IWUSR | S_IRUGO,
bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable);
static DEVICE_ATTR(high_impedance_enable, S_IWUSR | S_IRUGO,
bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable);
static DEVICE_ATTR(otg_pin_enable, S_IWUSR | S_IRUGO,
bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable);
static DEVICE_ATTR(stat_pin_enable, S_IWUSR | S_IRUGO,
bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable);
static DEVICE_ATTR(reported_mode, S_IRUGO,
bq2415x_sysfs_show_reported_mode, NULL);
static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO,
bq2415x_sysfs_show_mode, bq2415x_sysfs_set_mode);
static DEVICE_ATTR(timer, S_IWUSR | S_IRUGO,
bq2415x_sysfs_show_timer, bq2415x_sysfs_set_timer);
static DEVICE_ATTR(registers, S_IWUSR | S_IRUGO,
bq2415x_sysfs_show_registers, bq2415x_sysfs_set_registers);
static DEVICE_ATTR(otg_status, S_IRUGO, bq2415x_sysfs_show_status, NULL);
static DEVICE_ATTR(charge_status, S_IRUGO, bq2415x_sysfs_show_status, NULL);
static DEVICE_ATTR(boost_status, S_IRUGO, bq2415x_sysfs_show_status, NULL);
static DEVICE_ATTR(fault_status, S_IRUGO, bq2415x_sysfs_show_status, NULL);
static struct attribute *bq2415x_sysfs_attributes[] = {
/*
* TODO: some (appropriate) of these attrs should be switched to
* use power supply class props.
*/
&dev_attr_current_limit.attr,
&dev_attr_weak_battery_voltage.attr,
&dev_attr_battery_regulation_voltage.attr,
&dev_attr_charge_current.attr,
&dev_attr_termination_current.attr,
&dev_attr_charge_termination_enable.attr,
&dev_attr_high_impedance_enable.attr,
&dev_attr_otg_pin_enable.attr,
&dev_attr_stat_pin_enable.attr,
&dev_attr_reported_mode.attr,
&dev_attr_mode.attr,
&dev_attr_timer.attr,
&dev_attr_registers.attr,
&dev_attr_otg_status.attr,
&dev_attr_charge_status.attr,
&dev_attr_boost_status.attr,
&dev_attr_fault_status.attr,
NULL,
};
static const struct attribute_group bq2415x_sysfs_attr_group = {
.attrs = bq2415x_sysfs_attributes,
};
static int bq2415x_sysfs_init(struct bq2415x_device *bq)
{
return sysfs_create_group(&bq->charger.dev->kobj,
&bq2415x_sysfs_attr_group);
}
static void bq2415x_sysfs_exit(struct bq2415x_device *bq)
{
sysfs_remove_group(&bq->charger.dev->kobj, &bq2415x_sysfs_attr_group);
}
/* main bq2415x probe function */
static int bq2415x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
int num;
char *name;
struct bq2415x_device *bq;
if (!client->dev.platform_data) {
dev_err(&client->dev, "platform data not set\n");
return -ENODEV;
}
/* Get new ID for the new device */
ret = idr_pre_get(&bq2415x_id, GFP_KERNEL);
if (ret == 0)
return -ENOMEM;
mutex_lock(&bq2415x_id_mutex);
ret = idr_get_new(&bq2415x_id, client, &num);
mutex_unlock(&bq2415x_id_mutex);
if (ret < 0)
return ret;
name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num);
if (!name) {
dev_err(&client->dev, "failed to allocate device name\n");
ret = -ENOMEM;
goto error_1;
}
bq = kzalloc(sizeof(*bq), GFP_KERNEL);
if (!bq) {
dev_err(&client->dev, "failed to allocate device data\n");
ret = -ENOMEM;
goto error_2;
}
i2c_set_clientdata(client, bq);
bq->id = num;
bq->dev = &client->dev;
bq->chip = id->driver_data;
bq->name = name;
bq->mode = BQ2415X_MODE_NONE;
bq->reported_mode = BQ2415X_MODE_NONE;
bq->autotimer = 0;
bq->automode = 0;
memcpy(&bq->init_data, client->dev.platform_data,
sizeof(bq->init_data));
bq2415x_reset_chip(bq);
ret = bq2415x_power_supply_init(bq);
if (ret) {
dev_err(bq->dev, "failed to register power supply: %d\n", ret);
goto error_3;
}
ret = bq2415x_sysfs_init(bq);
if (ret) {
dev_err(bq->dev, "failed to create sysfs entries: %d\n", ret);
goto error_4;
}
ret = bq2415x_set_defaults(bq);
if (ret) {
dev_err(bq->dev, "failed to set default values: %d\n", ret);
goto error_5;
}
if (bq->init_data.set_mode_hook) {
if (bq->init_data.set_mode_hook(
bq2415x_hook_function, bq)) {
bq->automode = 1;
bq2415x_set_mode(bq, bq->reported_mode);
dev_info(bq->dev, "automode enabled\n");
} else {
bq->automode = -1;
dev_info(bq->dev, "automode failed\n");
}
} else {
bq->automode = -1;
dev_info(bq->dev, "automode not supported\n");
}
INIT_DELAYED_WORK(&bq->work, bq2415x_timer_work);
bq2415x_set_autotimer(bq, 1);
dev_info(bq->dev, "driver registered\n");
return 0;
error_5:
bq2415x_sysfs_exit(bq);
error_4:
bq2415x_power_supply_exit(bq);
error_3:
kfree(bq);
error_2:
kfree(name);
error_1:
mutex_lock(&bq2415x_id_mutex);
idr_remove(&bq2415x_id, num);
mutex_unlock(&bq2415x_id_mutex);
return ret;
}
/* main bq2415x remove function */
static int bq2415x_remove(struct i2c_client *client)
{
struct bq2415x_device *bq = i2c_get_clientdata(client);
if (bq->init_data.set_mode_hook)
bq->init_data.set_mode_hook(NULL, NULL);
bq2415x_sysfs_exit(bq);
bq2415x_power_supply_exit(bq);
bq2415x_reset_chip(bq);
mutex_lock(&bq2415x_id_mutex);
idr_remove(&bq2415x_id, bq->id);
mutex_unlock(&bq2415x_id_mutex);
dev_info(bq->dev, "driver unregistered\n");
kfree(bq->name);
kfree(bq);
return 0;
}
static const struct i2c_device_id bq2415x_i2c_id_table[] = {
{ "bq2415x", BQUNKNOWN },
{ "bq24150", BQ24150 },
{ "bq24150a", BQ24150A },
{ "bq24151", BQ24151 },
{ "bq24151a", BQ24151A },
{ "bq24152", BQ24152 },
{ "bq24153", BQ24153 },
{ "bq24153a", BQ24153A },
{ "bq24155", BQ24155 },
{ "bq24156", BQ24156 },
{ "bq24156a", BQ24156A },
{ "bq24158", BQ24158 },
{},
};
MODULE_DEVICE_TABLE(i2c, bq2415x_i2c_id_table);
static struct i2c_driver bq2415x_driver = {
.driver = {
.name = "bq2415x-charger",
},
.probe = bq2415x_probe,
.remove = bq2415x_remove,
.id_table = bq2415x_i2c_id_table,
};
static int __init bq2415x_init(void)
{
return i2c_add_driver(&bq2415x_driver);
}
module_init(bq2415x_init);
static void __exit bq2415x_exit(void)
{
i2c_del_driver(&bq2415x_driver);
}
module_exit(bq2415x_exit);
MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
MODULE_DESCRIPTION("bq2415x charger driver");
MODULE_LICENSE("GPL");
...@@ -230,6 +230,14 @@ static int bq27x00_battery_read_charge(struct bq27x00_device_info *di, u8 reg) ...@@ -230,6 +230,14 @@ static int bq27x00_battery_read_charge(struct bq27x00_device_info *di, u8 reg)
*/ */
static inline int bq27x00_battery_read_nac(struct bq27x00_device_info *di) static inline int bq27x00_battery_read_nac(struct bq27x00_device_info *di)
{ {
int flags;
bool is_bq27500 = di->chip == BQ27500;
bool is_higher = bq27xxx_is_chip_version_higher(di);
flags = bq27x00_read(di, BQ27x00_REG_FLAGS, !is_bq27500);
if (flags >= 0 && !is_higher && (flags & BQ27000_FLAG_CI))
return -ENODATA;
return bq27x00_battery_read_charge(di, BQ27x00_REG_NAC); return bq27x00_battery_read_charge(di, BQ27x00_REG_NAC);
} }
......
...@@ -80,13 +80,13 @@ static inline int ds278x_read_reg16(struct ds278x_info *info, int reg_msb, ...@@ -80,13 +80,13 @@ static inline int ds278x_read_reg16(struct ds278x_info *info, int reg_msb,
{ {
int ret; int ret;
ret = swab16(i2c_smbus_read_word_data(info->client, reg_msb)); ret = i2c_smbus_read_word_data(info->client, reg_msb);
if (ret < 0) { if (ret < 0) {
dev_err(&info->client->dev, "register read failed\n"); dev_err(&info->client->dev, "register read failed\n");
return ret; return ret;
} }
*val = ret; *val = swab16(ret);
return 0; return 0;
} }
......
...@@ -279,7 +279,8 @@ static int gab_probe(struct platform_device *pdev) ...@@ -279,7 +279,8 @@ static int gab_probe(struct platform_device *pdev)
} }
memcpy(psy->properties, gab_props, sizeof(gab_props)); memcpy(psy->properties, gab_props, sizeof(gab_props));
properties = psy->properties + sizeof(gab_props); properties = (enum power_supply_property *)
((char *)psy->properties + sizeof(gab_props));
/* /*
* getting channel from iio and copying the battery properties * getting channel from iio and copying the battery properties
...@@ -327,7 +328,7 @@ static int gab_probe(struct platform_device *pdev) ...@@ -327,7 +328,7 @@ static int gab_probe(struct platform_device *pdev)
ret = request_any_context_irq(irq, gab_charged, ret = request_any_context_irq(irq, gab_charged,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
"battery charged", adc_bat); "battery charged", adc_bat);
if (ret) if (ret < 0)
goto err_gpio; goto err_gpio;
} }
......
...@@ -33,7 +33,6 @@ struct jz_battery { ...@@ -33,7 +33,6 @@ struct jz_battery {
struct jz_battery_platform_data *pdata; struct jz_battery_platform_data *pdata;
struct platform_device *pdev; struct platform_device *pdev;
struct resource *mem;
void __iomem *base; void __iomem *base;
int irq; int irq;
...@@ -244,13 +243,14 @@ static int jz_battery_probe(struct platform_device *pdev) ...@@ -244,13 +243,14 @@ static int jz_battery_probe(struct platform_device *pdev)
struct jz_battery_platform_data *pdata = pdev->dev.parent->platform_data; struct jz_battery_platform_data *pdata = pdev->dev.parent->platform_data;
struct jz_battery *jz_battery; struct jz_battery *jz_battery;
struct power_supply *battery; struct power_supply *battery;
struct resource *mem;
if (!pdata) { if (!pdata) {
dev_err(&pdev->dev, "No platform_data supplied\n"); dev_err(&pdev->dev, "No platform_data supplied\n");
return -ENXIO; return -ENXIO;
} }
jz_battery = kzalloc(sizeof(*jz_battery), GFP_KERNEL); jz_battery = devm_kzalloc(&pdev->dev, sizeof(*jz_battery), GFP_KERNEL);
if (!jz_battery) { if (!jz_battery) {
dev_err(&pdev->dev, "Failed to allocate driver structure\n"); dev_err(&pdev->dev, "Failed to allocate driver structure\n");
return -ENOMEM; return -ENOMEM;
...@@ -260,33 +260,15 @@ static int jz_battery_probe(struct platform_device *pdev) ...@@ -260,33 +260,15 @@ static int jz_battery_probe(struct platform_device *pdev)
jz_battery->irq = platform_get_irq(pdev, 0); jz_battery->irq = platform_get_irq(pdev, 0);
if (jz_battery->irq < 0) { if (jz_battery->irq < 0) {
ret = jz_battery->irq;
dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret); dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret);
goto err_free; return jz_battery->irq;
} }
jz_battery->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!jz_battery->mem) {
ret = -ENOENT;
dev_err(&pdev->dev, "Failed to get platform mmio resource\n");
goto err_free;
}
jz_battery->mem = request_mem_region(jz_battery->mem->start, jz_battery->base = devm_request_and_ioremap(&pdev->dev, mem);
resource_size(jz_battery->mem), pdev->name); if (!jz_battery->base)
if (!jz_battery->mem) { return -EBUSY;
ret = -EBUSY;
dev_err(&pdev->dev, "Failed to request mmio memory region\n");
goto err_free;
}
jz_battery->base = ioremap_nocache(jz_battery->mem->start,
resource_size(jz_battery->mem));
if (!jz_battery->base) {
ret = -EBUSY;
dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
goto err_release_mem_region;
}
battery = &jz_battery->battery; battery = &jz_battery->battery;
battery->name = pdata->info.name; battery->name = pdata->info.name;
...@@ -309,7 +291,7 @@ static int jz_battery_probe(struct platform_device *pdev) ...@@ -309,7 +291,7 @@ static int jz_battery_probe(struct platform_device *pdev)
jz_battery); jz_battery);
if (ret) { if (ret) {
dev_err(&pdev->dev, "Failed to request irq %d\n", ret); dev_err(&pdev->dev, "Failed to request irq %d\n", ret);
goto err_iounmap; goto err;
} }
disable_irq(jz_battery->irq); disable_irq(jz_battery->irq);
...@@ -366,13 +348,8 @@ static int jz_battery_probe(struct platform_device *pdev) ...@@ -366,13 +348,8 @@ static int jz_battery_probe(struct platform_device *pdev)
gpio_free(jz_battery->pdata->gpio_charge); gpio_free(jz_battery->pdata->gpio_charge);
err_free_irq: err_free_irq:
free_irq(jz_battery->irq, jz_battery); free_irq(jz_battery->irq, jz_battery);
err_iounmap: err:
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
iounmap(jz_battery->base);
err_release_mem_region:
release_mem_region(jz_battery->mem->start, resource_size(jz_battery->mem));
err_free:
kfree(jz_battery);
return ret; return ret;
} }
...@@ -392,10 +369,6 @@ static int jz_battery_remove(struct platform_device *pdev) ...@@ -392,10 +369,6 @@ static int jz_battery_remove(struct platform_device *pdev)
free_irq(jz_battery->irq, jz_battery); free_irq(jz_battery->irq, jz_battery);
iounmap(jz_battery->base);
release_mem_region(jz_battery->mem->start, resource_size(jz_battery->mem));
kfree(jz_battery);
return 0; return 0;
} }
......
...@@ -235,25 +235,14 @@ static int lp8788_get_battery_present(struct lp8788_charger *pchg, ...@@ -235,25 +235,14 @@ static int lp8788_get_battery_present(struct lp8788_charger *pchg,
return 0; return 0;
} }
static int lp8788_get_vbatt_adc(struct lp8788_charger *pchg, static int lp8788_get_vbatt_adc(struct lp8788_charger *pchg, int *result)
unsigned int *result)
{ {
struct iio_channel *channel = pchg->chan[LP8788_VBATT]; struct iio_channel *channel = pchg->chan[LP8788_VBATT];
int scaleint;
int scalepart;
int ret;
if (!channel) if (!channel)
return -EINVAL; return -EINVAL;
ret = iio_read_channel_scale(channel, &scaleint, &scalepart); return iio_read_channel_processed(channel, result);
if (ret != IIO_VAL_INT_PLUS_MICRO)
return -EINVAL;
/* unit: mV */
*result = (scaleint + scalepart * 1000000) / 1000;
return 0;
} }
static int lp8788_get_battery_voltage(struct lp8788_charger *pchg, static int lp8788_get_battery_voltage(struct lp8788_charger *pchg,
...@@ -268,7 +257,7 @@ static int lp8788_get_battery_capacity(struct lp8788_charger *pchg, ...@@ -268,7 +257,7 @@ static int lp8788_get_battery_capacity(struct lp8788_charger *pchg,
struct lp8788 *lp = pchg->lp; struct lp8788 *lp = pchg->lp;
struct lp8788_charger_platform_data *pdata = pchg->pdata; struct lp8788_charger_platform_data *pdata = pchg->pdata;
unsigned int max_vbatt; unsigned int max_vbatt;
unsigned int vbatt; int vbatt;
enum lp8788_charging_state state; enum lp8788_charging_state state;
u8 data; u8 data;
int ret; int ret;
...@@ -304,19 +293,18 @@ static int lp8788_get_battery_temperature(struct lp8788_charger *pchg, ...@@ -304,19 +293,18 @@ static int lp8788_get_battery_temperature(struct lp8788_charger *pchg,
union power_supply_propval *val) union power_supply_propval *val)
{ {
struct iio_channel *channel = pchg->chan[LP8788_BATT_TEMP]; struct iio_channel *channel = pchg->chan[LP8788_BATT_TEMP];
int scaleint; int result;
int scalepart;
int ret; int ret;
if (!channel) if (!channel)
return -EINVAL; return -EINVAL;
ret = iio_read_channel_scale(channel, &scaleint, &scalepart); ret = iio_read_channel_processed(channel, &result);
if (ret != IIO_VAL_INT_PLUS_MICRO) if (ret < 0)
return -EINVAL; return -EINVAL;
/* unit: 0.1 'C */ /* unit: 0.1 'C */
val->intval = (scaleint + scalepart * 1000000) / 100; val->intval = result * 10;
return 0; return 0;
} }
...@@ -592,53 +580,22 @@ static void lp8788_irq_unregister(struct platform_device *pdev, ...@@ -592,53 +580,22 @@ static void lp8788_irq_unregister(struct platform_device *pdev,
} }
} }
static void lp8788_setup_adc_channel(struct lp8788_charger *pchg) static void lp8788_setup_adc_channel(const char *consumer_name,
struct lp8788_charger *pchg)
{ {
struct lp8788_charger_platform_data *pdata = pchg->pdata; struct lp8788_charger_platform_data *pdata = pchg->pdata;
struct device *dev = pchg->lp->dev;
struct iio_channel *chan; struct iio_channel *chan;
enum lp8788_adc_id id;
const char *chan_name[LPADC_MAX] = {
[LPADC_VBATT_5P5] = "vbatt-5p5",
[LPADC_VBATT_6P0] = "vbatt-6p0",
[LPADC_VBATT_5P0] = "vbatt-5p0",
[LPADC_ADC1] = "adc1",
[LPADC_ADC2] = "adc2",
[LPADC_ADC3] = "adc3",
[LPADC_ADC4] = "adc4",
};
if (!pdata) if (!pdata)
return; return;
id = pdata->vbatt_adc; /* ADC channel for battery voltage */
switch (id) { chan = iio_channel_get(consumer_name, pdata->adc_vbatt);
case LPADC_VBATT_5P5: pchg->chan[LP8788_VBATT] = IS_ERR(chan) ? NULL : chan;
case LPADC_VBATT_6P0:
case LPADC_VBATT_5P0:
chan = iio_channel_get(NULL, chan_name[id]);
pchg->chan[LP8788_VBATT] = IS_ERR(chan) ? NULL : chan;
break;
default:
dev_err(dev, "invalid ADC id for VBATT: %d\n", id);
pchg->chan[LP8788_VBATT] = NULL;
break;
}
id = pdata->batt_temp_adc; /* ADC channel for battery temperature */
switch (id) { chan = iio_channel_get(consumer_name, pdata->adc_batt_temp);
case LPADC_ADC1: pchg->chan[LP8788_BATT_TEMP] = IS_ERR(chan) ? NULL : chan;
case LPADC_ADC2:
case LPADC_ADC3:
case LPADC_ADC4:
chan = iio_channel_get(NULL, chan_name[id]);
pchg->chan[LP8788_BATT_TEMP] = IS_ERR(chan) ? NULL : chan;
break;
default:
dev_err(dev, "invalid ADC id for BATT_TEMP : %d\n", id);
pchg->chan[LP8788_BATT_TEMP] = NULL;
break;
}
} }
static void lp8788_release_adc_channel(struct lp8788_charger *pchg) static void lp8788_release_adc_channel(struct lp8788_charger *pchg)
...@@ -747,7 +704,7 @@ static int lp8788_charger_probe(struct platform_device *pdev) ...@@ -747,7 +704,7 @@ static int lp8788_charger_probe(struct platform_device *pdev)
if (ret) if (ret)
return ret; return ret;
lp8788_setup_adc_channel(pchg); lp8788_setup_adc_channel(pdev->name, pchg);
ret = lp8788_psy_register(pdev, pchg); ret = lp8788_psy_register(pdev, pchg);
if (ret) if (ret)
......
...@@ -572,7 +572,8 @@ static int max17042_init_chip(struct max17042_chip *chip) ...@@ -572,7 +572,8 @@ static int max17042_init_chip(struct max17042_chip *chip)
__func__); __func__);
return -EIO; return -EIO;
} }
max17042_verify_model_lock(chip);
ret = max17042_verify_model_lock(chip);
if (ret) { if (ret) {
dev_err(&chip->client->dev, "%s lock verify failed\n", dev_err(&chip->client->dev, "%s lock verify failed\n",
__func__); __func__);
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/of.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
...@@ -426,6 +427,54 @@ static int max8925_deinit_charger(struct max8925_power_info *info) ...@@ -426,6 +427,54 @@ static int max8925_deinit_charger(struct max8925_power_info *info)
return 0; return 0;
} }
#ifdef CONFIG_OF
static struct max8925_power_pdata *
max8925_power_dt_init(struct platform_device *pdev)
{
struct device_node *nproot = pdev->dev.parent->of_node;
struct device_node *np;
int batt_detect;
int topoff_threshold;
int fast_charge;
int no_temp_support;
int no_insert_detect;
struct max8925_power_pdata *pdata;
if (!nproot)
return pdev->dev.platform_data;
np = of_find_node_by_name(nproot, "charger");
if (!np) {
dev_err(&pdev->dev, "failed to find charger node\n");
return NULL;
}
pdata = devm_kzalloc(&pdev->dev,
sizeof(struct max8925_power_pdata),
GFP_KERNEL);
of_property_read_u32(np, "topoff-threshold", &topoff_threshold);
of_property_read_u32(np, "batt-detect", &batt_detect);
of_property_read_u32(np, "fast-charge", &fast_charge);
of_property_read_u32(np, "no-insert-detect", &no_insert_detect);
of_property_read_u32(np, "no-temp-support", &no_temp_support);
pdata->batt_detect = batt_detect;
pdata->fast_charge = fast_charge;
pdata->topoff_threshold = topoff_threshold;
pdata->no_insert_detect = no_insert_detect;
pdata->no_temp_support = no_temp_support;
return pdata;
}
#else
static struct max8925_power_pdata *
max8925_power_dt_init(struct platform_device *pdev)
{
return pdev->dev.platform_data;
}
#endif
static int max8925_power_probe(struct platform_device *pdev) static int max8925_power_probe(struct platform_device *pdev)
{ {
struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
...@@ -433,7 +482,7 @@ static int max8925_power_probe(struct platform_device *pdev) ...@@ -433,7 +482,7 @@ static int max8925_power_probe(struct platform_device *pdev)
struct max8925_power_info *info; struct max8925_power_info *info;
int ret; int ret;
pdata = pdev->dev.platform_data; pdata = max8925_power_dt_init(pdev);
if (!pdata) { if (!pdata) {
dev_err(&pdev->dev, "platform data isn't assigned to " dev_err(&pdev->dev, "platform data isn't assigned to "
"power supply\n"); "power supply\n");
......
...@@ -216,6 +216,86 @@ static void psy_unregister_thermal(struct power_supply *psy) ...@@ -216,6 +216,86 @@ static void psy_unregister_thermal(struct power_supply *psy)
return; return;
thermal_zone_device_unregister(psy->tzd); thermal_zone_device_unregister(psy->tzd);
} }
/* thermal cooling device callbacks */
static int ps_get_max_charge_cntl_limit(struct thermal_cooling_device *tcd,
unsigned long *state)
{
struct power_supply *psy;
union power_supply_propval val;
int ret;
psy = tcd->devdata;
ret = psy->get_property(psy,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, &val);
if (!ret)
*state = val.intval;
return ret;
}
static int ps_get_cur_chrage_cntl_limit(struct thermal_cooling_device *tcd,
unsigned long *state)
{
struct power_supply *psy;
union power_supply_propval val;
int ret;
psy = tcd->devdata;
ret = psy->get_property(psy,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val);
if (!ret)
*state = val.intval;
return ret;
}
static int ps_set_cur_charge_cntl_limit(struct thermal_cooling_device *tcd,
unsigned long state)
{
struct power_supply *psy;
union power_supply_propval val;
int ret;
psy = tcd->devdata;
val.intval = state;
ret = psy->set_property(psy,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val);
return ret;
}
static struct thermal_cooling_device_ops psy_tcd_ops = {
.get_max_state = ps_get_max_charge_cntl_limit,
.get_cur_state = ps_get_cur_chrage_cntl_limit,
.set_cur_state = ps_set_cur_charge_cntl_limit,
};
static int psy_register_cooler(struct power_supply *psy)
{
int i;
/* Register for cooling device if psy can control charging */
for (i = 0; i < psy->num_properties; i++) {
if (psy->properties[i] ==
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT) {
psy->tcd = thermal_cooling_device_register(
(char *)psy->name,
psy, &psy_tcd_ops);
if (IS_ERR(psy->tcd))
return PTR_ERR(psy->tcd);
break;
}
}
return 0;
}
static void psy_unregister_cooler(struct power_supply *psy)
{
if (IS_ERR_OR_NULL(psy->tcd))
return;
thermal_cooling_device_unregister(psy->tcd);
}
#else #else
static int psy_register_thermal(struct power_supply *psy) static int psy_register_thermal(struct power_supply *psy)
{ {
...@@ -225,6 +305,15 @@ static int psy_register_thermal(struct power_supply *psy) ...@@ -225,6 +305,15 @@ static int psy_register_thermal(struct power_supply *psy)
static void psy_unregister_thermal(struct power_supply *psy) static void psy_unregister_thermal(struct power_supply *psy)
{ {
} }
static int psy_register_cooler(struct power_supply *psy)
{
return 0;
}
static void psy_unregister_cooler(struct power_supply *psy)
{
}
#endif #endif
int power_supply_register(struct device *parent, struct power_supply *psy) int power_supply_register(struct device *parent, struct power_supply *psy)
...@@ -259,6 +348,10 @@ int power_supply_register(struct device *parent, struct power_supply *psy) ...@@ -259,6 +348,10 @@ int power_supply_register(struct device *parent, struct power_supply *psy)
if (rc) if (rc)
goto register_thermal_failed; goto register_thermal_failed;
rc = psy_register_cooler(psy);
if (rc)
goto register_cooler_failed;
rc = power_supply_create_triggers(psy); rc = power_supply_create_triggers(psy);
if (rc) if (rc)
goto create_triggers_failed; goto create_triggers_failed;
...@@ -268,6 +361,8 @@ int power_supply_register(struct device *parent, struct power_supply *psy) ...@@ -268,6 +361,8 @@ int power_supply_register(struct device *parent, struct power_supply *psy)
goto success; goto success;
create_triggers_failed: create_triggers_failed:
psy_unregister_cooler(psy);
register_cooler_failed:
psy_unregister_thermal(psy); psy_unregister_thermal(psy);
register_thermal_failed: register_thermal_failed:
device_del(dev); device_del(dev);
...@@ -284,6 +379,7 @@ void power_supply_unregister(struct power_supply *psy) ...@@ -284,6 +379,7 @@ void power_supply_unregister(struct power_supply *psy)
cancel_work_sync(&psy->changed_work); cancel_work_sync(&psy->changed_work);
sysfs_remove_link(&psy->dev->kobj, "powers"); sysfs_remove_link(&psy->dev->kobj, "powers");
power_supply_remove_triggers(psy); power_supply_remove_triggers(psy);
psy_unregister_cooler(psy);
psy_unregister_thermal(psy); psy_unregister_thermal(psy);
device_unregister(psy->dev); device_unregister(psy->dev);
} }
......
...@@ -164,6 +164,8 @@ static struct device_attribute power_supply_attrs[] = { ...@@ -164,6 +164,8 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(constant_charge_current_max), POWER_SUPPLY_ATTR(constant_charge_current_max),
POWER_SUPPLY_ATTR(constant_charge_voltage), POWER_SUPPLY_ATTR(constant_charge_voltage),
POWER_SUPPLY_ATTR(constant_charge_voltage_max), POWER_SUPPLY_ATTR(constant_charge_voltage_max),
POWER_SUPPLY_ATTR(charge_control_limit),
POWER_SUPPLY_ATTR(charge_control_limit_max),
POWER_SUPPLY_ATTR(energy_full_design), POWER_SUPPLY_ATTR(energy_full_design),
POWER_SUPPLY_ATTR(energy_empty_design), POWER_SUPPLY_ATTR(energy_empty_design),
POWER_SUPPLY_ATTR(energy_full), POWER_SUPPLY_ATTR(energy_full),
......
/*
* Nokia RX-51 battery driver
*
* Copyright (C) 2012 Pali Rohár <pali.rohar@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <linux/module.h>
#include <linux/param.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/slab.h>
#include <linux/i2c/twl4030-madc.h>
struct rx51_device_info {
struct device *dev;
struct power_supply bat;
};
/*
* Read ADCIN channel value, code copied from maemo kernel
*/
static int rx51_battery_read_adc(int channel)
{
struct twl4030_madc_request req;
req.channels = 1 << channel;
req.do_avg = 1;
req.method = TWL4030_MADC_SW1;
req.func_cb = NULL;
req.type = TWL4030_MADC_WAIT;
if (twl4030_madc_conversion(&req) <= 0)
return -ENODATA;
return req.rbuf[channel];
}
/*
* Read ADCIN channel 12 (voltage) and convert RAW value to micro voltage
* This conversion formula was extracted from maemo program bsi-read
*/
static int rx51_battery_read_voltage(struct rx51_device_info *di)
{
int voltage = rx51_battery_read_adc(12);
if (voltage < 0)
return voltage;
return 1000 * (10000 * voltage / 1705);
}
/*
* Temperature look-up tables
* TEMP = (1/(t1 + 1/298) - 273.15)
* Where t1 = (1/B) * ln((RAW_ADC_U * 2.5)/(R * I * 255))
* Formula is based on experimental data, RX-51 CAL data, maemo program bme
* and formula from da9052 driver with values R = 100, B = 3380, I = 0.00671
*/
/*
* Table1 (temperature for first 25 RAW values)
* Usage: TEMP = rx51_temp_table1[RAW]
* RAW is between 1 and 24
* TEMP is between 201 C and 55 C
*/
static u8 rx51_temp_table1[] = {
255, 201, 159, 138, 124, 114, 106, 99, 94, 89, 85, 82, 78, 75,
73, 70, 68, 66, 64, 62, 61, 59, 57, 56, 55
};
/*
* Table2 (lowest RAW value for temperature)
* Usage: RAW = rx51_temp_table2[TEMP-rx51_temp_table2_first]
* TEMP is between 53 C and -32 C
* RAW is between 25 and 993
*/
#define rx51_temp_table2_first 53
static u16 rx51_temp_table2[] = {
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 39,
40, 41, 43, 44, 46, 48, 49, 51, 53, 55, 57, 59, 61, 64,
66, 69, 71, 74, 77, 80, 83, 86, 90, 94, 97, 101, 106, 110,
115, 119, 125, 130, 136, 141, 148, 154, 161, 168, 176, 184, 202, 211,
221, 231, 242, 254, 266, 279, 293, 308, 323, 340, 357, 375, 395, 415,
437, 460, 485, 511, 539, 568, 600, 633, 669, 706, 747, 790, 836, 885,
937, 993, 1024
};
/*
* Read ADCIN channel 0 (battery temp) and convert value to tenths of Celsius
* Use Temperature look-up tables for conversation
*/
static int rx51_battery_read_temperature(struct rx51_device_info *di)
{
int min = 0;
int max = ARRAY_SIZE(rx51_temp_table2) - 1;
int raw = rx51_battery_read_adc(0);
/* Zero and negative values are undefined */
if (raw <= 0)
return INT_MAX;
/* ADC channels are 10 bit, higher value are undefined */
if (raw >= (1 << 10))
return INT_MIN;
/* First check for temperature in first direct table */
if (raw < ARRAY_SIZE(rx51_temp_table1))
return rx51_temp_table1[raw] * 100;
/* Binary search RAW value in second inverse table */
while (max - min > 1) {
int mid = (max + min) / 2;
if (rx51_temp_table2[mid] <= raw)
min = mid;
else if (rx51_temp_table2[mid] > raw)
max = mid;
if (rx51_temp_table2[mid] == raw)
break;
}
return (rx51_temp_table2_first - min) * 100;
}
/*
* Read ADCIN channel 4 (BSI) and convert RAW value to micro Ah
* This conversion formula was extracted from maemo program bsi-read
*/
static int rx51_battery_read_capacity(struct rx51_device_info *di)
{
int capacity = rx51_battery_read_adc(4);
if (capacity < 0)
return capacity;
return 1280 * (1200 * capacity)/(1024 - capacity);
}
/*
* Return power_supply property
*/
static int rx51_battery_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct rx51_device_info *di = container_of((psy),
struct rx51_device_info, bat);
switch (psp) {
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
val->intval = 4200000;
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = rx51_battery_read_voltage(di) ? 1 : 0;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
val->intval = rx51_battery_read_voltage(di);
break;
case POWER_SUPPLY_PROP_TEMP:
val->intval = rx51_battery_read_temperature(di);
break;
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
val->intval = rx51_battery_read_capacity(di);
break;
default:
return -EINVAL;
}
if (val->intval == INT_MAX || val->intval == INT_MIN)
return -EINVAL;
return 0;
}
static enum power_supply_property rx51_battery_props[] = {
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
};
static int __devinit rx51_battery_probe(struct platform_device *pdev)
{
struct rx51_device_info *di;
int ret;
di = kzalloc(sizeof(*di), GFP_KERNEL);
if (!di)
return -ENOMEM;
platform_set_drvdata(pdev, di);
di->bat.name = dev_name(&pdev->dev);
di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
di->bat.properties = rx51_battery_props;
di->bat.num_properties = ARRAY_SIZE(rx51_battery_props);
di->bat.get_property = rx51_battery_get_property;
ret = power_supply_register(di->dev, &di->bat);
if (ret) {
platform_set_drvdata(pdev, NULL);
kfree(di);
return ret;
}
return 0;
}
static int __devexit rx51_battery_remove(struct platform_device *pdev)
{
struct rx51_device_info *di = platform_get_drvdata(pdev);
power_supply_unregister(&di->bat);
platform_set_drvdata(pdev, NULL);
kfree(di);
return 0;
}
static struct platform_driver rx51_battery_driver = {
.probe = rx51_battery_probe,
.remove = __devexit_p(rx51_battery_remove),
.driver = {
.name = "rx51-battery",
.owner = THIS_MODULE,
},
};
module_platform_driver(rx51_battery_driver);
MODULE_ALIAS("platform:rx51-battery");
MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
MODULE_DESCRIPTION("Nokia RX-51 battery driver");
MODULE_LICENSE("GPL");
...@@ -114,12 +114,12 @@ static int twl4030_clear_set(u8 mod_no, u8 clear, u8 set, u8 reg) ...@@ -114,12 +114,12 @@ static int twl4030_clear_set(u8 mod_no, u8 clear, u8 set, u8 reg)
static int twl4030_bci_read(u8 reg, u8 *val) static int twl4030_bci_read(u8 reg, u8 *val)
{ {
return twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, val, reg); return twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, val, reg);
} }
static int twl4030_clear_set_boot_bci(u8 clear, u8 set) static int twl4030_clear_set_boot_bci(u8 clear, u8 set)
{ {
return twl4030_clear_set(TWL4030_MODULE_PM_MASTER, clear, return twl4030_clear_set(TWL_MODULE_PM_MASTER, clear,
TWL4030_CONFIG_DONE | TWL4030_BCIAUTOWEN | set, TWL4030_CONFIG_DONE | TWL4030_BCIAUTOWEN | set,
TWL4030_PM_MASTER_BOOT_BCI); TWL4030_PM_MASTER_BOOT_BCI);
} }
...@@ -152,7 +152,7 @@ static int twl4030_bci_have_vbus(struct twl4030_bci *bci) ...@@ -152,7 +152,7 @@ static int twl4030_bci_have_vbus(struct twl4030_bci *bci)
int ret; int ret;
u8 hwsts; u8 hwsts;
ret = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &hwsts, ret = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &hwsts,
TWL4030_PM_MASTER_STS_HW_CONDITIONS); TWL4030_PM_MASTER_STS_HW_CONDITIONS);
if (ret < 0) if (ret < 0)
return 0; return 0;
...@@ -199,7 +199,7 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) ...@@ -199,7 +199,7 @@ static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
return ret; return ret;
/* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */ /* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */
ret = twl4030_clear_set(TWL4030_MODULE_MAIN_CHARGE, 0, ret = twl4030_clear_set(TWL_MODULE_MAIN_CHARGE, 0,
TWL4030_USBFASTMCHG, TWL4030_BCIMFSTS4); TWL4030_USBFASTMCHG, TWL4030_BCIMFSTS4);
} else { } else {
ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, 0); ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, 0);
...@@ -238,7 +238,7 @@ static int twl4030_charger_enable_backup(int uvolt, int uamp) ...@@ -238,7 +238,7 @@ static int twl4030_charger_enable_backup(int uvolt, int uamp)
if (uvolt < 2500000 || if (uvolt < 2500000 ||
uamp < 25) { uamp < 25) {
/* disable charging of backup battery */ /* disable charging of backup battery */
ret = twl4030_clear_set(TWL4030_MODULE_PM_RECEIVER, ret = twl4030_clear_set(TWL_MODULE_PM_RECEIVER,
TWL4030_BBCHEN, 0, TWL4030_BB_CFG); TWL4030_BBCHEN, 0, TWL4030_BB_CFG);
return ret; return ret;
} }
...@@ -262,7 +262,7 @@ static int twl4030_charger_enable_backup(int uvolt, int uamp) ...@@ -262,7 +262,7 @@ static int twl4030_charger_enable_backup(int uvolt, int uamp)
else else
flags |= TWL4030_BBISEL_25uA; flags |= TWL4030_BBISEL_25uA;
ret = twl4030_clear_set(TWL4030_MODULE_PM_RECEIVER, ret = twl4030_clear_set(TWL_MODULE_PM_RECEIVER,
TWL4030_BBSEL_MASK | TWL4030_BBISEL_MASK, TWL4030_BBSEL_MASK | TWL4030_BBISEL_MASK,
flags, flags,
TWL4030_BB_CFG); TWL4030_BB_CFG);
......
...@@ -267,39 +267,21 @@ struct abx500_bm_data { ...@@ -267,39 +267,21 @@ struct abx500_bm_data {
int gnd_lift_resistance; int gnd_lift_resistance;
const struct abx500_maxim_parameters *maxi; const struct abx500_maxim_parameters *maxi;
const struct abx500_bm_capacity_levels *cap_levels; const struct abx500_bm_capacity_levels *cap_levels;
const struct abx500_battery_type *bat_type; struct abx500_battery_type *bat_type;
const struct abx500_bm_charger_parameters *chg_params; const struct abx500_bm_charger_parameters *chg_params;
const struct abx500_fg_parameters *fg_params; const struct abx500_fg_parameters *fg_params;
}; };
struct abx500_chargalg_platform_data { extern struct abx500_bm_data ab8500_bm_data;
char **supplied_to;
size_t num_supplicants;
};
struct abx500_charger_platform_data {
char **supplied_to;
size_t num_supplicants;
bool autopower_cfg;
};
struct abx500_btemp_platform_data { enum {
char **supplied_to; NTC_EXTERNAL = 0,
size_t num_supplicants; NTC_INTERNAL,
}; };
struct abx500_fg_platform_data { int bmdevs_of_probe(struct device *dev,
char **supplied_to; struct device_node *np,
size_t num_supplicants; struct abx500_bm_data **battery);
};
struct abx500_bm_plat_data {
struct abx500_bm_data *battery;
struct abx500_charger_platform_data *charger;
struct abx500_btemp_platform_data *btemp;
struct abx500_fg_platform_data *fg;
struct abx500_chargalg_platform_data *chargalg;
};
int abx500_set_register_interruptible(struct device *dev, u8 bank, u8 reg, int abx500_set_register_interruptible(struct device *dev, u8 bank, u8 reg,
u8 value); u8 value);
......
...@@ -211,16 +211,16 @@ struct lp8788_chg_param { ...@@ -211,16 +211,16 @@ struct lp8788_chg_param {
/* /*
* struct lp8788_charger_platform_data * struct lp8788_charger_platform_data
* @vbatt_adc : adc selection id for battery voltage * @adc_vbatt : adc channel name for battery voltage
* @batt_temp_adc : adc selection id for battery temperature * @adc_batt_temp : adc channel name for battery temperature
* @max_vbatt_mv : used for calculating battery capacity * @max_vbatt_mv : used for calculating battery capacity
* @chg_params : initial charging parameters * @chg_params : initial charging parameters
* @num_chg_params : numbers of charging parameters * @num_chg_params : numbers of charging parameters
* @charger_event : the charger event can be reported to the platform side * @charger_event : the charger event can be reported to the platform side
*/ */
struct lp8788_charger_platform_data { struct lp8788_charger_platform_data {
enum lp8788_adc_id vbatt_adc; const char *adc_vbatt;
enum lp8788_adc_id batt_temp_adc; const char *adc_batt_temp;
unsigned int max_vbatt_mv; unsigned int max_vbatt_mv;
struct lp8788_chg_param *chg_params; struct lp8788_chg_param *chg_params;
int num_chg_params; int num_chg_params;
......
/*
* bq2415x charger driver
*
* Copyright (C) 2011-2012 Pali Rohár <pali.rohar@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef BQ2415X_CHARGER_H
#define BQ2415X_CHARGER_H
/*
* This is platform data for bq2415x chip. It contains default board
* voltages and currents which can be also later configured via sysfs. If
* value is -1 then default chip value (specified in datasheet) will be
* used.
*
* Value resistor_sense is needed for for configuring charge and
* termination current. It it is less or equal to zero, configuring charge
* and termination current will not be possible.
*
* Function set_mode_hook is needed for automode (setting correct current
* limit when charger is connected/disconnected or setting boost mode).
* When is NULL, automode function is disabled. When is not NULL, it must
* have this prototype:
*
* int (*set_mode_hook)(
* void (*hook)(enum bq2415x_mode mode, void *data),
* void *data)
*
* hook is hook function (see below) and data is pointer to driver private
* data
*
* bq2415x driver will call it as:
*
* platform_data->set_mode_hook(bq2415x_hook_function, bq2415x_device);
*
* Board/platform function set_mode_hook return non zero value when hook
* function was successful registered. Platform code should call that hook
* function (which get from pointer, with data) every time when charger
* was connected/disconnected or require to enable boost mode. bq2415x
* driver then will set correct current limit, enable/disable charger or
* boost mode.
*
* Hook function has this prototype:
*
* void hook(enum bq2415x_mode mode, void *data);
*
* mode is bq2415x mode (charger or boost)
* data is pointer to driver private data (which get from
* set_charger_type_hook)
*
* When bq driver is being unloaded, it call function:
*
* platform_data->set_mode_hook(NULL, NULL);
*
* (hook function and driver private data are NULL)
*
* After that board/platform code must not call driver hook function! It
* is possible that pointer to hook function will not be valid and calling
* will cause undefined result.
*/
/* Supported modes with maximal current limit */
enum bq2415x_mode {
BQ2415X_MODE_NONE, /* unknown or no charger (100mA) */
BQ2415X_MODE_HOST_CHARGER, /* usb host/hub charger (500mA) */
BQ2415X_MODE_DEDICATED_CHARGER, /* dedicated charger (unlimited) */
BQ2415X_MODE_BOOST, /* boost mode (charging disabled) */
};
struct bq2415x_platform_data {
int current_limit; /* mA */
int weak_battery_voltage; /* mV */
int battery_regulation_voltage; /* mV */
int charge_current; /* mA */
int termination_current; /* mA */
int resistor_sense; /* m ohm */
int (*set_mode_hook)(void (*hook)(enum bq2415x_mode mode, void *data),
void *data);
};
#endif
...@@ -114,6 +114,8 @@ enum power_supply_property { ...@@ -114,6 +114,8 @@ enum power_supply_property {
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN, POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN,
POWER_SUPPLY_PROP_ENERGY_FULL, POWER_SUPPLY_PROP_ENERGY_FULL,
...@@ -186,6 +188,7 @@ struct power_supply { ...@@ -186,6 +188,7 @@ struct power_supply {
struct work_struct changed_work; struct work_struct changed_work;
#ifdef CONFIG_THERMAL #ifdef CONFIG_THERMAL
struct thermal_zone_device *tzd; struct thermal_zone_device *tzd;
struct thermal_cooling_device *tcd;
#endif #endif
#ifdef CONFIG_LEDS_TRIGGERS #ifdef CONFIG_LEDS_TRIGGERS
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment