162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) ST-Ericsson SA 2012 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Battery temperature driver for AB8500 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: 862306a36Sopenharmony_ci * Johan Palsson <johan.palsson@stericsson.com> 962306a36Sopenharmony_ci * Karl Komierowski <karl.komierowski@stericsson.com> 1062306a36Sopenharmony_ci * Arun R Murthy <arun.murthy@stericsson.com> 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/device.h> 1662306a36Sopenharmony_ci#include <linux/component.h> 1762306a36Sopenharmony_ci#include <linux/interrupt.h> 1862306a36Sopenharmony_ci#include <linux/delay.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/platform_device.h> 2162306a36Sopenharmony_ci#include <linux/power_supply.h> 2262306a36Sopenharmony_ci#include <linux/completion.h> 2362306a36Sopenharmony_ci#include <linux/workqueue.h> 2462306a36Sopenharmony_ci#include <linux/jiffies.h> 2562306a36Sopenharmony_ci#include <linux/of.h> 2662306a36Sopenharmony_ci#include <linux/mfd/core.h> 2762306a36Sopenharmony_ci#include <linux/mfd/abx500.h> 2862306a36Sopenharmony_ci#include <linux/mfd/abx500/ab8500.h> 2962306a36Sopenharmony_ci#include <linux/thermal.h> 3062306a36Sopenharmony_ci#include <linux/iio/consumer.h> 3162306a36Sopenharmony_ci#include <linux/fixp-arith.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include "ab8500-bm.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define BTEMP_THERMAL_LOW_LIMIT -10 3662306a36Sopenharmony_ci#define BTEMP_THERMAL_MED_LIMIT 0 3762306a36Sopenharmony_ci#define BTEMP_THERMAL_HIGH_LIMIT_52 52 3862306a36Sopenharmony_ci#define BTEMP_THERMAL_HIGH_LIMIT_57 57 3962306a36Sopenharmony_ci#define BTEMP_THERMAL_HIGH_LIMIT_62 62 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define BTEMP_BATCTRL_CURR_SRC_7UA 7 4262306a36Sopenharmony_ci#define BTEMP_BATCTRL_CURR_SRC_20UA 20 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define BTEMP_BATCTRL_CURR_SRC_16UA 16 4562306a36Sopenharmony_ci#define BTEMP_BATCTRL_CURR_SRC_18UA 18 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define BTEMP_BATCTRL_CURR_SRC_60UA 60 4862306a36Sopenharmony_ci#define BTEMP_BATCTRL_CURR_SRC_120UA 120 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/** 5162306a36Sopenharmony_ci * struct ab8500_btemp_interrupts - ab8500 interrupts 5262306a36Sopenharmony_ci * @name: name of the interrupt 5362306a36Sopenharmony_ci * @isr function pointer to the isr 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_cistruct ab8500_btemp_interrupts { 5662306a36Sopenharmony_ci char *name; 5762306a36Sopenharmony_ci irqreturn_t (*isr)(int irq, void *data); 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistruct ab8500_btemp_events { 6162306a36Sopenharmony_ci bool batt_rem; 6262306a36Sopenharmony_ci bool btemp_high; 6362306a36Sopenharmony_ci bool btemp_medhigh; 6462306a36Sopenharmony_ci bool btemp_lowmed; 6562306a36Sopenharmony_ci bool btemp_low; 6662306a36Sopenharmony_ci bool ac_conn; 6762306a36Sopenharmony_ci bool usb_conn; 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistruct ab8500_btemp_ranges { 7162306a36Sopenharmony_ci int btemp_high_limit; 7262306a36Sopenharmony_ci int btemp_med_limit; 7362306a36Sopenharmony_ci int btemp_low_limit; 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/** 7762306a36Sopenharmony_ci * struct ab8500_btemp - ab8500 BTEMP device information 7862306a36Sopenharmony_ci * @dev: Pointer to the structure device 7962306a36Sopenharmony_ci * @node: List of AB8500 BTEMPs, hence prepared for reentrance 8062306a36Sopenharmony_ci * @curr_source: What current source we use, in uA 8162306a36Sopenharmony_ci * @bat_temp: Dispatched battery temperature in degree Celsius 8262306a36Sopenharmony_ci * @prev_bat_temp Last measured battery temperature in degree Celsius 8362306a36Sopenharmony_ci * @parent: Pointer to the struct ab8500 8462306a36Sopenharmony_ci * @tz: Thermal zone for the battery 8562306a36Sopenharmony_ci * @adc_bat_ctrl: ADC channel for the battery control 8662306a36Sopenharmony_ci * @fg: Pointer to the struct fg 8762306a36Sopenharmony_ci * @bm: Platform specific battery management information 8862306a36Sopenharmony_ci * @btemp_psy: Structure for BTEMP specific battery properties 8962306a36Sopenharmony_ci * @events: Structure for information about events triggered 9062306a36Sopenharmony_ci * @btemp_ranges: Battery temperature range structure 9162306a36Sopenharmony_ci * @btemp_wq: Work queue for measuring the temperature periodically 9262306a36Sopenharmony_ci * @btemp_periodic_work: Work for measuring the temperature periodically 9362306a36Sopenharmony_ci * @initialized: True if battery id read. 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_cistruct ab8500_btemp { 9662306a36Sopenharmony_ci struct device *dev; 9762306a36Sopenharmony_ci struct list_head node; 9862306a36Sopenharmony_ci int curr_source; 9962306a36Sopenharmony_ci int bat_temp; 10062306a36Sopenharmony_ci int prev_bat_temp; 10162306a36Sopenharmony_ci struct ab8500 *parent; 10262306a36Sopenharmony_ci struct thermal_zone_device *tz; 10362306a36Sopenharmony_ci struct iio_channel *bat_ctrl; 10462306a36Sopenharmony_ci struct ab8500_fg *fg; 10562306a36Sopenharmony_ci struct ab8500_bm_data *bm; 10662306a36Sopenharmony_ci struct power_supply *btemp_psy; 10762306a36Sopenharmony_ci struct ab8500_btemp_events events; 10862306a36Sopenharmony_ci struct ab8500_btemp_ranges btemp_ranges; 10962306a36Sopenharmony_ci struct workqueue_struct *btemp_wq; 11062306a36Sopenharmony_ci struct delayed_work btemp_periodic_work; 11162306a36Sopenharmony_ci bool initialized; 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* BTEMP power supply properties */ 11562306a36Sopenharmony_cistatic enum power_supply_property ab8500_btemp_props[] = { 11662306a36Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 11762306a36Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 11862306a36Sopenharmony_ci POWER_SUPPLY_PROP_TEMP, 11962306a36Sopenharmony_ci}; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic LIST_HEAD(ab8500_btemp_list); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/** 12462306a36Sopenharmony_ci * ab8500_btemp_batctrl_volt_to_res() - convert batctrl voltage to resistance 12562306a36Sopenharmony_ci * @di: pointer to the ab8500_btemp structure 12662306a36Sopenharmony_ci * @v_batctrl: measured batctrl voltage 12762306a36Sopenharmony_ci * @inst_curr: measured instant current 12862306a36Sopenharmony_ci * 12962306a36Sopenharmony_ci * This function returns the battery resistance that is 13062306a36Sopenharmony_ci * derived from the BATCTRL voltage. 13162306a36Sopenharmony_ci * Returns value in Ohms. 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_cistatic int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di, 13462306a36Sopenharmony_ci int v_batctrl, int inst_curr) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci if (is_ab8500_1p1_or_earlier(di->parent)) { 13762306a36Sopenharmony_ci /* 13862306a36Sopenharmony_ci * For ABB cut1.0 and 1.1 BAT_CTRL is internally 13962306a36Sopenharmony_ci * connected to 1.8V through a 450k resistor 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_ci return (450000 * (v_batctrl)) / (1800 - v_batctrl); 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* 14562306a36Sopenharmony_ci * BAT_CTRL is internally 14662306a36Sopenharmony_ci * connected to 1.8V through a 80k resistor 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ci return (80000 * (v_batctrl)) / (1800 - v_batctrl); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/** 15262306a36Sopenharmony_ci * ab8500_btemp_read_batctrl_voltage() - measure batctrl voltage 15362306a36Sopenharmony_ci * @di: pointer to the ab8500_btemp structure 15462306a36Sopenharmony_ci * 15562306a36Sopenharmony_ci * This function returns the voltage on BATCTRL. Returns value in mV. 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_cistatic int ab8500_btemp_read_batctrl_voltage(struct ab8500_btemp *di) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci int vbtemp, ret; 16062306a36Sopenharmony_ci static int prev; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci ret = iio_read_channel_processed(di->bat_ctrl, &vbtemp); 16362306a36Sopenharmony_ci if (ret < 0) { 16462306a36Sopenharmony_ci dev_err(di->dev, 16562306a36Sopenharmony_ci "%s ADC conversion failed, using previous value", 16662306a36Sopenharmony_ci __func__); 16762306a36Sopenharmony_ci return prev; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci prev = vbtemp; 17062306a36Sopenharmony_ci return vbtemp; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci/** 17462306a36Sopenharmony_ci * ab8500_btemp_get_batctrl_res() - get battery resistance 17562306a36Sopenharmony_ci * @di: pointer to the ab8500_btemp structure 17662306a36Sopenharmony_ci * 17762306a36Sopenharmony_ci * This function returns the battery pack identification resistance. 17862306a36Sopenharmony_ci * Returns value in Ohms. 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_cistatic int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci int ret; 18362306a36Sopenharmony_ci int batctrl = 0; 18462306a36Sopenharmony_ci int res; 18562306a36Sopenharmony_ci int inst_curr; 18662306a36Sopenharmony_ci int i; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (!di->fg) 18962306a36Sopenharmony_ci di->fg = ab8500_fg_get(); 19062306a36Sopenharmony_ci if (!di->fg) { 19162306a36Sopenharmony_ci dev_err(di->dev, "No fg found\n"); 19262306a36Sopenharmony_ci return -EINVAL; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci ret = ab8500_fg_inst_curr_start(di->fg); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (ret) { 19862306a36Sopenharmony_ci dev_err(di->dev, "Failed to start current measurement\n"); 19962306a36Sopenharmony_ci return ret; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci do { 20362306a36Sopenharmony_ci msleep(20); 20462306a36Sopenharmony_ci } while (!ab8500_fg_inst_curr_started(di->fg)); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci i = 0; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci do { 20962306a36Sopenharmony_ci batctrl += ab8500_btemp_read_batctrl_voltage(di); 21062306a36Sopenharmony_ci i++; 21162306a36Sopenharmony_ci msleep(20); 21262306a36Sopenharmony_ci } while (!ab8500_fg_inst_curr_done(di->fg)); 21362306a36Sopenharmony_ci batctrl /= i; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci ret = ab8500_fg_inst_curr_finalize(di->fg, &inst_curr); 21662306a36Sopenharmony_ci if (ret) { 21762306a36Sopenharmony_ci dev_err(di->dev, "Failed to finalize current measurement\n"); 21862306a36Sopenharmony_ci return ret; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci res = ab8500_btemp_batctrl_volt_to_res(di, batctrl, inst_curr); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci dev_dbg(di->dev, "%s batctrl: %d res: %d inst_curr: %d samples: %d\n", 22462306a36Sopenharmony_ci __func__, batctrl, res, inst_curr, i); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci return res; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/** 23062306a36Sopenharmony_ci * ab8500_btemp_id() - Identify the connected battery 23162306a36Sopenharmony_ci * @di: pointer to the ab8500_btemp structure 23262306a36Sopenharmony_ci * 23362306a36Sopenharmony_ci * This function will try to identify the battery by reading the ID 23462306a36Sopenharmony_ci * resistor. Some brands use a combined ID resistor with a NTC resistor to 23562306a36Sopenharmony_ci * both be able to identify and to read the temperature of it. 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_cistatic int ab8500_btemp_id(struct ab8500_btemp *di) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci struct power_supply_battery_info *bi = di->bm->bi; 24062306a36Sopenharmony_ci int res; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci di->curr_source = BTEMP_BATCTRL_CURR_SRC_7UA; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci res = ab8500_btemp_get_batctrl_res(di); 24562306a36Sopenharmony_ci if (res < 0) { 24662306a36Sopenharmony_ci dev_err(di->dev, "%s get batctrl res failed\n", __func__); 24762306a36Sopenharmony_ci return -ENXIO; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (power_supply_battery_bti_in_range(bi, res)) { 25162306a36Sopenharmony_ci dev_info(di->dev, "Battery detected on BATCTRL (pin C3)" 25262306a36Sopenharmony_ci " resistance %d Ohm = %d Ohm +/- %d%%\n", 25362306a36Sopenharmony_ci res, bi->bti_resistance_ohm, 25462306a36Sopenharmony_ci bi->bti_resistance_tolerance); 25562306a36Sopenharmony_ci } else { 25662306a36Sopenharmony_ci dev_warn(di->dev, "Battery identified as unknown" 25762306a36Sopenharmony_ci ", resistance %d Ohm\n", res); 25862306a36Sopenharmony_ci return -ENXIO; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return 0; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci/** 26562306a36Sopenharmony_ci * ab8500_btemp_periodic_work() - Measuring the temperature periodically 26662306a36Sopenharmony_ci * @work: pointer to the work_struct structure 26762306a36Sopenharmony_ci * 26862306a36Sopenharmony_ci * Work function for measuring the temperature periodically 26962306a36Sopenharmony_ci */ 27062306a36Sopenharmony_cistatic void ab8500_btemp_periodic_work(struct work_struct *work) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci int interval; 27362306a36Sopenharmony_ci int bat_temp; 27462306a36Sopenharmony_ci struct ab8500_btemp *di = container_of(work, 27562306a36Sopenharmony_ci struct ab8500_btemp, btemp_periodic_work.work); 27662306a36Sopenharmony_ci /* Assume 25 degrees celsius as start temperature */ 27762306a36Sopenharmony_ci static int prev = 25; 27862306a36Sopenharmony_ci int ret; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (!di->initialized) { 28162306a36Sopenharmony_ci /* Identify the battery */ 28262306a36Sopenharmony_ci if (ab8500_btemp_id(di) < 0) 28362306a36Sopenharmony_ci dev_warn(di->dev, "failed to identify the battery\n"); 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* Failover if a reading is erroneous, use last meausurement */ 28762306a36Sopenharmony_ci ret = thermal_zone_get_temp(di->tz, &bat_temp); 28862306a36Sopenharmony_ci if (ret) { 28962306a36Sopenharmony_ci dev_err(di->dev, "error reading temperature\n"); 29062306a36Sopenharmony_ci bat_temp = prev; 29162306a36Sopenharmony_ci } else { 29262306a36Sopenharmony_ci /* Convert from millicentigrades to centigrades */ 29362306a36Sopenharmony_ci bat_temp /= 1000; 29462306a36Sopenharmony_ci prev = bat_temp; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* 29862306a36Sopenharmony_ci * Filter battery temperature. 29962306a36Sopenharmony_ci * Allow direct updates on temperature only if two samples result in 30062306a36Sopenharmony_ci * same temperature. Else only allow 1 degree change from previous 30162306a36Sopenharmony_ci * reported value in the direction of the new measurement. 30262306a36Sopenharmony_ci */ 30362306a36Sopenharmony_ci if ((bat_temp == di->prev_bat_temp) || !di->initialized) { 30462306a36Sopenharmony_ci if ((di->bat_temp != di->prev_bat_temp) || !di->initialized) { 30562306a36Sopenharmony_ci di->initialized = true; 30662306a36Sopenharmony_ci di->bat_temp = bat_temp; 30762306a36Sopenharmony_ci power_supply_changed(di->btemp_psy); 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci } else if (bat_temp < di->prev_bat_temp) { 31062306a36Sopenharmony_ci di->bat_temp--; 31162306a36Sopenharmony_ci power_supply_changed(di->btemp_psy); 31262306a36Sopenharmony_ci } else if (bat_temp > di->prev_bat_temp) { 31362306a36Sopenharmony_ci di->bat_temp++; 31462306a36Sopenharmony_ci power_supply_changed(di->btemp_psy); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci di->prev_bat_temp = bat_temp; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (di->events.ac_conn || di->events.usb_conn) 31962306a36Sopenharmony_ci interval = di->bm->temp_interval_chg; 32062306a36Sopenharmony_ci else 32162306a36Sopenharmony_ci interval = di->bm->temp_interval_nochg; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* Schedule a new measurement */ 32462306a36Sopenharmony_ci queue_delayed_work(di->btemp_wq, 32562306a36Sopenharmony_ci &di->btemp_periodic_work, 32662306a36Sopenharmony_ci round_jiffies(interval * HZ)); 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci/** 33062306a36Sopenharmony_ci * ab8500_btemp_batctrlindb_handler() - battery removal detected 33162306a36Sopenharmony_ci * @irq: interrupt number 33262306a36Sopenharmony_ci * @_di: void pointer that has to address of ab8500_btemp 33362306a36Sopenharmony_ci * 33462306a36Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED) 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_cistatic irqreturn_t ab8500_btemp_batctrlindb_handler(int irq, void *_di) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct ab8500_btemp *di = _di; 33962306a36Sopenharmony_ci dev_err(di->dev, "Battery removal detected!\n"); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci di->events.batt_rem = true; 34262306a36Sopenharmony_ci power_supply_changed(di->btemp_psy); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci return IRQ_HANDLED; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci/** 34862306a36Sopenharmony_ci * ab8500_btemp_templow_handler() - battery temp lower than 10 degrees 34962306a36Sopenharmony_ci * @irq: interrupt number 35062306a36Sopenharmony_ci * @_di: void pointer that has to address of ab8500_btemp 35162306a36Sopenharmony_ci * 35262306a36Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED) 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_cistatic irqreturn_t ab8500_btemp_templow_handler(int irq, void *_di) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct ab8500_btemp *di = _di; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (is_ab8500_3p3_or_earlier(di->parent)) { 35962306a36Sopenharmony_ci dev_dbg(di->dev, "Ignore false btemp low irq" 36062306a36Sopenharmony_ci " for ABB cut 1.0, 1.1, 2.0 and 3.3\n"); 36162306a36Sopenharmony_ci } else { 36262306a36Sopenharmony_ci dev_crit(di->dev, "Battery temperature lower than -10deg c\n"); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci di->events.btemp_low = true; 36562306a36Sopenharmony_ci di->events.btemp_high = false; 36662306a36Sopenharmony_ci di->events.btemp_medhigh = false; 36762306a36Sopenharmony_ci di->events.btemp_lowmed = false; 36862306a36Sopenharmony_ci power_supply_changed(di->btemp_psy); 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci return IRQ_HANDLED; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci/** 37562306a36Sopenharmony_ci * ab8500_btemp_temphigh_handler() - battery temp higher than max temp 37662306a36Sopenharmony_ci * @irq: interrupt number 37762306a36Sopenharmony_ci * @_di: void pointer that has to address of ab8500_btemp 37862306a36Sopenharmony_ci * 37962306a36Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED) 38062306a36Sopenharmony_ci */ 38162306a36Sopenharmony_cistatic irqreturn_t ab8500_btemp_temphigh_handler(int irq, void *_di) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci struct ab8500_btemp *di = _di; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci dev_crit(di->dev, "Battery temperature is higher than MAX temp\n"); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci di->events.btemp_high = true; 38862306a36Sopenharmony_ci di->events.btemp_medhigh = false; 38962306a36Sopenharmony_ci di->events.btemp_lowmed = false; 39062306a36Sopenharmony_ci di->events.btemp_low = false; 39162306a36Sopenharmony_ci power_supply_changed(di->btemp_psy); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci return IRQ_HANDLED; 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci/** 39762306a36Sopenharmony_ci * ab8500_btemp_lowmed_handler() - battery temp between low and medium 39862306a36Sopenharmony_ci * @irq: interrupt number 39962306a36Sopenharmony_ci * @_di: void pointer that has to address of ab8500_btemp 40062306a36Sopenharmony_ci * 40162306a36Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED) 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_cistatic irqreturn_t ab8500_btemp_lowmed_handler(int irq, void *_di) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct ab8500_btemp *di = _di; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci dev_dbg(di->dev, "Battery temperature is between low and medium\n"); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci di->events.btemp_lowmed = true; 41062306a36Sopenharmony_ci di->events.btemp_medhigh = false; 41162306a36Sopenharmony_ci di->events.btemp_high = false; 41262306a36Sopenharmony_ci di->events.btemp_low = false; 41362306a36Sopenharmony_ci power_supply_changed(di->btemp_psy); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci return IRQ_HANDLED; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci/** 41962306a36Sopenharmony_ci * ab8500_btemp_medhigh_handler() - battery temp between medium and high 42062306a36Sopenharmony_ci * @irq: interrupt number 42162306a36Sopenharmony_ci * @_di: void pointer that has to address of ab8500_btemp 42262306a36Sopenharmony_ci * 42362306a36Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED) 42462306a36Sopenharmony_ci */ 42562306a36Sopenharmony_cistatic irqreturn_t ab8500_btemp_medhigh_handler(int irq, void *_di) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct ab8500_btemp *di = _di; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci dev_dbg(di->dev, "Battery temperature is between medium and high\n"); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci di->events.btemp_medhigh = true; 43262306a36Sopenharmony_ci di->events.btemp_lowmed = false; 43362306a36Sopenharmony_ci di->events.btemp_high = false; 43462306a36Sopenharmony_ci di->events.btemp_low = false; 43562306a36Sopenharmony_ci power_supply_changed(di->btemp_psy); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci return IRQ_HANDLED; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci/** 44162306a36Sopenharmony_ci * ab8500_btemp_periodic() - Periodic temperature measurements 44262306a36Sopenharmony_ci * @di: pointer to the ab8500_btemp structure 44362306a36Sopenharmony_ci * @enable: enable or disable periodic temperature measurements 44462306a36Sopenharmony_ci * 44562306a36Sopenharmony_ci * Starts of stops periodic temperature measurements. Periodic measurements 44662306a36Sopenharmony_ci * should only be done when a charger is connected. 44762306a36Sopenharmony_ci */ 44862306a36Sopenharmony_cistatic void ab8500_btemp_periodic(struct ab8500_btemp *di, 44962306a36Sopenharmony_ci bool enable) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci dev_dbg(di->dev, "Enable periodic temperature measurements: %d\n", 45262306a36Sopenharmony_ci enable); 45362306a36Sopenharmony_ci /* 45462306a36Sopenharmony_ci * Make sure a new measurement is done directly by cancelling 45562306a36Sopenharmony_ci * any pending work 45662306a36Sopenharmony_ci */ 45762306a36Sopenharmony_ci cancel_delayed_work_sync(&di->btemp_periodic_work); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (enable) 46062306a36Sopenharmony_ci queue_delayed_work(di->btemp_wq, &di->btemp_periodic_work, 0); 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci/** 46462306a36Sopenharmony_ci * ab8500_btemp_get_temp() - get battery temperature 46562306a36Sopenharmony_ci * @di: pointer to the ab8500_btemp structure 46662306a36Sopenharmony_ci * 46762306a36Sopenharmony_ci * Returns battery temperature 46862306a36Sopenharmony_ci */ 46962306a36Sopenharmony_cistatic int ab8500_btemp_get_temp(struct ab8500_btemp *di) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci int temp = 0; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* 47462306a36Sopenharmony_ci * The BTEMP events are not reliabe on AB8500 cut3.3 47562306a36Sopenharmony_ci * and prior versions 47662306a36Sopenharmony_ci */ 47762306a36Sopenharmony_ci if (is_ab8500_3p3_or_earlier(di->parent)) { 47862306a36Sopenharmony_ci temp = di->bat_temp * 10; 47962306a36Sopenharmony_ci } else { 48062306a36Sopenharmony_ci if (di->events.btemp_low) { 48162306a36Sopenharmony_ci if (temp > di->btemp_ranges.btemp_low_limit) 48262306a36Sopenharmony_ci temp = di->btemp_ranges.btemp_low_limit * 10; 48362306a36Sopenharmony_ci else 48462306a36Sopenharmony_ci temp = di->bat_temp * 10; 48562306a36Sopenharmony_ci } else if (di->events.btemp_high) { 48662306a36Sopenharmony_ci if (temp < di->btemp_ranges.btemp_high_limit) 48762306a36Sopenharmony_ci temp = di->btemp_ranges.btemp_high_limit * 10; 48862306a36Sopenharmony_ci else 48962306a36Sopenharmony_ci temp = di->bat_temp * 10; 49062306a36Sopenharmony_ci } else if (di->events.btemp_lowmed) { 49162306a36Sopenharmony_ci if (temp > di->btemp_ranges.btemp_med_limit) 49262306a36Sopenharmony_ci temp = di->btemp_ranges.btemp_med_limit * 10; 49362306a36Sopenharmony_ci else 49462306a36Sopenharmony_ci temp = di->bat_temp * 10; 49562306a36Sopenharmony_ci } else if (di->events.btemp_medhigh) { 49662306a36Sopenharmony_ci if (temp < di->btemp_ranges.btemp_med_limit) 49762306a36Sopenharmony_ci temp = di->btemp_ranges.btemp_med_limit * 10; 49862306a36Sopenharmony_ci else 49962306a36Sopenharmony_ci temp = di->bat_temp * 10; 50062306a36Sopenharmony_ci } else 50162306a36Sopenharmony_ci temp = di->bat_temp * 10; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci return temp; 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci/** 50762306a36Sopenharmony_ci * ab8500_btemp_get_property() - get the btemp properties 50862306a36Sopenharmony_ci * @psy: pointer to the power_supply structure 50962306a36Sopenharmony_ci * @psp: pointer to the power_supply_property structure 51062306a36Sopenharmony_ci * @val: pointer to the power_supply_propval union 51162306a36Sopenharmony_ci * 51262306a36Sopenharmony_ci * This function gets called when an application tries to get the btemp 51362306a36Sopenharmony_ci * properties by reading the sysfs files. 51462306a36Sopenharmony_ci * online: presence of the battery 51562306a36Sopenharmony_ci * present: presence of the battery 51662306a36Sopenharmony_ci * technology: battery technology 51762306a36Sopenharmony_ci * temp: battery temperature 51862306a36Sopenharmony_ci * Returns error code in case of failure else 0(on success) 51962306a36Sopenharmony_ci */ 52062306a36Sopenharmony_cistatic int ab8500_btemp_get_property(struct power_supply *psy, 52162306a36Sopenharmony_ci enum power_supply_property psp, 52262306a36Sopenharmony_ci union power_supply_propval *val) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci struct ab8500_btemp *di = power_supply_get_drvdata(psy); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci switch (psp) { 52762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 52862306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 52962306a36Sopenharmony_ci if (di->events.batt_rem) 53062306a36Sopenharmony_ci val->intval = 0; 53162306a36Sopenharmony_ci else 53262306a36Sopenharmony_ci val->intval = 1; 53362306a36Sopenharmony_ci break; 53462306a36Sopenharmony_ci case POWER_SUPPLY_PROP_TEMP: 53562306a36Sopenharmony_ci val->intval = ab8500_btemp_get_temp(di); 53662306a36Sopenharmony_ci break; 53762306a36Sopenharmony_ci default: 53862306a36Sopenharmony_ci return -EINVAL; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci return 0; 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cistatic int ab8500_btemp_get_ext_psy_data(struct device *dev, void *data) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci struct power_supply *psy; 54662306a36Sopenharmony_ci struct power_supply *ext = dev_get_drvdata(dev); 54762306a36Sopenharmony_ci const char **supplicants = (const char **)ext->supplied_to; 54862306a36Sopenharmony_ci struct ab8500_btemp *di; 54962306a36Sopenharmony_ci union power_supply_propval ret; 55062306a36Sopenharmony_ci int j; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci psy = (struct power_supply *)data; 55362306a36Sopenharmony_ci di = power_supply_get_drvdata(psy); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci /* 55662306a36Sopenharmony_ci * For all psy where the name of your driver 55762306a36Sopenharmony_ci * appears in any supplied_to 55862306a36Sopenharmony_ci */ 55962306a36Sopenharmony_ci j = match_string(supplicants, ext->num_supplicants, psy->desc->name); 56062306a36Sopenharmony_ci if (j < 0) 56162306a36Sopenharmony_ci return 0; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* Go through all properties for the psy */ 56462306a36Sopenharmony_ci for (j = 0; j < ext->desc->num_properties; j++) { 56562306a36Sopenharmony_ci enum power_supply_property prop; 56662306a36Sopenharmony_ci prop = ext->desc->properties[j]; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (power_supply_get_property(ext, prop, &ret)) 56962306a36Sopenharmony_ci continue; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci switch (prop) { 57262306a36Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 57362306a36Sopenharmony_ci switch (ext->desc->type) { 57462306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_MAINS: 57562306a36Sopenharmony_ci /* AC disconnected */ 57662306a36Sopenharmony_ci if (!ret.intval && di->events.ac_conn) { 57762306a36Sopenharmony_ci di->events.ac_conn = false; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci /* AC connected */ 58062306a36Sopenharmony_ci else if (ret.intval && !di->events.ac_conn) { 58162306a36Sopenharmony_ci di->events.ac_conn = true; 58262306a36Sopenharmony_ci if (!di->events.usb_conn) 58362306a36Sopenharmony_ci ab8500_btemp_periodic(di, true); 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci break; 58662306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_USB: 58762306a36Sopenharmony_ci /* USB disconnected */ 58862306a36Sopenharmony_ci if (!ret.intval && di->events.usb_conn) { 58962306a36Sopenharmony_ci di->events.usb_conn = false; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci /* USB connected */ 59262306a36Sopenharmony_ci else if (ret.intval && !di->events.usb_conn) { 59362306a36Sopenharmony_ci di->events.usb_conn = true; 59462306a36Sopenharmony_ci if (!di->events.ac_conn) 59562306a36Sopenharmony_ci ab8500_btemp_periodic(di, true); 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci break; 59862306a36Sopenharmony_ci default: 59962306a36Sopenharmony_ci break; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci break; 60262306a36Sopenharmony_ci default: 60362306a36Sopenharmony_ci break; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci return 0; 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci/** 61062306a36Sopenharmony_ci * ab8500_btemp_external_power_changed() - callback for power supply changes 61162306a36Sopenharmony_ci * @psy: pointer to the structure power_supply 61262306a36Sopenharmony_ci * 61362306a36Sopenharmony_ci * This function is pointing to the function pointer external_power_changed 61462306a36Sopenharmony_ci * of the structure power_supply. 61562306a36Sopenharmony_ci * This function gets executed when there is a change in the external power 61662306a36Sopenharmony_ci * supply to the btemp. 61762306a36Sopenharmony_ci */ 61862306a36Sopenharmony_cistatic void ab8500_btemp_external_power_changed(struct power_supply *psy) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci class_for_each_device(power_supply_class, NULL, psy, 62162306a36Sopenharmony_ci ab8500_btemp_get_ext_psy_data); 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci/* ab8500 btemp driver interrupts and their respective isr */ 62562306a36Sopenharmony_cistatic struct ab8500_btemp_interrupts ab8500_btemp_irq[] = { 62662306a36Sopenharmony_ci {"BAT_CTRL_INDB", ab8500_btemp_batctrlindb_handler}, 62762306a36Sopenharmony_ci {"BTEMP_LOW", ab8500_btemp_templow_handler}, 62862306a36Sopenharmony_ci {"BTEMP_HIGH", ab8500_btemp_temphigh_handler}, 62962306a36Sopenharmony_ci {"BTEMP_LOW_MEDIUM", ab8500_btemp_lowmed_handler}, 63062306a36Sopenharmony_ci {"BTEMP_MEDIUM_HIGH", ab8500_btemp_medhigh_handler}, 63162306a36Sopenharmony_ci}; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic int __maybe_unused ab8500_btemp_resume(struct device *dev) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci struct ab8500_btemp *di = dev_get_drvdata(dev); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci ab8500_btemp_periodic(di, true); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci return 0; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cistatic int __maybe_unused ab8500_btemp_suspend(struct device *dev) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci struct ab8500_btemp *di = dev_get_drvdata(dev); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci ab8500_btemp_periodic(di, false); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci return 0; 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_cistatic char *supply_interface[] = { 65262306a36Sopenharmony_ci "ab8500_chargalg", 65362306a36Sopenharmony_ci "ab8500_fg", 65462306a36Sopenharmony_ci}; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic const struct power_supply_desc ab8500_btemp_desc = { 65762306a36Sopenharmony_ci .name = "ab8500_btemp", 65862306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_UNKNOWN, 65962306a36Sopenharmony_ci .properties = ab8500_btemp_props, 66062306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(ab8500_btemp_props), 66162306a36Sopenharmony_ci .get_property = ab8500_btemp_get_property, 66262306a36Sopenharmony_ci .external_power_changed = ab8500_btemp_external_power_changed, 66362306a36Sopenharmony_ci}; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cistatic int ab8500_btemp_bind(struct device *dev, struct device *master, 66662306a36Sopenharmony_ci void *data) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci struct ab8500_btemp *di = dev_get_drvdata(dev); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci /* Create a work queue for the btemp */ 67162306a36Sopenharmony_ci di->btemp_wq = 67262306a36Sopenharmony_ci alloc_workqueue("ab8500_btemp_wq", WQ_MEM_RECLAIM, 0); 67362306a36Sopenharmony_ci if (di->btemp_wq == NULL) { 67462306a36Sopenharmony_ci dev_err(dev, "failed to create work queue\n"); 67562306a36Sopenharmony_ci return -ENOMEM; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci /* Kick off periodic temperature measurements */ 67962306a36Sopenharmony_ci ab8500_btemp_periodic(di, true); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci return 0; 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistatic void ab8500_btemp_unbind(struct device *dev, struct device *master, 68562306a36Sopenharmony_ci void *data) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci struct ab8500_btemp *di = dev_get_drvdata(dev); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci /* Delete the work queue */ 69062306a36Sopenharmony_ci destroy_workqueue(di->btemp_wq); 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_cistatic const struct component_ops ab8500_btemp_component_ops = { 69462306a36Sopenharmony_ci .bind = ab8500_btemp_bind, 69562306a36Sopenharmony_ci .unbind = ab8500_btemp_unbind, 69662306a36Sopenharmony_ci}; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic int ab8500_btemp_probe(struct platform_device *pdev) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci struct power_supply_config psy_cfg = {}; 70162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 70262306a36Sopenharmony_ci struct ab8500_btemp *di; 70362306a36Sopenharmony_ci int irq, i, ret = 0; 70462306a36Sopenharmony_ci u8 val; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL); 70762306a36Sopenharmony_ci if (!di) 70862306a36Sopenharmony_ci return -ENOMEM; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci di->bm = &ab8500_bm_data; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* get parent data */ 71362306a36Sopenharmony_ci di->dev = dev; 71462306a36Sopenharmony_ci di->parent = dev_get_drvdata(pdev->dev.parent); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci /* Get thermal zone and ADC */ 71762306a36Sopenharmony_ci di->tz = thermal_zone_get_zone_by_name("battery-thermal"); 71862306a36Sopenharmony_ci if (IS_ERR(di->tz)) { 71962306a36Sopenharmony_ci ret = PTR_ERR(di->tz); 72062306a36Sopenharmony_ci /* 72162306a36Sopenharmony_ci * This usually just means we are probing before the thermal 72262306a36Sopenharmony_ci * zone, so just defer. 72362306a36Sopenharmony_ci */ 72462306a36Sopenharmony_ci if (ret == -ENODEV) 72562306a36Sopenharmony_ci ret = -EPROBE_DEFER; 72662306a36Sopenharmony_ci return dev_err_probe(dev, ret, 72762306a36Sopenharmony_ci "failed to get battery thermal zone\n"); 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci di->bat_ctrl = devm_iio_channel_get(dev, "bat_ctrl"); 73062306a36Sopenharmony_ci if (IS_ERR(di->bat_ctrl)) { 73162306a36Sopenharmony_ci ret = dev_err_probe(dev, PTR_ERR(di->bat_ctrl), 73262306a36Sopenharmony_ci "failed to get BAT CTRL ADC channel\n"); 73362306a36Sopenharmony_ci return ret; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci di->initialized = false; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci psy_cfg.supplied_to = supply_interface; 73962306a36Sopenharmony_ci psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface); 74062306a36Sopenharmony_ci psy_cfg.drv_data = di; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci /* Init work for measuring temperature periodically */ 74362306a36Sopenharmony_ci INIT_DEFERRABLE_WORK(&di->btemp_periodic_work, 74462306a36Sopenharmony_ci ab8500_btemp_periodic_work); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci /* Set BTEMP thermal limits. Low and Med are fixed */ 74762306a36Sopenharmony_ci di->btemp_ranges.btemp_low_limit = BTEMP_THERMAL_LOW_LIMIT; 74862306a36Sopenharmony_ci di->btemp_ranges.btemp_med_limit = BTEMP_THERMAL_MED_LIMIT; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci ret = abx500_get_register_interruptible(dev, AB8500_CHARGER, 75162306a36Sopenharmony_ci AB8500_BTEMP_HIGH_TH, &val); 75262306a36Sopenharmony_ci if (ret < 0) { 75362306a36Sopenharmony_ci dev_err(dev, "%s ab8500 read failed\n", __func__); 75462306a36Sopenharmony_ci return ret; 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci switch (val) { 75762306a36Sopenharmony_ci case BTEMP_HIGH_TH_57_0: 75862306a36Sopenharmony_ci case BTEMP_HIGH_TH_57_1: 75962306a36Sopenharmony_ci di->btemp_ranges.btemp_high_limit = 76062306a36Sopenharmony_ci BTEMP_THERMAL_HIGH_LIMIT_57; 76162306a36Sopenharmony_ci break; 76262306a36Sopenharmony_ci case BTEMP_HIGH_TH_52: 76362306a36Sopenharmony_ci di->btemp_ranges.btemp_high_limit = 76462306a36Sopenharmony_ci BTEMP_THERMAL_HIGH_LIMIT_52; 76562306a36Sopenharmony_ci break; 76662306a36Sopenharmony_ci case BTEMP_HIGH_TH_62: 76762306a36Sopenharmony_ci di->btemp_ranges.btemp_high_limit = 76862306a36Sopenharmony_ci BTEMP_THERMAL_HIGH_LIMIT_62; 76962306a36Sopenharmony_ci break; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* Register BTEMP power supply class */ 77362306a36Sopenharmony_ci di->btemp_psy = devm_power_supply_register(dev, &ab8500_btemp_desc, 77462306a36Sopenharmony_ci &psy_cfg); 77562306a36Sopenharmony_ci if (IS_ERR(di->btemp_psy)) { 77662306a36Sopenharmony_ci dev_err(dev, "failed to register BTEMP psy\n"); 77762306a36Sopenharmony_ci return PTR_ERR(di->btemp_psy); 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci /* Register interrupts */ 78162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ab8500_btemp_irq); i++) { 78262306a36Sopenharmony_ci irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name); 78362306a36Sopenharmony_ci if (irq < 0) 78462306a36Sopenharmony_ci return irq; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci ret = devm_request_threaded_irq(dev, irq, NULL, 78762306a36Sopenharmony_ci ab8500_btemp_irq[i].isr, 78862306a36Sopenharmony_ci IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT, 78962306a36Sopenharmony_ci ab8500_btemp_irq[i].name, di); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci if (ret) { 79262306a36Sopenharmony_ci dev_err(dev, "failed to request %s IRQ %d: %d\n" 79362306a36Sopenharmony_ci , ab8500_btemp_irq[i].name, irq, ret); 79462306a36Sopenharmony_ci return ret; 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci dev_dbg(dev, "Requested %s IRQ %d: %d\n", 79762306a36Sopenharmony_ci ab8500_btemp_irq[i].name, irq, ret); 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci platform_set_drvdata(pdev, di); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci list_add_tail(&di->node, &ab8500_btemp_list); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci return component_add(dev, &ab8500_btemp_component_ops); 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cistatic int ab8500_btemp_remove(struct platform_device *pdev) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci component_del(&pdev->dev, &ab8500_btemp_component_ops); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci return 0; 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(ab8500_btemp_pm_ops, ab8500_btemp_suspend, ab8500_btemp_resume); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cistatic const struct of_device_id ab8500_btemp_match[] = { 81762306a36Sopenharmony_ci { .compatible = "stericsson,ab8500-btemp", }, 81862306a36Sopenharmony_ci { }, 81962306a36Sopenharmony_ci}; 82062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ab8500_btemp_match); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_cistruct platform_driver ab8500_btemp_driver = { 82362306a36Sopenharmony_ci .probe = ab8500_btemp_probe, 82462306a36Sopenharmony_ci .remove = ab8500_btemp_remove, 82562306a36Sopenharmony_ci .driver = { 82662306a36Sopenharmony_ci .name = "ab8500-btemp", 82762306a36Sopenharmony_ci .of_match_table = ab8500_btemp_match, 82862306a36Sopenharmony_ci .pm = &ab8500_btemp_pm_ops, 82962306a36Sopenharmony_ci }, 83062306a36Sopenharmony_ci}; 83162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 83262306a36Sopenharmony_ciMODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy"); 83362306a36Sopenharmony_ciMODULE_ALIAS("platform:ab8500-btemp"); 83462306a36Sopenharmony_ciMODULE_DESCRIPTION("AB8500 battery temperature driver"); 835