0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #include <linux/init.h>
0014 #include <linux/module.h>
0015 #include <linux/device.h>
0016 #include <linux/component.h>
0017 #include <linux/interrupt.h>
0018 #include <linux/delay.h>
0019 #include <linux/slab.h>
0020 #include <linux/platform_device.h>
0021 #include <linux/power_supply.h>
0022 #include <linux/completion.h>
0023 #include <linux/workqueue.h>
0024 #include <linux/jiffies.h>
0025 #include <linux/of.h>
0026 #include <linux/mfd/core.h>
0027 #include <linux/mfd/abx500.h>
0028 #include <linux/mfd/abx500/ab8500.h>
0029 #include <linux/thermal.h>
0030 #include <linux/iio/consumer.h>
0031 #include <linux/fixp-arith.h>
0032
0033 #include "ab8500-bm.h"
0034
0035 #define BTEMP_THERMAL_LOW_LIMIT -10
0036 #define BTEMP_THERMAL_MED_LIMIT 0
0037 #define BTEMP_THERMAL_HIGH_LIMIT_52 52
0038 #define BTEMP_THERMAL_HIGH_LIMIT_57 57
0039 #define BTEMP_THERMAL_HIGH_LIMIT_62 62
0040
0041 #define BTEMP_BATCTRL_CURR_SRC_7UA 7
0042 #define BTEMP_BATCTRL_CURR_SRC_20UA 20
0043
0044 #define BTEMP_BATCTRL_CURR_SRC_16UA 16
0045 #define BTEMP_BATCTRL_CURR_SRC_18UA 18
0046
0047 #define BTEMP_BATCTRL_CURR_SRC_60UA 60
0048 #define BTEMP_BATCTRL_CURR_SRC_120UA 120
0049
0050
0051
0052
0053
0054
0055 struct ab8500_btemp_interrupts {
0056 char *name;
0057 irqreturn_t (*isr)(int irq, void *data);
0058 };
0059
0060 struct ab8500_btemp_events {
0061 bool batt_rem;
0062 bool btemp_high;
0063 bool btemp_medhigh;
0064 bool btemp_lowmed;
0065 bool btemp_low;
0066 bool ac_conn;
0067 bool usb_conn;
0068 };
0069
0070 struct ab8500_btemp_ranges {
0071 int btemp_high_limit;
0072 int btemp_med_limit;
0073 int btemp_low_limit;
0074 };
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095 struct ab8500_btemp {
0096 struct device *dev;
0097 struct list_head node;
0098 int curr_source;
0099 int bat_temp;
0100 int prev_bat_temp;
0101 struct ab8500 *parent;
0102 struct thermal_zone_device *tz;
0103 struct iio_channel *bat_ctrl;
0104 struct ab8500_fg *fg;
0105 struct ab8500_bm_data *bm;
0106 struct power_supply *btemp_psy;
0107 struct ab8500_btemp_events events;
0108 struct ab8500_btemp_ranges btemp_ranges;
0109 struct workqueue_struct *btemp_wq;
0110 struct delayed_work btemp_periodic_work;
0111 bool initialized;
0112 };
0113
0114
0115 static enum power_supply_property ab8500_btemp_props[] = {
0116 POWER_SUPPLY_PROP_PRESENT,
0117 POWER_SUPPLY_PROP_ONLINE,
0118 POWER_SUPPLY_PROP_TECHNOLOGY,
0119 POWER_SUPPLY_PROP_TEMP,
0120 };
0121
0122 static LIST_HEAD(ab8500_btemp_list);
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134 static int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di,
0135 int v_batctrl, int inst_curr)
0136 {
0137 if (is_ab8500_1p1_or_earlier(di->parent)) {
0138
0139
0140
0141
0142 return (450000 * (v_batctrl)) / (1800 - v_batctrl);
0143 }
0144
0145
0146
0147
0148
0149 return (80000 * (v_batctrl)) / (1800 - v_batctrl);
0150 }
0151
0152
0153
0154
0155
0156
0157
0158 static int ab8500_btemp_read_batctrl_voltage(struct ab8500_btemp *di)
0159 {
0160 int vbtemp, ret;
0161 static int prev;
0162
0163 ret = iio_read_channel_processed(di->bat_ctrl, &vbtemp);
0164 if (ret < 0) {
0165 dev_err(di->dev,
0166 "%s ADC conversion failed, using previous value",
0167 __func__);
0168 return prev;
0169 }
0170 prev = vbtemp;
0171 return vbtemp;
0172 }
0173
0174
0175
0176
0177
0178
0179
0180
0181 static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di)
0182 {
0183 int ret;
0184 int batctrl = 0;
0185 int res;
0186 int inst_curr;
0187 int i;
0188
0189 if (!di->fg)
0190 di->fg = ab8500_fg_get();
0191 if (!di->fg) {
0192 dev_err(di->dev, "No fg found\n");
0193 return -EINVAL;
0194 }
0195
0196 ret = ab8500_fg_inst_curr_start(di->fg);
0197
0198 if (ret) {
0199 dev_err(di->dev, "Failed to start current measurement\n");
0200 return ret;
0201 }
0202
0203 do {
0204 msleep(20);
0205 } while (!ab8500_fg_inst_curr_started(di->fg));
0206
0207 i = 0;
0208
0209 do {
0210 batctrl += ab8500_btemp_read_batctrl_voltage(di);
0211 i++;
0212 msleep(20);
0213 } while (!ab8500_fg_inst_curr_done(di->fg));
0214 batctrl /= i;
0215
0216 ret = ab8500_fg_inst_curr_finalize(di->fg, &inst_curr);
0217 if (ret) {
0218 dev_err(di->dev, "Failed to finalize current measurement\n");
0219 return ret;
0220 }
0221
0222 res = ab8500_btemp_batctrl_volt_to_res(di, batctrl, inst_curr);
0223
0224 dev_dbg(di->dev, "%s batctrl: %d res: %d inst_curr: %d samples: %d\n",
0225 __func__, batctrl, res, inst_curr, i);
0226
0227 return res;
0228 }
0229
0230
0231
0232
0233
0234
0235
0236
0237
0238 static int ab8500_btemp_id(struct ab8500_btemp *di)
0239 {
0240 struct power_supply_battery_info *bi = di->bm->bi;
0241 int res;
0242
0243 di->curr_source = BTEMP_BATCTRL_CURR_SRC_7UA;
0244
0245 res = ab8500_btemp_get_batctrl_res(di);
0246 if (res < 0) {
0247 dev_err(di->dev, "%s get batctrl res failed\n", __func__);
0248 return -ENXIO;
0249 }
0250
0251 if (power_supply_battery_bti_in_range(bi, res)) {
0252 dev_info(di->dev, "Battery detected on BATCTRL (pin C3)"
0253 " resistance %d Ohm = %d Ohm +/- %d%%\n",
0254 res, bi->bti_resistance_ohm,
0255 bi->bti_resistance_tolerance);
0256 } else {
0257 dev_warn(di->dev, "Battery identified as unknown"
0258 ", resistance %d Ohm\n", res);
0259 return -ENXIO;
0260 }
0261
0262 return 0;
0263 }
0264
0265
0266
0267
0268
0269
0270
0271 static void ab8500_btemp_periodic_work(struct work_struct *work)
0272 {
0273 int interval;
0274 int bat_temp;
0275 struct ab8500_btemp *di = container_of(work,
0276 struct ab8500_btemp, btemp_periodic_work.work);
0277
0278 static int prev = 25;
0279 int ret;
0280
0281 if (!di->initialized) {
0282
0283 if (ab8500_btemp_id(di) < 0)
0284 dev_warn(di->dev, "failed to identify the battery\n");
0285 }
0286
0287
0288 ret = thermal_zone_get_temp(di->tz, &bat_temp);
0289 if (ret) {
0290 dev_err(di->dev, "error reading temperature\n");
0291 bat_temp = prev;
0292 } else {
0293
0294 bat_temp /= 1000;
0295 prev = bat_temp;
0296 }
0297
0298
0299
0300
0301
0302
0303
0304 if ((bat_temp == di->prev_bat_temp) || !di->initialized) {
0305 if ((di->bat_temp != di->prev_bat_temp) || !di->initialized) {
0306 di->initialized = true;
0307 di->bat_temp = bat_temp;
0308 power_supply_changed(di->btemp_psy);
0309 }
0310 } else if (bat_temp < di->prev_bat_temp) {
0311 di->bat_temp--;
0312 power_supply_changed(di->btemp_psy);
0313 } else if (bat_temp > di->prev_bat_temp) {
0314 di->bat_temp++;
0315 power_supply_changed(di->btemp_psy);
0316 }
0317 di->prev_bat_temp = bat_temp;
0318
0319 if (di->events.ac_conn || di->events.usb_conn)
0320 interval = di->bm->temp_interval_chg;
0321 else
0322 interval = di->bm->temp_interval_nochg;
0323
0324
0325 queue_delayed_work(di->btemp_wq,
0326 &di->btemp_periodic_work,
0327 round_jiffies(interval * HZ));
0328 }
0329
0330
0331
0332
0333
0334
0335
0336
0337 static irqreturn_t ab8500_btemp_batctrlindb_handler(int irq, void *_di)
0338 {
0339 struct ab8500_btemp *di = _di;
0340 dev_err(di->dev, "Battery removal detected!\n");
0341
0342 di->events.batt_rem = true;
0343 power_supply_changed(di->btemp_psy);
0344
0345 return IRQ_HANDLED;
0346 }
0347
0348
0349
0350
0351
0352
0353
0354
0355 static irqreturn_t ab8500_btemp_templow_handler(int irq, void *_di)
0356 {
0357 struct ab8500_btemp *di = _di;
0358
0359 if (is_ab8500_3p3_or_earlier(di->parent)) {
0360 dev_dbg(di->dev, "Ignore false btemp low irq"
0361 " for ABB cut 1.0, 1.1, 2.0 and 3.3\n");
0362 } else {
0363 dev_crit(di->dev, "Battery temperature lower than -10deg c\n");
0364
0365 di->events.btemp_low = true;
0366 di->events.btemp_high = false;
0367 di->events.btemp_medhigh = false;
0368 di->events.btemp_lowmed = false;
0369 power_supply_changed(di->btemp_psy);
0370 }
0371
0372 return IRQ_HANDLED;
0373 }
0374
0375
0376
0377
0378
0379
0380
0381
0382 static irqreturn_t ab8500_btemp_temphigh_handler(int irq, void *_di)
0383 {
0384 struct ab8500_btemp *di = _di;
0385
0386 dev_crit(di->dev, "Battery temperature is higher than MAX temp\n");
0387
0388 di->events.btemp_high = true;
0389 di->events.btemp_medhigh = false;
0390 di->events.btemp_lowmed = false;
0391 di->events.btemp_low = false;
0392 power_supply_changed(di->btemp_psy);
0393
0394 return IRQ_HANDLED;
0395 }
0396
0397
0398
0399
0400
0401
0402
0403
0404 static irqreturn_t ab8500_btemp_lowmed_handler(int irq, void *_di)
0405 {
0406 struct ab8500_btemp *di = _di;
0407
0408 dev_dbg(di->dev, "Battery temperature is between low and medium\n");
0409
0410 di->events.btemp_lowmed = true;
0411 di->events.btemp_medhigh = false;
0412 di->events.btemp_high = false;
0413 di->events.btemp_low = false;
0414 power_supply_changed(di->btemp_psy);
0415
0416 return IRQ_HANDLED;
0417 }
0418
0419
0420
0421
0422
0423
0424
0425
0426 static irqreturn_t ab8500_btemp_medhigh_handler(int irq, void *_di)
0427 {
0428 struct ab8500_btemp *di = _di;
0429
0430 dev_dbg(di->dev, "Battery temperature is between medium and high\n");
0431
0432 di->events.btemp_medhigh = true;
0433 di->events.btemp_lowmed = false;
0434 di->events.btemp_high = false;
0435 di->events.btemp_low = false;
0436 power_supply_changed(di->btemp_psy);
0437
0438 return IRQ_HANDLED;
0439 }
0440
0441
0442
0443
0444
0445
0446
0447
0448
0449 static void ab8500_btemp_periodic(struct ab8500_btemp *di,
0450 bool enable)
0451 {
0452 dev_dbg(di->dev, "Enable periodic temperature measurements: %d\n",
0453 enable);
0454
0455
0456
0457
0458 cancel_delayed_work_sync(&di->btemp_periodic_work);
0459
0460 if (enable)
0461 queue_delayed_work(di->btemp_wq, &di->btemp_periodic_work, 0);
0462 }
0463
0464
0465
0466
0467
0468
0469
0470 static int ab8500_btemp_get_temp(struct ab8500_btemp *di)
0471 {
0472 int temp = 0;
0473
0474
0475
0476
0477
0478 if (is_ab8500_3p3_or_earlier(di->parent)) {
0479 temp = di->bat_temp * 10;
0480 } else {
0481 if (di->events.btemp_low) {
0482 if (temp > di->btemp_ranges.btemp_low_limit)
0483 temp = di->btemp_ranges.btemp_low_limit * 10;
0484 else
0485 temp = di->bat_temp * 10;
0486 } else if (di->events.btemp_high) {
0487 if (temp < di->btemp_ranges.btemp_high_limit)
0488 temp = di->btemp_ranges.btemp_high_limit * 10;
0489 else
0490 temp = di->bat_temp * 10;
0491 } else if (di->events.btemp_lowmed) {
0492 if (temp > di->btemp_ranges.btemp_med_limit)
0493 temp = di->btemp_ranges.btemp_med_limit * 10;
0494 else
0495 temp = di->bat_temp * 10;
0496 } else if (di->events.btemp_medhigh) {
0497 if (temp < di->btemp_ranges.btemp_med_limit)
0498 temp = di->btemp_ranges.btemp_med_limit * 10;
0499 else
0500 temp = di->bat_temp * 10;
0501 } else
0502 temp = di->bat_temp * 10;
0503 }
0504 return temp;
0505 }
0506
0507
0508
0509
0510
0511
0512
0513
0514
0515
0516
0517
0518
0519
0520
0521 static int ab8500_btemp_get_property(struct power_supply *psy,
0522 enum power_supply_property psp,
0523 union power_supply_propval *val)
0524 {
0525 struct ab8500_btemp *di = power_supply_get_drvdata(psy);
0526
0527 switch (psp) {
0528 case POWER_SUPPLY_PROP_PRESENT:
0529 case POWER_SUPPLY_PROP_ONLINE:
0530 if (di->events.batt_rem)
0531 val->intval = 0;
0532 else
0533 val->intval = 1;
0534 break;
0535 case POWER_SUPPLY_PROP_TECHNOLOGY:
0536 if (di->bm->bi)
0537 val->intval = di->bm->bi->technology;
0538 else
0539 val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
0540 break;
0541 case POWER_SUPPLY_PROP_TEMP:
0542 val->intval = ab8500_btemp_get_temp(di);
0543 break;
0544 default:
0545 return -EINVAL;
0546 }
0547 return 0;
0548 }
0549
0550 static int ab8500_btemp_get_ext_psy_data(struct device *dev, void *data)
0551 {
0552 struct power_supply *psy;
0553 struct power_supply *ext = dev_get_drvdata(dev);
0554 const char **supplicants = (const char **)ext->supplied_to;
0555 struct ab8500_btemp *di;
0556 union power_supply_propval ret;
0557 int j;
0558
0559 psy = (struct power_supply *)data;
0560 di = power_supply_get_drvdata(psy);
0561
0562
0563
0564
0565
0566 j = match_string(supplicants, ext->num_supplicants, psy->desc->name);
0567 if (j < 0)
0568 return 0;
0569
0570
0571 for (j = 0; j < ext->desc->num_properties; j++) {
0572 enum power_supply_property prop;
0573 prop = ext->desc->properties[j];
0574
0575 if (power_supply_get_property(ext, prop, &ret))
0576 continue;
0577
0578 switch (prop) {
0579 case POWER_SUPPLY_PROP_PRESENT:
0580 switch (ext->desc->type) {
0581 case POWER_SUPPLY_TYPE_MAINS:
0582
0583 if (!ret.intval && di->events.ac_conn) {
0584 di->events.ac_conn = false;
0585 }
0586
0587 else if (ret.intval && !di->events.ac_conn) {
0588 di->events.ac_conn = true;
0589 if (!di->events.usb_conn)
0590 ab8500_btemp_periodic(di, true);
0591 }
0592 break;
0593 case POWER_SUPPLY_TYPE_USB:
0594
0595 if (!ret.intval && di->events.usb_conn) {
0596 di->events.usb_conn = false;
0597 }
0598
0599 else if (ret.intval && !di->events.usb_conn) {
0600 di->events.usb_conn = true;
0601 if (!di->events.ac_conn)
0602 ab8500_btemp_periodic(di, true);
0603 }
0604 break;
0605 default:
0606 break;
0607 }
0608 break;
0609 default:
0610 break;
0611 }
0612 }
0613 return 0;
0614 }
0615
0616
0617
0618
0619
0620
0621
0622
0623
0624
0625 static void ab8500_btemp_external_power_changed(struct power_supply *psy)
0626 {
0627 struct ab8500_btemp *di = power_supply_get_drvdata(psy);
0628
0629 class_for_each_device(power_supply_class, NULL,
0630 di->btemp_psy, ab8500_btemp_get_ext_psy_data);
0631 }
0632
0633
0634 static struct ab8500_btemp_interrupts ab8500_btemp_irq[] = {
0635 {"BAT_CTRL_INDB", ab8500_btemp_batctrlindb_handler},
0636 {"BTEMP_LOW", ab8500_btemp_templow_handler},
0637 {"BTEMP_HIGH", ab8500_btemp_temphigh_handler},
0638 {"BTEMP_LOW_MEDIUM", ab8500_btemp_lowmed_handler},
0639 {"BTEMP_MEDIUM_HIGH", ab8500_btemp_medhigh_handler},
0640 };
0641
0642 static int __maybe_unused ab8500_btemp_resume(struct device *dev)
0643 {
0644 struct ab8500_btemp *di = dev_get_drvdata(dev);
0645
0646 ab8500_btemp_periodic(di, true);
0647
0648 return 0;
0649 }
0650
0651 static int __maybe_unused ab8500_btemp_suspend(struct device *dev)
0652 {
0653 struct ab8500_btemp *di = dev_get_drvdata(dev);
0654
0655 ab8500_btemp_periodic(di, false);
0656
0657 return 0;
0658 }
0659
0660 static char *supply_interface[] = {
0661 "ab8500_chargalg",
0662 "ab8500_fg",
0663 };
0664
0665 static const struct power_supply_desc ab8500_btemp_desc = {
0666 .name = "ab8500_btemp",
0667 .type = POWER_SUPPLY_TYPE_BATTERY,
0668 .properties = ab8500_btemp_props,
0669 .num_properties = ARRAY_SIZE(ab8500_btemp_props),
0670 .get_property = ab8500_btemp_get_property,
0671 .external_power_changed = ab8500_btemp_external_power_changed,
0672 };
0673
0674 static int ab8500_btemp_bind(struct device *dev, struct device *master,
0675 void *data)
0676 {
0677 struct ab8500_btemp *di = dev_get_drvdata(dev);
0678
0679
0680 di->btemp_wq =
0681 alloc_workqueue("ab8500_btemp_wq", WQ_MEM_RECLAIM, 0);
0682 if (di->btemp_wq == NULL) {
0683 dev_err(dev, "failed to create work queue\n");
0684 return -ENOMEM;
0685 }
0686
0687
0688 ab8500_btemp_periodic(di, true);
0689
0690 return 0;
0691 }
0692
0693 static void ab8500_btemp_unbind(struct device *dev, struct device *master,
0694 void *data)
0695 {
0696 struct ab8500_btemp *di = dev_get_drvdata(dev);
0697
0698
0699 destroy_workqueue(di->btemp_wq);
0700 }
0701
0702 static const struct component_ops ab8500_btemp_component_ops = {
0703 .bind = ab8500_btemp_bind,
0704 .unbind = ab8500_btemp_unbind,
0705 };
0706
0707 static int ab8500_btemp_probe(struct platform_device *pdev)
0708 {
0709 struct power_supply_config psy_cfg = {};
0710 struct device *dev = &pdev->dev;
0711 struct ab8500_btemp *di;
0712 int irq, i, ret = 0;
0713 u8 val;
0714
0715 di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL);
0716 if (!di)
0717 return -ENOMEM;
0718
0719 di->bm = &ab8500_bm_data;
0720
0721
0722 di->dev = dev;
0723 di->parent = dev_get_drvdata(pdev->dev.parent);
0724
0725
0726 di->tz = thermal_zone_get_zone_by_name("battery-thermal");
0727 if (IS_ERR(di->tz)) {
0728 return dev_err_probe(dev, PTR_ERR(di->tz),
0729 "failed to get battery thermal zone\n");
0730 }
0731 di->bat_ctrl = devm_iio_channel_get(dev, "bat_ctrl");
0732 if (IS_ERR(di->bat_ctrl)) {
0733 ret = dev_err_probe(dev, PTR_ERR(di->bat_ctrl),
0734 "failed to get BAT CTRL ADC channel\n");
0735 return ret;
0736 }
0737
0738 di->initialized = false;
0739
0740 psy_cfg.supplied_to = supply_interface;
0741 psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface);
0742 psy_cfg.drv_data = di;
0743
0744
0745 INIT_DEFERRABLE_WORK(&di->btemp_periodic_work,
0746 ab8500_btemp_periodic_work);
0747
0748
0749 di->btemp_ranges.btemp_low_limit = BTEMP_THERMAL_LOW_LIMIT;
0750 di->btemp_ranges.btemp_med_limit = BTEMP_THERMAL_MED_LIMIT;
0751
0752 ret = abx500_get_register_interruptible(dev, AB8500_CHARGER,
0753 AB8500_BTEMP_HIGH_TH, &val);
0754 if (ret < 0) {
0755 dev_err(dev, "%s ab8500 read failed\n", __func__);
0756 return ret;
0757 }
0758 switch (val) {
0759 case BTEMP_HIGH_TH_57_0:
0760 case BTEMP_HIGH_TH_57_1:
0761 di->btemp_ranges.btemp_high_limit =
0762 BTEMP_THERMAL_HIGH_LIMIT_57;
0763 break;
0764 case BTEMP_HIGH_TH_52:
0765 di->btemp_ranges.btemp_high_limit =
0766 BTEMP_THERMAL_HIGH_LIMIT_52;
0767 break;
0768 case BTEMP_HIGH_TH_62:
0769 di->btemp_ranges.btemp_high_limit =
0770 BTEMP_THERMAL_HIGH_LIMIT_62;
0771 break;
0772 }
0773
0774
0775 di->btemp_psy = devm_power_supply_register(dev, &ab8500_btemp_desc,
0776 &psy_cfg);
0777 if (IS_ERR(di->btemp_psy)) {
0778 dev_err(dev, "failed to register BTEMP psy\n");
0779 return PTR_ERR(di->btemp_psy);
0780 }
0781
0782
0783 for (i = 0; i < ARRAY_SIZE(ab8500_btemp_irq); i++) {
0784 irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name);
0785 if (irq < 0)
0786 return irq;
0787
0788 ret = devm_request_threaded_irq(dev, irq, NULL,
0789 ab8500_btemp_irq[i].isr,
0790 IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT,
0791 ab8500_btemp_irq[i].name, di);
0792
0793 if (ret) {
0794 dev_err(dev, "failed to request %s IRQ %d: %d\n"
0795 , ab8500_btemp_irq[i].name, irq, ret);
0796 return ret;
0797 }
0798 dev_dbg(dev, "Requested %s IRQ %d: %d\n",
0799 ab8500_btemp_irq[i].name, irq, ret);
0800 }
0801
0802 platform_set_drvdata(pdev, di);
0803
0804 list_add_tail(&di->node, &ab8500_btemp_list);
0805
0806 return component_add(dev, &ab8500_btemp_component_ops);
0807 }
0808
0809 static int ab8500_btemp_remove(struct platform_device *pdev)
0810 {
0811 component_del(&pdev->dev, &ab8500_btemp_component_ops);
0812
0813 return 0;
0814 }
0815
0816 static SIMPLE_DEV_PM_OPS(ab8500_btemp_pm_ops, ab8500_btemp_suspend, ab8500_btemp_resume);
0817
0818 static const struct of_device_id ab8500_btemp_match[] = {
0819 { .compatible = "stericsson,ab8500-btemp", },
0820 { },
0821 };
0822 MODULE_DEVICE_TABLE(of, ab8500_btemp_match);
0823
0824 struct platform_driver ab8500_btemp_driver = {
0825 .probe = ab8500_btemp_probe,
0826 .remove = ab8500_btemp_remove,
0827 .driver = {
0828 .name = "ab8500-btemp",
0829 .of_match_table = ab8500_btemp_match,
0830 .pm = &ab8500_btemp_pm_ops,
0831 },
0832 };
0833 MODULE_LICENSE("GPL v2");
0834 MODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy");
0835 MODULE_ALIAS("platform:ab8500-btemp");
0836 MODULE_DESCRIPTION("AB8500 battery temperature driver");