diff options
Diffstat (limited to 'drivers/power/max17042_battery.c')
| -rw-r--r-- | drivers/power/max17042_battery.c | 864 |
1 files changed, 801 insertions, 63 deletions
diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index d664ef58afa..c6c70c4afeb 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -33,6 +33,10 @@ #include <linux/power_supply.h> #include <linux/power/max17042_battery.h> #include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/debugfs.h> /* Status register bits */ #define STATUS_POR_BIT (1 << 1) @@ -48,32 +52,101 @@ /* 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 CONFIG_VS_BIT_ENBL (1 << 12) +#define CONFIG_TS_BIT_ENBL (1 << 13) +#define CONFIG_SS_BIT_ENBL (1 << 14) +#define CONFIG_STICK_ALL_ENBL (CONFIG_VS_BIT_ENBL | \ +CONFIG_TS_BIT_ENBL | CONFIG_SS_BIT_ENBL) #define VFSOC0_LOCK 0x0000 -#define VFSOC0_UNLOCK 0x0080 +#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_INIT_NUM_CYCLES 160 +#define MAX17047_INIT_NUM_CYCLES 96 + +#define MAX17042_dQ_ACC_DIV 4 +#define MAX17047_dQ_ACC_DIV 16 + +#define MAX17042_dP_ACC_200 0x3200 +#define MAX17047_dP_ACC_200 0x0C80 #define MAX17042_IC_VERSION 0x0092 #define MAX17047_IC_VERSION 0x00AC /* same for max17050 */ +#define MAX17042_AGE_DIV 256 + +#define INIT_DATA_PROPERTY "maxim,regs-init-data" +#define CONFIG_NODE "maxim,configuration" +#define VERSION_PROPERTY "version" +#define CONFIG_PROPERTY "config" +#define FULL_SOC_THRESH_PROPERTY "full_soc_thresh" +#define DESIGN_CAP_PROPERTY "design_cap" +#define ICHGT_TERM_PROPERTY "ichgt_term" +#define LEARN_CFG_PROPERTY "learn_cfg" +#define FILTER_CFG_PROPERTY "filter_cfg" +#define RELAX_CFG_PROPERTY "relax_cfg" +#define FULLCAP_PROPERTY "fullcap" +#define FULLCAPNOM_PROPERTY "fullcapnom" +#define QRTBL00_PROPERTY "qrtbl00" +#define QRTBL10_PROPERTY "qrtbl10" +#define QRTBL20_PROPERTY "qrtbl20" +#define QRTBL30_PROPERTY "qrtbl30" +#define RCOMP0_PROPERTY "rcomp0" +#define TCOMPC0_PROPERTY "tcompc0" +#define CELL_CHAR_TBL_PROPERTY "maxim,cell-char-tbl" +#define TGAIN_PROPERTY "tgain" +#define TOFF_PROPERTY "toff" +#define TEMP_CONV_NODE "maxim,temp-conv" +#define RESULT_PROPERTY "result" +#define START_PROPERTY "start" + +/* we need to set the alert threshold to a default value + before powerlib calls into the driver */ +#define DEFAULT_ALERT_THRESHOLD 1 + struct max17042_chip { struct i2c_client *client; struct power_supply battery; enum max170xx_chip_type chip_type; struct max17042_platform_data *pdata; struct work_struct work; + struct work_struct work_malicious_removed; int init_complete; + bool batt_undervoltage; + u16 alert_threshold; +#ifdef CONFIG_BATTERY_MAX17042_DEBUGFS + struct dentry *debugfs_root; + u8 debugfs_addr; + u8 debugfs_capacity; +#endif + struct mutex lock; /* lock to control access during reset */ + struct power_supply *psy_malicious; + int malicious_online; }; +#ifdef CONFIG_OF +const char *get_dts_batt_id(struct device *dev) +{ + int lenp; + const char *retval = NULL; + struct device_node *n = of_find_node_by_path("/chosen"); + + if (n) { + retval = of_get_property(n, "batt-id", &lenp); + if (!retval || !lenp) { + dev_err(dev, "%s: batt-id len %d\n", __func__, lenp); + retval = NULL; + } + of_node_put(n); + } + + return retval; +} +#endif + static int max17042_write_reg(struct i2c_client *client, u8 reg, u16 value) { int ret = i2c_smbus_write_word_data(client, reg, value); @@ -117,24 +190,54 @@ static enum power_supply_property max17042_battery_props[] = { POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_STATUS }; +/* input and output temperature is in deci-centigrade */ +static int max17042_conv_temp(struct max17042_temp_conv *conv, int t) +{ + int i; /* conversion table index */ + s16 *r = conv->result; + int dt; + + /* + * conv->result[0] corresponds to conv->start temp, conv->result[1] to + * conv->start + 1 temp, etc. Find index to the conv->result table for + * t to be between index and index + 1 temperatures. + */ + i = t / 10 - conv->start; /* t is in 1/10th C, conv->start is in C */ + if (t < 0) + i -= 1; + + /* Interpolate linearly if index and index + 1 are within the table */ + if (i < 0) { + return r[0]; + } else if (i >= conv->num_result - 1) { + return r[conv->num_result - 1]; + } else { + dt = t - (conv->start + i) * 10; /* in 1/10th C */ + return r[i] + (r[i + 1] - r[i]) * dt / 10; + } +} + static int max17042_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct max17042_chip *chip = container_of(psy, struct max17042_chip, battery); - int ret; + int ret = 0; if (!chip->init_complete) return -EAGAIN; + mutex_lock(&chip->lock); + switch (psp) { case POWER_SUPPLY_PROP_PRESENT: ret = max17042_read_reg(chip->client, MAX17042_STATUS); if (ret < 0) - return ret; + goto err; if (ret & MAX17042_STATUS_BattAbsent) val->intval = 0; @@ -144,14 +247,14 @@ static int max17042_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CYCLE_COUNT: ret = max17042_read_reg(chip->client, MAX17042_Cycles); if (ret < 0) - return ret; + goto err; val->intval = ret; break; case POWER_SUPPLY_PROP_VOLTAGE_MAX: ret = max17042_read_reg(chip->client, MAX17042_MinMaxVolt); if (ret < 0) - return ret; + goto err; val->intval = ret >> 8; val->intval *= 20000; /* Units of LSB = 20mV */ @@ -162,7 +265,7 @@ static int max17042_get_property(struct power_supply *psy, else ret = max17042_read_reg(chip->client, MAX17047_V_empty); if (ret < 0) - return ret; + goto err; val->intval = ret >> 7; val->intval *= 10000; /* Units of LSB = 10mV */ @@ -170,49 +273,67 @@ static int max17042_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_NOW: ret = max17042_read_reg(chip->client, MAX17042_VCELL); if (ret < 0) - return ret; + goto err; val->intval = ret * 625 / 8; break; case POWER_SUPPLY_PROP_VOLTAGE_AVG: ret = max17042_read_reg(chip->client, MAX17042_AvgVCELL); if (ret < 0) - return ret; + goto err; val->intval = ret * 625 / 8; break; case POWER_SUPPLY_PROP_VOLTAGE_OCV: ret = max17042_read_reg(chip->client, MAX17042_OCVInternal); if (ret < 0) - return ret; + goto err; val->intval = ret * 625 / 8; break; case POWER_SUPPLY_PROP_CAPACITY: +#ifdef CONFIG_BATTERY_MAX17042_DEBUGFS + if (chip->debugfs_capacity != 0xFF) { + val->intval = chip->debugfs_capacity; + break; + } +#endif + if (chip->pdata->batt_undervoltage_zero_soc && + chip->batt_undervoltage) { + val->intval = 0; + break; + } + ret = max17042_read_reg(chip->client, MAX17042_RepSOC); if (ret < 0) - return ret; + goto err; - val->intval = ret >> 8; + ret >>= 8; + if (ret == 0 && + chip->pdata->batt_undervoltage_zero_soc && + !chip->batt_undervoltage) + val->intval = 1; + else + val->intval = ret; break; case POWER_SUPPLY_PROP_CHARGE_FULL: ret = max17042_read_reg(chip->client, MAX17042_FullCAP); if (ret < 0) - return ret; + goto err; val->intval = ret * 1000 / 2; break; case POWER_SUPPLY_PROP_CHARGE_COUNTER: ret = max17042_read_reg(chip->client, MAX17042_QH); if (ret < 0) - return ret; + goto err; val->intval = ret * 1000 / 2; break; case POWER_SUPPLY_PROP_TEMP: ret = max17042_read_reg(chip->client, MAX17042_TEMP); if (ret < 0) - return ret; + goto err; val->intval = ret; /* The value is signed. */ @@ -220,15 +341,21 @@ static int max17042_get_property(struct power_supply *psy, val->intval = (0x7fff & ~val->intval) + 1; val->intval *= -1; } + /* The value is converted into deci-centigrade scale */ /* Units of LSB = 1 / 256 degree Celsius */ val->intval = val->intval * 10 / 256; + + /* Convert IC temp to "real" temp */ + if (chip->pdata->tcnv) + val->intval = max17042_conv_temp(chip->pdata->tcnv, + val->intval); break; case POWER_SUPPLY_PROP_CURRENT_NOW: if (chip->pdata->enable_current_sense) { ret = max17042_read_reg(chip->client, MAX17042_Current); if (ret < 0) - return ret; + goto err; val->intval = ret; if (val->intval & 0x8000) { @@ -239,7 +366,7 @@ static int max17042_get_property(struct power_supply *psy, } val->intval *= 1562500 / chip->pdata->r_sns; } else { - return -EINVAL; + ret = -EINVAL; } break; case POWER_SUPPLY_PROP_CURRENT_AVG: @@ -247,7 +374,7 @@ static int max17042_get_property(struct power_supply *psy, ret = max17042_read_reg(chip->client, MAX17042_AvgCurrent); if (ret < 0) - return ret; + goto err; val->intval = ret; if (val->intval & 0x8000) { @@ -258,13 +385,24 @@ static int max17042_get_property(struct power_supply *psy, } val->intval *= 1562500 / chip->pdata->r_sns; } else { - return -EINVAL; + ret = -EINVAL; } break; + case POWER_SUPPLY_PROP_STATUS: + if (power_supply_am_i_supplied(psy)) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + break; default: - return -EINVAL; + ret = -EINVAL; } - return 0; + + if (ret > 0) + ret = 0; +err: + mutex_unlock(&chip->lock); + return ret; } static int max17042_write_verify_reg(struct i2c_client *client, @@ -395,6 +533,52 @@ static int max17042_verify_model_lock(struct max17042_chip *chip) return ret; } +static int max17042_perform_soft_POR(struct max17042_chip *chip) +{ + int val; + int retries = 8; + + do { + /* 1. Lock the model and clear the POR bit */ + max10742_lock_model(chip); + val = max17042_read_reg(chip->client, MAX17042_STATUS); + max17042_write_reg(chip->client, MAX17042_STATUS, + val & (~STATUS_POR_BIT)); + /* 2. Verify model lock MLOCK1 = MLOCK2 = STATUS = 0 */ + if (!max17042_read_reg(chip->client, MAX17042_MLOCKReg1) && + !max17042_read_reg(chip->client, MAX17042_MLOCKReg2) && + !max17042_read_reg(chip->client, MAX17042_MLOCKReg2)) + break; + } while (--retries); + + if (!retries) { + dev_err(&chip->client->dev, "Unable to lock model\n"); + return -EINVAL; + } + + retries = 8; + + do { + /* 3. Send SoftPOR */ + max17042_write_reg(chip->client, MAX17042_VFSOC0Enable, + 0x000F); + /* 4. Wait atleast 2ms */ + usleep_range(2000, 4000); + /* 5. Verify POR bit is set */ + if (max17042_read_reg(chip->client, MAX17042_STATUS) & + STATUS_POR_BIT) + break; + } while (--retries); + + if (!retries) { + dev_err(&chip->client->dev, "Unable to set POR bit\n"); + return -EINVAL; + } + dev_dbg(&chip->client->dev, "Soft POR success\n"); + + return 0; +} + static void max17042_write_config_regs(struct max17042_chip *chip) { struct max17042_config_data *config = chip->pdata->config_data; @@ -442,8 +626,9 @@ static void max17042_update_capacity_regs(struct max17042_chip *chip) max17042_write_verify_reg(chip->client, MAX17042_FullCAP, config->fullcap); + /* Set DesignCap to fullcapnom here */ max17042_write_reg(chip->client, MAX17042_DesignCap, - config->design_cap); + config->fullcapnom); max17042_write_verify_reg(chip->client, MAX17042_FullCAPNom, config->fullcapnom); } @@ -458,21 +643,31 @@ static void max17042_reset_vfsoc0_reg(struct max17042_chip *chip) max17042_write_reg(chip->client, MAX17042_VFSOC0Enable, VFSOC0_LOCK); } +static void max17042_advance_to_coulomb_counter_mode(struct max17042_chip *chip) +{ + u16 value = (chip->chip_type == MAX17042 ? + MAX17042_INIT_NUM_CYCLES : MAX17047_INIT_NUM_CYCLES); + max17042_write_verify_reg(chip->client, MAX17042_Cycles, value); +} + static void max17042_load_new_capacity_params(struct max17042_chip *chip) { - u16 full_cap0, rep_cap, dq_acc, vfSoc; + u16 rep_cap, dq_acc, vfSoc; u32 rem_cap; + u16 dQ_ACC_DIV = (chip->chip_type == MAX17042 ? + MAX17042_dQ_ACC_DIV : MAX17047_dQ_ACC_DIV); + u16 dP_ACC_200 = (chip->chip_type == MAX17042 ? + MAX17042_dP_ACC_200 : MAX17047_dP_ACC_200); struct max17042_config_data *config = chip->pdata->config_data; - full_cap0 = max17042_read_reg(chip->client, MAX17042_FullCAP0); vfSoc = max17042_read_reg(chip->client, MAX17042_VFSOC); - /* fg_vfSoc needs to shifted by 8 bits to get the + /* 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 + * fullcapnom by vfSoc and devide by 100 */ - rem_cap = ((vfSoc >> 8) * full_cap0) / 100; + rem_cap = ((vfSoc >> 8) * config->fullcapnom) / 100; max17042_write_verify_reg(chip->client, MAX17042_RemCap, (u16)rem_cap); rep_cap = (u16)rem_cap; @@ -514,25 +709,14 @@ static inline void max17042_override_por_values(struct max17042_chip *chip) config->soc_alrt_thresh); max17042_override_por(client, MAX17042_CONFIG, config->config); max17042_override_por(client, MAX17042_SHDNTIMER, config->shdntimer); - - max17042_override_por(client, MAX17042_DesignCap, config->design_cap); - max17042_override_por(client, MAX17042_ICHGTerm, config->ichgt_term); - max17042_override_por(client, MAX17042_AtRate, config->at_rate); - max17042_override_por(client, MAX17042_LearnCFG, config->learn_cfg); - max17042_override_por(client, MAX17042_FilterCFG, config->filter_cfg); - max17042_override_por(client, MAX17042_RelaxCFG, config->relax_cfg); max17042_override_por(client, MAX17042_MiscCFG, config->misc_cfg); max17042_override_por(client, MAX17042_MaskSOC, config->masksoc); - max17042_override_por(client, MAX17042_FullCAP, config->fullcap); - max17042_override_por(client, MAX17042_FullCAPNom, config->fullcapnom); if (chip->chip_type == MAX17042) max17042_override_por(client, MAX17042_SOC_empty, config->socempty); max17042_override_por(client, MAX17042_LAvg_empty, config->lavg_empty); - max17042_override_por(client, MAX17042_dQacc, config->dqacc); - max17042_override_por(client, MAX17042_dPacc, config->dpacc); if (chip->chip_type == MAX17042) max17042_override_por(client, MAX17042_V_empty, config->vempty); @@ -541,9 +725,7 @@ static inline void max17042_override_por_values(struct max17042_chip *chip) max17042_override_por(client, MAX17042_TempNom, config->temp_nom); max17042_override_por(client, MAX17042_TempLim, config->temp_lim); max17042_override_por(client, MAX17042_FCTC, config->fctc); - max17042_override_por(client, MAX17042_RCOMP0, config->rcomp0); - max17042_override_por(client, MAX17042_TempCo, config->tcompc0); - if (chip->chip_type) { + if (chip->chip_type == MAX17042) { max17042_override_por(client, MAX17042_EmptyTempCo, config->empty_tempco); max17042_override_por(client, MAX17042_K_empty0, @@ -593,6 +775,9 @@ static int max17042_init_chip(struct max17042_chip *chip) /* reset vfsoc0 reg */ max17042_reset_vfsoc0_reg(chip); + /* advance to coulomb-counter mode */ + max17042_advance_to_coulomb_counter_mode(chip); + /* load new capacity params */ max17042_load_new_capacity_params(chip); @@ -606,13 +791,20 @@ static int max17042_init_chip(struct max17042_chip *chip) static void max17042_set_soc_threshold(struct max17042_chip *chip, u16 off) { u16 soc, soc_tr; - - /* program interrupt thesholds such that we should - * get interrupt for every 'off' perc change in the soc + /* + * Program interrupt thresholds to get interrupt for every 'off' + * percent change in the soc. Since we truncate soc value when + * reporting it, the reported SOC is equal to (min Salrt - 1) when soc + * falls below the min Salrt threshold and equal to max Salrt when soc + * exceeds the max Salrt threshold. */ + u16 off_max = off; + u16 off_min = off - 1; + soc = max17042_read_reg(chip->client, MAX17042_RepSOC) >> 8; - soc_tr = (soc + off) << 8; - soc_tr |= (soc - off); + soc_tr = (soc + off_max) << 8; + if (soc >= off_min) + soc_tr |= (soc - off_min); max17042_write_reg(chip->client, MAX17042_SALRT_Th, soc_tr); } @@ -622,12 +814,26 @@ static irqreturn_t max17042_thread_handler(int id, void *dev) u16 val; val = max17042_read_reg(chip->client, MAX17042_STATUS); - 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); + + dev_dbg(&chip->client->dev, "status:0x%x soc_tr:0x%x\n", + val, chip->alert_threshold); + + if ((val & STATUS_SMN_BIT) || (val & STATUS_SMX_BIT)) { + max17042_set_soc_threshold(chip, chip->alert_threshold); + /* clear the Smin Smax bits if set */ + if (chip->pdata->config_data->config & CONFIG_SS_BIT_ENBL) + val &= ~STATUS_SMN_BIT & ~STATUS_SMX_BIT; + } + + if (val & STATUS_VMN_BIT) { + dev_dbg(&chip->client->dev, "Battery undervoltage INTR\n"); + chip->batt_undervoltage = true; } + /* if sticky bits are used, clear them */ + if (chip->pdata->config_data->config & CONFIG_STICK_ALL_ENBL) + max17042_write_reg(chip->client, MAX17042_STATUS, val); + power_supply_changed(&chip->battery); return IRQ_HANDLED; } @@ -636,19 +842,308 @@ static void max17042_init_worker(struct work_struct *work) { struct max17042_chip *chip = container_of(work, struct max17042_chip, work); - int ret; + int ret = 0; + + mutex_lock(&chip->lock); /* 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; + if (!ret) { + chip->init_complete = 1; + if (chip->chip_type == MAX17047) { + max17042_write_reg(chip->client, MAX17047_Config_Ver, + chip->pdata->config_data->version); + } + } + + mutex_unlock(&chip->lock); +} + +static void max17042_malicious_removed_worker(struct work_struct *work) +{ + struct max17042_chip *chip = container_of(work, + struct max17042_chip, work_malicious_removed); + int ret; + + mutex_lock(&chip->lock); + + max17042_perform_soft_POR(chip); + ret = max17042_init_chip(chip); + if (!ret && chip->chip_type == MAX17047) + max17042_write_reg(chip->client, MAX17047_Config_Ver, + chip->pdata->config_data->version); + + dev_info(&chip->client->dev, "malicious ps removed, chip re-inited\n"); + + mutex_unlock(&chip->lock); + + power_supply_changed(&chip->battery); } #ifdef CONFIG_OF +static struct gpio * +max17042_get_gpio_list(struct device *dev, int *num_gpio_list) +{ + struct device_node *np = dev->of_node; + struct gpio *gpio_list; + int i, num_gpios, gpio_list_size; + enum of_gpio_flags flags; + + if (!np) + return NULL; + + num_gpios = of_gpio_count(np); + if (num_gpios <= 0) + return NULL; + + gpio_list_size = sizeof(struct gpio) * num_gpios; + gpio_list = devm_kzalloc(dev, gpio_list_size, GFP_KERNEL); + + if (!gpio_list) + return NULL; + + *num_gpio_list = num_gpios; + for (i = 0; i < num_gpios; i++) { + gpio_list[i].gpio = of_get_gpio_flags(np, i, &flags); + gpio_list[i].flags = flags; + of_property_read_string_index(np, "gpio-names", i, + &gpio_list[i].label); + } + + return gpio_list; +} + +static struct max17042_reg_data * +max17042_get_init_data(struct device *dev, int *num_init_data) +{ + struct device_node *np = dev->of_node; + const __be32 *property; + static struct max17042_reg_data *init_data; + int i, lenp, num_cells, init_data_size; + + if (!np) + return NULL; + + property = of_get_property(np, INIT_DATA_PROPERTY, &lenp); + + if (!property || lenp <= 0) + return NULL; + + /* + * Check data validity and whether number of cells is even + */ + if (lenp % sizeof(*property)) { + dev_err(dev, "%s has invalid data\n", INIT_DATA_PROPERTY); + return NULL; + } + + num_cells = lenp / sizeof(*property); + if (num_cells % 2) { + dev_err(dev, "%s must have even number of cells\n", + INIT_DATA_PROPERTY); + return NULL; + } + + *num_init_data = num_cells / 2; + init_data_size = sizeof(struct max17042_reg_data) * (num_cells / 2); + init_data = (struct max17042_reg_data *) + devm_kzalloc(dev, init_data_size, GFP_KERNEL); + + if (init_data) { + for (i = 0; i < num_cells / 2; i++) { + init_data[i].addr = be32_to_cpu(property[2 * i]); + init_data[i].data = be32_to_cpu(property[2 * i + 1]); + } + } + + return init_data; +} + +static int max17042_get_cell_char_tbl(struct device *dev, + struct device_node *np, + struct max17042_config_data *config_data) +{ + const __be16 *property; + int i, lenp; + + property = of_get_property(np, CELL_CHAR_TBL_PROPERTY, &lenp); + if (!property) + return -ENODEV ; + + if (lenp != sizeof(*property) * MAX17042_CHARACTERIZATION_DATA_SIZE) { + dev_err(dev, "%s must have %d cells\n", CELL_CHAR_TBL_PROPERTY, + MAX17042_CHARACTERIZATION_DATA_SIZE); + return -EINVAL; + } + + for (i = 0; i < MAX17042_CHARACTERIZATION_DATA_SIZE; i++) + config_data->cell_char_tbl[i] = be16_to_cpu(property[i]); + + return 0; +} + +static int max17042_cfg_rqrd_prop(struct device *dev, + struct device_node *np, + struct max17042_config_data *config_data) +{ + if (of_property_read_u16(np, VERSION_PROPERTY, + &config_data->version)) + return -EINVAL; + + if (of_property_read_u16(np, CONFIG_PROPERTY, + &config_data->config)) + return -EINVAL; + if (of_property_read_u16(np, FILTER_CFG_PROPERTY, + &config_data->filter_cfg)) + return -EINVAL; + if (of_property_read_u16(np, RELAX_CFG_PROPERTY, + &config_data->relax_cfg)) + return -EINVAL; + if (of_property_read_u16(np, LEARN_CFG_PROPERTY, + &config_data->learn_cfg)) + return -EINVAL; + if (of_property_read_u16(np, FULL_SOC_THRESH_PROPERTY, + &config_data->full_soc_thresh)) + return -EINVAL; + if (of_property_read_u16(np, RCOMP0_PROPERTY, + &config_data->rcomp0)) + return -EINVAL; + if (of_property_read_u16(np, TCOMPC0_PROPERTY, + &config_data->tcompc0)) + return -EINVAL; + if (of_property_read_u16(np, ICHGT_TERM_PROPERTY, + &config_data->ichgt_term)) + return -EINVAL; + if (of_property_read_u16(np, QRTBL00_PROPERTY, + &config_data->qrtbl00)) + return -EINVAL; + if (of_property_read_u16(np, QRTBL10_PROPERTY, + &config_data->qrtbl10)) + return -EINVAL; + if (of_property_read_u16(np, QRTBL20_PROPERTY, + &config_data->qrtbl20)) + return -EINVAL; + if (of_property_read_u16(np, QRTBL30_PROPERTY, + &config_data->qrtbl30)) + return -EINVAL; + if (of_property_read_u16(np, FULLCAP_PROPERTY, + &config_data->fullcap)) + return -EINVAL; + if (of_property_read_u16(np, DESIGN_CAP_PROPERTY, + &config_data->design_cap)) + return -EINVAL; + if (of_property_read_u16(np, FULLCAPNOM_PROPERTY, + &config_data->fullcapnom)) + return -EINVAL; + + return max17042_get_cell_char_tbl(dev, np, config_data); +} + +static void max17042_cfg_optnl_prop(struct device_node *np, + struct max17042_config_data *config_data) +{ + of_property_read_u16(np, TGAIN_PROPERTY, &config_data->tgain); + of_property_read_u16(np, TOFF_PROPERTY, &config_data->toff); +} + +static struct max17042_config_data * +max17042_get_config_data(struct device *dev) +{ + char *config_node = NULL; + char config_node_path[64]; + struct max17042_config_data *config_data; + struct device_node *np = dev->of_node; + + if (!np) + return NULL; + + config_node = (char *)get_dts_batt_id(dev); + if (config_node) { + snprintf(config_node_path, sizeof(config_node_path), + "%s-%s", CONFIG_NODE, config_node); + config_node = config_node_path; + } else { + config_node = CONFIG_NODE; + } + + dev_info(dev, "using %s profile\n", config_node); + + np = of_get_child_by_name(np, config_node); + if (!np) + return NULL; + + config_data = devm_kzalloc(dev, sizeof(*config_data), GFP_KERNEL); + if (!config_data) + return NULL; + + if (max17042_cfg_rqrd_prop(dev, np, config_data)) { + devm_kfree(dev, config_data); + return NULL; + } + + max17042_cfg_optnl_prop(np, config_data); + + return config_data; +} + +static struct max17042_temp_conv * +max17042_get_conv_table(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct max17042_temp_conv *temp_conv; + const __be16 *property; + int i, lenp, num; + u16 temp; + s16 start; + + if (!np) + return NULL; + + np = of_get_child_by_name(np, TEMP_CONV_NODE); + if (!np) + return NULL; + + property = of_get_property(np, RESULT_PROPERTY, &lenp); + if (!property || lenp <= 0) { + dev_err(dev, "%s must have %s property\n", TEMP_CONV_NODE, + RESULT_PROPERTY); + return NULL; + } + + if (of_property_read_u16(np, START_PROPERTY, &temp)) { + dev_err(dev, "%s must have %s property\n", TEMP_CONV_NODE, + START_PROPERTY); + return NULL; + } + + start = (s16) temp; + + temp_conv = devm_kzalloc(dev, sizeof(*temp_conv), GFP_KERNEL); + if (!temp_conv) + return NULL; + + num = lenp / sizeof(*property); + temp_conv->result = devm_kzalloc(dev, sizeof(s16) * num, GFP_KERNEL); + if (!temp_conv->result) { + devm_kfree(dev, temp_conv); + return NULL; + } + + temp_conv->start = start; + temp_conv->num_result = num; + + for (i = 0; i < num; i++) { + temp = be16_to_cpu(property[i]); + temp_conv->result[i] = (s16) temp; + } + + return temp_conv; +} + + static struct max17042_platform_data * max17042_get_pdata(struct device *dev) { @@ -663,6 +1158,9 @@ max17042_get_pdata(struct device *dev) if (!pdata) return NULL; + pdata->init_data = max17042_get_init_data(dev, &pdata->num_init_data); + pdata->gpio_list = max17042_get_gpio_list(dev, &pdata->num_gpio_list); + /* * Require current sense resistor value to be specified for * current-sense functionality to be enabled at all. @@ -672,6 +1170,21 @@ max17042_get_pdata(struct device *dev) pdata->enable_current_sense = true; } + pdata->enable_por_init = + of_property_read_bool(np, "maxim,enable_por_init"); + + pdata->batt_undervoltage_zero_soc = + of_property_read_bool(np, "maxim,batt_undervoltage_zero_soc"); + + pdata->config_data = max17042_get_config_data(dev); + if (!pdata->config_data) + dev_warn(dev, "config data is missing\n"); + + pdata->tcnv = max17042_get_conv_table(dev); + + of_property_read_string(np, "maxim,malicious_supply", + &pdata->malicious_supply); + return pdata; } #else @@ -682,6 +1195,176 @@ max17042_get_pdata(struct device *dev) } #endif +#ifdef CONFIG_BATTERY_MAX17042_DEBUGFS +static int max17042_debugfs_read_addr(void *data, u64 *val) +{ + struct max17042_chip *chip = (struct max17042_chip *)data; + *val = chip->debugfs_addr; + return 0; +} + +static int max17042_debugfs_write_addr(void *data, u64 val) +{ + struct max17042_chip *chip = (struct max17042_chip *)data; + chip->debugfs_addr = val; + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(addr_fops, max17042_debugfs_read_addr, + max17042_debugfs_write_addr, "0x%02llx\n"); + +static int max17042_debugfs_read_data(void *data, u64 *val) +{ + struct max17042_chip *chip = (struct max17042_chip *)data; + int ret = max17042_read_reg(chip->client, chip->debugfs_addr); + + if (ret < 0) + return ret; + + *val = ret; + return 0; +} + +static int max17042_debugfs_write_data(void *data, u64 val) +{ + struct max17042_chip *chip = (struct max17042_chip *)data; + return max17042_write_reg(chip->client, chip->debugfs_addr, val); +} +DEFINE_SIMPLE_ATTRIBUTE(data_fops, max17042_debugfs_read_data, + max17042_debugfs_write_data, "0x%02llx\n"); + +static int max17042_debugfs_read_capacity(void *data, u64 *val) +{ + struct max17042_chip *chip = (struct max17042_chip *)data; + *val = chip->debugfs_capacity; + return 0; +} + +static int max17042_debugfs_write_capacity(void *data, u64 val) +{ + struct max17042_chip *chip = (struct max17042_chip *)data; + chip->debugfs_capacity = val; + power_supply_changed(&chip->battery); + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(capacity_fops, max17042_debugfs_read_capacity, + max17042_debugfs_write_capacity, "%llu\n"); + +static int max17042_debugfs_create(struct max17042_chip *chip) +{ + chip->debugfs_root = debugfs_create_dir(dev_name(&chip->client->dev), + NULL); + if (!chip->debugfs_root) + return -ENOMEM; + + if (!debugfs_create_file("addr", S_IRUGO | S_IWUSR, chip->debugfs_root, + chip, &addr_fops)) + goto err_debugfs; + + if (!debugfs_create_file("data", S_IRUGO | S_IWUSR, chip->debugfs_root, + chip, &data_fops)) + goto err_debugfs; + + chip->debugfs_capacity = 0xFF; + if (!debugfs_create_file("capacity", S_IRUGO | S_IWUSR, + chip->debugfs_root, chip, &capacity_fops)) + goto err_debugfs; + + return 0; + +err_debugfs: + debugfs_remove_recursive(chip->debugfs_root); + chip->debugfs_root = NULL; + return -ENOMEM; +} +#endif + +static void max17042_external_power_changed(struct power_supply *psy) +{ + struct max17042_chip *chip; + union power_supply_propval val; + + chip = container_of(psy, struct max17042_chip, battery); + + if (!chip->psy_malicious) + return; + + chip->psy_malicious->get_property(chip->psy_malicious, + POWER_SUPPLY_PROP_ONLINE, &val); + + if (chip->malicious_online != val.intval) { + chip->malicious_online = val.intval; + if (!chip->malicious_online) { + dev_dbg(&chip->client->dev, "malicious ps removed\n"); + schedule_work(&chip->work_malicious_removed); + } + } +} + +static ssize_t max17042_show_alert_threshold(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct max17042_chip *chip = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", chip->alert_threshold); +} + +static ssize_t max17042_store_alert_threshold(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct max17042_chip *chip = dev_get_drvdata(dev); + unsigned long t; + u16 r; + + r = kstrtoul(buf, 10, &t); + if ((!r) && ( r < 100 )) { + chip->alert_threshold = (u16)t; + max17042_set_soc_threshold(chip, chip->alert_threshold); + } + + return r ? r : count; +} + +static DEVICE_ATTR(alert_threshold, S_IRUGO | S_IWUSR, + max17042_show_alert_threshold, max17042_store_alert_threshold); + + +static ssize_t max17042_show_battery_age(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct max17042_chip *chip = dev_get_drvdata(dev); + int ret = max17042_read_reg(chip->client, MAX17042_Age); + + return ret < 0 ? ret : sprintf(buf, "%u\n", ret / MAX17042_AGE_DIV); +} +static DEVICE_ATTR(battery_age, S_IRUGO, max17042_show_battery_age, NULL); + +static struct attribute *max17042_attrs[] = { + &dev_attr_alert_threshold.attr, + &dev_attr_battery_age.attr, + NULL, +}; + +static struct attribute_group max17042_attr_group = { + .attrs = max17042_attrs, +}; + +static bool max17042_new_config_data(struct max17042_chip *chip) +{ + int ret; + + if (chip->chip_type == MAX17042) + return false; + + ret = max17042_read_reg(chip->client, MAX17047_Config_Ver); + if (ret < 0) + return false; + + return (chip->pdata->config_data->version != ret); +} + static int max17042_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -689,6 +1372,7 @@ static int max17042_probe(struct i2c_client *client, struct max17042_chip *chip; int ret; int reg; + union power_supply_propval val; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) return -EIO; @@ -706,6 +1390,16 @@ static int max17042_probe(struct i2c_client *client, i2c_set_clientdata(client, chip); + if (chip->pdata->malicious_supply) { + chip->psy_malicious = power_supply_get_by_name( + chip->pdata->malicious_supply); + if (!chip->psy_malicious) { + dev_warn(&client->dev, + "malicious ps not found, deferring.\n"); + return -EPROBE_DEFER; + } + } + ret = max17042_read_reg(chip->client, MAX17042_DevName); if (ret == MAX17042_IC_VERSION) { dev_dbg(&client->dev, "chip type max17042 detected\n"); @@ -723,6 +1417,9 @@ static int max17042_probe(struct i2c_client *client, chip->battery.get_property = max17042_get_property; chip->battery.properties = max17042_battery_props; chip->battery.num_properties = ARRAY_SIZE(max17042_battery_props); + chip->battery.external_power_changed = max17042_external_power_changed; + + chip->alert_threshold = DEFAULT_ALERT_THRESHOLD; /* When current is not measured, * CURRENT_NOW and CURRENT_AVG properties should be invisible. */ @@ -748,16 +1445,37 @@ static int max17042_probe(struct i2c_client *client, return ret; } + if (chip->psy_malicious) { + INIT_WORK(&chip->work_malicious_removed, + max17042_malicious_removed_worker); + chip->psy_malicious->get_property(chip->psy_malicious, + POWER_SUPPLY_PROP_ONLINE, + &val); + chip->malicious_online = val.intval; + dev_dbg(&client->dev, "malicious_online = %d\n", + chip->malicious_online); + } + + ret = gpio_request_array(chip->pdata->gpio_list, + chip->pdata->num_gpio_list); + if (ret) { + dev_err(&client->dev, "cannot request GPIOs\n"); + return ret; + } + + mutex_init(&chip->lock); + if (client->irq) { ret = request_threaded_irq(client->irq, NULL, max17042_thread_handler, - IRQF_TRIGGER_FALLING, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, chip->battery.name, chip); if (!ret) { reg = max17042_read_reg(client, MAX17042_CONFIG); reg |= CONFIG_ALRT_BIT_ENBL; max17042_write_reg(client, MAX17042_CONFIG, reg); - max17042_set_soc_threshold(chip, 1); + max17042_set_soc_threshold(chip, chip->alert_threshold); } else { client->irq = 0; dev_err(&client->dev, "%s(): cannot get IRQ\n", @@ -766,13 +1484,25 @@ static int max17042_probe(struct i2c_client *client, } reg = max17042_read_reg(chip->client, MAX17042_STATUS); - if (reg & STATUS_POR_BIT) { + if (reg & STATUS_POR_BIT || max17042_new_config_data(chip)) { INIT_WORK(&chip->work, max17042_init_worker); schedule_work(&chip->work); } else { chip->init_complete = 1; } +#ifdef CONFIG_BATTERY_MAX17042_DEBUGFS + ret = max17042_debugfs_create(chip); + if (ret) { + dev_err(&client->dev, "cannot create debugfs\n"); + return ret; + } +#endif + + ret = sysfs_create_group(&client->dev.kobj, &max17042_attr_group); + if (ret) + dev_err(&client->dev, "failed to create sysfs files\n"); + return 0; } @@ -780,8 +1510,16 @@ static int max17042_remove(struct i2c_client *client) { struct max17042_chip *chip = i2c_get_clientdata(client); +#ifdef CONFIG_BATTERY_MAX17042_DEBUGFS + debugfs_remove_recursive(chip->debugfs_root); +#endif + + sysfs_remove_group(&client->dev.kobj, &max17042_attr_group); + if (client->irq) free_irq(client->irq, chip); + mutex_destroy(&chip->lock); + gpio_free_array(chip->pdata->gpio_list, chip->pdata->num_gpio_list); power_supply_unregister(&chip->battery); return 0; } @@ -811,7 +1549,7 @@ static int max17042_resume(struct device *dev) 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); + max17042_set_soc_threshold(chip, chip->alert_threshold); } return 0; |