static u8 smb327_set_termination_current_limit(struct i2c_client *client, int termination_current) { u8 reg_data, term_data; term_data = termination_current < 25 ? 0x0 : termination_current > 200 ? 0x7 : (termination_current - 25) / 25; /* Charge completion termination current */ smb327_i2c_read(client, SMB327_CHARGE_CURRENT, ®_data); reg_data &= ~CFG_TERMINATION_CURRENT_MASK; reg_data |= term_data << CFG_TERMINATION_CURRENT_SHIFT; smb327_i2c_write(client, SMB327_CHARGE_CURRENT, ®_data); /* set STAT assertion termination current */ smb327_i2c_read(client, SMB327_VARIOUS_FUNCTIONS, ®_data); reg_data &= ~CFG_STAT_ASSETION_TERM_MASK; reg_data |= term_data << CFG_STAT_ASSETION_TERM_SHIFT; smb327_i2c_write(client, SMB327_VARIOUS_FUNCTIONS, ®_data); return reg_data; }
static void smb327_i2c_write_array(struct i2c_client *client, u8 *buf, int size) { int i; for (i = 0; i < size; i += 3) smb327_i2c_write(client, (u8) (*(buf + i)), (buf + i) + 1); }
static void smb327_irq_disable(struct i2c_client *client) { u8 data; smb327_set_writable(client, 1); data = 0x6E; smb327_i2c_write(client, 0x04, &data); smb327_i2c_read(client, SMB327_INTERRUPT_SIGNAL_SELECTION, &data); data &= 0xfe; smb327_i2c_write(client, SMB327_INTERRUPT_SIGNAL_SELECTION, &data); smb327_set_writable(client, 0); }
static u8 smb327_set_charge_enable(struct i2c_client *client, int enable) { u8 chg_en; u8 reg_data; reg_data = 0x00; smb327_i2c_write(client, SMB327_FUNCTION_CONTROL_C, ®_data); smb327_i2c_read(client, SMB327_COMMAND, &chg_en); if (!enable) chg_en |= CMD_CHARGE_EN; else chg_en &= ~CMD_CHARGE_EN; smb327_i2c_write(client, SMB327_COMMAND, &chg_en); return chg_en; }
static void smb327_irq_enable(struct i2c_client *client) { u8 data; smb327_set_writable(client, 1); if (system_rev < 10) data = 0x4D; else data = 0x6D; smb327_i2c_write(client, 0x04, &data); smb327_i2c_read(client, SMB327_INTERRUPT_SIGNAL_SELECTION, &data); data |= 0x1; smb327_i2c_write(client, SMB327_INTERRUPT_SIGNAL_SELECTION, &data); smb327_set_writable(client, 0); }
static void smb327_set_writable(struct i2c_client *client, int writable) { u8 reg_data; smb327_i2c_read(client, SMB327_COMMAND, ®_data); if (writable) reg_data |= CMD_A_ALLOW_WRITE; else reg_data &= ~CMD_A_ALLOW_WRITE; smb327_i2c_write(client, SMB327_COMMAND, ®_data); }
static void smb327_charger_otg_control(struct i2c_client *client, int enable) { u8 reg_data; smb327_i2c_read(client, SMB327_OTG_POWER_AND_LDO, ®_data); if (!enable) reg_data |= CFG_OTG_ENABLE; else reg_data &= ~CFG_OTG_ENABLE; smb327_set_writable(client, 1); smb327_i2c_write(client, SMB327_OTG_POWER_AND_LDO, ®_data); smb327_set_writable(client, 0); }
static void smb327_set_command(struct i2c_client *client, int reg, int datum) { int val; u8 data = 0; val = smb327_i2c_read(client, reg, &data); if (val >= 0) { pr_debug("%s : reg(0x%02x): 0x%02x", __func__, reg, data); if (data != datum) { data = datum; if (smb327_i2c_write(client, reg, &data) < 0) pr_err("%s : error!\n", __func__); val = smb327_i2c_read(client, reg, &data); if (val >= 0) pr_debug(" => 0x%02x\n", data); } } }
static u8 smb327_set_fast_charging_current(struct i2c_client *client, int fast_charging_current) { u8 reg_data, chg_data; chg_data = fast_charging_current < 500 ? 0x0 : fast_charging_current > 1200 ? 0x7 : (fast_charging_current - 500) / 100; smb327_i2c_read(client, SMB327_CHARGE_CURRENT, ®_data); reg_data &= ~CFG_CHARGE_CURRENT_MASK; reg_data |= chg_data << CFG_CHARGE_CURRENT_SHIFT; smb327_i2c_write(client, SMB327_CHARGE_CURRENT, ®_data); pr_info("%s: Charge Current : 0x%x\n", __func__, reg_data); return reg_data; }
static u8 smb327_set_input_current_limit(struct i2c_client *client, int input_current) { u8 curr_data, reg_data; curr_data = input_current < 500 ? 0x0 : input_current > 1200 ? 0x7 : (input_current - 500) / 100; smb327_i2c_read(client, SMB327_INPUT_CURRENTLIMIT, ®_data); reg_data &= ~CFG_INPUT_CURRENT_MASK; reg_data |= curr_data << CFG_INPUT_CURRENT_SHIFT; smb327_i2c_write(client, SMB327_INPUT_CURRENTLIMIT, ®_data); pr_info("%s: set current limit : 0x%x\n", __func__, reg_data); return reg_data; }
static int smb327_get_charge_now(struct i2c_client *client) { u8 chg_now, data, addr; smb327_i2c_read(client, SMB327_BATTERY_CHARGING_STATUS_A, &chg_now); for (addr = 0; addr < 0x0c; addr++) { smb327_i2c_read(client, addr, &data); pr_info("smb327 addr : 0x%02x data : 0x%02x\n", addr, data); } for (addr = 0x31; addr <= 0x3D; addr++) { smb327_i2c_read(client, addr, &data); pr_info("smb327 addr : 0x%02x data : 0x%02x\n", addr, data); } /* Clear IRQ */ data = 0xFF; smb327_i2c_write(client, 0x30, &data); return (chg_now & 0x2); }
static u8 smb327_set_float_voltage(struct i2c_client *client, int float_voltage) { u8 reg_data, float_data; if (float_voltage < 3460) float_data = 0; else if (float_voltage <= 4340) float_data = (float_voltage - 3500) / 20 + 2; else if ((float_voltage == 4350) || (float_voltage == 4360)) float_data = 0x2D; /* (4340 -3500)/20 + 1 */ else float_data = 0x3F; smb327_i2c_read(client, SMB327_FLOAT_VOLTAGE, ®_data); reg_data &= ~CFG_FLOAT_VOLTAGE_MASK; reg_data |= float_data << CFG_FLOAT_VOLTAGE_SHIFT; smb327_i2c_write(client, SMB327_FLOAT_VOLTAGE, ®_data); return reg_data; }
static int smb327_get_charging_health(struct i2c_client *client) { int health = POWER_SUPPLY_HEALTH_GOOD; u8 status_reg, data; smb327_i2c_read(client, SMB327_INTERRUPT_STATUS_C, &status_reg); pr_info("%s : Interrupt status C(0x%02x)\n", __func__, status_reg); /* Is enabled ? */ if (status_reg & 0x2) { health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; } else if (status_reg & 0x4) { health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; } else if (status_reg & 0x80) { /* watchdog enable workaround */ /* clear IRQ */ data = 0xFF; smb327_i2c_write(client, 0x30, &data); /* reset function control */ smb327_charger_function_control(client); } return (int)health; }
static void smb327_charger_function_control(struct i2c_client *client) { struct sec_charger_info *charger = i2c_get_clientdata(client); u8 reg_data, charge_mode; smb327_set_writable(client, 1); if (system_rev < 10) reg_data = 0x4E; else reg_data = 0x6E; smb327_i2c_write(client, SMB327_FUNCTION_CONTROL_B, ®_data); smb327_i2c_read(client, SMB327_INTERRUPT_SIGNAL_SELECTION, ®_data); reg_data |= 0x1; smb327_i2c_write(client, SMB327_INTERRUPT_SIGNAL_SELECTION, ®_data); if (charger->cable_type == POWER_SUPPLY_TYPE_BATTERY) { /* turn off charger */ smb327_set_charge_enable(client, 0); } else { pr_info("%s: Input : %d, Charge : %d\n", __func__, charger->pdata->charging_current[charger->cable_type].input_current_limit, charger->charging_current); /* AICL enable */ smb327_i2c_read(client, SMB327_INPUT_CURRENTLIMIT, ®_data); reg_data &= ~CFG_AICL_ENABLE; reg_data &= ~CFG_AICL_VOLTAGE; /* AICL enable voltage 4.25V */ smb327_i2c_write(client, SMB327_INPUT_CURRENTLIMIT, ®_data); /* Function control A */ reg_data = 0xDA; smb327_i2c_write(client, SMB327_FUNCTION_CONTROL_A, ®_data); /* 4.2V float voltage */ smb327_set_float_voltage(client, charger->pdata->chg_float_voltage); /* Set termination current */ smb327_set_termination_current_limit(client, charger->pdata->charging_current[charger-> cable_type].full_check_current_1st); smb327_set_input_current_limit(client, charger->pdata->charging_current [charger->cable_type].input_current_limit); smb327_set_fast_charging_current(client, charger->charging_current); /* SET USB5/1, AC/USB Mode */ charge_mode = (charger->cable_type == POWER_SUPPLY_TYPE_MAINS) || (charger->cable_type == POWER_SUPPLY_TYPE_UARTOFF) ? 0x3 : 0x2; smb327_i2c_read(client, SMB327_COMMAND, ®_data); reg_data &= ~0x0C; reg_data |= charge_mode << 2; smb327_i2c_write(client, SMB327_COMMAND, ®_data); smb327_set_charge_enable(client, 1); } smb327_test_read(client); smb327_set_writable(client, 0); }