static int __devinit smb349_hwinit(struct smb349_struct *smb349_chg)
{
    int ret;

    ret = smb349_write_reg(smb349_chg->client, CMD_A_REG,
                           VOLATILE_W_PERM_BIT);
    if (ret) {
        pr_err("Failed to set VOLATILE_W_PERM_BIT rc=%d\n", ret);
        return ret;
    }

    ret = smb349_masked_write(smb349_chg->client, CHG_CTRL_REG,
                              CURR_TERM_END_CHG_BIT, CURR_TERM_END_CHG_BIT);
    if (ret) {
        pr_err("Failed to set CURR_TERM_END_CHG_BIT rc=%d\n", ret);
        return ret;
    }

    ret = chg_current_set(smb349_chg);
    if (ret) {
        pr_err("Failed to set FAST_CHG_CURRENT rc=%d\n", ret);
        return ret;
    }

    return 0;
}
static int set_reg(void *data, u64 val)
{
    int addr = (int)data;
    int ret;
    u8 temp;

    temp = (u16) val;
    ret = smb349_write_reg(the_smb349_chg->client, addr, temp);

    if (ret) {
        pr_err("smb349_write_reg to %x value =%d errored = %d\n",
               addr, temp, ret);
        return -EAGAIN;
    }
    return 0;
}
static int set_reg(void *data, u64 val)
{
	struct smb349_charger *chip = data;
	int rc;
	u8 temp;

	temp = (u8) val;
	rc = smb349_write_reg(chip, chip->peek_poke_address, temp);
	if (rc < 0) {
		dev_err(chip->dev,
			"Couldn't write 0x%02x to 0x%02x rc= %d\n",
			chip->peek_poke_address, temp, rc);
		return -EAGAIN;
	}
	return 0;
}
static int smb349_masked_write(struct i2c_client *client, int reg,
                               u8 mask, u8 val)
{
    s32 rc;
    u8 temp;

    rc = smb349_read_reg(client, reg, &temp);
    if (rc) {
        pr_err("smb349_read_reg failed: reg=%03X, rc=%d\n", reg, rc);
        return rc;
    }
    temp &= ~mask;
    temp |= val & mask;
    rc = smb349_write_reg(client, reg, temp);
    if (rc) {
        pr_err("smb349_write failed: reg=%03X, rc=%d\n", reg, rc);
        return rc;
    }
    return 0;
}
static int smb349_masked_write(struct smb349_charger *chip, int reg,
							u8 mask, u8 val)
{
	s32 rc;
	u8 temp;

	rc = smb349_read_reg(chip, reg, &temp);
	if (rc) {
		dev_err(chip->dev,
			"smb349_read_reg Failed: reg=%03X, rc=%d\n", reg, rc);
		return rc;
	}
	temp &= ~mask;
	temp |= val & mask;
	rc = smb349_write_reg(chip, reg, temp);
	if (rc) {
		dev_err(chip->dev,
			"smb349_write Failed: reg=%03X, rc=%d\n", reg, rc);
		return rc;
	}
	return 0;
}
static int smb349_hw_init(struct smb349_charger *chip)
{
	int rc;
	u8 reg = 0, mask = 0;

	/*
	 * If the charger is pre-configured for autonomous operation,
	 * do not apply additonal settings
	 */
	if (chip->chg_autonomous_mode) {
		dev_dbg(chip->dev, "Charger configured for autonomous mode\n");
		return 0;
	}

	rc = smb349_read_reg(chip, CHG_REVISION_REG, &reg);
	if (rc) {
		dev_err(chip->dev, "Couldn't read CHG_REVISION_REG rc=%d\n",
									rc);
		return rc;
	}
	/*
	 * A4 silicon revision of SMB349 fails charger type detection
	 * (apsd) due to interference on the D+/- lines by the USB phy.
	 * Set the workaround flag to disable charger type reporting
	 * for this revision.
	 */
	if ((reg & SMB349_REV_MASK) == SMB349_REV_A4)
		chip->workaround_flags |= WRKARND_APSD_FAIL;

	pr_debug("workaround_flags = %x\n", chip->workaround_flags);

	rc = smb349_enable_volatile_writes(chip);
	if (rc) {
		dev_err(chip->dev, "Couldn't configure volatile writes rc=%d\n",
				rc);
		return rc;
	}

	/* setup defaults for CHG_CNTRL_REG */
	reg = CHG_CTRL_BATT_MISSING_DET_THERM_IO;
	mask = CHG_CTRL_BATT_MISSING_DET_MASK;
	rc = smb349_masked_write(chip, CHG_CTRL_REG, mask, reg);
	if (rc) {
		dev_err(chip->dev, "Couldn't set CHG_CTRL_REG rc=%d\n", rc);
		return rc;
	}
	/* setup defaults for PIN_CTRL_REG */
	reg = CHG_PIN_CTRL_USBCS_REG_BIT | CHG_PIN_CTRL_CHG_EN_LOW_REG_BIT |
		CHG_PIN_CTRL_APSD_IRQ_BIT | CHG_PIN_CTRL_CHG_ERR_IRQ_BIT;
	mask = CHG_PIN_CTRL_CHG_EN_MASK | CHG_PIN_CTRL_USBCS_REG_MASK |
		CHG_PIN_CTRL_APSD_IRQ_MASK | CHG_PIN_CTRL_CHG_ERR_IRQ_MASK;
	rc = smb349_masked_write(chip, CHG_PIN_EN_CTRL_REG, mask, reg);
	if (rc) {
		dev_err(chip->dev, "Couldn't set CHG_PIN_EN_CTRL_REG rc=%d\n",
				rc);
		return rc;
	}
	/* setup USB 2.0/3.0 detection mechanism */
	rc = smb349_masked_write(chip, CHG_OTH_CURRENT_CTRL_REG,
					CHG_OTH_CTRL_USB_2_3_PIN_REG_MASK,
					CHG_OTH_CTRL_USB_2_3_REG_CTRL_BIT);
	if (rc) {
		dev_err(chip->dev, "Couldn't set USB_2_3_REG_CTRL_BIT rc=%d\n",
				rc);
		return rc;
	}
	/* setup USB suspend and APSD  */
	reg = VARIOUS_FUNC_USB_SUSP_EN_REG_BIT;
	if (!chip->disable_apsd)
		reg |= VARIOUS_FUNC_APSD_EN_BIT;
	mask = VARIOUS_FUNC_USB_SUSP_MASK | VARIOUS_FUNC_APSD_MASK;
	rc = smb349_masked_write(chip, VARIOUS_FUNC_REG, mask, reg);
	if (rc) {
		dev_err(chip->dev, "Couldn't set VARIOUS_FUNC_REG rc=%d\n",
				rc);
		return rc;
	}
	/* Fault and Status IRQ configuration */
	reg = FAULT_INT_HOT_COLD_HARD_BIT | FAULT_INT_HOT_COLD_SOFT_BIT
		| FAULT_INT_INPUT_UV_BIT | FAULT_INT_AICL_COMPLETE_BIT;
	rc = smb349_write_reg(chip, FAULT_INT_REG, reg);
	if (rc) {
		dev_err(chip->dev, "Couldn't set FAULT_INT_REG rc=%d\n", rc);
		return rc;
	}
	reg = STATUS_INT_CHG_TIMEOUT_BIT | STATUS_INT_OTG_DETECT_BIT |
		STATUS_INT_BATT_OV_BIT | STATUS_INT_TERM_TAPER_BIT |
		STATUS_INT_FAST_CHG_BIT | STATUS_INT_LOW_BATT_BIT |
		STATUS_INT_MISSING_BATT_BIT;
	rc = smb349_write_reg(chip, STATUS_INT_REG, reg);
	if (rc) {
		dev_err(chip->dev, "Couldn't set STATUS_INT_REG rc=%d\n", rc);
		return rc;
	}
	/* setup THERM Monitor */
	rc = smb349_masked_write(chip, THERM_A_CTRL_REG,
		THERM_A_THERM_MONITOR_EN_MASK, THERM_A_THERM_MONITOR_EN_BIT);
	if (rc) {
		dev_err(chip->dev, "Couldn't set THERM_A_CTRL_REG rc=%d\n",
				rc);
		return rc;
	}
	/* set the fast charge current limit */
	rc = smb349_fastchg_current_set(chip);
	if (rc) {
		dev_err(chip->dev, "Couldn't set fastchg current rc=%d\n", rc);
		return rc;
	}

	/* set the float voltage */
	if (chip->vfloat_mv != -EINVAL) {
		rc = smb349_float_voltage_set(chip, chip->vfloat_mv);
		if (rc < 0) {
			dev_err(chip->dev,
				"Couldn't set float voltage rc = %d\n", rc);
			return rc;
		}
	}

	/* set iterm */
	if (chip->iterm_ma != -EINVAL) {
		if (chip->iterm_disabled) {
			dev_err(chip->dev, "Error: Both iterm_disabled and iterm_ma set\n");
			return -EINVAL;
		} else {
			if (chip->iterm_ma <= 100)
				reg = CHG_ITERM_100MA;
			else if (chip->iterm_ma <= 200)
				reg = CHG_ITERM_200MA;
			else if (chip->iterm_ma <= 300)
				reg = CHG_ITERM_300MA;
			else if (chip->iterm_ma <= 400)
				reg = CHG_ITERM_400MA;
			else if (chip->iterm_ma <= 500)
				reg = CHG_ITERM_500MA;
			else if (chip->iterm_ma <= 600)
				reg = CHG_ITERM_600MA;
			else
				reg = CHG_ITERM_700MA;

			rc = smb349_masked_write(chip, CHG_OTH_CURRENT_CTRL_REG,
							CHG_ITERM_MASK, reg);
			if (rc) {
				dev_err(chip->dev,
					"Couldn't set iterm rc = %d\n", rc);
				return rc;
			}

			rc = smb349_masked_write(chip, CHG_CTRL_REG,
						CHG_CTRL_CURR_TERM_END_MASK, 0);
			if (rc) {
				dev_err(chip->dev,
					"Couldn't enable iterm rc = %d\n", rc);
				return rc;
			}
		}
	} else  if (chip->iterm_disabled) {
		rc = smb349_masked_write(chip, CHG_CTRL_REG,
					CHG_CTRL_CURR_TERM_END_MASK,
					CHG_CTRL_CURR_TERM_END_MASK);
		if (rc) {
			dev_err(chip->dev, "Couldn't set iterm rc = %d\n",
								rc);
			return rc;
		}
	}

	/* set recharge-threshold */
	if (chip->recharge_mv != -EINVAL) {
		if (chip->recharge_disabled) {
			dev_err(chip->dev, "Error: Both recharge_disabled and recharge_mv set\n");
			return -EINVAL;
		} else {
			reg = 0;
			if (chip->recharge_mv > 50)
				reg = CHG_CTRL_RECHG_100MV_BIT;

			rc = smb349_masked_write(chip, CHG_CTRL_REG,
					CHG_CTRL_RECHG_50_100_MASK |
					CHG_CTRL_AUTO_RECHARGE_MASK, reg);
			if (rc) {
				dev_err(chip->dev,
					"Couldn't set rechg-cfg rc = %d\n", rc);
				return rc;
			}
		}
	} else if (chip->recharge_disabled) {
		rc = smb349_masked_write(chip, CHG_CTRL_REG,
				CHG_CTRL_AUTO_RECHARGE_MASK,
				CHG_CTRL_AUTO_RECHARGE_MASK);
		if (rc) {
			dev_err(chip->dev,
				"Couldn't disable auto-rechg rc = %d\n", rc);
			return rc;
		}
	}

	/* enable/disable charging */
	rc = smb349_masked_write(chip, CMD_A_REG, CMD_A_CHG_ENABLE_BIT,
			chip->charging_disabled ? 0 : CMD_A_CHG_ENABLE_BIT);
	if (rc) {
		dev_err(chip->dev, "Unable to %s charging. rc=%d\n",
			chip->charging_disabled ? "disable" : "enable", rc);
	}

	return rc;
}