[<prev] [next>] [day] [month] [year] [list]
Message-id: <13353478.106091322541052039.JavaMail.weblogic@epml22>
Date: Tue, 29 Nov 2011 04:30:52 +0000 (GMT)
From: MyungJoo Ham <myungjoo.ham@...sung.com>
To: "dirk.brandewie@...il.com" <dirk.brandewie@...il.com>,
"linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>
Cc: "cbouatmailru@...il.com" <cbouatmailru@...il.com>,
±èµ¿±Ù <dg77.kim@...sung.com>,
¹Ú°æ¹Î <kyungmin.park@...sung.com>,
"Jason.Wortham@...im-ic.com" <Jason.Wortham@...im-ic.com>,
"alan@...ux.intel.com" <alan@...ux.intel.com>
Subject: Re: [PATCH 3/4] max17042: Add POR init procedure from Maxim appnote
Sender : dirk.brandewie@...il.com<dirk.brandewie@...il.com>
Date : 2011-11-29 04:49 (GMT+09:00)
Title : [PATCH 3/4] max17042: Add POR init procedure from Maxim appnote
>
> From: Dirk Brandewie <dirk.brandewie@...il.com>
>
> Add power on reset (POR) init procedure defined by the maxim
> appnote. Using this procedure ensures that the part is
> configured/initialized correctly at POR and improves early accuracy of
> the fuel gauge and informs the fuel gauge with the battery
> characterization parameters. The battery characterization parameters
> come from the maxim characterization procedure.
Did you omit your sign-off?
Acked-by: MyungJoo Ham <myungjoo.ham@...sung.com>
> Summary:
> ---
> drivers/power/max17042_battery.c | 348 ++++++++++++++++++++++++++++++++
> include/linux/power/max17042_battery.h | 56 +++++
> 2 files changed, 404 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c
> index 33fdce0..1eef5f9 100644
> --- a/drivers/power/max17042_battery.c
> +++ b/drivers/power/max17042_battery.c
> @@ -26,10 +26,34 @@
> #include
> #include
> #include
> +#include
> #include
> #include
> #include
>
> +/* 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)
> +
> +#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
> +
> struct max17042_chip {
> struct i2c_client *client;
> struct power_supply battery;
> @@ -181,6 +205,324 @@ static int max17042_get_property(struct power_supply *psy,
> return 0;
> }
>
> +static int max17042_write_verify_reg(struct i2c_client *client,
> + u8 reg, u16 value)
> +{
> + int retries = 8;
> + int ret;
> + u16 read_value;
> +
> + do {
> + ret = i2c_smbus_write_word_data(client, reg, value);
> + read_value = max17042_read_reg(client, reg);
> + if (read_value != value) {
> + ret = -EIO;
> + retries--;
> + }
> + } while (retries && read_value != value);
> +
> + if (ret < 0)
> + dev_err(&client->dev, "%s: err %d ", __func__, ret);
> +
> + return ret;
> +}
> +
> +static inline void max17042_override_por(
> + struct i2c_client *client, u8 reg, u16 value)
> +{
> + if (value)
> + max17042_write_reg(client, reg, value);
> +}
> +
> +static inline void max10742_unlock_model(struct max17042_chip *chip)
> +{
> + struct i2c_client *client = chip->client;
> + max17042_write_reg(client, MAX17042_MLOCKReg1, MODEL_UNLOCK1);
> + max17042_write_reg(client, MAX17042_MLOCKReg2, MODEL_UNLOCK2);
> +}
> +
> +static inline void max10742_lock_model(struct max17042_chip *chip)
> +{
> + struct i2c_client *client = chip->client;
> + max17042_write_reg(client, MAX17042_MLOCKReg1, MODEL_LOCK1);
> + max17042_write_reg(client, MAX17042_MLOCKReg2, MODEL_LOCK2);
> +}
> +
> +static inline void max17042_write_model_data(struct max17042_chip *chip,
> + u8 addr, int size)
> +{
> + struct i2c_client *client = chip->client;
> + int i;
> + for (i = 0; i < size; i++)
> + max17042_write_reg(client, addr + i,
> + chip->pdata->config_data->cell_char_tbl[i]);
> +}
> +
> +static inline void max17042_read_model_data(struct max17042_chip *chip,
> + u8 addr, u16 *data, int size)
> +{
> + struct i2c_client *client = chip->client;
> + int i;
> +
> + for (i = 0; i < size; i++)
> + data[i] = max17042_read_reg(client, addr + i);
> +}
> +
> +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 ", __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, " ");
> + return -EINVAL;
> + }
> + return 0;
> +}
> +
> +static int max17042_init_model(struct max17042_chip *chip)
> +{
> + int ret;
> + int table_size =
> + sizeof(chip->pdata->config_data->cell_char_tbl)/sizeof(u16);
> + u16 *temp_data;
> +
> + temp_data = kzalloc(table_size, 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,
> + 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 =
> + sizeof(chip->pdata->config_data->cell_char_tbl)/sizeof(u16);
> + u16 *temp_data;
> +
> + temp_data = kzalloc(table_size, 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])
> + return -EINVAL;
> + kfree(temp_data);
> + return 0;
> +}
> +
> +static void max17042_write_config_regs(struct max17042_chip *chip)
> +{
> + struct max17042_config_data *config = chip->pdata->config_data;
> +
> + max17042_write_reg(chip->client, MAX17042_CONFIG, config->config);
> + max17042_write_reg(chip->client, MAX17042_LearnCFG, config->learn_cfg);
> + max17042_write_reg(chip->client, MAX17042_FilterCFG,
> + config->filter_cfg);
> + max17042_write_reg(chip->client, MAX17042_RelaxCFG, config->relax_cfg);
> +}
> +
> +static void max17042_write_custom_regs(struct max17042_chip *chip)
> +{
> + struct max17042_config_data *config = chip->pdata->config_data;
> +
> + max17042_write_verify_reg(chip->client, MAX17042_RCOMP0,
> + config->rcomp0);
> + max17042_write_verify_reg(chip->client, MAX17042_TempCo,
> + config->tcompc0);
> + max17042_write_reg(chip->client, MAX17042_EmptyTempCo,
> + config->empty_tempco);
> + max17042_write_verify_reg(chip->client, MAX17042_K_empty0,
> + config->kempty0);
> + max17042_write_verify_reg(chip->client, MAX17042_ICHGTerm,
> + config->ichgt_term);
> +}
> +
> +static void max17042_update_capacity_regs(struct max17042_chip *chip)
> +{
> + struct max17042_config_data *config = chip->pdata->config_data;
> +
> + max17042_write_verify_reg(chip->client, MAX17042_FullCAP,
> + config->fullcap);
> + max17042_write_reg(chip->client, MAX17042_DesignCap,
> + config->design_cap);
> + max17042_write_verify_reg(chip->client, MAX17042_FullCAPNom,
> + config->fullcapnom);
> +}
> +
> +static void max17042_reset_vfsoc0_reg(struct max17042_chip *chip)
> +{
> + u16 vfSoc;
> +
> + vfSoc = max17042_read_reg(chip->client, MAX17042_VFSOC);
> + max17042_write_reg(chip->client, MAX17042_VFSOC0Enable, VFSOC0_UNLOCK);
> + max17042_write_verify_reg(chip->client, MAX17042_VFSOC0, vfSoc);
> + max17042_write_reg(chip->client, MAX17042_VFSOC0Enable, VFSOC0_LOCK);
> +}
> +
> +static void max17042_load_new_capacity_params(struct max17042_chip *chip)
> +{
> + u16 full_cap0, rep_cap, dq_acc, vfSoc;
> + u32 rem_cap;
> +
> + 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
> + * 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(chip->client, MAX17042_RemCap, (u16)rem_cap);
> +
> + rep_cap = (u16)rem_cap;
> + max17042_write_verify_reg(chip->client, 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(chip->client, MAX17042_dQacc, dq_acc);
> + max17042_write_verify_reg(chip->client, MAX17042_dPacc, dP_ACC_200);
> +
> + max17042_write_verify_reg(chip->client, MAX17042_FullCAP,
> + config->fullcap);
> + max17042_write_reg(chip->client, MAX17042_DesignCap,
> + config->design_cap);
> + max17042_write_verify_reg(chip->client, MAX17042_FullCAPNom,
> + config->fullcapnom);
> +}
> +
> +/*
> + * 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 i2c_client *client = chip->client;
> + struct max17042_config_data *config = chip->pdata->config_data;
> +
> + max17042_override_por(client, MAX17042_TGAIN, config->tgain);
> + max17042_override_por(client, MAx17042_TOFF, config->toff);
> + max17042_override_por(client, MAX17042_CGAIN, config->cgain);
> + max17042_override_por(client, MAX17042_COFF, config->coff);
> +
> + max17042_override_por(client, MAX17042_VALRT_Th, config->valrt_thresh);
> + max17042_override_por(client, MAX17042_TALRT_Th, config->talrt_thresh);
> + max17042_override_por(client, MAX17042_SALRT_Th,
> + 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);
> + 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);
> +
> + max17042_override_por(client, MAX17042_V_empty, config->vempty);
> + 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);
> + max17042_override_por(client, MAX17042_EmptyTempCo,
> + config->empty_tempco);
> + max17042_override_por(client, MAX17042_K_empty0, config->kempty0);
> +}
> +
> +static int max17042_init_chip(struct max17042_chip *chip)
> +{
> + int val;
> + int ret;
> +
> + val = max17042_read_reg(chip->client, MAX17042_STATUS);
> +
> + if (val & STATUS_POR_BIT) {
> +
> + 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 ",
> + __func__);
> + return -EIO;
> + }
> + max17042_verify_model_lock(chip);
> + if (ret) {
> + dev_err(&chip->client->dev, "%s lock verify failed ",
> + __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 */
> + val = max17042_read_reg(chip->client, MAX17042_STATUS);
> + max17042_write_reg(chip->client, MAX17042_STATUS,
> + val & (~STATUS_POR_BIT));
> + }
> + return 0;
> +}
> +
> +
> static void max17042_init_worker(struct work_struct *work)
> {
> struct max17042_chip *chip = container_of(work,
> @@ -194,6 +536,12 @@ static void max17042_init_worker(struct work_struct *work)
> max17042_set_reg(client, chip->pdata->init_data,
> chip->pdata->num_init_data);
>
> + if (chip->pdata->enable_por_init && chip->pdata->config_data) {
> + ret = max17042_init_chip(chip);
> + if (ret)
> + return;
> + }
> +
> if (!chip->pdata->enable_current_sense) {
> max17042_write_reg(client, MAX17042_CGAIN, 0x0000);
> max17042_write_reg(client, MAX17042_MiscCFG, 0x0003);
> diff --git a/include/linux/power/max17042_battery.h b/include/linux/power/max17042_battery.h
> index 67eeada..e01b167 100644
> --- a/include/linux/power/max17042_battery.h
> +++ b/include/linux/power/max17042_battery.h
> @@ -27,6 +27,8 @@
> #define MAX17042_BATTERY_FULL (100)
> #define MAX17042_DEFAULT_SNS_RESISTOR (10000)
>
> +#define MAX17042_CHARACTERIZATION_DATA_SIZE 48
> +
> enum max17042_register {
> MAX17042_STATUS = 0x00,
> MAX17042_VALRT_Th = 0x01,
> @@ -124,10 +126,64 @@ struct max17042_reg_data {
> u16 data;
> };
>
> +struct max17042_config_data {
> + /* External current sense resistor value in milli-ohms */
> + u32 cur_sense_val;
> +
> + /* A/D measurement */
> + u16 tgain; /* 0x2C */
> + u16 toff; /* 0x2D */
> + u16 cgain; /* 0x2E */
> + u16 coff; /* 0x2F */
> +
> + /* Alert / Status */
> + u16 valrt_thresh; /* 0x01 */
> + u16 talrt_thresh; /* 0x02 */
> + u16 soc_alrt_thresh; /* 0x03 */
> + u16 config; /* 0x01D */
> + u16 shdntimer; /* 0x03F */
> +
> + /* App data */
> + u16 design_cap; /* 0x18 */
> + u16 ichgt_term; /* 0x1E */
> +
> + /* MG3 config */
> + u16 at_rate; /* 0x04 */
> + u16 learn_cfg; /* 0x28 */
> + u16 filter_cfg; /* 0x29 */
> + u16 relax_cfg; /* 0x2A */
> + u16 misc_cfg; /* 0x2B */
> + u16 masksoc; /* 0x32 */
> +
> + /* MG3 save and restore */
> + u16 fullcap; /* 0x10 */
> + u16 fullcapnom; /* 0x23 */
> + u16 socempty; /* 0x33 */
> + u16 lavg_empty; /* 0x36 */
> + u16 dqacc; /* 0x45 */
> + u16 dpacc; /* 0x46 */
> +
> + /* Cell technology from power_supply.h */
> + u16 cell_technology;
> +
> + /* Cell Data */
> + u16 vempty; /* 0x12 */
> + u16 temp_nom; /* 0x24 */
> + u16 temp_lim; /* 0x25 */
> + u16 fctc; /* 0x37 */
> + u16 rcomp0; /* 0x38 */
> + u16 tcompc0; /* 0x39 */
> + u16 empty_tempco; /* 0x3A */
> + u16 kempty0; /* 0x3B */
> + u16 cell_char_tbl[MAX17042_CHARACTERIZATION_DATA_SIZE];
> +} __packed;
> +
> struct max17042_platform_data {
> struct max17042_reg_data *init_data;
> + struct max17042_config_data *config_data;
> int num_init_data; /* Number of enties in init_data array */
> bool enable_current_sense;
> + bool enable_por_init; /* Use POR init from Maxim appnote */
>
> /*
> * R_sns in micro-ohms.
> --
> 1.7.7.3
>
>
MyungJoo Ham (ÇÔ¸íÁÖ)
Mobile Software Platform Lab,
Digital Media and Communications (DMC) Business
Samsung Electronics
cell: +82-10-6714-2858 / office: +82-31-279-8033
Powered by blists - more mailing lists