static int smb136_get_charging_state(void) { struct smb136_chg_data *chg = smb136_chg; int status = POWER_SUPPLY_STATUS_UNKNOWN; u8 command_a_data, statue_e_data, statue_h_data; smb136_i2c_read(chg->client, SMB_CommandA, &command_a_data); smb136_i2c_read(chg->client, SMB_StatusE, &statue_e_data); smb136_i2c_read(chg->client, SMB_StatusH, &statue_h_data); if (statue_h_data & 0x01) { pr_debug("%s: POWER_SUPPLY_STATUS_CHARGING\n", __func__); return POWER_SUPPLY_STATUS_CHARGING; } else { if (!(statue_e_data & 0x06)) { pr_debug("%s: POWER_SUPPLY_STATUS_DISCHARGING\n" , __func__); return POWER_SUPPLY_STATUS_DISCHARGING; } else if ((statue_e_data & 0xc0)) { pr_debug("%s: POWER_SUPPLY_STATUS_FULL\n", __func__); return POWER_SUPPLY_STATUS_FULL; } } pr_info("%s: POWER_SUPPLY_STATUS_UNKNOWN\n", __func__); pr_info("%s: 0x31h(0x%02x), 0x36h(0x%02x), 0x39h(0x%02x)\n", __func__, command_a_data, statue_e_data, statue_h_data); return (int)status; }
static ssize_t command_read(struct device *dev, struct device_attribute *attr, char *buf) { struct smb136_chg_data *chg = smb136_chg; struct i2c_client *client = chg->client; u8 d1, d2; smb136_i2c_read(client, SMB_CommandA, &d1); smb136_i2c_read(client, SMB_ChargeCurrent, &d2); return sprintf(buf,"cmd=0x%x current=0x%x\n", d1, d2); }
static void smb136_test_read(struct i2c_client *client) { u8 data = 0; u32 addr = 0; for (addr = 0; addr <= 0x0C; addr++) { smb136_i2c_read(client, addr, &data); dev_dbg(&client->dev, "smb136 addr : 0x%02x data : 0x%02x\n", addr, data); } for (addr = 0x31; addr <= 0x3D; addr++) { smb136_i2c_read(client, addr, &data); dev_dbg(&client->dev, "smb136 addr : 0x%02x data : 0x%02x\n", addr, data); } }
static irqreturn_t smb136_irq_thread(int irq, void *data) { struct smb136_chip *chip = data; #if defined (CONFIG_MACH_Q1_CHN) && defined (CONFIG_SMB136_CHARGER) int ret = 0; u8 data1=0; #endif dev_info(&chip->client->dev, "%s\n", __func__); smb136_test_read(chip->client); #if defined (CONFIG_MACH_Q1_CHN) && defined (CONFIG_SMB136_CHARGER) smb136_i2c_read(chip->client, 0x33, &data1); if (data1 & 0x02) { if(is_ovp_status == false) { is_ovp_status = true; if (chip->pdata->ovp_cb) ret = chip->pdata->ovp_cb(true); dev_info(&chip->client->dev, "$s OVP!!\n"); } } else { if(is_ovp_status == true) { is_ovp_status = false; if(chip->pdata->ovp_cb) ret = chip->pdata->ovp_cb(false); dev_info(&chip->client->dev, "$s ovp status released!!\n"); } } #endif return IRQ_HANDLED; }
static int smb136_get_charging_current(void) { struct smb136_chg_data *chg = smb136_chg; u8 data = 0; int get_current = 0; smb136_i2c_read(chg->client, SMB_ChargeCurrent, &data); switch (data >> 5) { case 0: get_current = 500; break; case 1: get_current = 650; break; case 2: get_current = 750; break; case 3: get_current = 850; break; case 4: get_current = 950; break; case 5: get_current = 1100; break; case 6: get_current = 1300; break; case 7: get_current = 1500; break; default: get_current = 500; break; } pr_debug("%s: Get charging current as %dmA.\n", __func__, get_current); return get_current; }
static int smb136_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct smb136_chip *chip = container_of(psy, struct smb136_chip, charger); u8 data; switch (psp) { case POWER_SUPPLY_PROP_STATUS: val->intval = smb136_get_charging_status(chip->client); break; case POWER_SUPPLY_PROP_CHARGE_TYPE: val->intval = chip->cable_type; break; case POWER_SUPPLY_PROP_ONLINE: val->intval = chip->is_enable; break; case POWER_SUPPLY_PROP_CURRENT_NOW: smb136_i2c_read(chip->client, SMB_ChargeCurrent, &data); switch (data >> 5) { case 0: val->intval = 500; break; case 1: val->intval = 650; break; case 2: val->intval = 750; break; case 3: val->intval = 850; break; case 4: val->intval = 950; break; case 5: val->intval = 1100; break; case 6: val->intval = 1300; break; case 7: val->intval = 1500; break; } break; default: return -EINVAL; } dev_info(&chip->client->dev, "%s: smb136_get_property (%d,%d)\n", __func__, psp, val->intval); return 0; }
static void smb136_read_regs(struct i2c_client *client, char *str) { u8 data = 0; u32 addr = 0; for (addr = 0; addr <= 0x0C; addr++) { smb136_i2c_read(client, addr, &data); sprintf(str+strlen(str), "0x%x, ", data); } /* "#" considered as new line in application */ sprintf(str+strlen(str), "#"); for (addr = 0x30; addr <= 0x3D; addr++) { smb136_i2c_read(client, addr, &data); sprintf(str+strlen(str), "0x%x, ", data); } }
static void smb136_test_read(struct i2c_client *client) { struct smb136_chip * chg = i2c_get_clientdata(client); u8 data = 0; u32 addr = 0; for(addr=0;addr<0x0c;addr++) { smb136_i2c_read(chg->client, addr, &data); dev_info(&client->dev, "SMB136 addr : 0x%02x data : 0x%02x\n", addr,data); } for(addr=0x31;addr<0x3D;addr++) { smb136_i2c_read(chg->client, addr, &data); dev_info(&client->dev, "SMB136 addr : 0x%02x data : 0x%02x\n", addr,data); } }
static void smb136_allow_volatile_writes(struct i2c_client *client) { int val, reg; u8 data; reg = SMB136_COMMAND_A; val = smb136_i2c_read(client, reg, &data); if ((val >= 0) && !(data & 0x80)) { dev_dbg(&client->dev, "%s : reg(0x%02x): 0x%02x", __func__, reg, data); data |= (0x1 << 7); if (smb136_i2c_write(client, reg, &data) < 0) dev_err(&client->dev, "%s : error!\n", __func__); val = smb136_i2c_read(client, reg, &data); if (val >= 0) { data = (u8) data; dev_dbg(&client->dev, " => 0x%02x\n", data); } } }
static void smb136_set_command(struct i2c_client *client, int reg, int datum) { int val; u8 data = 0; val = smb136_i2c_read(client, reg, &data); if (val >= 0) { dev_dbg(&client->dev, "%s : reg(0x%02x): 0x%02x", __func__, reg, data); if (data != datum) { data = datum; if (smb136_i2c_write(client, reg, &data) < 0) dev_err(&client->dev, "%s : error!\n", __func__); val = smb136_i2c_read(client, reg, &data); if (val >= 0) dev_dbg(&client->dev, " => 0x%02x\n", data); } } }
static int smb136_get_charging_health(struct i2c_client *client) { int health = POWER_SUPPLY_HEALTH_GOOD; u8 data_d = 0; u8 data_e = 0; smb136_i2c_read(client, SMB136_STATUS_D, &data_d); dev_info(&client->dev, "%s : charger status D(0x%02x)\n", __func__, data_d); smb136_i2c_read(client, SMB136_STATUS_E, &data_e); dev_info(&client->dev, "%s : charger status E(0x%02x)\n", __func__, data_e); /* Is enabled ? */ if (data_e & 0x01) { if ((data_d & 0x40) != 0x40) /* Input current is NOT OK */ health = POWER_SUPPLY_HEALTH_UNDERVOLTAGE; } return (int)health; }
u32 smb136_is_already_fullcharged(void) { u8 data=0; smb136_i2c_read(smb136_i2c_client, 0x36, &data); printk("SMB136 addr : 0x36 data : 0x%02x\n",data); if((data & 0xc0) == 0xc0) // At least one charge cycle terminated, Charge current < Termination Current return 1; else return 0; }
u32 smb136_is_fullcharging(void) { u8 data=0; smb136_i2c_read(smb136_i2c_client, 0x36, &data); printk("SMB136 addr : 0x36 data : 0x%02x\n",data); if(data & 0x40) // Charge current < Termination Current return 1; else return 0; }
u32 smb136_is_charging_active(void) { u8 data=0; smb136_i2c_read(smb136_i2c_client, 0x39, &data); printk("SMB136 addr : 0x39 data : 0x%02x\n",data); if(data & 0x01) return 1; else return 0; }
static void smb136_test_read(void) { struct smb136_chg_data *chg = smb136_chg; u8 data = 0; u32 addr = 0; if (!charger_i2c_init) { pr_info("%s : smb136 charger IC i2c is not initialized!!\n" , __func__); return ; } for (addr = 0; addr < 0x0c; addr++) { smb136_i2c_read(chg->client, addr, &data); pr_info("SMB136 addr : 0x%02x data : 0x%02x\n", addr, data); } for (addr = 0x31; addr < 0x3D; addr++) { smb136_i2c_read(chg->client, addr, &data); pr_info("SMB136 addr : 0x%02x data : 0x%02x\n", addr, data); } }
int smb136_get_charging_status(void) { struct smb136_chg_data *chg = smb136_chg; enum charging_status_type_t status = CHARGING_STATUS_NONE; u8 data1 = 0; u8 data2 = 0; smb136_i2c_read(chg->client, 0x36, &data1); smb136_i2c_read(chg->client, 0x39, &data2); printk("%s : 0x36h(0x%02x), 0x39h(0x%02x)\n", __func__, data1, data2); if(data2 & 0x01) status = CHARGING_STATUS_ACTIVE; else { if ((data1 & 0x08) == 0x08) // if error bit check, ignore the status of charger-ic status = CHARGING_STATUS_ERROR; else if((data1 & 0xc0) == 0xc0) // At least one charge cycle terminated, Charge current < Termination Current status = CHARGING_STATUS_FULL; } return (int)status; }
static int smb136_get_charging_status(struct i2c_client *client) { int status = POWER_SUPPLY_STATUS_UNKNOWN; u8 data_d = 0; u8 data_e = 0; smb136_i2c_read(client, SMB136_STATUS_D, &data_d); dev_info(&client->dev, "%s : charger status D(0x%02x)\n", __func__, data_d); smb136_i2c_read(client, SMB136_STATUS_E, &data_e); dev_info(&client->dev, "%s : charger status E(0x%02x)\n", __func__, data_e); /* At least one charge cycle terminated, * Charge current < Termination Current */ if ((data_e & 0x40) == 0x40) { /* top-off by full charging */ status = POWER_SUPPLY_STATUS_FULL; goto charging_status_end; } /* Is enabled ? */ if (data_e & 0x01) { /* check for 0x06 : no charging (0b00) */ /* not charging */ if (!(data_e & 0x06)) { status = POWER_SUPPLY_STATUS_NOT_CHARGING; goto charging_status_end; } else { status = POWER_SUPPLY_STATUS_CHARGING; goto charging_status_end; } } else status = POWER_SUPPLY_STATUS_DISCHARGING; charging_status_end: return (int)status; }
u32 smb136_is_fullcharging(void) { u8 data=0; smb136_i2c_read(smb136_i2c_client, 0x36, &data); printk("SMB136 addr : 0x36 data : 0x%02x\n",data); if ((data & 0x08) == 0x08) // if error bit check, ignore the status of charger-ic return 0; if(data & 0x40) // Charge current < Termination Current return 1; else return 0; }
void smb136_test_read(void) { u8 data; u32 addr; if(!charger_i2c_init) { printk("%s : smb136 charger IC i2c is not initialized!!\n", __func__); return ; } for(addr=0;addr<0x0c;addr++) { smb136_i2c_read(smb136_i2c_client, addr, &data); printk("SMB136 addr : 0x%02x data : 0x%02x\n", addr,data); } for(addr=0x31;addr<0x3D;addr++) { smb136_i2c_read(smb136_i2c_client, addr, &data); printk("SMB136 addr : 0x%02x data : 0x%02x\n", addr,data); } }
static int smb136_get_charging_status(struct i2c_client *client) { struct smb136_chip *chg = i2c_get_clientdata(client); int status = POWER_SUPPLY_STATUS_UNKNOWN; u8 data1 = 0; u8 data2 = 0; smb136_i2c_read(chg->client, 0x36, &data1); smb136_i2c_read(chg->client, 0x39, &data2); dev_info(&client->dev, "%s : 0x36h(0x%02x), 0x39h(0x%02x)\n", __func__, data1, data2); if(data2 & 0x01) status = POWER_SUPPLY_STATUS_CHARGING; else { if ((data1 & 0x08) == 0x08) // if error bit check, ignore the status of charger-ic status = POWER_SUPPLY_STATUS_NOT_CHARGING; else if((data1 & 0xc0) == 0xc0) // At least one charge cycle terminated, Charge current < Termination Current status = POWER_SUPPLY_STATUS_FULL; } return (int)status; }
u32 smb136_is_already_fullcharged(void) { u8 data=0; smb136_i2c_read(smb136_i2c_client, 0x36, &data); printk("SMB136 addr : 0x36 data : 0x%02x\n",data); #ifdef CONFIG_TARGET_LOCALE_VZW if ((data & 0x08) == 0x08) // hanapark_P1 : return if charge error (2010-10-02) return 0; #endif if((data & 0xc0) == 0xc0) // At least one charge cycle terminated, Charge current < Termination Current return 1; else return 0; }
ssize_t sec_hal_chg_store_attrs(struct device *dev, const ptrdiff_t offset, const char *buf, size_t count) { struct power_supply *psy = dev_get_drvdata(dev); struct sec_charger_info *chg = container_of(psy, struct sec_charger_info, psy_chg); int ret = 0; int x = 0; u8 data = 0; switch (offset) { case CHG_REG: if (sscanf(buf, "%x\n", &x) == 1) { chg->reg_addr = x; smb136_i2c_read(chg->client, chg->reg_addr, &data); chg->reg_data = data; dev_dbg(dev, "%s: (read) addr = 0x%x, data = 0x%x\n", __func__, chg->reg_addr, chg->reg_data); ret = count; } break; case CHG_DATA: if (sscanf(buf, "%x\n", &x) == 1) { data = (u8)x; dev_dbg(dev, "%s: (write) addr = 0x%x, data = 0x%x\n", __func__, chg->reg_addr, data); smb136_i2c_write(chg->client, chg->reg_addr, &data); ret = count; } break; default: ret = -EINVAL; break; } return ret; }
static void smb136_charger_function_conrol( struct i2c_client *client) { struct sec_charger_info *charger = i2c_get_clientdata(client); u8 data; u8 cur_reg; u8 chg_cur_reg; if (charger->charging_current < 0) { dev_dbg(&client->dev, "%s : OTG is activated. Ignore command!\n", __func__); return; } smb136_allow_volatile_writes(client); if (charger->cable_type == POWER_SUPPLY_TYPE_BATTERY) { /* turn off charger */ smb136_set_command(client, SMB136_COMMAND_A, 0x80); } else { /* Pre-charge curr 250mA */ dev_dbg(&client->dev, "%s : fast charging current (%dmA)\n", __func__, charger->charging_current); dev_dbg(&client->dev, "%s : termination current (%dmA)\n", __func__, charger->pdata->charging_current[ charger->cable_type].full_check_current_1st); smb136_i2c_read(client, SMB136_CHARGE_CURRENT, &chg_cur_reg); chg_cur_reg &= 0x18; data = smb136_get_fast_charging_current_data( charger->charging_current) | chg_cur_reg; data |= smb136_get_termination_current_limit_data( charger->pdata->charging_current[ charger->cable_type].full_check_current_1st); smb136_set_command(client, SMB136_CHARGE_CURRENT, data); /* Pin enable control */ /* DCIN Input Pre-bias Enable */ smb136_i2c_read(client, SMB136_PIN_ENABLE_CONTROL, &data); if (charger->pdata->chg_gpio_en) data |= 0x40; smb136_set_command(client, SMB136_PIN_ENABLE_CONTROL, data); /* Input current limit */ dev_dbg(&client->dev, "%s : input current (%dmA)\n", __func__, charger->pdata->charging_current [charger->cable_type].input_current_limit); data = 0; data = smb136_get_input_current_limit_data( charger, charger->pdata->charging_current [charger->cable_type].input_current_limit); smb136_i2c_read(client, SMB136_INPUT_CURRENTLIMIT, &cur_reg); data |= (cur_reg & 0xF); smb136_set_command(client, SMB136_INPUT_CURRENTLIMIT, data); /* Float voltage, Vprechg : 2.4V */ dev_dbg(&client->dev, "%s : float voltage (%dmV)\n", __func__, charger->pdata->chg_float_voltage); data = 0; data |= smb136_get_float_voltage_data( charger->pdata->chg_float_voltage); smb136_set_command(client, SMB136_FLOAT_VOLTAGE, data); /* 4. Automatic Recharge Disabed */ data = 0x8C; smb136_i2c_write(client, SMB136_CHARGE_CONTROL_A, &data); /* 5. Safty timer Disabled */ data = 0x28; smb136_i2c_write(client, SMB136_CHARGE_CONTROL_B, &data); /* 6. Disable USB D+/D- Detection */ data = 0x28; smb136_i2c_write(client, SMB136_OTG_CONTROL, &data); /* 9. Re-load Enable */ data = 0x4B; smb136_i2c_write(client, SMB136_SAFTY, &data); /* HC or USB5 mode */ smb136_i2c_read(client, SMB136_COMMAND_A, &data); data &= 0xF3; switch (charger->cable_type) { case POWER_SUPPLY_TYPE_MAINS: case POWER_SUPPLY_TYPE_MISC: /* High-current mode */ data |= 0x0C; break; case POWER_SUPPLY_TYPE_USB: case POWER_SUPPLY_TYPE_USB_DCP: case POWER_SUPPLY_TYPE_USB_CDP: case POWER_SUPPLY_TYPE_USB_ACA: /* USB5 */ data |= 0x08; break; default: /* USB1 */ data = 0x00; break; } smb136_set_command(client, SMB136_COMMAND_A, data); } }
void smb136_set_otg_mode(int enable) { struct i2c_client *client = smb136_i2c_client; u8 data; printk("[SMB136] %s (enable : %d)\n", __func__, enable); if(!charger_i2c_init) { printk("%s : smb136 charger IC i2c is not initialized!!\n", __func__); return ; } if(enable) // Enable OTG Mode { // 1. Set OTG Mode (Clear Bit5 of Pin Control) smb136_i2c_read(client, SMB_PinControl, &data); data &= ~(0x1 << 5); smb136_i2c_write(client, SMB_PinControl, data); udelay(10); // 2. Enable OTG Mode (Set Bit1 of Command Register A) smb136_i2c_read(client, SMB_CommandA, &data); data |= (0x1 << 1); smb136_i2c_write(client, SMB_CommandA, data); udelay(10); } else // Re-init charger IC { // 1. Allow volatile writes to 00~09h, USB 500mA Mode, USB5/1 Mode data = 0x88; smb136_i2c_write(client, SMB_CommandA, data); udelay(10); // 2. Change USB5/1/HC Control from Pin to I2C data = 0x08; smb136_i2c_write(client, SMB_PinControl, data); udelay(10); // 3. Allow volatile writes to 00~09h, USB 500mA Mode, USB5/1 Mode data = 0x88; smb136_i2c_write(client, SMB_CommandA, data); udelay(10); // 4. Disable Automatic Input Current Limit data = 0xe6; smb136_i2c_write(client, SMB_InputCurrentLimit, data); udelay(10); //5. Fast Charge Current set 500mA #ifdef CONFIG_TARGET_LOCALE_VZW data = 0xf2; #else data = 0xf4; #endif smb136_i2c_write(client, SMB_ChargeCurrent, data); udelay(10); //6. Automatic Recharge Disabed data = 0x8c; smb136_i2c_write(client, SMB_ControlA, data); udelay(10); //7. Safty timer Disabled data = 0x28; smb136_i2c_write(client, SMB_ControlB, data); udelay(10); //8. Disable USB D+/D- Detection data = 0x28; smb136_i2c_write(client, SMB_OTGControl, data); udelay(10); //9. Set Output Polarity for STAT data = 0xca; smb136_i2c_write(client, SMB_FloatVoltage, data); udelay(10); //10. Re-load Enable data = 0x4b; smb136_i2c_write(client, SMB_SafetyTimer, data); udelay(10); } smb136_test_read(); }
static int smb136_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); struct smb136_chip *chip; int ret = 0; int gpio = 0; u8 data; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) return -EIO; if (smb136_i2c_read(client, 0x36, &data)<0) /* check HW */ return -EIO; dev_info(&client->dev, "%s : SMB136 Charger Driver Loading\n", __func__); chip = kzalloc(sizeof(struct smb136_chip), GFP_KERNEL); if (!chip) return -ENOMEM; chip->client = client; chip->pdata = client->dev.platform_data; i2c_set_clientdata(client, chip); if (!chip->pdata) { dev_err(&client->dev, "%s : No platform data supplied\n", __func__); ret = -EINVAL; goto err_pdata; } if (chip->pdata->set_charger_name) chip->pdata->set_charger_name(); chip->is_enable = false; chip->cable_type = CABLE_TYPE_NONE; chip->charger.name = "smb136-charger"; chip->charger.type = POWER_SUPPLY_TYPE_BATTERY; chip->charger.get_property = smb136_get_property; chip->charger.set_property = smb136_set_property; chip->charger.properties = smb136_charger_props; chip->charger.num_properties = ARRAY_SIZE(smb136_charger_props); ret = power_supply_register(&client->dev, &chip->charger); if (ret) { dev_err(&client->dev, "failed: power supply register\n"); kfree(chip); return ret; } /* CHG_EN pin control - active low */ if (chip->pdata->gpio_chg_en) { s3c_gpio_cfgpin(chip->pdata->gpio_chg_en, S3C_GPIO_OUTPUT); s3c_gpio_setpull(chip->pdata->gpio_chg_en, S3C_GPIO_PULL_NONE); gpio = gpio_request(chip->pdata->gpio_chg_en, "CHG_EN"); if (!gpio) { gpio_direction_output(chip->pdata->gpio_chg_en, GPIO_LEVEL_HIGH); gpio_free(chip->pdata->gpio_chg_en); } else dev_err(&client->dev, "faile to request gpio(CHG_EN)\n"); } if (chip->pdata->gpio_otg_en) { s3c_gpio_cfgpin(chip->pdata->gpio_otg_en, S3C_GPIO_OUTPUT); s3c_gpio_setpull(chip->pdata->gpio_otg_en, S3C_GPIO_PULL_NONE); gpio = gpio_request(chip->pdata->gpio_otg_en, "OTG_EN"); if (!gpio) { gpio_direction_output(chip->pdata->gpio_otg_en, GPIO_LEVEL_LOW); gpio_free(chip->pdata->gpio_otg_en); } else dev_err(&client->dev, "faile to request gpio(OTG_EN)\n"); } if (chip->pdata->gpio_ta_nconnected) { s3c_gpio_cfgpin(chip->pdata->gpio_ta_nconnected, S3C_GPIO_INPUT); s3c_gpio_setpull(chip->pdata->gpio_ta_nconnected, S3C_GPIO_PULL_NONE); } if (chip->pdata->gpio_chg_ing) { #if 1 #if defined (CONFIG_MACH_Q1_CHN) && defined (CONFIG_SMB136_CHARGER) s3c_gpio_cfgpin(chip->pdata->gpio_chg_ing, S3C_GPIO_SFN(0xf)); #endif client->irq = gpio_to_irq(chip->pdata->gpio_chg_ing); ret = smb136_irq_init(chip); if (ret) goto err_pdata; #else s3c_gpio_cfgpin(chip->pdata->gpio_chg_ing, S3C_GPIO_INPUT); s3c_gpio_setpull(chip->pdata->gpio_chg_ing, S3C_GPIO_PULL_NONE); #endif } smb136_test_read(client); return 0; err_pdata: kfree(chip); return ret; }