static irqreturn_t pm860x_vchg_handler(int irq, void *data) { struct pm860x_charger_info *info = data; int vchg = 0; if (info->present) goto out; measure_vchg(info, &vchg); mutex_lock(&info->lock); if (!info->online) { int status; /* check if over-temp on pm8606 or not */ status = pm860x_reg_read(info->i2c_8606, PM8606_FLAGS); if (status & OVER_TEMP_FLAG) { /* clear over temp flag and set auto recover */ pm860x_set_bits(info->i2c_8606, PM8606_FLAGS, OVER_TEMP_FLAG, OVER_TEMP_FLAG); pm860x_set_bits(info->i2c_8606, PM8606_VSYS, OVTEMP_AUTORECOVER, OVTEMP_AUTORECOVER); dev_dbg(info->dev, "%s, pm8606 over-temp occure\n", __func__); } } if (vchg > VCHG_NORMAL_CHECK) { set_vchg_threshold(info, VCHG_OVP_LOW, 0); info->allowed = 0; dev_dbg(info->dev, "%s,pm8607 over-vchg occure,vchg = %dmv\n", __func__, vchg); } else if (vchg < VCHG_OVP_LOW) { set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_NORMAL_HIGH); info->allowed = 1; dev_dbg(info->dev, "%s,pm8607 over-vchg recover,vchg = %dmv\n", __func__, vchg); } mutex_unlock(&info->lock); dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed); set_charging_fsm(info); out: return IRQ_HANDLED; }
void pm860x_set_vchg_threshold(int min, int max) { struct pm860x_charger_info *info = ginfo; set_vchg_threshold(info, min, max); pr_info( \ "[%s][%s]min[%d]max[%d]\n", __FILE__, __FUNCTION__, min, max); }EXPORT_SYMBOL(pm860x_set_vchg_threshold);
static int start_fastcharge(struct pm860x_charger_info *info) { int ret; dev_dbg(info->dev, "Start Fast-charging!\n"); /* set fastcharge termination current & voltage, disable charging */ ret = pm860x_reg_write(info->i2c, PM8607_CHG_CTRL1, CC1_MODE_OFF | CC1_ITERM_60MA | CC1_VFCHG_4_2V); if (ret < 0) goto out; ret = pm860x_reg_write(info->i2c_8606, PM8606_PREREGULATORA, PREREG1_540MA | PREREG1_VSYS_4_5V); if (ret < 0) goto out; ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL2, 0x1f, CC2_ICHG_500MA); if (ret < 0) goto out; /* set 270 minutes timeout */ ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL3, (0xf << 4), CC3_270MIN_TIMEOUT); if (ret < 0) goto out; /* set IBAT & TBAT monitor */ ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL4, CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN, CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN); if (ret < 0) goto out; ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL6, CC6_BAT_OV_EN | CC6_BAT_UV_EN | CC6_UV_VBAT_SET, CC6_BAT_OV_EN | CC6_BAT_UV_EN | CC6_UV_VBAT_SET); if (ret < 0) goto out; ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL7, CC7_BAT_REM_EN | CC7_IFSM_EN, CC7_BAT_REM_EN | CC7_IFSM_EN); if (ret < 0) goto out; /* launch fast-charge */ ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3, CC1_MODE_FASTCHARGE); /* vchg threshold setting */ set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_NORMAL_HIGH); out: return ret; }
static int pm860x_charger_probe(struct platform_device *pdev) { struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); struct pm860x_charger_info *info; int ret; int count; int i; int j; info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; count = pdev->num_resources; for (i = 0, j = 0; i < count; i++) { info->irq[j] = platform_get_irq(pdev, i); if (info->irq[j] < 0) continue; j++; } info->irq_nums = j; info->chip = chip; info->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion; info->i2c_8606 = (chip->id == CHIP_PM8607) ? chip->companion : chip->client; if (!info->i2c_8606) { dev_err(&pdev->dev, "Missed I2C address of 88PM8606!\n"); ret = -EINVAL; goto out; } info->dev = &pdev->dev; /* set init value for the case we are not using battery */ set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_OVP_LOW); mutex_init(&info->lock); platform_set_drvdata(pdev, info); info->usb.name = "usb"; info->usb.type = POWER_SUPPLY_TYPE_USB; info->usb.supplied_to = pm860x_supplied_to; info->usb.num_supplicants = ARRAY_SIZE(pm860x_supplied_to); info->usb.properties = pm860x_usb_props; info->usb.num_properties = ARRAY_SIZE(pm860x_usb_props); info->usb.get_property = pm860x_usb_get_prop; ret = power_supply_register(&pdev->dev, &info->usb); if (ret) goto out; pm860x_init_charger(info); for (i = 0; i < ARRAY_SIZE(info->irq); i++) { ret = request_threaded_irq(info->irq[i], NULL, pm860x_irq_descs[i].handler, IRQF_ONESHOT, pm860x_irq_descs[i].name, info); if (ret < 0) { dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", info->irq[i], ret); goto out_irq; } } return 0; out_irq: while (--i >= 0) free_irq(info->irq[i], info); out: return ret; }
static int start_fastcharge(struct pm860x_charger_info *info) { unsigned char buf[6]; int ret; dev_dbg(info->dev, "Start Fast-charging!\n"); pr_info( \ "charger:start_fastcharge\n"); set_vbatt_threshold(info, 0, 0); set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_NORMAL_HIGH); pm860x_calc_resistor(); ret = pm860x_reg_write(info->i2c_8606, PM8606_PREREGULATORA, PREREG1_1500MA | PREREG1_VSYS_4_5V); if (ret < 0) goto out; /* set fastcharge termination current & voltage, disable charging */ ret = pm860x_reg_write(info->i2c, PM8607_CHG_CTRL1, CC1_MODE_OFF | CC1_ITERM_60MA | CC1_VFCHG_4_2V); if (ret < 0) goto out; switch (info->charge_type) { case USB_CHARGER: pr_info("[%s][%s] charger:start_fastcharge:USB_CHARGER\n", __FILE__, __func__); break; case AC_STANDARD_CHARGER: ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL2, 0x1f, CC2_ICHG_500MA); pr_info( \ "charger:start_fastcharge:AC_STANDARD_CHARGER\n"); break; case AC_OTHER_CHARGER: ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL2, 0x1f, CC2_ICHG_1000MA); pr_info( \ "charger:start_fastcharge:AC_OTHER_CHARGER\n"); break; default: ret = -EINVAL; pr_info( \ "charger:start_fastcharge:type none\n"); break; } if (ret < 0) goto out; /* set 270 minutes timeout */ ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL3, (0xf << 4), CC3_270MIN_TIMEOUT); if (ret < 0) goto out; /* set IBAT & TBAT monitor */ ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL4, CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN, CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN); if (ret < 0) goto out; ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL6, CC6_BAT_OV_EN | CC6_BAT_UV_EN | CC6_UV_VBAT_SET, CC6_BAT_OV_EN | CC6_BAT_UV_EN | CC6_UV_VBAT_SET); if (ret < 0) goto out; ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL7, CC7_BAT_REM_EN | CC7_IFSM_EN, CC7_BAT_REM_EN | CC7_IFSM_EN); if (ret < 0) goto out; /*hw fix workaround: disable BC_SHORT by setting in testpage, only occur before sanremo C1*/ if((info->chip->chip_version <= PM8607_CHIP_C1) && !info->bc_short){ info->bc_short = 1;/* disable bc_short mechanism*/ buf[0] = buf[2] = 0x0; buf[1] = 0x60; buf[3] = 0xff; buf[4] = 0x9f; buf[5] = 0xfd; pm860x_page_bulk_write(info->i2c, 0xC8, 6, buf); pm860x_page_reg_write(info->i2c, 0xCF, 0x02); } /* trigger fastcharge */ ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3, CC1_MODE_FASTCHARGE); out: return ret; }
static __devinit int pm860x_charger_probe(struct platform_device *pdev) { struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); struct pm860x_platform_data *pdata = chip->dev->platform_data; struct pm860x_charger_info *info; int ret, i, j, count; info = kzalloc(sizeof(struct pm860x_charger_info), GFP_KERNEL); if (!info) return -ENOMEM; ret = device_create_file(&pdev->dev, &dev_attr_control); if (ret < 0) goto out; count = pdev->num_resources; for (i = 0, j = 0; i < count; i++) { info->irq[j] = platform_get_irq(pdev, i); if (info->irq[j] < 0) continue; j++; } ginfo = info; info->irq_nums = j; info->batdet = pdata->batt_det; pr_info( \ "charger:pm860x_charger_probe:info->batdet=[%d]\n", info->batdet); info->chip = chip; info->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion; info->i2c_8606 = (chip->id == CHIP_PM8607) ? chip->companion : chip->client; if (!info->i2c_8606) { dev_err(&pdev->dev, "Missed I2C address of 88PM8606!\n"); ret = -EINVAL; goto out_dev; } info->dev = &pdev->dev; #ifdef CONFIG_ALTERNATE_CHARGER pm860x_registerChargerEventsCb(pm860x_default_chg_handler,0); pm860x_registerChargerEventsCb(pm860x_default_chg_handler,1); pm860x_registerChargerEventsCb(pm860x_default_chg_handler,2); pm860x_registerChargerEventsCb(pm860x_default_chg_handler,3); pm860x_registerChargerEventsCb(pm860x_default_chg_handler,4); //pm860x_registerChargerEventsCb(pm860x_default_chg_handler,5); pm860x_registerChargerEventsCb(m_spa_voltThresholdEvent,5); pm860x_registerChargerEventsCb(pm860x_default_chg_handler,6); pm860x_register_control_cb_func(spa_Charger_Ctrl); #else ChargerEventsCb[0] = pm860x_charger_handler; ChargerEventsCb[1] = pm860x_done_handler; ChargerEventsCb[2] = pm860x_exception_handler; ChargerEventsCb[3] = pm860x_exception_handler; ChargerEventsCb[4] = pm860x_temp_handler; ChargerEventsCb[5] = pm860x_vbattery_handler; ChargerEventsCb[6] = pm860x_vchg_handler; #endif /* set init value for the case we are not using battery*/ set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_OVP_LOW); #if !defined(CONFIG_USB_VBUS_88PM860X) if (ChargerEventsCb[0]) { ret = request_threaded_irq(info->irq[0], NULL, ChargerEventsCb[0], IRQF_ONESHOT, "usb supply detect", info); if (ret < 0) { dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", info->irq[0], ret); goto out_dev; } } else pr_info( \ "pm860x_charger_probe:no callback for charger event[%d]\n", 0); #endif if (ChargerEventsCb[1]) { ret = request_threaded_irq(info->irq[1], NULL, ChargerEventsCb[1], IRQF_ONESHOT, "charge done", info); if (ret < 0) { dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", info->irq[1], ret); goto out_irq1; } } else pr_info( \ "pm860x_charger_probe:no callback for charger event[%d]\n", 1); if (ChargerEventsCb[2]) { ret = request_threaded_irq(info->irq[2], NULL, ChargerEventsCb[2], IRQF_ONESHOT, "charge timeout", info); if (ret < 0) { dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", info->irq[2], ret); goto out_irq2; } } else pr_info( \ "pm860x_charger_probe:no callback for charger event[%d]\n", 2); if (ChargerEventsCb[3]) { ret = request_threaded_irq(info->irq[3], NULL, ChargerEventsCb[3], IRQF_ONESHOT, "charge fault", info); if (ret < 0) { dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", info->irq[3], ret); goto out_irq3; } } else pr_info( \ "pm860x_charger_probe:no callback for charger event[%d]\n", 3); if (ChargerEventsCb[4]) { ret = request_threaded_irq(info->irq[4], NULL, ChargerEventsCb[4], IRQF_ONESHOT, "temperature", info); if (ret < 0) { dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", info->irq[4], ret); goto out_irq4; } } else pr_info( \ "pm860x_charger_probe:no callback for charger event[%d]\n", 4); if (ChargerEventsCb[5]) { ret = request_threaded_irq(info->irq[5], NULL, ChargerEventsCb[5], IRQF_ONESHOT, "vbatt", info); if (ret < 0) { dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", info->irq[5], ret); goto out_irq5; } } else pr_info( \ "pm860x_charger_probe:no callback for charger event[%d]\n", 5); if (ChargerEventsCb[6]) { ret = request_threaded_irq(info->irq[6], NULL, ChargerEventsCb[6], IRQF_ONESHOT, "vchg", info); if (ret < 0) { dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", info->irq[6], ret); goto out_irq6; } if (info->irq_nums <= 6) { dev_err(chip->dev, "IRQ numbers aren't matched\n"); goto out_nums; } } else pr_info( \ "pm860x_charger_probe:no callback for charger event[%d]\n", 6); mutex_init(&info->lock); platform_set_drvdata(pdev, info); #ifndef CONFIG_ALTERNATE_CHARGER info->usb.name = "usb"; info->usb.type = POWER_SUPPLY_TYPE_USB; info->usb.supplied_to = pm860x_supplied_to; info->usb.num_supplicants = ARRAY_SIZE(pm860x_supplied_to); info->usb.properties = pm860x_usb_props; info->usb.num_properties = ARRAY_SIZE(pm860x_usb_props); info->usb.get_property = pm860x_usb_get_prop; ret = power_supply_register(&pdev->dev, &info->usb); if (ret) goto out_nums; info->ac.name = "ac"; info->ac.type = POWER_SUPPLY_TYPE_MAINS; info->ac.supplied_to = pm860x_supplied_to; info->ac.num_supplicants = ARRAY_SIZE(pm860x_supplied_to); info->ac.properties = pm860x_ac_props; info->ac.num_properties = ARRAY_SIZE(pm860x_ac_props); info->ac.get_property = pm860x_ac_get_prop; ret = power_supply_register(&pdev->dev, &info->ac); if (ret) goto out_nums; pm860x_init_charger(info); #endif INIT_WORK(&info->vbus_work, pm860x_vbus_work); device_init_wakeup(&pdev->dev, 1); #ifdef CONFIG_PROC_FS create_pm860x_power_proc_file(); #endif return 0; out_nums: free_irq(info->irq[6], info); out_irq6: free_irq(info->irq[5], info); out_irq5: free_irq(info->irq[4], info); out_irq4: free_irq(info->irq[3], info); out_irq3: free_irq(info->irq[2], info); out_irq2: free_irq(info->irq[1], info); out_irq1: #if !defined(CONFIG_USB_VBUS_88PM860X) free_irq(info->irq[0], info); #endif out_dev: device_remove_file(&pdev->dev, &dev_attr_control); out: kfree(info); return ret; }