Commit 2b0e7ac0 authored by Linus Walleij's avatar Linus Walleij Committed by Sebastian Reichel
Browse files

power: supply: ab8500: Integrate thermal zone



Instead of providing our own homebrewn thermal measurement
code for an NTC and passing tables, we put the NTC thermistor
into the device tree, create a passive thermal zone, and poll
this thermal zone for the temperature.

Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
Signed-off-by: default avatarSebastian Reichel <sebastian.reichel@collabora.com>
parent d662a7df
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -728,6 +728,8 @@ config BATTERY_GAUGE_LTC2941
config AB8500_BM
	bool "AB8500 Battery Management Driver"
	depends on AB8500_CORE && AB8500_GPADC && (IIO = y) && OF
	select THERMAL
	select THERMAL_OF
	help
	  Say Y to include support for AB8500 battery management.

+0 −33
Original line number Diff line number Diff line
@@ -260,18 +260,6 @@ enum bup_vch_sel {
#define BUS_PP_PRECHG_CURRENT_MASK		0x0E
#define BUS_POWER_PATH_PRECHG_ENA		0x01

/**
 * struct ab8500_res_to_temp - defines one point in a temp to res curve. To
 * be used in battery packs that combines the identification resistor with a
 * NTC resistor.
 * @temp:			battery pack temperature in Celsius
 * @resist:			NTC resistor net total resistance
 */
struct ab8500_res_to_temp {
	int temp;
	int resist;
};

/* Forward declaration */
struct ab8500_fg;

@@ -351,8 +339,6 @@ struct ab8500_maxim_parameters {
 * @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'
 * @n_r_t_tbl_elements:		number of elements in r_to_t_tbl
 * @r_to_t_tbl:			table containing resistance to temp points
 */
struct ab8500_battery_type {
	int resis_high;
@@ -365,8 +351,6 @@ struct ab8500_battery_type {
	int maint_b_chg_timer_h;
	int low_high_cur_lvl;
	int low_high_vol_lvl;
	int n_temp_tbl_elements;
	const struct ab8500_res_to_temp *r_to_t_tbl;
};

/**
@@ -449,23 +433,6 @@ struct ab8500_bm_data {
	const struct ab8500_fg_parameters *fg_params;
};

enum {
	NTC_EXTERNAL = 0,
	NTC_INTERNAL,
};

/**
 * struct res_to_temp - defines one point in a temp to res curve. To
 * be used in battery packs that combines the identification resistor with a
 * NTC resistor.
 * @temp:			battery pack temperature in Celsius
 * @resist:			NTC resistor net total resistance
 */
struct res_to_temp {
	int temp;
	int resist;
};

/* Forward declaration */
struct ab8500_fg;

+0 −24
Original line number Diff line number Diff line
@@ -43,28 +43,6 @@ static struct power_supply_battery_ocv_table ocv_cap_tbl[] = {
	{ .ocv = 3094000, .capacity = 0},
};

/*
 * Note that the res_to_temp table must be strictly sorted by falling
 * resistance values to work.
 */
static const struct ab8500_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. Factory resistance is 300 mOhm and the
@@ -92,8 +70,6 @@ static struct ab8500_battery_type bat_type_thermistor_unknown = {
	.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,
};

static const struct ab8500_bm_capacity_levels cap_levels = {
+22 −81
Original line number Diff line number Diff line
@@ -26,13 +26,12 @@
#include <linux/mfd/core.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/thermal.h>
#include <linux/iio/consumer.h>
#include <linux/fixp-arith.h>

#include "ab8500-bm.h"

#define VTVOUT_V			1800

#define BTEMP_THERMAL_LOW_LIMIT		-10
#define BTEMP_THERMAL_MED_LIMIT		0
#define BTEMP_THERMAL_HIGH_LIMIT_52	52
@@ -82,7 +81,7 @@ struct ab8500_btemp_ranges {
 * @bat_temp:		Dispatched battery temperature in degree Celsius
 * @prev_bat_temp	Last measured battery temperature in degree Celsius
 * @parent:		Pointer to the struct ab8500
 * @adc_btemp_ball:	ADC channel for the battery ball temperature
 * @tz:			Thermal zone for the battery
 * @adc_bat_ctrl:	ADC channel for the battery control
 * @fg:			Pointer to the struct fg
 * @bm:           	Platform specific battery management information
@@ -100,7 +99,7 @@ struct ab8500_btemp {
	int bat_temp;
	int prev_bat_temp;
	struct ab8500 *parent;
	struct iio_channel *btemp_ball;
	struct thermal_zone_device *tz;
	struct iio_channel *bat_ctrl;
	struct ab8500_fg *fg;
	struct ab8500_bm_data *bm;
@@ -228,76 +227,6 @@ static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di)
	return res;
}

/**
 * ab8500_btemp_res_to_temp() - resistance to temperature
 * @di:		pointer to the ab8500_btemp structure
 * @tbl:	pointer to the resiatance to temperature table
 * @tbl_size:	size of the resistance to temperature table
 * @res:	resistance to calculate the temperature from
 *
 * This function returns the battery temperature in degrees Celsius
 * based on the NTC resistance.
 */
static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di,
	const struct ab8500_res_to_temp *tbl, int tbl_size, int res)
{
	int i;
	/*
	 * Calculate the formula for the straight line
	 * Simple interpolation if we are within
	 * the resistance table limits, extrapolate
	 * if resistance is outside the limits.
	 */
	if (res > tbl[0].resist)
		i = 0;
	else if (res <= tbl[tbl_size - 1].resist)
		i = tbl_size - 2;
	else {
		i = 0;
		while (!(res <= tbl[i].resist &&
			res > tbl[i + 1].resist))
			i++;
	}

	return fixp_linear_interpolate(tbl[i].resist, tbl[i].temp,
				       tbl[i + 1].resist, tbl[i + 1].temp,
				       res);
}

/**
 * ab8500_btemp_measure_temp() - measure battery temperature
 * @di:		pointer to the ab8500_btemp structure
 *
 * Returns battery temperature (on success) else the previous temperature
 */
static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
{
	int temp, ret;
	static int prev;
	int rntc, vntc;

	ret = iio_read_channel_processed(di->btemp_ball, &vntc);
	if (ret < 0) {
		dev_err(di->dev,
			"%s ADC conversion failed,"
			" using previous value\n", __func__);
		return prev;
	}
	/*
	 * The PCB NTC is sourced from VTVOUT via a 230kOhm
	 * resistor.
	 */
	rntc = 230000 * vntc / (VTVOUT_V - vntc);

	temp = ab8500_btemp_res_to_temp(di,
		di->bm->bat_type->r_to_t_tbl,
		di->bm->bat_type->n_temp_tbl_elements, rntc);
	prev = temp;

	dev_dbg(di->dev, "Battery temperature is %d\n", temp);
	return temp;
}

/**
 * ab8500_btemp_id() - Identify the connected battery
 * @di:		pointer to the ab8500_btemp structure
@@ -347,6 +276,9 @@ static void ab8500_btemp_periodic_work(struct work_struct *work)
	int bat_temp;
	struct ab8500_btemp *di = container_of(work,
		struct ab8500_btemp, btemp_periodic_work.work);
	/* Assume 25 degrees celsius as start temperature */
	static int prev = 25;
	int ret;

	if (!di->initialized) {
		/* Identify the battery */
@@ -354,7 +286,17 @@ static void ab8500_btemp_periodic_work(struct work_struct *work)
			dev_warn(di->dev, "failed to identify the battery\n");
	}

	bat_temp = ab8500_btemp_measure_temp(di);
	/* Failover if a reading is erroneous, use last meausurement */
	ret = thermal_zone_get_temp(di->tz, &bat_temp);
	if (ret) {
		dev_err(di->dev, "error reading temperature\n");
		bat_temp = prev;
	} else {
		/* Convert from millicentigrades to centigrades */
		bat_temp /= 1000;
		prev = bat_temp;
	}

	/*
	 * Filter battery temperature.
	 * Allow direct updates on temperature only if two samples result in
@@ -783,12 +725,11 @@ static int ab8500_btemp_probe(struct platform_device *pdev)
	di->dev = dev;
	di->parent = dev_get_drvdata(pdev->dev.parent);

	/* Get ADC channels */
	di->btemp_ball = devm_iio_channel_get(dev, "btemp_ball");
	if (IS_ERR(di->btemp_ball)) {
		ret = dev_err_probe(dev, PTR_ERR(di->btemp_ball),
				    "failed to get BTEMP BALL ADC channel\n");
		return ret;
	/* Get thermal zone and ADC */
	di->tz = thermal_zone_get_zone_by_name("battery-thermal");
	if (IS_ERR(di->tz)) {
		return dev_err_probe(dev, PTR_ERR(di->tz),
				     "failed to get battery thermal zone\n");
	}
	di->bat_ctrl = devm_iio_channel_get(dev, "bat_ctrl");
	if (IS_ERR(di->bat_ctrl)) {