cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ab8500_btemp.c (22408B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) ST-Ericsson SA 2012
      4 *
      5 * Battery temperature driver for AB8500
      6 *
      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		-10
     36#define BTEMP_THERMAL_MED_LIMIT		0
     37#define BTEMP_THERMAL_HIGH_LIMIT_52	52
     38#define BTEMP_THERMAL_HIGH_LIMIT_57	57
     39#define BTEMP_THERMAL_HIGH_LIMIT_62	62
     40
     41#define BTEMP_BATCTRL_CURR_SRC_7UA	7
     42#define BTEMP_BATCTRL_CURR_SRC_20UA	20
     43
     44#define BTEMP_BATCTRL_CURR_SRC_16UA	16
     45#define BTEMP_BATCTRL_CURR_SRC_18UA	18
     46
     47#define BTEMP_BATCTRL_CURR_SRC_60UA	60
     48#define BTEMP_BATCTRL_CURR_SRC_120UA	120
     49
     50/**
     51 * struct ab8500_btemp_interrupts - ab8500 interrupts
     52 * @name:	name of the interrupt
     53 * @isr		function pointer to the isr
     54 */
     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 information
     78 * @dev:		Pointer to the structure device
     79 * @node:		List of AB8500 BTEMPs, hence prepared for reentrance
     80 * @curr_source:	What current source we use, in uA
     81 * @bat_temp:		Dispatched battery temperature in degree Celsius
     82 * @prev_bat_temp	Last measured battery temperature in degree Celsius
     83 * @parent:		Pointer to the struct ab8500
     84 * @tz:			Thermal zone for the battery
     85 * @adc_bat_ctrl:	ADC channel for the battery control
     86 * @fg:			Pointer to the struct fg
     87 * @bm:           	Platform specific battery management information
     88 * @btemp_psy:		Structure for BTEMP specific battery properties
     89 * @events:		Structure for information about events triggered
     90 * @btemp_ranges:	Battery temperature range structure
     91 * @btemp_wq:		Work queue for measuring the temperature periodically
     92 * @btemp_periodic_work:	Work for measuring the temperature periodically
     93 * @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_TECHNOLOGY,
    119	POWER_SUPPLY_PROP_TEMP,
    120};
    121
    122static LIST_HEAD(ab8500_btemp_list);
    123
    124/**
    125 * ab8500_btemp_batctrl_volt_to_res() - convert batctrl voltage to resistance
    126 * @di:		pointer to the ab8500_btemp structure
    127 * @v_batctrl:	measured batctrl voltage
    128 * @inst_curr:	measured instant current
    129 *
    130 * This function returns the battery resistance that is
    131 * derived from the BATCTRL voltage.
    132 * Returns value in Ohms.
    133 */
    134static int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di,
    135	int v_batctrl, int inst_curr)
    136{
    137	if (is_ab8500_1p1_or_earlier(di->parent)) {
    138		/*
    139		 * For ABB cut1.0 and 1.1 BAT_CTRL is internally
    140		 * connected to 1.8V through a 450k resistor
    141		 */
    142		return (450000 * (v_batctrl)) / (1800 - v_batctrl);
    143	}
    144
    145	/*
    146	 * BAT_CTRL is internally
    147	 * connected to 1.8V through a 80k resistor
    148	 */
    149	return (80000 * (v_batctrl)) / (1800 - v_batctrl);
    150}
    151
    152/**
    153 * ab8500_btemp_read_batctrl_voltage() - measure batctrl voltage
    154 * @di:		pointer to the ab8500_btemp structure
    155 *
    156 * This function returns the voltage on BATCTRL. Returns value in mV.
    157 */
    158static int ab8500_btemp_read_batctrl_voltage(struct ab8500_btemp *di)
    159{
    160	int vbtemp, ret;
    161	static int prev;
    162
    163	ret = iio_read_channel_processed(di->bat_ctrl, &vbtemp);
    164	if (ret < 0) {
    165		dev_err(di->dev,
    166			"%s ADC conversion failed, using previous value",
    167			__func__);
    168		return prev;
    169	}
    170	prev = vbtemp;
    171	return vbtemp;
    172}
    173
    174/**
    175 * ab8500_btemp_get_batctrl_res() - get battery resistance
    176 * @di:		pointer to the ab8500_btemp structure
    177 *
    178 * This function returns the battery pack identification resistance.
    179 * Returns value in Ohms.
    180 */
    181static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di)
    182{
    183	int ret;
    184	int batctrl = 0;
    185	int res;
    186	int inst_curr;
    187	int i;
    188
    189	if (!di->fg)
    190		di->fg = ab8500_fg_get();
    191	if (!di->fg) {
    192		dev_err(di->dev, "No fg found\n");
    193		return -EINVAL;
    194	}
    195
    196	ret = ab8500_fg_inst_curr_start(di->fg);
    197
    198	if (ret) {
    199		dev_err(di->dev, "Failed to start current measurement\n");
    200		return ret;
    201	}
    202
    203	do {
    204		msleep(20);
    205	} while (!ab8500_fg_inst_curr_started(di->fg));
    206
    207	i = 0;
    208
    209	do {
    210		batctrl += ab8500_btemp_read_batctrl_voltage(di);
    211		i++;
    212		msleep(20);
    213	} while (!ab8500_fg_inst_curr_done(di->fg));
    214	batctrl /= i;
    215
    216	ret = ab8500_fg_inst_curr_finalize(di->fg, &inst_curr);
    217	if (ret) {
    218		dev_err(di->dev, "Failed to finalize current measurement\n");
    219		return ret;
    220	}
    221
    222	res = ab8500_btemp_batctrl_volt_to_res(di, batctrl, inst_curr);
    223
    224	dev_dbg(di->dev, "%s batctrl: %d res: %d inst_curr: %d samples: %d\n",
    225		__func__, batctrl, res, inst_curr, i);
    226
    227	return res;
    228}
    229
    230/**
    231 * ab8500_btemp_id() - Identify the connected battery
    232 * @di:		pointer to the ab8500_btemp structure
    233 *
    234 * This function will try to identify the battery by reading the ID
    235 * resistor. Some brands use a combined ID resistor with a NTC resistor to
    236 * both be able to identify and to read the temperature of it.
    237 */
    238static int ab8500_btemp_id(struct ab8500_btemp *di)
    239{
    240	struct power_supply_battery_info *bi = di->bm->bi;
    241	int res;
    242
    243	di->curr_source = BTEMP_BATCTRL_CURR_SRC_7UA;
    244
    245	res =  ab8500_btemp_get_batctrl_res(di);
    246	if (res < 0) {
    247		dev_err(di->dev, "%s get batctrl res failed\n", __func__);
    248		return -ENXIO;
    249	}
    250
    251	if (power_supply_battery_bti_in_range(bi, res)) {
    252		dev_info(di->dev, "Battery detected on BATCTRL (pin C3)"
    253			 " resistance %d Ohm = %d Ohm +/- %d%%\n",
    254			 res, bi->bti_resistance_ohm,
    255			 bi->bti_resistance_tolerance);
    256	} else {
    257		dev_warn(di->dev, "Battery identified as unknown"
    258			 ", resistance %d Ohm\n", res);
    259		return -ENXIO;
    260	}
    261
    262	return 0;
    263}
    264
    265/**
    266 * ab8500_btemp_periodic_work() - Measuring the temperature periodically
    267 * @work:	pointer to the work_struct structure
    268 *
    269 * Work function for measuring the temperature periodically
    270 */
    271static void ab8500_btemp_periodic_work(struct work_struct *work)
    272{
    273	int interval;
    274	int bat_temp;
    275	struct ab8500_btemp *di = container_of(work,
    276		struct ab8500_btemp, btemp_periodic_work.work);
    277	/* Assume 25 degrees celsius as start temperature */
    278	static int prev = 25;
    279	int ret;
    280
    281	if (!di->initialized) {
    282		/* Identify the battery */
    283		if (ab8500_btemp_id(di) < 0)
    284			dev_warn(di->dev, "failed to identify the battery\n");
    285	}
    286
    287	/* Failover if a reading is erroneous, use last meausurement */
    288	ret = thermal_zone_get_temp(di->tz, &bat_temp);
    289	if (ret) {
    290		dev_err(di->dev, "error reading temperature\n");
    291		bat_temp = prev;
    292	} else {
    293		/* Convert from millicentigrades to centigrades */
    294		bat_temp /= 1000;
    295		prev = bat_temp;
    296	}
    297
    298	/*
    299	 * Filter battery temperature.
    300	 * Allow direct updates on temperature only if two samples result in
    301	 * same temperature. Else only allow 1 degree change from previous
    302	 * reported value in the direction of the new measurement.
    303	 */
    304	if ((bat_temp == di->prev_bat_temp) || !di->initialized) {
    305		if ((di->bat_temp != di->prev_bat_temp) || !di->initialized) {
    306			di->initialized = true;
    307			di->bat_temp = bat_temp;
    308			power_supply_changed(di->btemp_psy);
    309		}
    310	} else if (bat_temp < di->prev_bat_temp) {
    311		di->bat_temp--;
    312		power_supply_changed(di->btemp_psy);
    313	} else if (bat_temp > di->prev_bat_temp) {
    314		di->bat_temp++;
    315		power_supply_changed(di->btemp_psy);
    316	}
    317	di->prev_bat_temp = bat_temp;
    318
    319	if (di->events.ac_conn || di->events.usb_conn)
    320		interval = di->bm->temp_interval_chg;
    321	else
    322		interval = di->bm->temp_interval_nochg;
    323
    324	/* Schedule a new measurement */
    325	queue_delayed_work(di->btemp_wq,
    326		&di->btemp_periodic_work,
    327		round_jiffies(interval * HZ));
    328}
    329
    330/**
    331 * ab8500_btemp_batctrlindb_handler() - battery removal detected
    332 * @irq:       interrupt number
    333 * @_di:       void pointer that has to address of ab8500_btemp
    334 *
    335 * Returns IRQ status(IRQ_HANDLED)
    336 */
    337static irqreturn_t ab8500_btemp_batctrlindb_handler(int irq, void *_di)
    338{
    339	struct ab8500_btemp *di = _di;
    340	dev_err(di->dev, "Battery removal detected!\n");
    341
    342	di->events.batt_rem = true;
    343	power_supply_changed(di->btemp_psy);
    344
    345	return IRQ_HANDLED;
    346}
    347
    348/**
    349 * ab8500_btemp_templow_handler() - battery temp lower than 10 degrees
    350 * @irq:       interrupt number
    351 * @_di:       void pointer that has to address of ab8500_btemp
    352 *
    353 * Returns IRQ status(IRQ_HANDLED)
    354 */
    355static irqreturn_t ab8500_btemp_templow_handler(int irq, void *_di)
    356{
    357	struct ab8500_btemp *di = _di;
    358
    359	if (is_ab8500_3p3_or_earlier(di->parent)) {
    360		dev_dbg(di->dev, "Ignore false btemp low irq"
    361			" for ABB cut 1.0, 1.1, 2.0 and 3.3\n");
    362	} else {
    363		dev_crit(di->dev, "Battery temperature lower than -10deg c\n");
    364
    365		di->events.btemp_low = true;
    366		di->events.btemp_high = false;
    367		di->events.btemp_medhigh = false;
    368		di->events.btemp_lowmed = false;
    369		power_supply_changed(di->btemp_psy);
    370	}
    371
    372	return IRQ_HANDLED;
    373}
    374
    375/**
    376 * ab8500_btemp_temphigh_handler() - battery temp higher than max temp
    377 * @irq:       interrupt number
    378 * @_di:       void pointer that has to address of ab8500_btemp
    379 *
    380 * Returns IRQ status(IRQ_HANDLED)
    381 */
    382static irqreturn_t ab8500_btemp_temphigh_handler(int irq, void *_di)
    383{
    384	struct ab8500_btemp *di = _di;
    385
    386	dev_crit(di->dev, "Battery temperature is higher than MAX temp\n");
    387
    388	di->events.btemp_high = true;
    389	di->events.btemp_medhigh = false;
    390	di->events.btemp_lowmed = false;
    391	di->events.btemp_low = false;
    392	power_supply_changed(di->btemp_psy);
    393
    394	return IRQ_HANDLED;
    395}
    396
    397/**
    398 * ab8500_btemp_lowmed_handler() - battery temp between low and medium
    399 * @irq:       interrupt number
    400 * @_di:       void pointer that has to address of ab8500_btemp
    401 *
    402 * Returns IRQ status(IRQ_HANDLED)
    403 */
    404static irqreturn_t ab8500_btemp_lowmed_handler(int irq, void *_di)
    405{
    406	struct ab8500_btemp *di = _di;
    407
    408	dev_dbg(di->dev, "Battery temperature is between low and medium\n");
    409
    410	di->events.btemp_lowmed = true;
    411	di->events.btemp_medhigh = false;
    412	di->events.btemp_high = false;
    413	di->events.btemp_low = false;
    414	power_supply_changed(di->btemp_psy);
    415
    416	return IRQ_HANDLED;
    417}
    418
    419/**
    420 * ab8500_btemp_medhigh_handler() - battery temp between medium and high
    421 * @irq:       interrupt number
    422 * @_di:       void pointer that has to address of ab8500_btemp
    423 *
    424 * Returns IRQ status(IRQ_HANDLED)
    425 */
    426static irqreturn_t ab8500_btemp_medhigh_handler(int irq, void *_di)
    427{
    428	struct ab8500_btemp *di = _di;
    429
    430	dev_dbg(di->dev, "Battery temperature is between medium and high\n");
    431
    432	di->events.btemp_medhigh = true;
    433	di->events.btemp_lowmed = false;
    434	di->events.btemp_high = false;
    435	di->events.btemp_low = false;
    436	power_supply_changed(di->btemp_psy);
    437
    438	return IRQ_HANDLED;
    439}
    440
    441/**
    442 * ab8500_btemp_periodic() - Periodic temperature measurements
    443 * @di:		pointer to the ab8500_btemp structure
    444 * @enable:	enable or disable periodic temperature measurements
    445 *
    446 * Starts of stops periodic temperature measurements. Periodic measurements
    447 * should only be done when a charger is connected.
    448 */
    449static void ab8500_btemp_periodic(struct ab8500_btemp *di,
    450	bool enable)
    451{
    452	dev_dbg(di->dev, "Enable periodic temperature measurements: %d\n",
    453		enable);
    454	/*
    455	 * Make sure a new measurement is done directly by cancelling
    456	 * any pending work
    457	 */
    458	cancel_delayed_work_sync(&di->btemp_periodic_work);
    459
    460	if (enable)
    461		queue_delayed_work(di->btemp_wq, &di->btemp_periodic_work, 0);
    462}
    463
    464/**
    465 * ab8500_btemp_get_temp() - get battery temperature
    466 * @di:		pointer to the ab8500_btemp structure
    467 *
    468 * Returns battery temperature
    469 */
    470static int ab8500_btemp_get_temp(struct ab8500_btemp *di)
    471{
    472	int temp = 0;
    473
    474	/*
    475	 * The BTEMP events are not reliabe on AB8500 cut3.3
    476	 * and prior versions
    477	 */
    478	if (is_ab8500_3p3_or_earlier(di->parent)) {
    479		temp = di->bat_temp * 10;
    480	} else {
    481		if (di->events.btemp_low) {
    482			if (temp > di->btemp_ranges.btemp_low_limit)
    483				temp = di->btemp_ranges.btemp_low_limit * 10;
    484			else
    485				temp = di->bat_temp * 10;
    486		} else if (di->events.btemp_high) {
    487			if (temp < di->btemp_ranges.btemp_high_limit)
    488				temp = di->btemp_ranges.btemp_high_limit * 10;
    489			else
    490				temp = di->bat_temp * 10;
    491		} else if (di->events.btemp_lowmed) {
    492			if (temp > di->btemp_ranges.btemp_med_limit)
    493				temp = di->btemp_ranges.btemp_med_limit * 10;
    494			else
    495				temp = di->bat_temp * 10;
    496		} else if (di->events.btemp_medhigh) {
    497			if (temp < di->btemp_ranges.btemp_med_limit)
    498				temp = di->btemp_ranges.btemp_med_limit * 10;
    499			else
    500				temp = di->bat_temp * 10;
    501		} else
    502			temp = di->bat_temp * 10;
    503	}
    504	return temp;
    505}
    506
    507/**
    508 * ab8500_btemp_get_property() - get the btemp properties
    509 * @psy:        pointer to the power_supply structure
    510 * @psp:        pointer to the power_supply_property structure
    511 * @val:        pointer to the power_supply_propval union
    512 *
    513 * This function gets called when an application tries to get the btemp
    514 * properties by reading the sysfs files.
    515 * online:	presence of the battery
    516 * present:	presence of the battery
    517 * technology:	battery technology
    518 * temp:	battery temperature
    519 * Returns error code in case of failure else 0(on success)
    520 */
    521static int ab8500_btemp_get_property(struct power_supply *psy,
    522	enum power_supply_property psp,
    523	union power_supply_propval *val)
    524{
    525	struct ab8500_btemp *di = power_supply_get_drvdata(psy);
    526
    527	switch (psp) {
    528	case POWER_SUPPLY_PROP_PRESENT:
    529	case POWER_SUPPLY_PROP_ONLINE:
    530		if (di->events.batt_rem)
    531			val->intval = 0;
    532		else
    533			val->intval = 1;
    534		break;
    535	case POWER_SUPPLY_PROP_TECHNOLOGY:
    536		if (di->bm->bi)
    537			val->intval = di->bm->bi->technology;
    538		else
    539			val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
    540		break;
    541	case POWER_SUPPLY_PROP_TEMP:
    542		val->intval = ab8500_btemp_get_temp(di);
    543		break;
    544	default:
    545		return -EINVAL;
    546	}
    547	return 0;
    548}
    549
    550static int ab8500_btemp_get_ext_psy_data(struct device *dev, void *data)
    551{
    552	struct power_supply *psy;
    553	struct power_supply *ext = dev_get_drvdata(dev);
    554	const char **supplicants = (const char **)ext->supplied_to;
    555	struct ab8500_btemp *di;
    556	union power_supply_propval ret;
    557	int j;
    558
    559	psy = (struct power_supply *)data;
    560	di = power_supply_get_drvdata(psy);
    561
    562	/*
    563	 * For all psy where the name of your driver
    564	 * appears in any supplied_to
    565	 */
    566	j = match_string(supplicants, ext->num_supplicants, psy->desc->name);
    567	if (j < 0)
    568		return 0;
    569
    570	/* Go through all properties for the psy */
    571	for (j = 0; j < ext->desc->num_properties; j++) {
    572		enum power_supply_property prop;
    573		prop = ext->desc->properties[j];
    574
    575		if (power_supply_get_property(ext, prop, &ret))
    576			continue;
    577
    578		switch (prop) {
    579		case POWER_SUPPLY_PROP_PRESENT:
    580			switch (ext->desc->type) {
    581			case POWER_SUPPLY_TYPE_MAINS:
    582				/* AC disconnected */
    583				if (!ret.intval && di->events.ac_conn) {
    584					di->events.ac_conn = false;
    585				}
    586				/* AC connected */
    587				else if (ret.intval && !di->events.ac_conn) {
    588					di->events.ac_conn = true;
    589					if (!di->events.usb_conn)
    590						ab8500_btemp_periodic(di, true);
    591				}
    592				break;
    593			case POWER_SUPPLY_TYPE_USB:
    594				/* USB disconnected */
    595				if (!ret.intval && di->events.usb_conn) {
    596					di->events.usb_conn = false;
    597				}
    598				/* USB connected */
    599				else if (ret.intval && !di->events.usb_conn) {
    600					di->events.usb_conn = true;
    601					if (!di->events.ac_conn)
    602						ab8500_btemp_periodic(di, true);
    603				}
    604				break;
    605			default:
    606				break;
    607			}
    608			break;
    609		default:
    610			break;
    611		}
    612	}
    613	return 0;
    614}
    615
    616/**
    617 * ab8500_btemp_external_power_changed() - callback for power supply changes
    618 * @psy:       pointer to the structure power_supply
    619 *
    620 * This function is pointing to the function pointer external_power_changed
    621 * of the structure power_supply.
    622 * This function gets executed when there is a change in the external power
    623 * supply to the btemp.
    624 */
    625static void ab8500_btemp_external_power_changed(struct power_supply *psy)
    626{
    627	struct ab8500_btemp *di = power_supply_get_drvdata(psy);
    628
    629	class_for_each_device(power_supply_class, NULL,
    630		di->btemp_psy, ab8500_btemp_get_ext_psy_data);
    631}
    632
    633/* ab8500 btemp driver interrupts and their respective isr */
    634static struct ab8500_btemp_interrupts ab8500_btemp_irq[] = {
    635	{"BAT_CTRL_INDB", ab8500_btemp_batctrlindb_handler},
    636	{"BTEMP_LOW", ab8500_btemp_templow_handler},
    637	{"BTEMP_HIGH", ab8500_btemp_temphigh_handler},
    638	{"BTEMP_LOW_MEDIUM", ab8500_btemp_lowmed_handler},
    639	{"BTEMP_MEDIUM_HIGH", ab8500_btemp_medhigh_handler},
    640};
    641
    642static int __maybe_unused ab8500_btemp_resume(struct device *dev)
    643{
    644	struct ab8500_btemp *di = dev_get_drvdata(dev);
    645
    646	ab8500_btemp_periodic(di, true);
    647
    648	return 0;
    649}
    650
    651static int __maybe_unused ab8500_btemp_suspend(struct device *dev)
    652{
    653	struct ab8500_btemp *di = dev_get_drvdata(dev);
    654
    655	ab8500_btemp_periodic(di, false);
    656
    657	return 0;
    658}
    659
    660static char *supply_interface[] = {
    661	"ab8500_chargalg",
    662	"ab8500_fg",
    663};
    664
    665static const struct power_supply_desc ab8500_btemp_desc = {
    666	.name			= "ab8500_btemp",
    667	.type			= POWER_SUPPLY_TYPE_BATTERY,
    668	.properties		= ab8500_btemp_props,
    669	.num_properties		= ARRAY_SIZE(ab8500_btemp_props),
    670	.get_property		= ab8500_btemp_get_property,
    671	.external_power_changed	= ab8500_btemp_external_power_changed,
    672};
    673
    674static int ab8500_btemp_bind(struct device *dev, struct device *master,
    675			     void *data)
    676{
    677	struct ab8500_btemp *di = dev_get_drvdata(dev);
    678
    679	/* Create a work queue for the btemp */
    680	di->btemp_wq =
    681		alloc_workqueue("ab8500_btemp_wq", WQ_MEM_RECLAIM, 0);
    682	if (di->btemp_wq == NULL) {
    683		dev_err(dev, "failed to create work queue\n");
    684		return -ENOMEM;
    685	}
    686
    687	/* Kick off periodic temperature measurements */
    688	ab8500_btemp_periodic(di, true);
    689
    690	return 0;
    691}
    692
    693static void ab8500_btemp_unbind(struct device *dev, struct device *master,
    694				void *data)
    695{
    696	struct ab8500_btemp *di = dev_get_drvdata(dev);
    697
    698	/* Delete the work queue */
    699	destroy_workqueue(di->btemp_wq);
    700	flush_scheduled_work();
    701}
    702
    703static const struct component_ops ab8500_btemp_component_ops = {
    704	.bind = ab8500_btemp_bind,
    705	.unbind = ab8500_btemp_unbind,
    706};
    707
    708static int ab8500_btemp_probe(struct platform_device *pdev)
    709{
    710	struct power_supply_config psy_cfg = {};
    711	struct device *dev = &pdev->dev;
    712	struct ab8500_btemp *di;
    713	int irq, i, ret = 0;
    714	u8 val;
    715
    716	di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL);
    717	if (!di)
    718		return -ENOMEM;
    719
    720	di->bm = &ab8500_bm_data;
    721
    722	/* get parent data */
    723	di->dev = dev;
    724	di->parent = dev_get_drvdata(pdev->dev.parent);
    725
    726	/* Get thermal zone and ADC */
    727	di->tz = thermal_zone_get_zone_by_name("battery-thermal");
    728	if (IS_ERR(di->tz)) {
    729		return dev_err_probe(dev, PTR_ERR(di->tz),
    730				     "failed to get battery thermal zone\n");
    731	}
    732	di->bat_ctrl = devm_iio_channel_get(dev, "bat_ctrl");
    733	if (IS_ERR(di->bat_ctrl)) {
    734		ret = dev_err_probe(dev, PTR_ERR(di->bat_ctrl),
    735				    "failed to get BAT CTRL ADC channel\n");
    736		return ret;
    737	}
    738
    739	di->initialized = false;
    740
    741	psy_cfg.supplied_to = supply_interface;
    742	psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface);
    743	psy_cfg.drv_data = di;
    744
    745	/* Init work for measuring temperature periodically */
    746	INIT_DEFERRABLE_WORK(&di->btemp_periodic_work,
    747		ab8500_btemp_periodic_work);
    748
    749	/* Set BTEMP thermal limits. Low and Med are fixed */
    750	di->btemp_ranges.btemp_low_limit = BTEMP_THERMAL_LOW_LIMIT;
    751	di->btemp_ranges.btemp_med_limit = BTEMP_THERMAL_MED_LIMIT;
    752
    753	ret = abx500_get_register_interruptible(dev, AB8500_CHARGER,
    754		AB8500_BTEMP_HIGH_TH, &val);
    755	if (ret < 0) {
    756		dev_err(dev, "%s ab8500 read failed\n", __func__);
    757		return ret;
    758	}
    759	switch (val) {
    760	case BTEMP_HIGH_TH_57_0:
    761	case BTEMP_HIGH_TH_57_1:
    762		di->btemp_ranges.btemp_high_limit =
    763			BTEMP_THERMAL_HIGH_LIMIT_57;
    764		break;
    765	case BTEMP_HIGH_TH_52:
    766		di->btemp_ranges.btemp_high_limit =
    767			BTEMP_THERMAL_HIGH_LIMIT_52;
    768		break;
    769	case BTEMP_HIGH_TH_62:
    770		di->btemp_ranges.btemp_high_limit =
    771			BTEMP_THERMAL_HIGH_LIMIT_62;
    772		break;
    773	}
    774
    775	/* Register BTEMP power supply class */
    776	di->btemp_psy = devm_power_supply_register(dev, &ab8500_btemp_desc,
    777						   &psy_cfg);
    778	if (IS_ERR(di->btemp_psy)) {
    779		dev_err(dev, "failed to register BTEMP psy\n");
    780		return PTR_ERR(di->btemp_psy);
    781	}
    782
    783	/* Register interrupts */
    784	for (i = 0; i < ARRAY_SIZE(ab8500_btemp_irq); i++) {
    785		irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name);
    786		if (irq < 0)
    787			return irq;
    788
    789		ret = devm_request_threaded_irq(dev, irq, NULL,
    790			ab8500_btemp_irq[i].isr,
    791			IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT,
    792			ab8500_btemp_irq[i].name, di);
    793
    794		if (ret) {
    795			dev_err(dev, "failed to request %s IRQ %d: %d\n"
    796				, ab8500_btemp_irq[i].name, irq, ret);
    797			return ret;
    798		}
    799		dev_dbg(dev, "Requested %s IRQ %d: %d\n",
    800			ab8500_btemp_irq[i].name, irq, ret);
    801	}
    802
    803	platform_set_drvdata(pdev, di);
    804
    805	list_add_tail(&di->node, &ab8500_btemp_list);
    806
    807	return component_add(dev, &ab8500_btemp_component_ops);
    808}
    809
    810static int ab8500_btemp_remove(struct platform_device *pdev)
    811{
    812	component_del(&pdev->dev, &ab8500_btemp_component_ops);
    813
    814	return 0;
    815}
    816
    817static SIMPLE_DEV_PM_OPS(ab8500_btemp_pm_ops, ab8500_btemp_suspend, ab8500_btemp_resume);
    818
    819static const struct of_device_id ab8500_btemp_match[] = {
    820	{ .compatible = "stericsson,ab8500-btemp", },
    821	{ },
    822};
    823MODULE_DEVICE_TABLE(of, ab8500_btemp_match);
    824
    825struct platform_driver ab8500_btemp_driver = {
    826	.probe = ab8500_btemp_probe,
    827	.remove = ab8500_btemp_remove,
    828	.driver = {
    829		.name = "ab8500-btemp",
    830		.of_match_table = ab8500_btemp_match,
    831		.pm = &ab8500_btemp_pm_ops,
    832	},
    833};
    834MODULE_LICENSE("GPL v2");
    835MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy");
    836MODULE_ALIAS("platform:ab8500-btemp");
    837MODULE_DESCRIPTION("AB8500 battery temperature driver");