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