brintos

brintos / linux-shallow public Read only

0
0
Text · 21.7 KiB · 56f136b Raw
832 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) ST-Ericsson SA 20124 *5 * Battery temperature driver for AB85006 *7 * Author:8 *	Johan Palsson <johan.palsson@stericsson.com>9 *	Karl Komierowski <karl.komierowski@stericsson.com>10 *	Arun R Murthy <arun.murthy@stericsson.com>11 */12 13#include <linux/init.h>14#include <linux/module.h>15#include <linux/device.h>16#include <linux/component.h>17#include <linux/interrupt.h>18#include <linux/delay.h>19#include <linux/slab.h>20#include <linux/platform_device.h>21#include <linux/power_supply.h>22#include <linux/completion.h>23#include <linux/workqueue.h>24#include <linux/jiffies.h>25#include <linux/of.h>26#include <linux/mfd/core.h>27#include <linux/mfd/abx500.h>28#include <linux/mfd/abx500/ab8500.h>29#include <linux/thermal.h>30#include <linux/iio/consumer.h>31#include <linux/fixp-arith.h>32 33#include "ab8500-bm.h"34 35#define BTEMP_THERMAL_LOW_LIMIT		-1036#define BTEMP_THERMAL_MED_LIMIT		037#define BTEMP_THERMAL_HIGH_LIMIT_52	5238#define BTEMP_THERMAL_HIGH_LIMIT_57	5739#define BTEMP_THERMAL_HIGH_LIMIT_62	6240 41#define BTEMP_BATCTRL_CURR_SRC_7UA	742#define BTEMP_BATCTRL_CURR_SRC_20UA	2043 44#define BTEMP_BATCTRL_CURR_SRC_16UA	1645#define BTEMP_BATCTRL_CURR_SRC_18UA	1846 47#define BTEMP_BATCTRL_CURR_SRC_60UA	6048#define BTEMP_BATCTRL_CURR_SRC_120UA	12049 50/**51 * struct ab8500_btemp_interrupts - ab8500 interrupts52 * @name:	name of the interrupt53 * @isr		function pointer to the isr54 */55struct ab8500_btemp_interrupts {56	char *name;57	irqreturn_t (*isr)(int irq, void *data);58};59 60struct ab8500_btemp_events {61	bool batt_rem;62	bool btemp_high;63	bool btemp_medhigh;64	bool btemp_lowmed;65	bool btemp_low;66	bool ac_conn;67	bool usb_conn;68};69 70struct ab8500_btemp_ranges {71	int btemp_high_limit;72	int btemp_med_limit;73	int btemp_low_limit;74};75 76/**77 * struct ab8500_btemp - ab8500 BTEMP device information78 * @dev:		Pointer to the structure device79 * @node:		List of AB8500 BTEMPs, hence prepared for reentrance80 * @curr_source:	What current source we use, in uA81 * @bat_temp:		Dispatched battery temperature in degree Celsius82 * @prev_bat_temp	Last measured battery temperature in degree Celsius83 * @parent:		Pointer to the struct ab850084 * @tz:			Thermal zone for the battery85 * @adc_bat_ctrl:	ADC channel for the battery control86 * @fg:			Pointer to the struct fg87 * @bm:           	Platform specific battery management information88 * @btemp_psy:		Structure for BTEMP specific battery properties89 * @events:		Structure for information about events triggered90 * @btemp_ranges:	Battery temperature range structure91 * @btemp_wq:		Work queue for measuring the temperature periodically92 * @btemp_periodic_work:	Work for measuring the temperature periodically93 * @initialized:	True if battery id read.94 */95struct ab8500_btemp {96	struct device *dev;97	struct list_head node;98	int curr_source;99	int bat_temp;100	int prev_bat_temp;101	struct ab8500 *parent;102	struct thermal_zone_device *tz;103	struct iio_channel *bat_ctrl;104	struct ab8500_fg *fg;105	struct ab8500_bm_data *bm;106	struct power_supply *btemp_psy;107	struct ab8500_btemp_events events;108	struct ab8500_btemp_ranges btemp_ranges;109	struct workqueue_struct *btemp_wq;110	struct delayed_work btemp_periodic_work;111	bool initialized;112};113 114/* BTEMP power supply properties */115static enum power_supply_property ab8500_btemp_props[] = {116	POWER_SUPPLY_PROP_PRESENT,117	POWER_SUPPLY_PROP_ONLINE,118	POWER_SUPPLY_PROP_TEMP,119};120 121static LIST_HEAD(ab8500_btemp_list);122 123/**124 * ab8500_btemp_batctrl_volt_to_res() - convert batctrl voltage to resistance125 * @di:		pointer to the ab8500_btemp structure126 * @v_batctrl:	measured batctrl voltage127 * @inst_curr:	measured instant current128 *129 * This function returns the battery resistance that is130 * derived from the BATCTRL voltage.131 * Returns value in Ohms.132 */133static int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di,134	int v_batctrl, int inst_curr)135{136	if (is_ab8500_1p1_or_earlier(di->parent)) {137		/*138		 * For ABB cut1.0 and 1.1 BAT_CTRL is internally139		 * connected to 1.8V through a 450k resistor140		 */141		return (450000 * (v_batctrl)) / (1800 - v_batctrl);142	}143 144	/*145	 * BAT_CTRL is internally146	 * connected to 1.8V through a 80k resistor147	 */148	return (80000 * (v_batctrl)) / (1800 - v_batctrl);149}150 151/**152 * ab8500_btemp_read_batctrl_voltage() - measure batctrl voltage153 * @di:		pointer to the ab8500_btemp structure154 *155 * This function returns the voltage on BATCTRL. Returns value in mV.156 */157static int ab8500_btemp_read_batctrl_voltage(struct ab8500_btemp *di)158{159	int vbtemp, ret;160	static int prev;161 162	ret = iio_read_channel_processed(di->bat_ctrl, &vbtemp);163	if (ret < 0) {164		dev_err(di->dev,165			"%s ADC conversion failed, using previous value",166			__func__);167		return prev;168	}169	prev = vbtemp;170	return vbtemp;171}172 173/**174 * ab8500_btemp_get_batctrl_res() - get battery resistance175 * @di:		pointer to the ab8500_btemp structure176 *177 * This function returns the battery pack identification resistance.178 * Returns value in Ohms.179 */180static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di)181{182	int ret;183	int batctrl = 0;184	int res;185	int inst_curr;186	int i;187 188	if (!di->fg)189		di->fg = ab8500_fg_get();190	if (!di->fg) {191		dev_err(di->dev, "No fg found\n");192		return -EINVAL;193	}194 195	ret = ab8500_fg_inst_curr_start(di->fg);196 197	if (ret) {198		dev_err(di->dev, "Failed to start current measurement\n");199		return ret;200	}201 202	do {203		msleep(20);204	} while (!ab8500_fg_inst_curr_started(di->fg));205 206	i = 0;207 208	do {209		batctrl += ab8500_btemp_read_batctrl_voltage(di);210		i++;211		msleep(20);212	} while (!ab8500_fg_inst_curr_done(di->fg));213	batctrl /= i;214 215	ret = ab8500_fg_inst_curr_finalize(di->fg, &inst_curr);216	if (ret) {217		dev_err(di->dev, "Failed to finalize current measurement\n");218		return ret;219	}220 221	res = ab8500_btemp_batctrl_volt_to_res(di, batctrl, inst_curr);222 223	dev_dbg(di->dev, "%s batctrl: %d res: %d inst_curr: %d samples: %d\n",224		__func__, batctrl, res, inst_curr, i);225 226	return res;227}228 229/**230 * ab8500_btemp_id() - Identify the connected battery231 * @di:		pointer to the ab8500_btemp structure232 *233 * This function will try to identify the battery by reading the ID234 * resistor. Some brands use a combined ID resistor with a NTC resistor to235 * both be able to identify and to read the temperature of it.236 */237static int ab8500_btemp_id(struct ab8500_btemp *di)238{239	struct power_supply_battery_info *bi = di->bm->bi;240	int res;241 242	di->curr_source = BTEMP_BATCTRL_CURR_SRC_7UA;243 244	res =  ab8500_btemp_get_batctrl_res(di);245	if (res < 0) {246		dev_err(di->dev, "%s get batctrl res failed\n", __func__);247		return -ENXIO;248	}249 250	if (power_supply_battery_bti_in_range(bi, res)) {251		dev_info(di->dev, "Battery detected on BATCTRL (pin C3)"252			 " resistance %d Ohm = %d Ohm +/- %d%%\n",253			 res, bi->bti_resistance_ohm,254			 bi->bti_resistance_tolerance);255	} else {256		dev_warn(di->dev, "Battery identified as unknown"257			 ", resistance %d Ohm\n", res);258		return -ENXIO;259	}260 261	return 0;262}263 264/**265 * ab8500_btemp_periodic_work() - Measuring the temperature periodically266 * @work:	pointer to the work_struct structure267 *268 * Work function for measuring the temperature periodically269 */270static void ab8500_btemp_periodic_work(struct work_struct *work)271{272	int interval;273	int bat_temp;274	struct ab8500_btemp *di = container_of(work,275		struct ab8500_btemp, btemp_periodic_work.work);276	/* Assume 25 degrees celsius as start temperature */277	static int prev = 25;278	int ret;279 280	if (!di->initialized) {281		/* Identify the battery */282		if (ab8500_btemp_id(di) < 0)283			dev_warn(di->dev, "failed to identify the battery\n");284	}285 286	/* Failover if a reading is erroneous, use last meausurement */287	ret = thermal_zone_get_temp(di->tz, &bat_temp);288	if (ret) {289		dev_err(di->dev, "error reading temperature\n");290		bat_temp = prev;291	} else {292		/* Convert from millicentigrades to centigrades */293		bat_temp /= 1000;294		prev = bat_temp;295	}296 297	/*298	 * Filter battery temperature.299	 * Allow direct updates on temperature only if two samples result in300	 * same temperature. Else only allow 1 degree change from previous301	 * reported value in the direction of the new measurement.302	 */303	if ((bat_temp == di->prev_bat_temp) || !di->initialized) {304		if ((di->bat_temp != di->prev_bat_temp) || !di->initialized) {305			di->initialized = true;306			di->bat_temp = bat_temp;307			power_supply_changed(di->btemp_psy);308		}309	} else if (bat_temp < di->prev_bat_temp) {310		di->bat_temp--;311		power_supply_changed(di->btemp_psy);312	} else if (bat_temp > di->prev_bat_temp) {313		di->bat_temp++;314		power_supply_changed(di->btemp_psy);315	}316	di->prev_bat_temp = bat_temp;317 318	if (di->events.ac_conn || di->events.usb_conn)319		interval = di->bm->temp_interval_chg;320	else321		interval = di->bm->temp_interval_nochg;322 323	/* Schedule a new measurement */324	queue_delayed_work(di->btemp_wq,325		&di->btemp_periodic_work,326		round_jiffies(interval * HZ));327}328 329/**330 * ab8500_btemp_batctrlindb_handler() - battery removal detected331 * @irq:       interrupt number332 * @_di:       void pointer that has to address of ab8500_btemp333 *334 * Returns IRQ status(IRQ_HANDLED)335 */336static irqreturn_t ab8500_btemp_batctrlindb_handler(int irq, void *_di)337{338	struct ab8500_btemp *di = _di;339	dev_err(di->dev, "Battery removal detected!\n");340 341	di->events.batt_rem = true;342	power_supply_changed(di->btemp_psy);343 344	return IRQ_HANDLED;345}346 347/**348 * ab8500_btemp_templow_handler() - battery temp lower than 10 degrees349 * @irq:       interrupt number350 * @_di:       void pointer that has to address of ab8500_btemp351 *352 * Returns IRQ status(IRQ_HANDLED)353 */354static irqreturn_t ab8500_btemp_templow_handler(int irq, void *_di)355{356	struct ab8500_btemp *di = _di;357 358	if (is_ab8500_3p3_or_earlier(di->parent)) {359		dev_dbg(di->dev, "Ignore false btemp low irq"360			" for ABB cut 1.0, 1.1, 2.0 and 3.3\n");361	} else {362		dev_crit(di->dev, "Battery temperature lower than -10deg c\n");363 364		di->events.btemp_low = true;365		di->events.btemp_high = false;366		di->events.btemp_medhigh = false;367		di->events.btemp_lowmed = false;368		power_supply_changed(di->btemp_psy);369	}370 371	return IRQ_HANDLED;372}373 374/**375 * ab8500_btemp_temphigh_handler() - battery temp higher than max temp376 * @irq:       interrupt number377 * @_di:       void pointer that has to address of ab8500_btemp378 *379 * Returns IRQ status(IRQ_HANDLED)380 */381static irqreturn_t ab8500_btemp_temphigh_handler(int irq, void *_di)382{383	struct ab8500_btemp *di = _di;384 385	dev_crit(di->dev, "Battery temperature is higher than MAX temp\n");386 387	di->events.btemp_high = true;388	di->events.btemp_medhigh = false;389	di->events.btemp_lowmed = false;390	di->events.btemp_low = false;391	power_supply_changed(di->btemp_psy);392 393	return IRQ_HANDLED;394}395 396/**397 * ab8500_btemp_lowmed_handler() - battery temp between low and medium398 * @irq:       interrupt number399 * @_di:       void pointer that has to address of ab8500_btemp400 *401 * Returns IRQ status(IRQ_HANDLED)402 */403static irqreturn_t ab8500_btemp_lowmed_handler(int irq, void *_di)404{405	struct ab8500_btemp *di = _di;406 407	dev_dbg(di->dev, "Battery temperature is between low and medium\n");408 409	di->events.btemp_lowmed = true;410	di->events.btemp_medhigh = false;411	di->events.btemp_high = false;412	di->events.btemp_low = false;413	power_supply_changed(di->btemp_psy);414 415	return IRQ_HANDLED;416}417 418/**419 * ab8500_btemp_medhigh_handler() - battery temp between medium and high420 * @irq:       interrupt number421 * @_di:       void pointer that has to address of ab8500_btemp422 *423 * Returns IRQ status(IRQ_HANDLED)424 */425static irqreturn_t ab8500_btemp_medhigh_handler(int irq, void *_di)426{427	struct ab8500_btemp *di = _di;428 429	dev_dbg(di->dev, "Battery temperature is between medium and high\n");430 431	di->events.btemp_medhigh = true;432	di->events.btemp_lowmed = false;433	di->events.btemp_high = false;434	di->events.btemp_low = false;435	power_supply_changed(di->btemp_psy);436 437	return IRQ_HANDLED;438}439 440/**441 * ab8500_btemp_periodic() - Periodic temperature measurements442 * @di:		pointer to the ab8500_btemp structure443 * @enable:	enable or disable periodic temperature measurements444 *445 * Starts of stops periodic temperature measurements. Periodic measurements446 * should only be done when a charger is connected.447 */448static void ab8500_btemp_periodic(struct ab8500_btemp *di,449	bool enable)450{451	dev_dbg(di->dev, "Enable periodic temperature measurements: %d\n",452		enable);453	/*454	 * Make sure a new measurement is done directly by cancelling455	 * any pending work456	 */457	cancel_delayed_work_sync(&di->btemp_periodic_work);458 459	if (enable)460		queue_delayed_work(di->btemp_wq, &di->btemp_periodic_work, 0);461}462 463/**464 * ab8500_btemp_get_temp() - get battery temperature465 * @di:		pointer to the ab8500_btemp structure466 *467 * Returns battery temperature468 */469static int ab8500_btemp_get_temp(struct ab8500_btemp *di)470{471	int temp = 0;472 473	/*474	 * The BTEMP events are not reliabe on AB8500 cut3.3475	 * and prior versions476	 */477	if (is_ab8500_3p3_or_earlier(di->parent)) {478		temp = di->bat_temp * 10;479	} else {480		if (di->events.btemp_low) {481			if (temp > di->btemp_ranges.btemp_low_limit)482				temp = di->btemp_ranges.btemp_low_limit * 10;483			else484				temp = di->bat_temp * 10;485		} else if (di->events.btemp_high) {486			if (temp < di->btemp_ranges.btemp_high_limit)487				temp = di->btemp_ranges.btemp_high_limit * 10;488			else489				temp = di->bat_temp * 10;490		} else if (di->events.btemp_lowmed) {491			if (temp > di->btemp_ranges.btemp_med_limit)492				temp = di->btemp_ranges.btemp_med_limit * 10;493			else494				temp = di->bat_temp * 10;495		} else if (di->events.btemp_medhigh) {496			if (temp < di->btemp_ranges.btemp_med_limit)497				temp = di->btemp_ranges.btemp_med_limit * 10;498			else499				temp = di->bat_temp * 10;500		} else501			temp = di->bat_temp * 10;502	}503	return temp;504}505 506/**507 * ab8500_btemp_get_property() - get the btemp properties508 * @psy:        pointer to the power_supply structure509 * @psp:        pointer to the power_supply_property structure510 * @val:        pointer to the power_supply_propval union511 *512 * This function gets called when an application tries to get the btemp513 * properties by reading the sysfs files.514 * online:	presence of the battery515 * present:	presence of the battery516 * technology:	battery technology517 * temp:	battery temperature518 * Returns error code in case of failure else 0(on success)519 */520static int ab8500_btemp_get_property(struct power_supply *psy,521	enum power_supply_property psp,522	union power_supply_propval *val)523{524	struct ab8500_btemp *di = power_supply_get_drvdata(psy);525 526	switch (psp) {527	case POWER_SUPPLY_PROP_PRESENT:528	case POWER_SUPPLY_PROP_ONLINE:529		if (di->events.batt_rem)530			val->intval = 0;531		else532			val->intval = 1;533		break;534	case POWER_SUPPLY_PROP_TEMP:535		val->intval = ab8500_btemp_get_temp(di);536		break;537	default:538		return -EINVAL;539	}540	return 0;541}542 543static int ab8500_btemp_get_ext_psy_data(struct device *dev, void *data)544{545	struct power_supply *psy;546	struct power_supply *ext = dev_get_drvdata(dev);547	const char **supplicants = (const char **)ext->supplied_to;548	struct ab8500_btemp *di;549	union power_supply_propval ret;550	int j;551 552	psy = (struct power_supply *)data;553	di = power_supply_get_drvdata(psy);554 555	/*556	 * For all psy where the name of your driver557	 * appears in any supplied_to558	 */559	j = match_string(supplicants, ext->num_supplicants, psy->desc->name);560	if (j < 0)561		return 0;562 563	/* Go through all properties for the psy */564	for (j = 0; j < ext->desc->num_properties; j++) {565		enum power_supply_property prop;566		prop = ext->desc->properties[j];567 568		if (power_supply_get_property(ext, prop, &ret))569			continue;570 571		switch (prop) {572		case POWER_SUPPLY_PROP_PRESENT:573			switch (ext->desc->type) {574			case POWER_SUPPLY_TYPE_MAINS:575				/* AC disconnected */576				if (!ret.intval && di->events.ac_conn) {577					di->events.ac_conn = false;578				}579				/* AC connected */580				else if (ret.intval && !di->events.ac_conn) {581					di->events.ac_conn = true;582					if (!di->events.usb_conn)583						ab8500_btemp_periodic(di, true);584				}585				break;586			case POWER_SUPPLY_TYPE_USB:587				/* USB disconnected */588				if (!ret.intval && di->events.usb_conn) {589					di->events.usb_conn = false;590				}591				/* USB connected */592				else if (ret.intval && !di->events.usb_conn) {593					di->events.usb_conn = true;594					if (!di->events.ac_conn)595						ab8500_btemp_periodic(di, true);596				}597				break;598			default:599				break;600			}601			break;602		default:603			break;604		}605	}606	return 0;607}608 609/**610 * ab8500_btemp_external_power_changed() - callback for power supply changes611 * @psy:       pointer to the structure power_supply612 *613 * This function is pointing to the function pointer external_power_changed614 * of the structure power_supply.615 * This function gets executed when there is a change in the external power616 * supply to the btemp.617 */618static void ab8500_btemp_external_power_changed(struct power_supply *psy)619{620	power_supply_for_each_device(psy, ab8500_btemp_get_ext_psy_data);621}622 623/* ab8500 btemp driver interrupts and their respective isr */624static struct ab8500_btemp_interrupts ab8500_btemp_irq[] = {625	{"BAT_CTRL_INDB", ab8500_btemp_batctrlindb_handler},626	{"BTEMP_LOW", ab8500_btemp_templow_handler},627	{"BTEMP_HIGH", ab8500_btemp_temphigh_handler},628	{"BTEMP_LOW_MEDIUM", ab8500_btemp_lowmed_handler},629	{"BTEMP_MEDIUM_HIGH", ab8500_btemp_medhigh_handler},630};631 632static int __maybe_unused ab8500_btemp_resume(struct device *dev)633{634	struct ab8500_btemp *di = dev_get_drvdata(dev);635 636	ab8500_btemp_periodic(di, true);637 638	return 0;639}640 641static int __maybe_unused ab8500_btemp_suspend(struct device *dev)642{643	struct ab8500_btemp *di = dev_get_drvdata(dev);644 645	ab8500_btemp_periodic(di, false);646 647	return 0;648}649 650static char *supply_interface[] = {651	"ab8500_chargalg",652	"ab8500_fg",653};654 655static const struct power_supply_desc ab8500_btemp_desc = {656	.name			= "ab8500_btemp",657	.type			= POWER_SUPPLY_TYPE_UNKNOWN,658	.properties		= ab8500_btemp_props,659	.num_properties		= ARRAY_SIZE(ab8500_btemp_props),660	.get_property		= ab8500_btemp_get_property,661	.external_power_changed	= ab8500_btemp_external_power_changed,662};663 664static int ab8500_btemp_bind(struct device *dev, struct device *master,665			     void *data)666{667	struct ab8500_btemp *di = dev_get_drvdata(dev);668 669	/* Create a work queue for the btemp */670	di->btemp_wq =671		alloc_workqueue("ab8500_btemp_wq", WQ_MEM_RECLAIM, 0);672	if (di->btemp_wq == NULL) {673		dev_err(dev, "failed to create work queue\n");674		return -ENOMEM;675	}676 677	/* Kick off periodic temperature measurements */678	ab8500_btemp_periodic(di, true);679 680	return 0;681}682 683static void ab8500_btemp_unbind(struct device *dev, struct device *master,684				void *data)685{686	struct ab8500_btemp *di = dev_get_drvdata(dev);687 688	/* Delete the work queue */689	destroy_workqueue(di->btemp_wq);690}691 692static const struct component_ops ab8500_btemp_component_ops = {693	.bind = ab8500_btemp_bind,694	.unbind = ab8500_btemp_unbind,695};696 697static int ab8500_btemp_probe(struct platform_device *pdev)698{699	struct power_supply_config psy_cfg = {};700	struct device *dev = &pdev->dev;701	struct ab8500_btemp *di;702	int irq, i, ret = 0;703	u8 val;704 705	di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL);706	if (!di)707		return -ENOMEM;708 709	di->bm = &ab8500_bm_data;710 711	/* get parent data */712	di->dev = dev;713	di->parent = dev_get_drvdata(pdev->dev.parent);714 715	/* Get thermal zone and ADC */716	di->tz = thermal_zone_get_zone_by_name("battery-thermal");717	if (IS_ERR(di->tz)) {718		ret = PTR_ERR(di->tz);719		/*720		 * This usually just means we are probing before the thermal721		 * zone, so just defer.722		 */723		if (ret == -ENODEV)724			ret = -EPROBE_DEFER;725		return dev_err_probe(dev, ret,726				     "failed to get battery thermal zone\n");727	}728	di->bat_ctrl = devm_iio_channel_get(dev, "bat_ctrl");729	if (IS_ERR(di->bat_ctrl)) {730		ret = dev_err_probe(dev, PTR_ERR(di->bat_ctrl),731				    "failed to get BAT CTRL ADC channel\n");732		return ret;733	}734 735	di->initialized = false;736 737	psy_cfg.supplied_to = supply_interface;738	psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface);739	psy_cfg.drv_data = di;740 741	/* Init work for measuring temperature periodically */742	INIT_DEFERRABLE_WORK(&di->btemp_periodic_work,743		ab8500_btemp_periodic_work);744 745	/* Set BTEMP thermal limits. Low and Med are fixed */746	di->btemp_ranges.btemp_low_limit = BTEMP_THERMAL_LOW_LIMIT;747	di->btemp_ranges.btemp_med_limit = BTEMP_THERMAL_MED_LIMIT;748 749	ret = abx500_get_register_interruptible(dev, AB8500_CHARGER,750		AB8500_BTEMP_HIGH_TH, &val);751	if (ret < 0) {752		dev_err(dev, "%s ab8500 read failed\n", __func__);753		return ret;754	}755	switch (val) {756	case BTEMP_HIGH_TH_57_0:757	case BTEMP_HIGH_TH_57_1:758		di->btemp_ranges.btemp_high_limit =759			BTEMP_THERMAL_HIGH_LIMIT_57;760		break;761	case BTEMP_HIGH_TH_52:762		di->btemp_ranges.btemp_high_limit =763			BTEMP_THERMAL_HIGH_LIMIT_52;764		break;765	case BTEMP_HIGH_TH_62:766		di->btemp_ranges.btemp_high_limit =767			BTEMP_THERMAL_HIGH_LIMIT_62;768		break;769	}770 771	/* Register BTEMP power supply class */772	di->btemp_psy = devm_power_supply_register(dev, &ab8500_btemp_desc,773						   &psy_cfg);774	if (IS_ERR(di->btemp_psy)) {775		dev_err(dev, "failed to register BTEMP psy\n");776		return PTR_ERR(di->btemp_psy);777	}778 779	/* Register interrupts */780	for (i = 0; i < ARRAY_SIZE(ab8500_btemp_irq); i++) {781		irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name);782		if (irq < 0)783			return irq;784 785		ret = devm_request_threaded_irq(dev, irq, NULL,786			ab8500_btemp_irq[i].isr,787			IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT,788			ab8500_btemp_irq[i].name, di);789 790		if (ret) {791			dev_err(dev, "failed to request %s IRQ %d: %d\n"792				, ab8500_btemp_irq[i].name, irq, ret);793			return ret;794		}795		dev_dbg(dev, "Requested %s IRQ %d: %d\n",796			ab8500_btemp_irq[i].name, irq, ret);797	}798 799	platform_set_drvdata(pdev, di);800 801	list_add_tail(&di->node, &ab8500_btemp_list);802 803	return component_add(dev, &ab8500_btemp_component_ops);804}805 806static void ab8500_btemp_remove(struct platform_device *pdev)807{808	component_del(&pdev->dev, &ab8500_btemp_component_ops);809}810 811static SIMPLE_DEV_PM_OPS(ab8500_btemp_pm_ops, ab8500_btemp_suspend, ab8500_btemp_resume);812 813static const struct of_device_id ab8500_btemp_match[] = {814	{ .compatible = "stericsson,ab8500-btemp", },815	{ },816};817MODULE_DEVICE_TABLE(of, ab8500_btemp_match);818 819struct platform_driver ab8500_btemp_driver = {820	.probe = ab8500_btemp_probe,821	.remove_new = ab8500_btemp_remove,822	.driver = {823		.name = "ab8500-btemp",824		.of_match_table = ab8500_btemp_match,825		.pm = &ab8500_btemp_pm_ops,826	},827};828MODULE_LICENSE("GPL v2");829MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy");830MODULE_ALIAS("platform:ab8500-btemp");831MODULE_DESCRIPTION("AB8500 battery temperature driver");832