This patch adds device tree support for battery temperature monitor driver
Signed-off-by: Rajanikanth H.V rajanikanth.hv@stericsson.com --- .../bindings/power_supply/ab8500/btemp.txt | 52 ++++++ arch/arm/boot/dts/dbx5x0.dtsi | 8 + drivers/mfd/ab8500-core.c | 1 + drivers/power/Kconfig | 6 - drivers/power/ab8500_bmdata.h | 4 +- drivers/power/ab8500_btemp.c | 181 ++++++++++++++++---- 6 files changed, 213 insertions(+), 39 deletions(-) create mode 100644 Documentation/devicetree/bindings/power_supply/ab8500/btemp.txt
diff --git a/Documentation/devicetree/bindings/power_supply/ab8500/btemp.txt b/Documentation/devicetree/bindings/power_supply/ab8500/btemp.txt new file mode 100644 index 0000000..b214efc --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/ab8500/btemp.txt @@ -0,0 +1,52 @@ +=== AB8500 Battery Temperature Monitor Driver === + +The properties below describes the node for battery +temperature monitor driver. + +Required Properties: +- compatible = "stericsson,ab8500-btemp" + +supplied-to: + This is a logical binding w.r.t power supply event change + across energy-management-module drivers where in the + runtime battery properties are shared along with uevent + notification. + ref: di->btemp_psy.external_power_changed = + ab8500_btemp_external_power_changed; + ab8500_btemp.c + + Need for this property: + btemp, fg and charger updates power-supply properties + based on the events listed above. + Event handler invokes power supply change notifier + which in-turn invokes registered power supply class call-back + based on the 'supplied_to' string. + ref: + power_supply_changed_work(..) ./drivers/power/power_supply_core.c + + example: + ab8500-btemp { + /* Other enery management module */ + supplied-to = "ab8500_chargalg", "ab8500_fg"; + num_supplicants = <2>; + }; + +thermister-interface: + 'btemp' and 'batctrl' are the pins interfaced for battery temperature + measurement, btemp is used when NTC(negative temperature coefficient) + resister is interfaced external to battery and batctrl is used when + NTC resister is internal to battery. + + +li-ion-9100-battery: + use this to add support for the 9100 Li-ION battery, + this adjust the bkup battery charger parameters + Note: this property is used for tablet version of snowball board. + + example: + ab8500-btemp { + thermister-internal-to-battery = <1>; + li_ion_9100_battery = <0>; + }; +Note: +interrupts are defined and registered in the driver diff --git a/arch/arm/boot/dts/dbx5x0.dtsi b/arch/arm/boot/dts/dbx5x0.dtsi index d69c087..643e7fd 100644 --- a/arch/arm/boot/dts/dbx5x0.dtsi +++ b/arch/arm/boot/dts/dbx5x0.dtsi @@ -360,6 +360,14 @@ li_ion_9100 = <0>; };
+ ab8500-btemp { + compatible = "stericsson,ab8500-btemp"; + supplied_to = "ab8500_chargalg", "ab8500_fg"; + num_supplicants = <2>; + thermister_on_batctrl = <1>; + li_ion_9100 = <0>; + }; + ab8500-usb { compatible = "stericsson,ab8500-usb"; interrupts = < 90 0x4 diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index c413cfa..7ffba9b 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -1047,6 +1047,7 @@ static struct mfd_cell __devinitdata ab8500_bm_devs[] = { }, { .name = "ab8500-btemp", + .of_compatible = "stericsson,ab8500-btemp", .num_resources = ARRAY_SIZE(ab8500_btemp_resources), .resources = ab8500_btemp_resources, }, diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index c1892f3..1434871 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -304,12 +304,6 @@ config AB8500_BM help 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. endif # POWER_SUPPLY
source "drivers/power/avs/Kconfig" diff --git a/drivers/power/ab8500_bmdata.h b/drivers/power/ab8500_bmdata.h index 748334a..0bd0a45 100644 --- a/drivers/power/ab8500_bmdata.h +++ b/drivers/power/ab8500_bmdata.h @@ -22,7 +22,7 @@ static struct abx500_res_to_temp temp_tbl_A_thermister[] = { {65, 12500}, }; static struct abx500_res_to_temp temp_tbl_B_thermister[] = { - {-5, 165418}, + {-5, 200000}, { 0, 159024}, { 5, 151921}, {10, 144300}, @@ -229,7 +229,7 @@ static struct abx500_battery_type bat_type_thermister[] = { }, { .name = POWER_SUPPLY_TECHNOLOGY_LIPO, - .resis_high = 165418, + .resis_high = 200000, .resis_low = 82869, .battery_resistance = 300, .charge_full_design = 900, diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c index bba3cca..ac08f1a 100644 --- a/drivers/power/ab8500_btemp.c +++ b/drivers/power/ab8500_btemp.c @@ -16,6 +16,7 @@ #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/slab.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/power_supply.h> #include <linux/completion.h> @@ -25,6 +26,7 @@ #include <linux/mfd/abx500/ab8500-bm.h> #include <linux/mfd/abx500/ab8500-gpadc.h> #include <linux/jiffies.h> +#include "ab8500_bmdata.h"
#define VTVOUT_V 1800
@@ -960,42 +962,153 @@ static int __devexit ab8500_btemp_remove(struct platform_device *pdev) return 0; }
-static int __devinit ab8500_btemp_probe(struct platform_device *pdev) +static int __devinit +btemp_of_probe(struct device *dev, + struct device_node *np, + struct abx500_bm_plat_data *bm_pdata) { - int irq, i, ret = 0; - 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; + u8 val; + u32 pval; + int i; + int ext_thermister, lion_battery, ret = 0; + const char *bm_dev_name; + struct abx500_btemp_platform_data *btemp = bm_pdata->btemp; + struct abx500_bm_data *bat; + struct abx500_battery_type *btype; + + ret = of_property_read_u32(np, "num_supplicants", &pval); + if (ret) { + dev_err(dev, "missing property num_supplicants\n"); + ret = -EINVAL; + goto inval_pval; } - - di = kzalloc(sizeof(*di), GFP_KERNEL); - if (!di) - return -ENOMEM; - - /* get parent data */ - di->dev = &pdev->dev; - di->parent = dev_get_drvdata(pdev->dev.parent); - 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"); + btemp->num_supplicants = pval; + btemp->supplied_to = + devm_kzalloc(dev, btemp->num_supplicants * + sizeof(const char *), GFP_KERNEL); + if (btemp->supplied_to == NULL) { + dev_err(dev, "%s no mem for supplied_to\n", __func__); + ret = -ENOMEM; + goto inval_pval; + } + for (val = 0; val < btemp->num_supplicants; ++val) + if (of_property_read_string_index + (np, "supplied_to", val, &bm_dev_name) == 0) + *(btemp->supplied_to + val) = (char *)bm_dev_name; + else { + dev_err(dev, "insufficient number of supplied_to data found\n"); + ret = -EINVAL; + goto free_dev_mem; + } + ret = of_property_read_u32(np, "thermister_on_batctrl", &pval); + if (ret) { + dev_err(dev, "missing property thermister_on_batctrl\n"); ret = -EINVAL; - goto free_device_info; + goto free_dev_mem; + } + bm_pdata->battery = &ab8500_bm_data; + bat = bm_pdata->battery; + ext_thermister = 0; + if (pval == 0) { + bat->n_btypes = 4; + bat->bat_type = bat_type_ext_thermister; + bat->adc_therm = ABx500_ADC_THERM_BATTEMP; + ext_thermister = 1; + } + ret = of_property_read_u32(np, "li_ion_9100", &pval); + if (ret) { + dev_err(dev, "missing property li_ion_9100\n"); + ret = -EINVAL; + goto free_dev_mem; + } + lion_battery = 0; + if (pval == 1) { + 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; + lion_battery = 1; + } + /* select the battery resolution table */ + for (i = 0; i < bat->n_btypes; ++i) { + btype = (bat->bat_type + i); + if (ext_thermister) { + btype->batres_tbl = + temp_to_batres_tbl_ext_thermister; + } else if (lion_battery) { + btype->batres_tbl = + temp_to_batres_tbl_9100; + } else { + btype->batres_tbl = + temp_to_batres_tbl_thermister; + } + } + return ret; +free_dev_mem: + devm_kfree(dev, btemp->supplied_to); +inval_pval: + return ret; +} + +static int __devinit ab8500_btemp_probe(struct platform_device *pdev) +{ + u8 val; + int i; + int irq, ret = 0; + struct abx500_bm_plat_data *pdata = pdev->dev.platform_data; + struct device_node *np = pdev->dev.of_node; + struct ab8500_btemp *di; + + di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL); + if (!di) { + dev_err(&pdev->dev, "%s no mem for ab8500_btemp\n", __func__); + ret = -ENOMEM; }
- /* get battery specific platform data */ - di->bat = plat_data->battery; - if (!di->bat) { - dev_err(di->dev, "no battery platform data supplied\n"); + if (np) { + if (!pdata) { + pdata = + devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(&pdev->dev, + "%s no mem for pdata\n", __func__); + ret = -ENOMEM; + goto err_no_mem; + } + pdata->btemp = devm_kzalloc(&pdev->dev, + sizeof(*pdata->btemp), GFP_KERNEL); + if (!pdata->btemp) { + devm_kfree(&pdev->dev, pdata); + dev_err(&pdev->dev, + "%s no mem for pdata->btemp\n", + __func__); + ret = -ENOMEM; + goto free_device_info; + } + } + /* get battery specific platform data */ + ret = btemp_of_probe(&pdev->dev, np, pdata); + if (ret) { + devm_kfree(&pdev->dev, pdata->btemp); + devm_kfree(&pdev->dev, pdata); + goto free_device_info; + } + } + if (!pdata) { + dev_err(&pdev->dev, + "%s no btemp platform data found\n", __func__); ret = -EINVAL; goto free_device_info; } + di->pdata = pdata->btemp; + di->bat = pdata->battery; + /* get parent data */ + di->dev = &pdev->dev; + di->parent = dev_get_drvdata(pdev->dev.parent); + di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
/* BTEMP supply */ di->btemp_psy.name = "ab8500_btemp"; @@ -1008,7 +1121,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"); @@ -1065,7 +1177,7 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev) IRQF_SHARED | IRQF_NO_SUSPEND, ab8500_btemp_irq[i].name, di);
- if (ret) { + if (ret < 0) { dev_err(di->dev, "failed to request %s IRQ %d: %d\n" , ab8500_btemp_irq[i].name, irq, ret); goto free_irq; @@ -1093,11 +1205,17 @@ free_irq: free_btemp_wq: destroy_workqueue(di->btemp_wq); free_device_info: - kfree(di); + devm_kfree(&pdev->dev, di); +err_no_mem:
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 +1224,7 @@ static struct platform_driver ab8500_btemp_driver = { .driver = { .name = "ab8500-btemp", .owner = THIS_MODULE, + .of_match_table = ab8500_btemp_match, }, };