Merge tag 'for-v4.8-rc' into psy-next
authorSebastian Reichel <sre@kernel.org>
Mon, 15 Aug 2016 23:17:42 +0000 (01:17 +0200)
committerSebastian Reichel <sre@kernel.org>
Mon, 15 Aug 2016 23:17:42 +0000 (01:17 +0200)
Power Supply Fixes for 4.8 cycle

1  2 
drivers/power/supply/max17042_battery.c
drivers/power/supply/tps65217_charger.c

index 9c65f13,0000000..da7a75f
mode 100644,000000..100644
--- /dev/null
@@@ -1,1016 -1,0 +1,1019 @@@
-                                       u8 addr, u32 *data, int size)
 +/*
 + * Fuel gauge driver for Maxim 17042 / 8966 / 8997
 + *  Note that Maxim 8966 and 8997 are mfd and this is its subdevice.
 + *
 + * Copyright (C) 2011 Samsung Electronics
 + * MyungJoo Ham <myungjoo.ham@samsung.com>
 + *
 + * This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License as published by
 + * the Free Software Foundation; either version 2 of the License, or
 + * (at your option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software
 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 + *
 + * This driver is based on max17040_battery.c
 + */
 +
 +#include <linux/init.h>
 +#include <linux/module.h>
 +#include <linux/slab.h>
 +#include <linux/i2c.h>
 +#include <linux/delay.h>
 +#include <linux/interrupt.h>
 +#include <linux/pm.h>
 +#include <linux/mod_devicetable.h>
 +#include <linux/power_supply.h>
 +#include <linux/power/max17042_battery.h>
 +#include <linux/of.h>
 +#include <linux/regmap.h>
 +
 +/* Status register bits */
 +#define STATUS_POR_BIT         (1 << 1)
 +#define STATUS_BST_BIT         (1 << 3)
 +#define STATUS_VMN_BIT         (1 << 8)
 +#define STATUS_TMN_BIT         (1 << 9)
 +#define STATUS_SMN_BIT         (1 << 10)
 +#define STATUS_BI_BIT          (1 << 11)
 +#define STATUS_VMX_BIT         (1 << 12)
 +#define STATUS_TMX_BIT         (1 << 13)
 +#define STATUS_SMX_BIT         (1 << 14)
 +#define STATUS_BR_BIT          (1 << 15)
 +
 +/* Interrupt mask bits */
 +#define CONFIG_ALRT_BIT_ENBL  (1 << 2)
 +#define STATUS_INTR_SOCMIN_BIT        (1 << 10)
 +#define STATUS_INTR_SOCMAX_BIT        (1 << 14)
 +
 +#define VFSOC0_LOCK           0x0000
 +#define VFSOC0_UNLOCK         0x0080
 +#define MODEL_UNLOCK1 0X0059
 +#define MODEL_UNLOCK2 0X00C4
 +#define MODEL_LOCK1           0X0000
 +#define MODEL_LOCK2           0X0000
 +
 +#define dQ_ACC_DIV    0x4
 +#define dP_ACC_100    0x1900
 +#define dP_ACC_200    0x3200
 +
 +#define MAX17042_VMAX_TOLERANCE               50 /* 50 mV */
 +
 +struct max17042_chip {
 +      struct i2c_client *client;
 +      struct regmap *regmap;
 +      struct power_supply *battery;
 +      enum max170xx_chip_type chip_type;
 +      struct max17042_platform_data *pdata;
 +      struct work_struct work;
 +      int    init_complete;
 +};
 +
 +static enum power_supply_property max17042_battery_props[] = {
 +      POWER_SUPPLY_PROP_PRESENT,
 +      POWER_SUPPLY_PROP_CYCLE_COUNT,
 +      POWER_SUPPLY_PROP_VOLTAGE_MAX,
 +      POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
 +      POWER_SUPPLY_PROP_VOLTAGE_NOW,
 +      POWER_SUPPLY_PROP_VOLTAGE_AVG,
 +      POWER_SUPPLY_PROP_VOLTAGE_OCV,
 +      POWER_SUPPLY_PROP_CAPACITY,
 +      POWER_SUPPLY_PROP_CHARGE_FULL,
 +      POWER_SUPPLY_PROP_CHARGE_COUNTER,
 +      POWER_SUPPLY_PROP_TEMP,
 +      POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
 +      POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
 +      POWER_SUPPLY_PROP_TEMP_MIN,
 +      POWER_SUPPLY_PROP_TEMP_MAX,
 +      POWER_SUPPLY_PROP_HEALTH,
 +      POWER_SUPPLY_PROP_CURRENT_NOW,
 +      POWER_SUPPLY_PROP_CURRENT_AVG,
 +};
 +
 +static int max17042_get_temperature(struct max17042_chip *chip, int *temp)
 +{
 +      int ret;
 +      u32 data;
 +      struct regmap *map = chip->regmap;
 +
 +      ret = regmap_read(map, MAX17042_TEMP, &data);
 +      if (ret < 0)
 +              return ret;
 +
 +      *temp = data;
 +      /* The value is signed. */
 +      if (*temp & 0x8000) {
 +              *temp = (0x7fff & ~*temp) + 1;
 +              *temp *= -1;
 +      }
 +
 +      /* The value is converted into deci-centigrade scale */
 +      /* Units of LSB = 1 / 256 degree Celsius */
 +      *temp = *temp * 10 / 256;
 +      return 0;
 +}
 +
 +static int max17042_get_battery_health(struct max17042_chip *chip, int *health)
 +{
 +      int temp, vavg, vbatt, ret;
 +      u32 val;
 +
 +      ret = regmap_read(chip->regmap, MAX17042_AvgVCELL, &val);
 +      if (ret < 0)
 +              goto health_error;
 +
 +      /* bits [0-3] unused */
 +      vavg = val * 625 / 8;
 +      /* Convert to millivolts */
 +      vavg /= 1000;
 +
 +      ret = regmap_read(chip->regmap, MAX17042_VCELL, &val);
 +      if (ret < 0)
 +              goto health_error;
 +
 +      /* bits [0-3] unused */
 +      vbatt = val * 625 / 8;
 +      /* Convert to millivolts */
 +      vbatt /= 1000;
 +
 +      if (vavg < chip->pdata->vmin) {
 +              *health = POWER_SUPPLY_HEALTH_DEAD;
 +              goto out;
 +      }
 +
 +      if (vbatt > chip->pdata->vmax + MAX17042_VMAX_TOLERANCE) {
 +              *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
 +              goto out;
 +      }
 +
 +      ret = max17042_get_temperature(chip, &temp);
 +      if (ret < 0)
 +              goto health_error;
 +
 +      if (temp <= chip->pdata->temp_min) {
 +              *health = POWER_SUPPLY_HEALTH_COLD;
 +              goto out;
 +      }
 +
 +      if (temp >= chip->pdata->temp_max) {
 +              *health = POWER_SUPPLY_HEALTH_OVERHEAT;
 +              goto out;
 +      }
 +
 +      *health = POWER_SUPPLY_HEALTH_GOOD;
 +
 +out:
 +      return 0;
 +
 +health_error:
 +      return ret;
 +}
 +
 +static int max17042_get_property(struct power_supply *psy,
 +                          enum power_supply_property psp,
 +                          union power_supply_propval *val)
 +{
 +      struct max17042_chip *chip = power_supply_get_drvdata(psy);
 +      struct regmap *map = chip->regmap;
 +      int ret;
 +      u32 data;
 +
 +      if (!chip->init_complete)
 +              return -EAGAIN;
 +
 +      switch (psp) {
 +      case POWER_SUPPLY_PROP_PRESENT:
 +              ret = regmap_read(map, MAX17042_STATUS, &data);
 +              if (ret < 0)
 +                      return ret;
 +
 +              if (data & MAX17042_STATUS_BattAbsent)
 +                      val->intval = 0;
 +              else
 +                      val->intval = 1;
 +              break;
 +      case POWER_SUPPLY_PROP_CYCLE_COUNT:
 +              ret = regmap_read(map, MAX17042_Cycles, &data);
 +              if (ret < 0)
 +                      return ret;
 +
 +              val->intval = data;
 +              break;
 +      case POWER_SUPPLY_PROP_VOLTAGE_MAX:
 +              ret = regmap_read(map, MAX17042_MinMaxVolt, &data);
 +              if (ret < 0)
 +                      return ret;
 +
 +              val->intval = data >> 8;
 +              val->intval *= 20000; /* Units of LSB = 20mV */
 +              break;
 +      case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
 +              if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042)
 +                      ret = regmap_read(map, MAX17042_V_empty, &data);
 +              else
 +                      ret = regmap_read(map, MAX17047_V_empty, &data);
 +              if (ret < 0)
 +                      return ret;
 +
 +              val->intval = data >> 7;
 +              val->intval *= 10000; /* Units of LSB = 10mV */
 +              break;
 +      case POWER_SUPPLY_PROP_VOLTAGE_NOW:
 +              ret = regmap_read(map, MAX17042_VCELL, &data);
 +              if (ret < 0)
 +                      return ret;
 +
 +              val->intval = data * 625 / 8;
 +              break;
 +      case POWER_SUPPLY_PROP_VOLTAGE_AVG:
 +              ret = regmap_read(map, MAX17042_AvgVCELL, &data);
 +              if (ret < 0)
 +                      return ret;
 +
 +              val->intval = data * 625 / 8;
 +              break;
 +      case POWER_SUPPLY_PROP_VOLTAGE_OCV:
 +              ret = regmap_read(map, MAX17042_OCVInternal, &data);
 +              if (ret < 0)
 +                      return ret;
 +
 +              val->intval = data * 625 / 8;
 +              break;
 +      case POWER_SUPPLY_PROP_CAPACITY:
 +              ret = regmap_read(map, MAX17042_RepSOC, &data);
 +              if (ret < 0)
 +                      return ret;
 +
 +              val->intval = data >> 8;
 +              break;
 +      case POWER_SUPPLY_PROP_CHARGE_FULL:
 +              ret = regmap_read(map, MAX17042_FullCAP, &data);
 +              if (ret < 0)
 +                      return ret;
 +
 +              val->intval = data * 1000 / 2;
 +              break;
 +      case POWER_SUPPLY_PROP_CHARGE_COUNTER:
 +              ret = regmap_read(map, MAX17042_QH, &data);
 +              if (ret < 0)
 +                      return ret;
 +
 +              val->intval = data * 1000 / 2;
 +              break;
 +      case POWER_SUPPLY_PROP_TEMP:
 +              ret = max17042_get_temperature(chip, &val->intval);
 +              if (ret < 0)
 +                      return ret;
 +              break;
 +      case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
 +              ret = regmap_read(map, MAX17042_TALRT_Th, &data);
 +              if (ret < 0)
 +                      return ret;
 +              /* LSB is Alert Minimum. In deci-centigrade */
 +              val->intval = (data & 0xff) * 10;
 +              break;
 +      case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
 +              ret = regmap_read(map, MAX17042_TALRT_Th, &data);
 +              if (ret < 0)
 +                      return ret;
 +              /* MSB is Alert Maximum. In deci-centigrade */
 +              val->intval = (data >> 8) * 10;
 +              break;
 +      case POWER_SUPPLY_PROP_TEMP_MIN:
 +              val->intval = chip->pdata->temp_min;
 +              break;
 +      case POWER_SUPPLY_PROP_TEMP_MAX:
 +              val->intval = chip->pdata->temp_max;
 +              break;
 +      case POWER_SUPPLY_PROP_HEALTH:
 +              ret = max17042_get_battery_health(chip, &val->intval);
 +              if (ret < 0)
 +                      return ret;
 +              break;
 +      case POWER_SUPPLY_PROP_CURRENT_NOW:
 +              if (chip->pdata->enable_current_sense) {
 +                      ret = regmap_read(map, MAX17042_Current, &data);
 +                      if (ret < 0)
 +                              return ret;
 +
 +                      val->intval = data;
 +                      if (val->intval & 0x8000) {
 +                              /* Negative */
 +                              val->intval = ~val->intval & 0x7fff;
 +                              val->intval++;
 +                              val->intval *= -1;
 +                      }
 +                      val->intval *= 1562500 / chip->pdata->r_sns;
 +              } else {
 +                      return -EINVAL;
 +              }
 +              break;
 +      case POWER_SUPPLY_PROP_CURRENT_AVG:
 +              if (chip->pdata->enable_current_sense) {
 +                      ret = regmap_read(map, MAX17042_AvgCurrent, &data);
 +                      if (ret < 0)
 +                              return ret;
 +
 +                      val->intval = data;
 +                      if (val->intval & 0x8000) {
 +                              /* Negative */
 +                              val->intval = ~val->intval & 0x7fff;
 +                              val->intval++;
 +                              val->intval *= -1;
 +                      }
 +                      val->intval *= 1562500 / chip->pdata->r_sns;
 +              } else {
 +                      return -EINVAL;
 +              }
 +              break;
 +      default:
 +              return -EINVAL;
 +      }
 +      return 0;
 +}
 +
 +static int max17042_set_property(struct power_supply *psy,
 +                          enum power_supply_property psp,
 +                          const union power_supply_propval *val)
 +{
 +      struct max17042_chip *chip = power_supply_get_drvdata(psy);
 +      struct regmap *map = chip->regmap;
 +      int ret = 0;
 +      u32 data;
 +      int8_t temp;
 +
 +      switch (psp) {
 +      case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
 +              ret = regmap_read(map, MAX17042_TALRT_Th, &data);
 +              if (ret < 0)
 +                      return ret;
 +
 +              /* Input in deci-centigrade, convert to centigrade */
 +              temp = val->intval / 10;
 +              /* force min < max */
 +              if (temp >= (int8_t)(data >> 8))
 +                      temp = (int8_t)(data >> 8) - 1;
 +              /* Write both MAX and MIN ALERT */
 +              data = (data & 0xff00) + temp;
 +              ret = regmap_write(map, MAX17042_TALRT_Th, data);
 +              break;
 +      case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
 +              ret = regmap_read(map, MAX17042_TALRT_Th, &data);
 +              if (ret < 0)
 +                      return ret;
 +
 +              /* Input in Deci-Centigrade, convert to centigrade */
 +              temp = val->intval / 10;
 +              /* force max > min */
 +              if (temp <= (int8_t)(data & 0xff))
 +                      temp = (int8_t)(data & 0xff) + 1;
 +              /* Write both MAX and MIN ALERT */
 +              data = (data & 0xff) + (temp << 8);
 +              ret = regmap_write(map, MAX17042_TALRT_Th, data);
 +              break;
 +      default:
 +              ret = -EINVAL;
 +      }
 +
 +      return ret;
 +}
 +
 +static int max17042_property_is_writeable(struct power_supply *psy,
 +              enum power_supply_property psp)
 +{
 +      int ret;
 +
 +      switch (psp) {
 +      case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
 +      case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
 +              ret = 1;
 +              break;
 +      default:
 +              ret = 0;
 +      }
 +
 +      return ret;
 +}
 +
 +static int max17042_write_verify_reg(struct regmap *map, u8 reg, u32 value)
 +{
 +      int retries = 8;
 +      int ret;
 +      u32 read_value;
 +
 +      do {
 +              ret = regmap_write(map, reg, value);
 +              regmap_read(map, reg, &read_value);
 +              if (read_value != value) {
 +                      ret = -EIO;
 +                      retries--;
 +              }
 +      } while (retries && read_value != value);
 +
 +      if (ret < 0)
 +              pr_err("%s: err %d\n", __func__, ret);
 +
 +      return ret;
 +}
 +
 +static inline void max17042_override_por(struct regmap *map,
 +                                       u8 reg, u16 value)
 +{
 +      if (value)
 +              regmap_write(map, reg, value);
 +}
 +
 +static inline void max10742_unlock_model(struct max17042_chip *chip)
 +{
 +      struct regmap *map = chip->regmap;
 +
 +      regmap_write(map, MAX17042_MLOCKReg1, MODEL_UNLOCK1);
 +      regmap_write(map, MAX17042_MLOCKReg2, MODEL_UNLOCK2);
 +}
 +
 +static inline void max10742_lock_model(struct max17042_chip *chip)
 +{
 +      struct regmap *map = chip->regmap;
 +
 +      regmap_write(map, MAX17042_MLOCKReg1, MODEL_LOCK1);
 +      regmap_write(map, MAX17042_MLOCKReg2, MODEL_LOCK2);
 +}
 +
 +static inline void max17042_write_model_data(struct max17042_chip *chip,
 +                                      u8 addr, int size)
 +{
 +      struct regmap *map = chip->regmap;
 +      int i;
 +
 +      for (i = 0; i < size; i++)
 +              regmap_write(map, addr + i,
 +                      chip->pdata->config_data->cell_char_tbl[i]);
 +}
 +
 +static inline void max17042_read_model_data(struct max17042_chip *chip,
-       for (i = 0; i < size; i++)
-               regmap_read(map, addr + i, &data[i]);
++                                      u8 addr, u16 *data, int size)
 +{
 +      struct regmap *map = chip->regmap;
 +      int i;
++      u32 tmp;
 +
-       u32 *temp_data;
++      for (i = 0; i < size; i++) {
++              regmap_read(map, addr + i, &tmp);
++              data[i] = (u16)tmp;
++      }
 +}
 +
 +static inline int max17042_model_data_compare(struct max17042_chip *chip,
 +                                      u16 *data1, u16 *data2, int size)
 +{
 +      int i;
 +
 +      if (memcmp(data1, data2, size)) {
 +              dev_err(&chip->client->dev, "%s compare failed\n", __func__);
 +              for (i = 0; i < size; i++)
 +                      dev_info(&chip->client->dev, "0x%x, 0x%x",
 +                              data1[i], data2[i]);
 +              dev_info(&chip->client->dev, "\n");
 +              return -EINVAL;
 +      }
 +      return 0;
 +}
 +
 +static int max17042_init_model(struct max17042_chip *chip)
 +{
 +      int ret;
 +      int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl);
-               (u16 *)temp_data,
++      u16 *temp_data;
 +
 +      temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL);
 +      if (!temp_data)
 +              return -ENOMEM;
 +
 +      max10742_unlock_model(chip);
 +      max17042_write_model_data(chip, MAX17042_MODELChrTbl,
 +                              table_size);
 +      max17042_read_model_data(chip, MAX17042_MODELChrTbl, temp_data,
 +                              table_size);
 +
 +      ret = max17042_model_data_compare(
 +              chip,
 +              chip->pdata->config_data->cell_char_tbl,
-       u32 *temp_data;
++              temp_data,
 +              table_size);
 +
 +      max10742_lock_model(chip);
 +      kfree(temp_data);
 +
 +      return ret;
 +}
 +
 +static int max17042_verify_model_lock(struct max17042_chip *chip)
 +{
 +      int i;
 +      int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl);
++      u16 *temp_data;
 +      int ret = 0;
 +
 +      temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL);
 +      if (!temp_data)
 +              return -ENOMEM;
 +
 +      max17042_read_model_data(chip, MAX17042_MODELChrTbl, temp_data,
 +                              table_size);
 +      for (i = 0; i < table_size; i++)
 +              if (temp_data[i])
 +                      ret = -EINVAL;
 +
 +      kfree(temp_data);
 +      return ret;
 +}
 +
 +static void max17042_write_config_regs(struct max17042_chip *chip)
 +{
 +      struct max17042_config_data *config = chip->pdata->config_data;
 +      struct regmap *map = chip->regmap;
 +
 +      regmap_write(map, MAX17042_CONFIG, config->config);
 +      regmap_write(map, MAX17042_LearnCFG, config->learn_cfg);
 +      regmap_write(map, MAX17042_FilterCFG,
 +                      config->filter_cfg);
 +      regmap_write(map, MAX17042_RelaxCFG, config->relax_cfg);
 +      if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047 ||
 +                      chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050)
 +              regmap_write(map, MAX17047_FullSOCThr,
 +                                              config->full_soc_thresh);
 +}
 +
 +static void  max17042_write_custom_regs(struct max17042_chip *chip)
 +{
 +      struct max17042_config_data *config = chip->pdata->config_data;
 +      struct regmap *map = chip->regmap;
 +
 +      max17042_write_verify_reg(map, MAX17042_RCOMP0, config->rcomp0);
 +      max17042_write_verify_reg(map, MAX17042_TempCo, config->tcompc0);
 +      max17042_write_verify_reg(map, MAX17042_ICHGTerm, config->ichgt_term);
 +      if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) {
 +              regmap_write(map, MAX17042_EmptyTempCo, config->empty_tempco);
 +              max17042_write_verify_reg(map, MAX17042_K_empty0,
 +                                      config->kempty0);
 +      } else {
 +              max17042_write_verify_reg(map, MAX17047_QRTbl00,
 +                                              config->qrtbl00);
 +              max17042_write_verify_reg(map, MAX17047_QRTbl10,
 +                                              config->qrtbl10);
 +              max17042_write_verify_reg(map, MAX17047_QRTbl20,
 +                                              config->qrtbl20);
 +              max17042_write_verify_reg(map, MAX17047_QRTbl30,
 +                                              config->qrtbl30);
 +      }
 +}
 +
 +static void max17042_update_capacity_regs(struct max17042_chip *chip)
 +{
 +      struct max17042_config_data *config = chip->pdata->config_data;
 +      struct regmap *map = chip->regmap;
 +
 +      max17042_write_verify_reg(map, MAX17042_FullCAP,
 +                              config->fullcap);
 +      regmap_write(map, MAX17042_DesignCap, config->design_cap);
 +      max17042_write_verify_reg(map, MAX17042_FullCAPNom,
 +                              config->fullcapnom);
 +}
 +
 +static void max17042_reset_vfsoc0_reg(struct max17042_chip *chip)
 +{
 +      unsigned int vfSoc;
 +      struct regmap *map = chip->regmap;
 +
 +      regmap_read(map, MAX17042_VFSOC, &vfSoc);
 +      regmap_write(map, MAX17042_VFSOC0Enable, VFSOC0_UNLOCK);
 +      max17042_write_verify_reg(map, MAX17042_VFSOC0, vfSoc);
 +      regmap_write(map, MAX17042_VFSOC0Enable, VFSOC0_LOCK);
 +}
 +
 +static void max17042_load_new_capacity_params(struct max17042_chip *chip)
 +{
 +      u32 full_cap0, rep_cap, dq_acc, vfSoc;
 +      u32 rem_cap;
 +
 +      struct max17042_config_data *config = chip->pdata->config_data;
 +      struct regmap *map = chip->regmap;
 +
 +      regmap_read(map, MAX17042_FullCAP0, &full_cap0);
 +      regmap_read(map, MAX17042_VFSOC, &vfSoc);
 +
 +      /* fg_vfSoc needs to shifted by 8 bits to get the
 +       * perc in 1% accuracy, to get the right rem_cap multiply
 +       * full_cap0, fg_vfSoc and devide by 100
 +       */
 +      rem_cap = ((vfSoc >> 8) * full_cap0) / 100;
 +      max17042_write_verify_reg(map, MAX17042_RemCap, rem_cap);
 +
 +      rep_cap = rem_cap;
 +      max17042_write_verify_reg(map, MAX17042_RepCap, rep_cap);
 +
 +      /* Write dQ_acc to 200% of Capacity and dP_acc to 200% */
 +      dq_acc = config->fullcap / dQ_ACC_DIV;
 +      max17042_write_verify_reg(map, MAX17042_dQacc, dq_acc);
 +      max17042_write_verify_reg(map, MAX17042_dPacc, dP_ACC_200);
 +
 +      max17042_write_verify_reg(map, MAX17042_FullCAP,
 +                      config->fullcap);
 +      regmap_write(map, MAX17042_DesignCap,
 +                      config->design_cap);
 +      max17042_write_verify_reg(map, MAX17042_FullCAPNom,
 +                      config->fullcapnom);
 +      /* Update SOC register with new SOC */
 +      regmap_write(map, MAX17042_RepSOC, vfSoc);
 +}
 +
 +/*
 + * Block write all the override values coming from platform data.
 + * This function MUST be called before the POR initialization proceedure
 + * specified by maxim.
 + */
 +static inline void max17042_override_por_values(struct max17042_chip *chip)
 +{
 +      struct regmap *map = chip->regmap;
 +      struct max17042_config_data *config = chip->pdata->config_data;
 +
 +      max17042_override_por(map, MAX17042_TGAIN, config->tgain);
 +      max17042_override_por(map, MAx17042_TOFF, config->toff);
 +      max17042_override_por(map, MAX17042_CGAIN, config->cgain);
 +      max17042_override_por(map, MAX17042_COFF, config->coff);
 +
 +      max17042_override_por(map, MAX17042_VALRT_Th, config->valrt_thresh);
 +      max17042_override_por(map, MAX17042_TALRT_Th, config->talrt_thresh);
 +      max17042_override_por(map, MAX17042_SALRT_Th,
 +                                              config->soc_alrt_thresh);
 +      max17042_override_por(map, MAX17042_CONFIG, config->config);
 +      max17042_override_por(map, MAX17042_SHDNTIMER, config->shdntimer);
 +
 +      max17042_override_por(map, MAX17042_DesignCap, config->design_cap);
 +      max17042_override_por(map, MAX17042_ICHGTerm, config->ichgt_term);
 +
 +      max17042_override_por(map, MAX17042_AtRate, config->at_rate);
 +      max17042_override_por(map, MAX17042_LearnCFG, config->learn_cfg);
 +      max17042_override_por(map, MAX17042_FilterCFG, config->filter_cfg);
 +      max17042_override_por(map, MAX17042_RelaxCFG, config->relax_cfg);
 +      max17042_override_por(map, MAX17042_MiscCFG, config->misc_cfg);
 +      max17042_override_por(map, MAX17042_MaskSOC, config->masksoc);
 +
 +      max17042_override_por(map, MAX17042_FullCAP, config->fullcap);
 +      max17042_override_por(map, MAX17042_FullCAPNom, config->fullcapnom);
 +      if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042)
 +              max17042_override_por(map, MAX17042_SOC_empty,
 +                                              config->socempty);
 +      max17042_override_por(map, MAX17042_LAvg_empty, config->lavg_empty);
 +      max17042_override_por(map, MAX17042_dQacc, config->dqacc);
 +      max17042_override_por(map, MAX17042_dPacc, config->dpacc);
 +
 +      if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042)
 +              max17042_override_por(map, MAX17042_V_empty, config->vempty);
 +      else
 +              max17042_override_por(map, MAX17047_V_empty, config->vempty);
 +      max17042_override_por(map, MAX17042_TempNom, config->temp_nom);
 +      max17042_override_por(map, MAX17042_TempLim, config->temp_lim);
 +      max17042_override_por(map, MAX17042_FCTC, config->fctc);
 +      max17042_override_por(map, MAX17042_RCOMP0, config->rcomp0);
 +      max17042_override_por(map, MAX17042_TempCo, config->tcompc0);
 +      if (chip->chip_type) {
 +              max17042_override_por(map, MAX17042_EmptyTempCo,
 +                                              config->empty_tempco);
 +              max17042_override_por(map, MAX17042_K_empty0,
 +                                              config->kempty0);
 +      }
 +}
 +
 +static int max17042_init_chip(struct max17042_chip *chip)
 +{
 +      struct regmap *map = chip->regmap;
 +      int ret;
 +
 +      max17042_override_por_values(chip);
 +      /* After Power up, the MAX17042 requires 500mS in order
 +       * to perform signal debouncing and initial SOC reporting
 +       */
 +      msleep(500);
 +
 +      /* Initialize configaration */
 +      max17042_write_config_regs(chip);
 +
 +      /* write cell characterization data */
 +      ret = max17042_init_model(chip);
 +      if (ret) {
 +              dev_err(&chip->client->dev, "%s init failed\n",
 +                      __func__);
 +              return -EIO;
 +      }
 +
 +      ret = max17042_verify_model_lock(chip);
 +      if (ret) {
 +              dev_err(&chip->client->dev, "%s lock verify failed\n",
 +                      __func__);
 +              return -EIO;
 +      }
 +      /* write custom parameters */
 +      max17042_write_custom_regs(chip);
 +
 +      /* update capacity params */
 +      max17042_update_capacity_regs(chip);
 +
 +      /* delay must be atleast 350mS to allow VFSOC
 +       * to be calculated from the new configuration
 +       */
 +      msleep(350);
 +
 +      /* reset vfsoc0 reg */
 +      max17042_reset_vfsoc0_reg(chip);
 +
 +      /* load new capacity params */
 +      max17042_load_new_capacity_params(chip);
 +
 +      /* Init complete, Clear the POR bit */
 +      regmap_update_bits(map, MAX17042_STATUS, STATUS_POR_BIT, 0x0);
 +      return 0;
 +}
 +
 +static void max17042_set_soc_threshold(struct max17042_chip *chip, u16 off)
 +{
 +      struct regmap *map = chip->regmap;
 +      u32 soc, soc_tr;
 +
 +      /* program interrupt thesholds such that we should
 +       * get interrupt for every 'off' perc change in the soc
 +       */
 +      regmap_read(map, MAX17042_RepSOC, &soc);
 +      soc >>= 8;
 +      soc_tr = (soc + off) << 8;
 +      soc_tr |= (soc - off);
 +      regmap_write(map, MAX17042_SALRT_Th, soc_tr);
 +}
 +
 +static irqreturn_t max17042_thread_handler(int id, void *dev)
 +{
 +      struct max17042_chip *chip = dev;
 +      u32 val;
 +
 +      regmap_read(chip->regmap, MAX17042_STATUS, &val);
 +      if ((val & STATUS_INTR_SOCMIN_BIT) ||
 +              (val & STATUS_INTR_SOCMAX_BIT)) {
 +              dev_info(&chip->client->dev, "SOC threshold INTR\n");
 +              max17042_set_soc_threshold(chip, 1);
 +      }
 +
 +      power_supply_changed(chip->battery);
 +      return IRQ_HANDLED;
 +}
 +
 +static void max17042_init_worker(struct work_struct *work)
 +{
 +      struct max17042_chip *chip = container_of(work,
 +                              struct max17042_chip, work);
 +      int ret;
 +
 +      /* Initialize registers according to values from the platform data */
 +      if (chip->pdata->enable_por_init && chip->pdata->config_data) {
 +              ret = max17042_init_chip(chip);
 +              if (ret)
 +                      return;
 +      }
 +
 +      chip->init_complete = 1;
 +}
 +
 +#ifdef CONFIG_OF
 +static struct max17042_platform_data *
 +max17042_get_pdata(struct device *dev)
 +{
 +      struct device_node *np = dev->of_node;
 +      u32 prop;
 +      struct max17042_platform_data *pdata;
 +
 +      if (!np)
 +              return dev->platform_data;
 +
 +      pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
 +      if (!pdata)
 +              return NULL;
 +
 +      /*
 +       * Require current sense resistor value to be specified for
 +       * current-sense functionality to be enabled at all.
 +       */
 +      if (of_property_read_u32(np, "maxim,rsns-microohm", &prop) == 0) {
 +              pdata->r_sns = prop;
 +              pdata->enable_current_sense = true;
 +      }
 +
 +      if (of_property_read_s32(np, "maxim,cold-temp", &pdata->temp_min))
 +              pdata->temp_min = INT_MIN;
 +      if (of_property_read_s32(np, "maxim,over-heat-temp", &pdata->temp_max))
 +              pdata->temp_max = INT_MAX;
 +      if (of_property_read_s32(np, "maxim,dead-volt", &pdata->vmin))
 +              pdata->vmin = INT_MIN;
 +      if (of_property_read_s32(np, "maxim,over-volt", &pdata->vmax))
 +              pdata->vmax = INT_MAX;
 +
 +      return pdata;
 +}
 +#else
 +static struct max17042_platform_data *
 +max17042_get_pdata(struct device *dev)
 +{
 +      return dev->platform_data;
 +}
 +#endif
 +
 +static const struct regmap_config max17042_regmap_config = {
 +      .reg_bits = 8,
 +      .val_bits = 16,
 +      .val_format_endian = REGMAP_ENDIAN_NATIVE,
 +};
 +
 +static const struct power_supply_desc max17042_psy_desc = {
 +      .name           = "max170xx_battery",
 +      .type           = POWER_SUPPLY_TYPE_BATTERY,
 +      .get_property   = max17042_get_property,
 +      .set_property   = max17042_set_property,
 +      .property_is_writeable  = max17042_property_is_writeable,
 +      .properties     = max17042_battery_props,
 +      .num_properties = ARRAY_SIZE(max17042_battery_props),
 +};
 +
 +static const struct power_supply_desc max17042_no_current_sense_psy_desc = {
 +      .name           = "max170xx_battery",
 +      .type           = POWER_SUPPLY_TYPE_BATTERY,
 +      .get_property   = max17042_get_property,
 +      .set_property   = max17042_set_property,
 +      .property_is_writeable  = max17042_property_is_writeable,
 +      .properties     = max17042_battery_props,
 +      .num_properties = ARRAY_SIZE(max17042_battery_props) - 2,
 +};
 +
 +static int max17042_probe(struct i2c_client *client,
 +                      const struct i2c_device_id *id)
 +{
 +      struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
 +      const struct power_supply_desc *max17042_desc = &max17042_psy_desc;
 +      struct power_supply_config psy_cfg = {};
 +      struct max17042_chip *chip;
 +      int ret;
 +      int i;
 +      u32 val;
 +
 +      if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
 +              return -EIO;
 +
 +      chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
 +      if (!chip)
 +              return -ENOMEM;
 +
 +      chip->client = client;
 +      chip->regmap = devm_regmap_init_i2c(client, &max17042_regmap_config);
 +      if (IS_ERR(chip->regmap)) {
 +              dev_err(&client->dev, "Failed to initialize regmap\n");
 +              return -EINVAL;
 +      }
 +
 +      chip->pdata = max17042_get_pdata(&client->dev);
 +      if (!chip->pdata) {
 +              dev_err(&client->dev, "no platform data provided\n");
 +              return -EINVAL;
 +      }
 +
 +      i2c_set_clientdata(client, chip);
 +      chip->chip_type = id->driver_data;
 +      psy_cfg.drv_data = chip;
 +
 +      /* When current is not measured,
 +       * CURRENT_NOW and CURRENT_AVG properties should be invisible. */
 +      if (!chip->pdata->enable_current_sense)
 +              max17042_desc = &max17042_no_current_sense_psy_desc;
 +
 +      if (chip->pdata->r_sns == 0)
 +              chip->pdata->r_sns = MAX17042_DEFAULT_SNS_RESISTOR;
 +
 +      if (chip->pdata->init_data)
 +              for (i = 0; i < chip->pdata->num_init_data; i++)
 +                      regmap_write(chip->regmap,
 +                                      chip->pdata->init_data[i].addr,
 +                                      chip->pdata->init_data[i].data);
 +
 +      if (!chip->pdata->enable_current_sense) {
 +              regmap_write(chip->regmap, MAX17042_CGAIN, 0x0000);
 +              regmap_write(chip->regmap, MAX17042_MiscCFG, 0x0003);
 +              regmap_write(chip->regmap, MAX17042_LearnCFG, 0x0007);
 +      }
 +
 +      chip->battery = devm_power_supply_register(&client->dev, max17042_desc,
 +                                                 &psy_cfg);
 +      if (IS_ERR(chip->battery)) {
 +              dev_err(&client->dev, "failed: power supply register\n");
 +              return PTR_ERR(chip->battery);
 +      }
 +
 +      if (client->irq) {
 +              ret = devm_request_threaded_irq(&client->dev, client->irq,
 +                                              NULL,
 +                                              max17042_thread_handler,
 +                                              IRQF_TRIGGER_FALLING |
 +                                              IRQF_ONESHOT,
 +                                              chip->battery->desc->name,
 +                                              chip);
 +              if (!ret) {
 +                      regmap_update_bits(chip->regmap, MAX17042_CONFIG,
 +                                      CONFIG_ALRT_BIT_ENBL,
 +                                      CONFIG_ALRT_BIT_ENBL);
 +                      max17042_set_soc_threshold(chip, 1);
 +              } else {
 +                      client->irq = 0;
 +                      dev_err(&client->dev, "%s(): cannot get IRQ\n",
 +                              __func__);
 +              }
 +      }
 +
 +      regmap_read(chip->regmap, MAX17042_STATUS, &val);
 +      if (val & STATUS_POR_BIT) {
 +              INIT_WORK(&chip->work, max17042_init_worker);
 +              schedule_work(&chip->work);
 +      } else {
 +              chip->init_complete = 1;
 +      }
 +
 +      return 0;
 +}
 +
 +#ifdef CONFIG_PM_SLEEP
 +static int max17042_suspend(struct device *dev)
 +{
 +      struct max17042_chip *chip = dev_get_drvdata(dev);
 +
 +      /*
 +       * disable the irq and enable irq_wake
 +       * capability to the interrupt line.
 +       */
 +      if (chip->client->irq) {
 +              disable_irq(chip->client->irq);
 +              enable_irq_wake(chip->client->irq);
 +      }
 +
 +      return 0;
 +}
 +
 +static int max17042_resume(struct device *dev)
 +{
 +      struct max17042_chip *chip = dev_get_drvdata(dev);
 +
 +      if (chip->client->irq) {
 +              disable_irq_wake(chip->client->irq);
 +              enable_irq(chip->client->irq);
 +              /* re-program the SOC thresholds to 1% change */
 +              max17042_set_soc_threshold(chip, 1);
 +      }
 +
 +      return 0;
 +}
 +#endif
 +
 +static SIMPLE_DEV_PM_OPS(max17042_pm_ops, max17042_suspend,
 +                      max17042_resume);
 +
 +#ifdef CONFIG_OF
 +static const struct of_device_id max17042_dt_match[] = {
 +      { .compatible = "maxim,max17042" },
 +      { .compatible = "maxim,max17047" },
 +      { .compatible = "maxim,max17050" },
 +      { },
 +};
 +MODULE_DEVICE_TABLE(of, max17042_dt_match);
 +#endif
 +
 +static const struct i2c_device_id max17042_id[] = {
 +      { "max17042", MAXIM_DEVICE_TYPE_MAX17042 },
 +      { "max17047", MAXIM_DEVICE_TYPE_MAX17047 },
 +      { "max17050", MAXIM_DEVICE_TYPE_MAX17050 },
 +      { }
 +};
 +MODULE_DEVICE_TABLE(i2c, max17042_id);
 +
 +static struct i2c_driver max17042_i2c_driver = {
 +      .driver = {
 +              .name   = "max17042",
 +              .of_match_table = of_match_ptr(max17042_dt_match),
 +              .pm     = &max17042_pm_ops,
 +      },
 +      .probe          = max17042_probe,
 +      .id_table       = max17042_id,
 +};
 +module_i2c_driver(max17042_i2c_driver);
 +
 +MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
 +MODULE_DESCRIPTION("MAX17042 Fuel Gauge");
 +MODULE_LICENSE("GPL");
index c8c4a0c,0000000..9fd019f
mode 100644,000000..100644
--- /dev/null
@@@ -1,294 -1,0 +1,295 @@@
 +/*
 + * Battery charger driver for TI's tps65217
 + *
 + * Copyright (c) 2015, Collabora Ltd.
 +
 + * This program is free software; you can redistribute it and/or modify it
 + * under the terms and conditions of the GNU General Public License,
 + * version 2, as published by the Free Software Foundation.
 +
 + * This program is distributed in the hope it will be useful, but WITHOUT
 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 + * more details.
 +
 + * You should have received a copy of the GNU General Public License
 + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 + */
 +
 +/*
 + * Battery charger driver for TI's tps65217
 + */
 +#include <linux/kernel.h>
 +#include <linux/kthread.h>
 +#include <linux/device.h>
 +#include <linux/module.h>
 +#include <linux/platform_device.h>
 +#include <linux/init.h>
 +#include <linux/interrupt.h>
 +#include <linux/slab.h>
 +#include <linux/err.h>
 +#include <linux/of.h>
 +#include <linux/of_device.h>
 +#include <linux/power_supply.h>
 +
 +#include <linux/mfd/core.h>
 +#include <linux/mfd/tps65217.h>
 +
 +#define POLL_INTERVAL         (HZ * 2)
 +
 +struct tps65217_charger {
 +      struct tps65217 *tps;
 +      struct device *dev;
 +      struct power_supply *ac;
 +
 +      int     ac_online;
 +      int     prev_ac_online;
 +
 +      struct task_struct      *poll_task;
 +
 +      int     irq;
 +};
 +
 +static enum power_supply_property tps65217_ac_props[] = {
 +      POWER_SUPPLY_PROP_ONLINE,
 +};
 +
 +static int tps65217_config_charger(struct tps65217_charger *charger)
 +{
 +      int ret;
 +
 +      dev_dbg(charger->dev, "%s\n", __func__);
 +
 +      /*
 +       * tps65217 rev. G, p. 31 (see p. 32 for NTC schematic)
 +       *
 +       * The device can be configured to support a 100k NTC (B = 3960) by
 +       * setting the the NTC_TYPE bit in register CHGCONFIG1 to 1. However it
 +       * is not recommended to do so. In sleep mode, the charger continues
 +       * charging the battery, but all register values are reset to default
 +       * values. Therefore, the charger would get the wrong temperature
 +       * information. If 100k NTC setting is required, please contact the
 +       * factory.
 +       *
 +       * ATTENTION, conflicting information, from p. 46
 +       *
 +       * NTC TYPE (for battery temperature measurement)
 +       *   0 – 100k (curve 1, B = 3960)
 +       *   1 – 10k  (curve 2, B = 3480) (default on reset)
 +       *
 +       */
 +      ret = tps65217_clear_bits(charger->tps, TPS65217_REG_CHGCONFIG1,
 +                                TPS65217_CHGCONFIG1_NTC_TYPE,
 +                                TPS65217_PROTECT_NONE);
 +      if (ret) {
 +              dev_err(charger->dev,
 +                      "failed to set 100k NTC setting: %d\n", ret);
 +              return ret;
 +      }
 +
 +      return 0;
 +}
 +
 +static int tps65217_enable_charging(struct tps65217_charger *charger)
 +{
 +      int ret;
 +
 +      /* charger already enabled */
 +      if (charger->ac_online)
 +              return 0;
 +
 +      dev_dbg(charger->dev, "%s: enable charging\n", __func__);
 +      ret = tps65217_set_bits(charger->tps, TPS65217_REG_CHGCONFIG1,
 +                              TPS65217_CHGCONFIG1_CHG_EN,
 +                              TPS65217_CHGCONFIG1_CHG_EN,
 +                              TPS65217_PROTECT_NONE);
 +      if (ret) {
 +              dev_err(charger->dev,
 +                      "%s: Error in writing CHG_EN in reg 0x%x: %d\n",
 +                      __func__, TPS65217_REG_CHGCONFIG1, ret);
 +              return ret;
 +      }
 +
 +      charger->ac_online = 1;
 +
 +      return 0;
 +}
 +
 +static int tps65217_ac_get_property(struct power_supply *psy,
 +                      enum power_supply_property psp,
 +                      union power_supply_propval *val)
 +{
 +      struct tps65217_charger *charger = power_supply_get_drvdata(psy);
 +
 +      if (psp == POWER_SUPPLY_PROP_ONLINE) {
 +              val->intval = charger->ac_online;
 +              return 0;
 +      }
 +      return -EINVAL;
 +}
 +
 +static irqreturn_t tps65217_charger_irq(int irq, void *dev)
 +{
 +      int ret, val;
 +      struct tps65217_charger *charger = dev;
 +
 +      charger->prev_ac_online = charger->ac_online;
 +
 +      ret = tps65217_reg_read(charger->tps, TPS65217_REG_STATUS, &val);
 +      if (ret < 0) {
 +              dev_err(charger->dev, "%s: Error in reading reg 0x%x\n",
 +                      __func__, TPS65217_REG_STATUS);
 +              return IRQ_HANDLED;
 +      }
 +
 +      dev_dbg(charger->dev, "%s: 0x%x\n", __func__, val);
 +
 +      /* check for AC status bit */
 +      if (val & TPS65217_STATUS_ACPWR) {
 +              ret = tps65217_enable_charging(charger);
 +              if (ret) {
 +                      dev_err(charger->dev,
 +                              "failed to enable charger: %d\n", ret);
 +                      return IRQ_HANDLED;
 +              }
 +      } else {
 +              charger->ac_online = 0;
 +      }
 +
 +      if (charger->prev_ac_online != charger->ac_online)
 +              power_supply_changed(charger->ac);
 +
 +      ret = tps65217_reg_read(charger->tps, TPS65217_REG_CHGCONFIG0, &val);
 +      if (ret < 0) {
 +              dev_err(charger->dev, "%s: Error in reading reg 0x%x\n",
 +                      __func__, TPS65217_REG_CHGCONFIG0);
 +              return IRQ_HANDLED;
 +      }
 +
 +      if (val & TPS65217_CHGCONFIG0_ACTIVE)
 +              dev_dbg(charger->dev, "%s: charger is charging\n", __func__);
 +      else
 +              dev_dbg(charger->dev,
 +                      "%s: charger is NOT charging\n", __func__);
 +
 +      return IRQ_HANDLED;
 +}
 +
 +static int tps65217_charger_poll_task(void *data)
 +{
 +      set_freezable();
 +
 +      while (!kthread_should_stop()) {
 +              schedule_timeout_interruptible(POLL_INTERVAL);
 +              try_to_freeze();
 +              tps65217_charger_irq(-1, data);
 +      }
 +      return 0;
 +}
 +
 +static const struct power_supply_desc tps65217_charger_desc = {
 +      .name                   = "tps65217-ac",
 +      .type                   = POWER_SUPPLY_TYPE_MAINS,
 +      .get_property           = tps65217_ac_get_property,
 +      .properties             = tps65217_ac_props,
 +      .num_properties         = ARRAY_SIZE(tps65217_ac_props),
 +};
 +
 +static int tps65217_charger_probe(struct platform_device *pdev)
 +{
 +      struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
 +      struct tps65217_charger *charger;
 +      struct power_supply_config cfg = {};
 +      int irq;
 +      int ret;
 +
 +      dev_dbg(&pdev->dev, "%s\n", __func__);
 +
 +      charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
 +      if (!charger)
 +              return -ENOMEM;
 +
++      platform_set_drvdata(pdev, charger);
 +      charger->tps = tps;
 +      charger->dev = &pdev->dev;
 +
 +      cfg.of_node = pdev->dev.of_node;
 +      cfg.drv_data = charger;
 +
 +      charger->ac = devm_power_supply_register(&pdev->dev,
 +                                               &tps65217_charger_desc,
 +                                               &cfg);
 +      if (IS_ERR(charger->ac)) {
 +              dev_err(&pdev->dev, "failed: power supply register\n");
 +              return PTR_ERR(charger->ac);
 +      }
 +
 +      irq = platform_get_irq_byname(pdev, "AC");
 +      if (irq < 0)
 +              irq = -ENXIO;
 +      charger->irq = irq;
 +
 +      ret = tps65217_config_charger(charger);
 +      if (ret < 0) {
 +              dev_err(charger->dev, "charger config failed, err %d\n", ret);
 +              return ret;
 +      }
 +
 +      if (irq != -ENXIO) {
 +              ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
 +                                              tps65217_charger_irq,
 +                                              0, "tps65217-charger",
 +                                              charger);
 +              if (ret) {
 +                      dev_err(charger->dev,
 +                              "Unable to register irq %d err %d\n", irq,
 +                              ret);
 +                      return ret;
 +              }
 +
 +              /* Check current state */
 +              tps65217_charger_irq(irq, charger);
 +      } else {
 +              charger->poll_task = kthread_run(tps65217_charger_poll_task,
 +                                              charger, "ktps65217charger");
 +              if (IS_ERR(charger->poll_task)) {
 +                      ret = PTR_ERR(charger->poll_task);
 +                      dev_err(charger->dev,
 +                              "Unable to run kthread err %d\n", ret);
 +                      return ret;
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +static int tps65217_charger_remove(struct platform_device *pdev)
 +{
 +      struct tps65217_charger *charger = platform_get_drvdata(pdev);
 +
 +      if (charger->irq == -ENXIO)
 +              kthread_stop(charger->poll_task);
 +
 +      return 0;
 +}
 +
 +static const struct of_device_id tps65217_charger_match_table[] = {
 +      { .compatible = "ti,tps65217-charger", },
 +      { /* sentinel */ }
 +};
 +MODULE_DEVICE_TABLE(of, tps65217_charger_match_table);
 +
 +static struct platform_driver tps65217_charger_driver = {
 +      .probe  = tps65217_charger_probe,
 +      .remove = tps65217_charger_remove,
 +      .driver = {
 +              .name   = "tps65217-charger",
 +              .of_match_table = of_match_ptr(tps65217_charger_match_table),
 +      },
 +
 +};
 +module_platform_driver(tps65217_charger_driver);
 +
 +MODULE_LICENSE("GPL v2");
 +MODULE_AUTHOR("Enric Balletbo Serra <enric.balletbo@collabora.com>");
 +MODULE_DESCRIPTION("TPS65217 battery charger driver");