static irqreturn_t smb347_interrupt(int irq, void *data) { struct smb347_charger *smb = data; int stat_c, irqstat_e, irqstat_c; irqreturn_t ret = IRQ_NONE; stat_c = smb347_read(smb, STAT_C); if (stat_c < 0) { dev_warn(&smb->client->dev, "reading STAT_C failed\n"); return IRQ_NONE; } irqstat_c = smb347_read(smb, IRQSTAT_C); if (irqstat_c < 0) { dev_warn(&smb->client->dev, "reading IRQSTAT_C failed\n"); return IRQ_NONE; } irqstat_e = smb347_read(smb, IRQSTAT_E); if (irqstat_e < 0) { dev_warn(&smb->client->dev, "reading IRQSTAT_E failed\n"); return IRQ_NONE; } if (stat_c & STAT_C_CHARGER_ERROR) { dev_err(&smb->client->dev, "error in charger, disabling charging\n"); smb347_charging_disable(smb); power_supply_changed(&smb->battery); ret = IRQ_HANDLED; } if (irqstat_c & (IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TAPER_IRQ)) { if (irqstat_c & IRQSTAT_C_TERMINATION_STAT) power_supply_changed(&smb->battery); ret = IRQ_HANDLED; } if (irqstat_e & (IRQSTAT_E_USBIN_UV_IRQ | IRQSTAT_E_DCIN_UV_IRQ)) { if (smb347_update_status(smb) > 0) { smb347_update_online(smb); power_supply_changed(&smb->mains); power_supply_changed(&smb->usb); } ret = IRQ_HANDLED; } return ret; }
static irqreturn_t smb347_interrupt(int irq, void *data) { struct smb347_charger *smb = data; int stat_c, irqstat_e, irqstat_c; irqreturn_t ret = IRQ_NONE; stat_c = smb347_read(smb, STAT_C); if (stat_c < 0) { dev_warn(&smb->client->dev, "reading STAT_C failed\n"); return IRQ_NONE; } irqstat_c = smb347_read(smb, IRQSTAT_C); if (irqstat_c < 0) { dev_warn(&smb->client->dev, "reading IRQSTAT_C failed\n"); return IRQ_NONE; } irqstat_e = smb347_read(smb, IRQSTAT_E); if (irqstat_e < 0) { dev_warn(&smb->client->dev, "reading IRQSTAT_E failed\n"); return IRQ_NONE; } /* * If we get charger error we report the error back to user and * disable charging. */ if (stat_c & STAT_C_CHARGER_ERROR) { dev_err(&smb->client->dev, "error in charger, disabling charging\n"); smb347_charging_disable(smb); power_supply_changed(&smb->battery); ret = IRQ_HANDLED; } /* * If we reached the termination current the battery is charged and * we can update the status now. Charging is automatically * disabled by the hardware. */ if (irqstat_c & (IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TAPER_IRQ)) { if (irqstat_c & IRQSTAT_C_TERMINATION_STAT) power_supply_changed(&smb->battery); ret = IRQ_HANDLED; } /* * If we got an under voltage interrupt it means that AC/USB input * was disconnected. */ if (irqstat_e & (IRQSTAT_E_USBIN_UV_IRQ | IRQSTAT_E_DCIN_UV_IRQ)) ret = IRQ_HANDLED; if (smb347_update_status(smb) > 0) { smb347_update_online(smb); power_supply_changed(&smb->mains); power_supply_changed(&smb->usb); ret = IRQ_HANDLED; } return ret; }
static int smb347_hw_init(struct smb347_charger *smb) { int ret; ret = smb347_set_writable(smb, true); if (ret < 0) return ret; /* * Program the platform specific configuration values to the device * first. */ ret = smb347_set_charge_current(smb); if (ret < 0) goto fail; ret = smb347_set_current_limits(smb); if (ret < 0) goto fail; ret = smb347_set_voltage_limits(smb); if (ret < 0) goto fail; ret = smb347_set_temp_limits(smb); if (ret < 0) goto fail; /* If USB charging is disabled we put the USB in suspend mode */ if (!smb->pdata->use_usb) { ret = smb347_read(smb, CMD_A); if (ret < 0) goto fail; ret |= CMD_A_SUSPEND_ENABLED; ret = smb347_write(smb, CMD_A, ret); if (ret < 0) goto fail; } ret = smb347_read(smb, CFG_OTHER); if (ret < 0) goto fail; /* * If configured by platform data, we enable hardware Auto-OTG * support for driving VBUS. Otherwise we disable it. */ ret &= ~CFG_OTHER_RID_MASK; if (smb->pdata->use_usb_otg) ret |= CFG_OTHER_RID_ENABLED_AUTO_OTG; ret = smb347_write(smb, CFG_OTHER, ret); if (ret < 0) goto fail; ret = smb347_read(smb, CFG_PIN); if (ret < 0) goto fail; /* * Make the charging functionality controllable by a write to the * command register unless pin control is specified in the platform * data. */ ret &= ~(CFG_PIN_EN_CTRL_MASK | CFG_PIN_USB_MODE_CTRL); switch (smb->pdata->enable_control) { case SMB347_CHG_ENABLE_SW: /* Do nothing, 0 means i2c control */ break; case SMB347_CHG_ENABLE_PIN_ACTIVE_LOW: ret |= CFG_PIN_EN_CTRL_ACTIVE_LOW; break; case SMB347_CHG_ENABLE_PIN_ACTIVE_HIGH: ret |= CFG_PIN_EN_CTRL_ACTIVE_HIGH; break; } if (smb->pdata->usb_mode_pin_ctrl) ret |= CFG_PIN_USB_MODE_CTRL; /* Disable Automatic Power Source Detection (APSD) interrupt. */ ret &= ~CFG_PIN_EN_APSD_IRQ; ret = smb347_write(smb, CFG_PIN, ret); if (ret < 0) goto fail; ret = smb347_update_status(smb); if (ret < 0) goto fail; ret = smb347_update_online(smb); fail: smb347_set_writable(smb, false); return ret; }
static int smb347_battery_get_property(struct power_supply *psy, enum power_supply_property prop, union power_supply_propval *val) { struct smb347_charger *smb = container_of(psy, struct smb347_charger, battery); const struct smb347_charger_platform_data *pdata = smb->pdata; int ret; ret = smb347_update_status(smb); if (ret < 0) return ret; if (ret > 0) { smb347_update_online(smb); power_supply_changed(&smb->mains); power_supply_changed(&smb->usb); } switch (prop) { case POWER_SUPPLY_PROP_STATUS: if (!smb347_is_online(smb)) { val->intval = POWER_SUPPLY_STATUS_DISCHARGING; break; } if (smb347_charging_status(smb)) val->intval = POWER_SUPPLY_STATUS_CHARGING; else val->intval = POWER_SUPPLY_STATUS_FULL; break; case POWER_SUPPLY_PROP_CHARGE_TYPE: if (!smb347_is_online(smb)) return -ENODATA; /* * We handle trickle and pre-charging the same, and taper * and none the same. */ switch (smb347_charging_status(smb)) { case 1: val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; break; case 2: val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; break; default: val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; break; } break; case POWER_SUPPLY_PROP_TECHNOLOGY: val->intval = pdata->battery_info.technology; break; case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: val->intval = pdata->battery_info.voltage_min_design; break; case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: val->intval = pdata->battery_info.voltage_max_design; break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: if (!smb347_is_online(smb)) return -ENODATA; ret = smb347_read(smb, STAT_A); if (ret < 0) return ret; ret &= STAT_A_FLOAT_VOLTAGE_MASK; if (ret > 0x3d) ret = 0x3d; val->intval = 3500000 + ret * 20000; break; case POWER_SUPPLY_PROP_CURRENT_NOW: if (!smb347_is_online(smb)) return -ENODATA; ret = smb347_read(smb, STAT_B); if (ret < 0) return ret; /* * The current value is composition of FCC and PCC values * and we can detect which table to use from bit 5. */ if (ret & 0x20) { val->intval = hw_to_current(fcc_tbl, ARRAY_SIZE(fcc_tbl), ret & 7); } else { ret >>= 3; val->intval = hw_to_current(pcc_tbl, ARRAY_SIZE(pcc_tbl), ret & 7); } break; case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: val->intval = pdata->battery_info.charge_full_design; break; case POWER_SUPPLY_PROP_MODEL_NAME: val->strval = pdata->battery_info.name; break; default: return -EINVAL; } return 0; }
static irqreturn_t smb347_interrupt(int irq, void *data) { struct smb347_charger *smb = data; int stat_c, t; u8 irqstat[6]; irqreturn_t ret = IRQ_NONE; t = i2c_smbus_read_i2c_block_data(smb->client, IRQSTAT_A, 6, irqstat); if (t < 0) { dev_warn(&smb->client->dev, "reading IRQSTAT registers failed\n"); return IRQ_NONE; } stat_c = smb347_read(smb, STAT_C); if (stat_c < 0) { dev_warn(&smb->client->dev, "reading STAT_C failed\n"); return IRQ_NONE; } pr_debug("%s: stat c=%x irq a=%x b=%x c=%x d=%x e=%x f=%x\n", __func__, stat_c, irqstat[0], irqstat[1], irqstat[2], irqstat[3], irqstat[4], irqstat[5]); /* * If we get charger error we report the error back to user and * disable charging. */ if (stat_c & STAT_C_CHARGER_ERROR) { dev_err(&smb->client->dev, "error in charger, disabling charging\n"); smb347_charging_disable(smb); power_supply_changed(&smb->battery); ret = IRQ_HANDLED; } else if (((stat_c & STAT_C_CHG_STATUS) || (irqstat[2] & (IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TERMINATION_STAT))) && !smb->is_fully_charged) { dev_info(&smb->client->dev, "charge terminated"); smb->is_fully_charged = true; smb347_charging_disable(smb); power_supply_changed(&smb->battery); ret = IRQ_HANDLED; } if (irqstat[2] & IRQSTAT_C_TAPER_IRQ) ret = IRQ_HANDLED; /* * If we got an under voltage interrupt it means that AC/USB input * was disconnected. */ if (irqstat[4] & (IRQSTAT_E_USBIN_UV_IRQ | IRQSTAT_E_DCIN_UV_IRQ)) ret = IRQ_HANDLED; if (smb347_update_status(smb) > 0) { smb347_update_online(smb); power_supply_changed(&smb->mains); power_supply_changed(&smb->usb); ret = IRQ_HANDLED; } return ret; }
static int smb347_hw_init(struct smb347_charger *smb) { int ret; ret = smb347_set_writable(smb, true); if (ret < 0) return ret; ret = smb347_set_charge_current(smb); if (ret < 0) goto fail; ret = smb347_set_current_limits(smb); if (ret < 0) goto fail; ret = smb347_set_voltage_limits(smb); if (ret < 0) goto fail; ret = smb347_set_temp_limits(smb); if (ret < 0) goto fail; if (!smb->pdata->use_usb) { ret = smb347_read(smb, CMD_A); if (ret < 0) goto fail; ret |= CMD_A_SUSPEND_ENABLED; ret = smb347_write(smb, CMD_A, ret); if (ret < 0) goto fail; } ret = smb347_read(smb, CFG_OTHER); if (ret < 0) goto fail; ret &= ~CFG_OTHER_RID_MASK; if (smb->pdata->use_usb_otg) ret |= CFG_OTHER_RID_ENABLED_AUTO_OTG; ret = smb347_write(smb, CFG_OTHER, ret); if (ret < 0) goto fail; ret = smb347_read(smb, CFG_PIN); if (ret < 0) goto fail; ret &= ~CFG_PIN_EN_CTRL_MASK; switch (smb->pdata->enable_control) { case SMB347_CHG_ENABLE_SW: break; case SMB347_CHG_ENABLE_PIN_ACTIVE_LOW: ret |= CFG_PIN_EN_CTRL_ACTIVE_LOW; break; case SMB347_CHG_ENABLE_PIN_ACTIVE_HIGH: ret |= CFG_PIN_EN_CTRL_ACTIVE_HIGH; break; } ret &= ~CFG_PIN_EN_APSD_IRQ; ret = smb347_write(smb, CFG_PIN, ret); if (ret < 0) goto fail; ret = smb347_update_status(smb); if (ret < 0) goto fail; ret = smb347_update_online(smb); fail: smb347_set_writable(smb, false); return ret; }