From: "Rajanikanth H.V" rajanikanth.hv@linaro.org
This patch addes device tree support for battery temperature and fuel guage driver
Signed-off-by: Rajanikanth H.V rajanikanth.hv@linaro.org --- .../bindings/power_supply/ab8500/ab8500-btemp.txt | 209 +++++++++++++ .../bindings/power_supply/ab8500/ab8500-fg.txt | 51 +++ arch/arm/boot/dts/db8500.dtsi | 327 ++++++++++++++++++++ drivers/mfd/ab8500-core.c | 16 +- drivers/power/ab8500_btemp.c | 299 +++++++++++++++++- drivers/power/ab8500_fg.c | 58 +++- include/linux/mfd/abx500.h | 17 +- include/linux/of.h | 33 ++ 8 files changed, 966 insertions(+), 44 deletions(-) create mode 100644 Documentation/devicetree/bindings/power_supply/ab8500/ab8500-btemp.txt create mode 100644 Documentation/devicetree/bindings/power_supply/ab8500/ab8500-fg.txt
diff --git a/Documentation/devicetree/bindings/power_supply/ab8500/ab8500-btemp.txt b/Documentation/devicetree/bindings/power_supply/ab8500/ab8500-btemp.txt new file mode 100644 index 0000000..9908934 --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/ab8500/ab8500-btemp.txt @@ -0,0 +1,209 @@ +* St-Ericsson AB8500 Power Management Integrated Circuit (PMIC) + + battery temperature monitor: + +* Required properties: +- compatible: "stericsson,ab8500-btemp" + +* Sub-node: +- ab8500_bm_data: + +example: + ab8500-btemp { + compatible = "stericsson,ab8500-btemp"; + ... + ... + bat = <&ab8500_bm_data>; + }; + +ab8500_bm_data: + Contain the battery management/monitor node with the following + information to support different battery types. + +{ + temp_under under this temp, charging is stopped + temp_low between this temp and temp_under charging is reduced + temp_high between this temp and temp_over charging is reduced + temp_over over this temp, charging is stopped + temp_now present battery temperature + temp_interval_chg temperature measurement interval in s when charging + temp_interval_nochg temperature measurement interval in s when not charging + main_safety_tmr_h safety timer for main charger + usb_safety_tmr_h safety timer for usb charger + bkup_bat_v voltage which we charge the backup battery with + bkup_bat_i current which we charge the backup battery with + no_maintenance indicates that maintenance charging is disabled + abx500_adc_therm placement of thermistor, batctrl or battemp adc + chg_unknown_bat flag to enable charging of unknown batteries + enable_overshoot flag to enable VBAT overshoot control + auto_trig flag to enable auto adc trigger + fg_res resistance of FG resistor in 0.1mOhm + n_btypes number of elements in array bat_type + batt_id index of the identified battery in array bat_type + interval_charging charge alg cycle period time when charging (sec) + interval_not_charging charge alg cycle period time when not charging (sec) + temp_hysteresis temperature hysteresis + gnd_lift_resistance Battery ground to phone ground resistance (mOhm) +} +e.g: + ab8500_bm_data: bm_data { + temp_under = <3>; + temp_low = <8>; + temp_high = <43>; + temp_over = <48>; + temp_now = <0>; + temp_interval_chg = <20>; + temp_interval_nochg = <120>; + main_safety_tmr_h = <4>; + usb_safety_tmr_h = <4>; + bkup_bat_v = <1>; /* BUP_VCH_SEL_2P6V */ + bkup_bat_i = <4>; /* BUP_ICH_SEL_150UA */ + no_maintenance = <0>; + chg_unknown_bat = <0>; + enable_overshoot = <0>; + auto_trig = <0>; + adc_therm = <0>; + fg_res = <100>; + batt_id = <0>; + interval_charging = <5>; + interval_not_charging = <120>; + temp_hysteresis = <3>; + gnd_lift_resistance = <34>; + }; + +* subnodes for the node 'ab8500_bm_data': + - maxi : maximization parameters + - cap_levels: capacity in percent for the different capacity levels + - bat_type : table of supported battery types + - chg_params: charger parameters + - fg_params : fuel gauge parameters + +example: +ab8500_bm_data: bm_data { + ... + ... + n_btypes = <3>; /* number of battery types */ + ... + maxi = <&ab8500_bm_data_maxim_parameters>; + cap_levels = <&ab8500_bm_data_cap_levels>; + bat_type = <&ab8500_battery_type_0 &ab8500_battery_type_1 &ab8500_battery_type_2>; + chg_params = <&ab8500_bm_data_charger_parameters>; + fg_params = <&ab8500_bm_data_fuel_guage_parameters>; + +}; + +bx500_battery_type: + prepare battery type information from individual battery specification as: + { + name : battery technology + resis_high : battery upper resistance limit + resis_low : battery lower resistance limit + charge_full_design : Maximum battery capacity in mAh + nominal_voltage : Nominal voltage of the battery in mV + termination_vol : max voltage upto which battery can be charged + termination_curr : battery charging termination current in mA + recharge_vol : battery voltage limit that will trigger a new + full charging cycle in the case where maintenance + charging has been disabled + normal_cur_lvl : charger current in normal state in mA + normal_vol_lvl : charger voltage in normal state in mV + maint_a_cur_lvl : charger current in maintenance A state in mA + maint_a_vol_lvl : charger voltage in maintenance A state in mV + maint_a_chg_timer_h : charge time in maintenance A state + maint_b_cur_lvl : charger current in maintenance B state in mA + maint_b_vol_lvl : charger voltage in maintenance B state in mV + maint_b_chg_timer_h : charge time in maintenance B state + low_high_cur_lvl : charger current in temp low/high state in mA + low_high_vol_lvl : charger voltage in temp low/high state in mV' + battery_resistance : battery inner resistance in mOhm. + n_r_t_tbl_elements : number of elements in r_to_t_tbl + r_to_t_tbl : table containing resistance to temp points + n_v_cap_tbl_elements: number of elements in v_to_cap_tbl + v_to_cap_tbl : Voltage to capacity (in %) table + n_batres_tbl_elements: number of elements in the batres_tbl + batres_tbl : battery internal resistance vs temperature table + } + + Note: + Selected battery shall adhere to the specification provided in: + http://www.giga-concept.fr/media/uploads/products/documents/2008/09/9100.pdf + +-example: + + ab8500_battery_type_1: ab8500_battery_therm_on_batctrl { + bat_name = <3>; + resis_high = <53407>; + resis_low = <12500>; + 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>; + battery_resistance = <300>; + + n_temp_tbl_elements = <15>; + + r_to_t_tbl - defines one point in a temp to res curve. To be used + in battery packs that combines the identification resistor + with a NTC resistor. + + r_to_t_tbl = < + 0xfffffffb 53407 /* -5 */ + 0 48594 'battery pack temperature in Celcius' 'NTC resistor net total resistance' + 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>; + + n_v_cap_tbl_elements = <20>; + v_to_cap_tbl = < /* Table for translating voltage to capacity */ + 4171 100 'Voltage in mV' 'Capacity in percent' + 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>; + + n_batres_tbl_elements = <7>; /* ARRAY_SIZE(batres_tbl), */ + batres_tbl = < /* defines one point in a temp vs battery internal resistance curve. */ + 40 120 'battery pack temperature in Celcius' 'battery internal reistance in mOhm' + 30 135 + 20 165 + 10 230 + 00 325 + 0xfffffff6 445 /* -10 */ + 0xffffffec 595>; /* -20 */ + }; diff --git a/Documentation/devicetree/bindings/power_supply/ab8500/ab8500-fg.txt b/Documentation/devicetree/bindings/power_supply/ab8500/ab8500-fg.txt new file mode 100644 index 0000000..5e84852 --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/ab8500/ab8500-fg.txt @@ -0,0 +1,51 @@ +* St-Ericsson AB8500 Power Management Integrated Circuit (PMIC) + fuel guage: + +* Required properties: +- compatible: "stericsson,ab8500-fg" + +* Sub-node: +- ab8500_bm_data: + +example: + ab8500-btemp { + compatible = "stericsson,ab8500-fg"; + ... + ... + bat = <&ab8500_bm_data>; + }; + +Refer: + Documentation/devicetree/bindings/power_supply/ab8500/ab8500-btemp.txt for + information on 'ab8500_bm_data' + +* subnode for the node 'ab8500_bm_data': + - fg_params : fuel gauge parameters + +example: + +ab8500_bm_data: bm_data { + ... + ... + ... + fg_params = <&ab8500_bm_data_fuel_guage_parameters>; +} + + /* Fuel gauge algorithm parameters, in seconds */ + ab8500_bm_data_fuel_guage_parameters: bm_data_fuel_guage_parameters { + recovery_sleep_timer = <10>; /* Time between measurements while recovering */ + recovery_total_time = <100>; /* Total recovery time */ + init_timer = <1>; /* Measurement interval during startup */ + init_discard_time = <5>; /* Time we discard voltage measurement at startup */ + init_total_time = <40>; /* Total init time during startup */ + high_curr_time = <60>; /* Time current has to be high to go to recovery */ + accu_charging = <30>; /* FG accumulation time while charging */ + accu_high_curr = <30>; /* FG accumulation time in high current mode */ + high_curr_threshold = <50>; /* High current threshold, in mA */ + lowbat_threshold = <3100>;/* Low battery threshold, in mV */ + battok_falling_th_sel0 = <2860>;/* Over battery threshold, in mV */ + battok_raising_th_sel1 = <2860>;/* Threshold in mV for battOk signal sel0 Resolution in 50 mV step. */ + user_cap_limit = <15>; /* Threshold in mV for battOk signal sel1 Resolution in 50 mV step. */ + maint_thres = <97>; /* Capacity reported from user must be within this limit to be considered as sane, in percentage points. + }; * This is the threshold where we stop reporting battery full while in maintenance, in per cent + */ diff --git a/arch/arm/boot/dts/db8500.dtsi b/arch/arm/boot/dts/db8500.dtsi index 26f895f..6f3b5df 100644 --- a/arch/arm/boot/dts/db8500.dtsi +++ b/arch/arm/boot/dts/db8500.dtsi @@ -330,6 +330,333 @@ vddadc-supply = <&ab8500_ldo_tvout_reg>; };
+ ab8500-fg { + compatible = "stericsson,ab8500-fg"; + interrupts = <24 0x4 + 8 0x4 + 28 0x4 + 27 0x4 + 26 0x4>; + interrupt-names = "NCONV_ACCU", + "BATT_OVV", + "LOW_BAT_F", + "CC_INT_CALIB", + "CCEOC"; + supplied_to = "ab8500_chargalg", "ab8500_usb"; + num_supplicants = <2>; + bat = <&ab8500_bm_data>; + }; + + ab8500-btemp { + compatible = "stericsson,ab8500-btemp"; + interrupts = <20 0x04 + 80 0x04 + 81 0x04 + 82 0x04 + 83 0x04>; + interrupt-names = "BAT_CTRL_INDB", + "BTEMP_LOW", + "BTEMP_HIGH", + "BTEMP_LOW_MEDIUM", + "BTEMP_MEDIUM_HIGH"; + supplied_to = "ab8500_chargalg", "ab8500_fg"; + num_supplicants = <2>; + bat = <&ab8500_bm_data>; + }; + + ab8500_battery_type_0: ab8500_battery_unknown { + bat_name = <0>; + resis_high = <0>; + resis_low = <0>; + 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>; + battery_resistance = <300>; + + n_temp_tbl_elements = <15>; + r_to_t_tbl = < + 0xfffffffb 214834 /* -5 */ + 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>; + + n_v_cap_tbl_elements = <24>; + v_to_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>; + + n_batres_tbl_elements = <7>; + batres_tbl = < + 40 120 + 30 135 + 20 165 + 10 230 + 00 325 + 0xfffffff6 445 /* -10 */ + 0xffffffec 595>; /* -20 */ + }; + + ab8500_battery_type_1: ab8500_battery_therm_on_batctrl { + bat_name = <3>; + resis_high = <53407>; + resis_low = <12500>; + 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>; + battery_resistance = <300>; + + n_temp_tbl_elements = <15>; + r_to_t_tbl = < + 0xfffffffb 53407 /* -5 */ + 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>; + + n_v_cap_tbl_elements = <20>; + v_to_cap_tbl = < + 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>; + + n_batres_tbl_elements = <7>; /* ARRAY_SIZE(batres_tbl), */ + batres_tbl = < + 40 120 + 30 135 + 20 165 + 10 230 + 00 325 + 0xfffffff6 445 /* -10 */ + 0xffffffec 595>; /* -20 */ + }; + + ab8500_battery_type_2: ab8500_battery_therm_on_batctrl_1 { + bat_name = <3>; + resis_high = <165418>; + resis_low = <82869>; + 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>; + battery_resistance = <300>; + + n_temp_tbl_elements = <15>; + r_to_t_tbl = < + 0xfffffffb 165418 + 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>; + + n_v_cap_tbl_elements = <20>; + v_to_cap_tbl = < + 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>; + + n_batres_tbl_elements = <7>; /* ARRAY_SIZE(batres_tbl), */ + batres_tbl = < + 40 120 + 30 135 + 20 165 + 10 230 + 00 325 + 0xfffffff6 445 /* -10 */ + 0xffffffec 595>; /* -20 */ + }; + + ab8500_bm_data_cap_levels: bm_data_cap_levels { + critical = <2>; + low = <10>; + normal = <70>; + high = <95>; + full = <100>; + }; + + ab8500_bm_data_maxim_parameters: bm_data_maxim_parameters { + ena_maxi = <1>; + chg_curr = <910>; + wait_cycles = <10>; + charger_curr_step = <100>; + }; + + ab8500_bm_data_charger_parameters: bm_data_charger_parameters { + usb_volt_max = <5500>; + usb_curr_max = <1500>; + ac_volt_max = <7500>; + ac_curr_max = <1500>; + }; + + ab8500_bm_data_fuel_guage_parameters: bm_data_fuel_guage_parameters { + 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>; + }; + + ab8500_bm_data: bm_data { + temp_under = <3>; + temp_low = <8>; + temp_high = <43>; + temp_over = <48>; + temp_now = <0>; + temp_interval_chg = <20>; + temp_interval_nochg = <120>; + main_safety_tmr_h = <4>; + usb_safety_tmr_h = <4>; + bkup_bat_v = <1>; /* BUP_VCH_SEL_2P6V */ + bkup_bat_i = <4>; /* BUP_ICH_SEL_150UA */ + no_maintenance = <0>; /* CONFIG_AB8500_9100_LI_ION_BATTERY not considered */ + chg_unknown_bat = <0>; /* CONFIG_AB8500_9100_LI_ION_BATTERY not considered */ + enable_overshoot = <0>; + auto_trig = <0>; + adc_therm = <0>; + fg_res = <100>; + n_btypes = <3>; + batt_id = <0>; + interval_charging = <5>; + interval_not_charging = <120>; + temp_hysteresis = <3>; + gnd_lift_resistance = <34>; + maxi = <&ab8500_bm_data_maxim_parameters>; + cap_levels = <&ab8500_bm_data_cap_levels>; + bat_type = <&ab8500_battery_type_0 &ab8500_battery_type_1 &ab8500_battery_type_2>; + chg_params = <&ab8500_bm_data_charger_parameters>; + fg_params = <&ab8500_bm_data_fuel_guage_parameters>; + }; + ab8500-usb { compatible = "stericsson,ab8500-usb"; interrupts = < 90 0x4 diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 6d613e8..837488b 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -1419,15 +1419,15 @@ static int __devinit ab8500_probe(struct platform_device *pdev) ab8500->irq_base); if (ret) goto out_freeirq; - }
- if (!no_bm) { - /* Add battery management devices */ - ret = mfd_add_devices(ab8500->dev, 0, ab8500_bm_devs, - ARRAY_SIZE(ab8500_bm_devs), NULL, - ab8500->irq_base); - if (ret) - dev_err(ab8500->dev, "error adding bm devices\n"); + if (!no_bm) { + /* Add battery management devices */ + ret = mfd_add_devices(ab8500->dev, 0, ab8500_bm_devs, + ARRAY_SIZE(ab8500_bm_devs), NULL, + ab8500->irq_base); + if (ret) + dev_err(ab8500->dev, "error adding bm devices\n"); + } }
if (is_ab9540(ab8500)) diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c index bba3cca..04da929 100644 --- a/drivers/power/ab8500_btemp.c +++ b/drivers/power/ab8500_btemp.c @@ -20,6 +20,7 @@ #include <linux/power_supply.h> #include <linux/completion.h> #include <linux/workqueue.h> +#include <linux/of.h> #include <linux/mfd/abx500/ab8500.h> #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500-bm.h> @@ -480,7 +481,7 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di) vntc = ab8500_gpadc_convert(di->gpadc, BTEMP_BALL); if (vntc < 0) { dev_err(di->dev, - "%s gpadc conversion failed," + "%s gpadc conversion failed, " " using previous value\n", __func__); return prev; } @@ -960,15 +961,249 @@ static int __devexit ab8500_btemp_remove(struct platform_device *pdev) return 0; }
+int populate_abx8500_bm_data(struct device *dev, + struct abx500_bm_data *bm_data, + struct device_node *np) +{ + int i, plen, itbl, ret = 0; + phandle *temp_phandle; + struct abx500_battery_type *temp_bat_type; + struct property *pbat_type; + struct device_node *np_bat; + const __be32 *p; + u32 u; + +#define get_bm_data_property(node_p, prop_name)\ + be32_to_cpup(of_get_property(node_p, prop_name, NULL)); + + temp_phandle = (phandle *)of_get_property(np, "bat", NULL); + BUG_ON(!temp_phandle); + np_bat = of_find_node_by_phandle(be32_to_cpup(temp_phandle)); + BUG_ON(!np_bat); + + bm_data->temp_under = get_bm_data_property(np_bat, "temp_under"); + bm_data->temp_low = get_bm_data_property(np_bat, "temp_low"); + bm_data->temp_high = get_bm_data_property(np_bat, "temp_high"); + bm_data->temp_over = get_bm_data_property(np_bat, "temp_over"); + bm_data->temp_now = get_bm_data_property(np_bat, "temp_now"); + bm_data->temp_interval_chg = + get_bm_data_property(np_bat, "temp_interval_chg"); + bm_data->temp_interval_nochg = + get_bm_data_property(np_bat, "temp_interval_nochg"); + bm_data->main_safety_tmr_h = + get_bm_data_property(np_bat, "main_safety_tmr_h"); + bm_data->usb_safety_tmr_h = + get_bm_data_property(np_bat, "usb_safety_tmr_h"); + bm_data->bkup_bat_v = + get_bm_data_property(np_bat, "bkup_bat_v"); + bm_data->bkup_bat_i = + get_bm_data_property(np_bat, "bkup_bat_i"); + bm_data->no_maintenance = + get_bm_data_property(np_bat, "no_maintenance"); + bm_data->chg_unknown_bat = + get_bm_data_property(np_bat, "chg_unknown_bat"); + bm_data->enable_overshoot = + get_bm_data_property(np_bat, "enable_overshoot"); + bm_data->auto_trig = + get_bm_data_property(np_bat, "auto_trig"); + bm_data->adc_therm = + get_bm_data_property(np_bat, "adc_therm"); + bm_data->fg_res = + get_bm_data_property(np_bat, "fg_res"); + bm_data->n_btypes = + get_bm_data_property(np_bat, "n_btypes"); + bm_data->batt_id = + get_bm_data_property(np_bat, "batt_id"); + bm_data->interval_charging = + get_bm_data_property(np_bat, "interval_charging"); + bm_data->interval_not_charging = + get_bm_data_property(np_bat, "interval_not_charging"); + bm_data->temp_hysteresis = + get_bm_data_property(np_bat, "temp_hysteresis"); + bm_data->gnd_lift_resistance = + get_bm_data_property(np_bat, "gnd_lift_resistance"); + + temp_phandle = (phandle *)of_get_property(np_bat, "bat_type", &plen); + if (temp_phandle == NULL) { + dev_warn(dev, "battery type not found\n"); + return -EINVAL; + } + + plen = plen/sizeof(u32); + + if (bm_data->n_btypes != plen) { + dev_crit(dev, "Invalid number of battery types\n"); + return -EINVAL; + } + + bm_data->bat_type = + kzalloc(plen * sizeof(struct abx500_battery_type), GFP_KERNEL); + if (bm_data->bat_type == NULL) { + dev_crit(dev, "no mem for bm_data->bat_type\n"); + return -ENOMEM; + } + + of_node_put(np_bat); + + /* + * traverse 'plen' times in 'allnext' for battery types and + * fillup bm_data->bat_type + */ + i = 0; + for_each_node_by_phandle(np_bat, plen, temp_phandle) + { + temp_bat_type = (struct abx500_battery_type *) + (bm_data->bat_type + i++); + temp_bat_type->name = + get_bm_data_property(np_bat, "bat_name"); + temp_bat_type->resis_high = + get_bm_data_property(np_bat, "resis_high"); + temp_bat_type->resis_low = + get_bm_data_property(np_bat, "resis_low"); + temp_bat_type->charge_full_design = + get_bm_data_property(np_bat, "charge_full_design"); + temp_bat_type->nominal_voltage = + get_bm_data_property(np_bat, "nominal_voltage"); + temp_bat_type->termination_vol = + get_bm_data_property(np_bat, "termination_vol"); + temp_bat_type->termination_curr = + get_bm_data_property(np_bat, "termination_curr"); + temp_bat_type->recharge_vol = + get_bm_data_property(np_bat, "recharge_vol"); + temp_bat_type->normal_cur_lvl = + get_bm_data_property(np_bat, "normal_cur_lvl"); + temp_bat_type->normal_vol_lvl = + get_bm_data_property(np_bat, "normal_vol_lvl"); + temp_bat_type->maint_a_cur_lvl = + get_bm_data_property(np_bat, "maint_a_cur_lvl"); + temp_bat_type->maint_a_vol_lvl = + get_bm_data_property(np_bat, "maint_a_vol_lvl"); + temp_bat_type->maint_a_chg_timer_h = + get_bm_data_property(np_bat, "maint_a_chg_timer_h"); + temp_bat_type->maint_b_cur_lvl = + get_bm_data_property(np_bat, "maint_b_cur_lvl"); + temp_bat_type->maint_b_vol_lvl = + get_bm_data_property(np_bat, "maint_b_vol_lvl"); + temp_bat_type->maint_b_chg_timer_h = + get_bm_data_property(np_bat, "maint_b_chg_timer_h"); + temp_bat_type->low_high_cur_lvl = + get_bm_data_property(np_bat, "low_high_cur_lvl"); + temp_bat_type->low_high_vol_lvl = + get_bm_data_property(np_bat, "low_high_vol_lvl"); + temp_bat_type->battery_resistance = + get_bm_data_property(np_bat, "battery_resistance"); + + temp_bat_type->n_temp_tbl_elements = + get_bm_data_property(np_bat, "n_temp_tbl_elements"); + temp_bat_type->r_to_t_tbl = (struct abx500_res_to_temp *) + kzalloc(sizeof(struct abx500_res_to_temp) * + temp_bat_type->n_temp_tbl_elements, GFP_KERNEL); + if (temp_bat_type->r_to_t_tbl == NULL) { + dev_crit(dev, "no mem for r_to_t_tbl\n"); + kfree(bm_data->bat_type); + return -ENOMEM; + } + itbl = 0; + of_property_for_each_u32(np_bat, "r_to_t_tbl", pbat_type, p, u) + *((int *)(temp_bat_type->r_to_t_tbl) + itbl++) = (int)u; + + temp_bat_type->n_v_cap_tbl_elements = + get_bm_data_property(np_bat, "n_v_cap_tbl_elements"); + temp_bat_type->v_to_cap_tbl = (struct abx500_v_to_cap *) + kzalloc(sizeof(struct abx500_v_to_cap) * + temp_bat_type->n_v_cap_tbl_elements, GFP_KERNEL); + if (temp_bat_type->v_to_cap_tbl == NULL) { + ret = -ENOMEM; + dev_crit(dev, "no mem for v_to_cap_tbl\n"); + goto out_free_mem1; + } + itbl = 0; + of_property_for_each_u32(np_bat, + "v_to_cap_tbl", pbat_type, p, u) + *((int *)(temp_bat_type->v_to_cap_tbl) + itbl++) = + (int)u; + + temp_bat_type->n_batres_tbl_elements = + get_bm_data_property(np_bat, "n_batres_tbl_elements"); + temp_bat_type->batres_tbl = (struct batres_vs_temp *) + kzalloc(sizeof(struct batres_vs_temp) * + temp_bat_type->n_temp_tbl_elements, GFP_KERNEL); + if (temp_bat_type->batres_tbl == NULL) { + ret = -ENOMEM; + dev_crit(dev, "no mem for batres_tbl\n"); + goto out_free_mem2; + } + itbl = 0; + of_property_for_each_u32(np_bat, "batres_tbl", pbat_type, p, u) + *((int *)(temp_bat_type->batres_tbl) + itbl++) = (int)u; + } + + of_node_put(np_bat); + + bm_data->chg_params = (struct abx500_bm_charger_parameters *) + kzalloc(sizeof(struct abx500_bm_charger_parameters), + GFP_KERNEL); + if (bm_data->chg_params == NULL) { + dev_crit(dev, "Failed to alloc memory for chg_params\n"); + ret = -ENOMEM; + goto out_free_mem3; + } + itbl = 0; + of_property_for_each_u32(np_bat, "chg_params", pbat_type, p, u) + *((int *)(bm_data->chg_params) + itbl++) = (int)u; + + bm_data->fg_params = (struct abx500_fg_parameters *) + kzalloc(sizeof(struct abx500_fg_parameters), GFP_KERNEL); + if (bm_data->fg_params == NULL) { + dev_crit(dev, "Failed to alloc memory for fg_params\n"); + ret = -ENOMEM; + goto out_free_mem4; + } + itbl = 0; + of_property_for_each_u32(np_bat, "fg_params", pbat_type, p, u) + *((int *)(bm_data->fg_params) + itbl++) = (int)u; + + return ret; + +out_free_mem1: + kfree(bm_data->bat_type); + kfree(temp_bat_type->r_to_t_tbl); + return ret; + +out_free_mem2: + kfree(bm_data->bat_type); + kfree(temp_bat_type->r_to_t_tbl); + kfree(temp_bat_type->v_to_cap_tbl); + return ret; + +out_free_mem3: + kfree(bm_data->bat_type); + kfree(temp_bat_type->r_to_t_tbl); + kfree(temp_bat_type->v_to_cap_tbl); + kfree(temp_bat_type->batres_tbl); + return ret; + +out_free_mem4: + kfree(bm_data->bat_type); + kfree(temp_bat_type->r_to_t_tbl); + kfree(temp_bat_type->v_to_cap_tbl); + kfree(temp_bat_type->batres_tbl); + kfree(bm_data->chg_params); + return ret; +} +EXPORT_SYMBOL_GPL(populate_abx8500_bm_data); + static int __devinit ab8500_btemp_probe(struct platform_device *pdev) { int irq, i, ret = 0; u8 val; - struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data; + struct device_node *np = pdev->dev.of_node; struct ab8500_btemp *di; + const unsigned int *btemp_p_val; + const char *pvalue = NULL;
- if (!plat_data) { - dev_err(&pdev->dev, "No platform data\n"); + if (!np) { + dev_err(&pdev->dev, "No platform data or DT found\n"); return -EINVAL; }
@@ -982,20 +1217,46 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev) 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; + btemp_p_val = of_get_property(np, "num_supplicants", NULL); + BUG_ON(!btemp_p_val); + + di->pdata = + kzalloc(sizeof(struct abx500_btemp_platform_data), GFP_KERNEL); + if (di->pdata == NULL) { + kfree(di); + return -ENOMEM; }
- /* get battery specific platform data */ - di->bat = plat_data->battery; + di->pdata->num_supplicants = be32_to_cpup(btemp_p_val); + di->pdata->supplied_to = + kzalloc(di->pdata->num_supplicants * + sizeof(const char *), GFP_KERNEL); + if (di->pdata->supplied_to == NULL) { + kfree(di); + kfree(di->pdata); + return -ENOMEM; + } + + for (val = 0; val < di->pdata->num_supplicants; ++val) + if (of_property_read_string_index + (np, "supplied_to", val, &pvalue) == 0) + *(di->pdata->supplied_to + val) = (char *)pvalue; + else { + dev_warn(di->dev, "insufficient number of supplied_to data found\n"); + goto free_device_info; + } + dev_dbg(di->dev, "getting DT battery information\n"); + di->bat = kzalloc(sizeof(struct abx500_bm_data), GFP_KERNEL); if (!di->bat) { - dev_err(di->dev, "no battery platform data supplied\n"); - ret = -EINVAL; + kfree(di); + kfree(di->pdata); + return -ENOMEM; + } + if (populate_abx8500_bm_data(di->dev, di->bat, np) < 0) { + dev_warn(di->dev, "Failed to bind DT\n"); goto free_device_info; } + dev_dbg(di->dev, "getting DT battery information...done\n");
/* BTEMP supply */ di->btemp_psy.name = "ab8500_btemp"; @@ -1008,7 +1269,6 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev) di->btemp_psy.external_power_changed = ab8500_btemp_external_power_changed;
- /* Create a work queue for the btemp */ di->btemp_wq = create_singlethread_workqueue("ab8500_btemp_wq"); @@ -1016,7 +1276,6 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev) dev_err(di->dev, "failed to create work queue\n"); goto free_device_info; } - /* Init work for measuring temperature periodically */ INIT_DELAYED_WORK_DEFERRABLE(&di->btemp_periodic_work, ab8500_btemp_periodic_work); @@ -1090,14 +1349,23 @@ free_irq: irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name); free_irq(irq, di); } + free_btemp_wq: destroy_workqueue(di->btemp_wq); + free_device_info: kfree(di); + kfree(di->pdata); + kfree(di->bat);
return ret; }
+static const struct of_device_id ab8500_btemp_match[] = { + {.compatible = "stericsson,ab8500-btemp",}, + {}, +}; + static struct platform_driver ab8500_btemp_driver = { .probe = ab8500_btemp_probe, .remove = __devexit_p(ab8500_btemp_remove), @@ -1106,6 +1374,7 @@ static struct platform_driver ab8500_btemp_driver = { .driver = { .name = "ab8500-btemp", .owner = THIS_MODULE, + .of_match_table = ab8500_btemp_match, }, };
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index bf02225..1d0e32a 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -31,6 +31,7 @@ #include <linux/mfd/abx500.h> #include <linux/time.h> #include <linux/completion.h> +#include <linux/of.h>
#define MILLI_TO_MICRO 1000 #define FG_LSB_IN_MA 1627 @@ -2446,11 +2447,12 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev) { int i, irq; int ret = 0; - struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data; + const char *fg_p_val; + struct device_node *np = pdev->dev.of_node; struct ab8500_fg *di;
- if (!plat_data) { - dev_err(&pdev->dev, "No platform data\n"); + if (!np) { + dev_err(&pdev->dev, "No DT node for platform data available\n"); return -EINVAL; }
@@ -2464,20 +2466,42 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev) di->dev = &pdev->dev; di->parent = dev_get_drvdata(pdev->dev.parent); 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; + di->pdata = + kzalloc(sizeof(struct abx500_btemp_platform_data), GFP_KERNEL); + if (di->pdata == NULL) { + kfree(di); + return -ENOMEM; + } + di->pdata->num_supplicants = + be32_to_cpup(of_get_property(np, "num_supplicants", NULL)); + di->pdata->supplied_to = + kzalloc(di->pdata->num_supplicants * + sizeof(const char *), GFP_KERNEL); + if (di->pdata->supplied_to == NULL) { + kfree(di); + kfree(di->pdata); + return -ENOMEM; }
+ for (i = 0; i < di->pdata->num_supplicants; ++i) + if (of_property_read_string_index + (np, "supplied_to", i, &fg_p_val) == 0) + *(di->pdata->supplied_to + i) = (char *)fg_p_val; + else { + dev_warn(di->dev, "insufficient number of supplied_to data found\n"); + goto free_device_info; + } + /* get battery specific platform data */ - di->bat = plat_data->battery; + di->bat = kzalloc(sizeof(struct abx500_bm_data), GFP_KERNEL); if (!di->bat) { - dev_err(di->dev, "no battery platform data supplied\n"); - ret = -EINVAL; + kfree(di); + kfree(di->pdata); + return -ENOMEM; + } + ret = populate_abx8500_bm_data(di->dev, di->bat, np); + if (ret < 0) { + dev_warn(di->dev, "Failed to bind DT\n"); goto free_device_info; }
@@ -2607,10 +2631,15 @@ free_inst_curr_wq: destroy_workqueue(di->fg_wq); free_device_info: kfree(di); + kfree(di->pdata);
return ret; }
+static const struct of_device_id ab8500_fg_match[] = { + {.compatible = "stericsson,ab8500-fg",}, + {}, +}; static struct platform_driver ab8500_fg_driver = { .probe = ab8500_fg_probe, .remove = __devexit_p(ab8500_fg_remove), @@ -2619,6 +2648,7 @@ static struct platform_driver ab8500_fg_driver = { .driver = { .name = "ab8500-fg", .owner = THIS_MODULE, + .of_match_table = ab8500_fg_match, }, };
@@ -2632,7 +2662,7 @@ static void __exit ab8500_fg_exit(void) platform_driver_unregister(&ab8500_fg_driver); }
-subsys_initcall_sync(ab8500_fg_init); +subsys_initcall(ab8500_fg_init); module_exit(ab8500_fg_exit);
MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h index 1318ca6..9dbc4d1 100644 --- a/include/linux/mfd/abx500.h +++ b/include/linux/mfd/abx500.h @@ -113,13 +113,13 @@ struct ab3100 { * struct ab3100_platform_data * Data supplied to initialize board connections to the AB3100 * @reg_constraints: regulator constraints for target board - * the order of these constraints are: LDO A, C, D, E, - * F, G, H, K, EXT and BUCK. + * the order of these constraints are: LDO A, C, D, E, + * F, G, H, K, EXT and BUCK. * @reg_initvals: initial values for the regulator registers - * plus two sleep settings for LDO E and the BUCK converter. - * exactly AB3100_NUM_REGULATORS+2 values must be sent in. - * Order: LDO A, C, E, E sleep, F, G, H, K, EXT, BUCK, - * BUCK sleep, LDO D. (LDO D need to be initialized last.) + * plus two sleep settings for LDO E and the BUCK converter. + * exactly AB3100_NUM_REGULATORS+2 values must be sent in. + * Order: LDO A, C, E, E sleep, F, G, H, K, EXT, BUCK, + * BUCK sleep, LDO D. (LDO D need to be initialized last.) * @external_voltage: voltage level of the external regulator. */ struct ab3100_platform_data { @@ -131,7 +131,7 @@ struct ab3100_platform_data { int ab3100_event_register(struct ab3100 *ab3100, struct notifier_block *nb); int ab3100_event_unregister(struct ab3100 *ab3100, - struct notifier_block *nb); + struct notifier_block *nb);
/** * struct abx500_init_setting @@ -387,6 +387,9 @@ struct abx500_bm_data { const struct abx500_fg_parameters *fg_params; };
+int populate_abx8500_bm_data(struct device *, struct abx500_bm_data *, + struct device_node *); + struct abx500_chargalg_platform_data { char **supplied_to; size_t num_supplicants; diff --git a/include/linux/of.h b/include/linux/of.h index 2ec1083..61b4ac8 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -183,6 +183,39 @@ extern struct device_node *of_find_matching_node(struct device_node *from, #define for_each_matching_node(dn, matches) \ for (dn = of_find_matching_node(NULL, matches); dn; \ dn = of_find_matching_node(dn, matches)) + +/* + * syntax: for_each_node_by_phandle(np, len, phandle); + * @np : pointer to node to start in the list + * @len: number of nodes expected + * @phandle: current phandle which is obtained through of_get_property(...) + * + * e.g: + * node_a: <> { + * ... + * }; + * node_b: <> { + * ... + * }; + * node_c: <> { + * ... + * }; + * + * node_x: <> { + * p1 = <v1>; + * p2 = <v2>; + * n_nodes = <3> + * p3 = <&node_a &node_b &node_c ....>; + * }; + * + * Note: + * - invoke of_node_put(...) as it uses of_find_node_by_phandle(...) + */ +#define for_each_node_by_phandle(np, len, phandle) \ + for (np = of_find_node_by_phandle(be32_to_cpup(phandle));\ + (len--);\ + np = np->allnext) + extern struct device_node *of_find_node_by_path(const char *path); extern struct device_node *of_find_node_by_phandle(phandle handle); extern struct device_node *of_get_parent(const struct device_node *node);