void pm860x_set_charger_type(enum enum_charger_type type )
{
	struct power_supply *psy;
	union power_supply_propval data;
	struct pm860x_charger_info *info = ginfo;
	int ret = -EINVAL;
	int vbatt, type_changed = 0;

	psy = power_supply_get_by_name(pm860x_supplied_to[0]);
	if (!psy)
		goto out;
	ret = psy->get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &data);
	if (ret)
		goto out;
	vbatt = data.intval / 1000;

	pr_info("[%s][%s] charger type : %s detect\n", __FILE__, __func__,
		(type == USB_CHARGER) ? "usb" : "ac");

	mutex_lock(&info->lock);
	info->online = 1;
	if (info->charge_type != type) {
		switch (type) {
		case USB_CHARGER:
			info->charge_type = USB_CHARGER;
			break;
		case AC_STANDARD_CHARGER:
			info->charge_type = AC_STANDARD_CHARGER;
			break;
		case AC_OTHER_CHARGER:
			info->charge_type = AC_OTHER_CHARGER;
			break;
		default:
			break;
		}
		type_changed = 1;
	}
	if (info->state == FSM_FASTCHARGE && type_changed) {
		info->allowed = 0;
		mutex_unlock(&info->lock);
		set_charging_fsm(info);
		mutex_lock(&info->lock);
		info->allowed = 1;
		mutex_unlock(&info->lock);
		set_charging_fsm(info);
	} else {
		mutex_unlock(&info->lock);
	}
#ifndef ALTERNATE_CHARGER
	if (info->charge_type == USB_CHARGER)
		power_supply_changed(&info->usb);
	else
		power_supply_changed(&info->ac);
#endif
out:
	return;
}
static irqreturn_t pm860x_temp_handler(int irq, void *data)
{
	struct power_supply *psy;
	struct pm860x_charger_info *info = data;
	union power_supply_propval temp;
	int value;
	int ret;

	psy = power_supply_get_by_name(pm860x_supplied_to[0]);
	if (!psy)
		goto out;
	ret = psy->get_property(psy, POWER_SUPPLY_PROP_TEMP, &temp);
	if (ret)
		goto out;
	value = temp.intval / 10;

	mutex_lock(&info->lock);
	/* Temperature < -10 C or >40 C, Will not allow charge */
	if (value < -10 || value > 40)
		info->allowed = 0;
	else
		info->allowed = 1;
	dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed);
	mutex_unlock(&info->lock);

	set_charging_fsm(info);
out:
	return IRQ_HANDLED;
}
static irqreturn_t pm860x_charger_handler(int irq, void *data)
{
	struct pm860x_charger_info *info = data;
	int ret;

	mutex_lock(&info->lock);
	ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
	if (ret < 0) {
		mutex_unlock(&info->lock);
		goto out;
	}
	if (ret & STATUS2_CHG) {
		info->online = 1;
		info->allowed = 1;
	} else {
		info->online = 0;
		info->allowed = 0;
	}
	mutex_unlock(&info->lock);
	dev_dbg(info->dev, "%s, Charger:%s, Allowed:%d\n", __func__,
		(info->online) ? "online" : "N/A", info->allowed);

	set_charging_fsm(info);

	power_supply_changed(&info->usb);
out:
	return IRQ_HANDLED;
}
static ssize_t control_charging(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t count)
{
	struct pm860x_charger_info *info = dev_get_drvdata(dev);
	int input;

	if (!sscanf(buf, "%d", &input))
		return strnlen(buf, PAGE_SIZE);
#ifdef CONFIG_ALTERNATE_CHARGER
		mutex_lock(&info->lock);
		ctrl_cb_func(input);
		mutex_unlock(&info->lock);
		dev_dbg(info->dev, \
			"alternate control charger: %s\n",\
			(input) ? "stop" : "start");
#else
	if (info) {
		mutex_lock(&info->lock);
		info->allowed = input; /* 0=stop charging; 1=start charging */
		mutex_unlock(&info->lock);
		set_charging_fsm(info);
	} else
		printk(KERN_ERR "[88pm860x_charger.c] cannot get driver info\n");
#endif
	return strnlen(buf, PAGE_SIZE);
}
static irqreturn_t pm860x_done_handler(int irq, void *data)
{
	struct pm860x_charger_info *info = data;
	int ret;

	mdelay(5);/* delay 5ms to make sure read the correct status of CHG_DET*/

	pr_info( \
	"charger:pm860x_done_handler:enter\n");
	
	mutex_lock(&info->lock);
	if (info->state == FSM_PRECHARGE) {
		info->allowed = 1;
	} else {
		info->allowed = 0;
		/* CHG_DONE interrupt is faster than CHG_DET interrupt when
		 * plug in/out usb, So we can not rely on info->online, we
		 * need check pm8607 status register to check usb is online
		 * or not, then we can decide it is real charge done
		   automatically or it is triggered by usb plug out;
		   */
		ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
		if (ret & STATUS2_CHG)
			pm860x_battery_update_soc();
	}
	dev_dbg(info->dev, "%s, Allowed : %d\n", __func__, info->allowed);
	mutex_unlock(&info->lock);

	set_charging_fsm(info);
	return IRQ_HANDLED;
}
static void pm860x_vbus_work(struct work_struct *work)
{
	struct pm860x_charger_info *info = container_of(work,
		struct pm860x_charger_info, vbus_work);
	int ret;

	mutex_lock(&info->lock);
	if (info->vbus_output) {
		info->online = 0;
		/* disable VCHG interrupt */
		disable_irq(info->irq[6]);
	} else {
		ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
		if (ret < 0) {
			mutex_unlock(&info->lock);
			goto out;
		}
		if (ret & STATUS2_CHG)
			info->online = 1;
		else
			info->online = 0;
		enable_irq(info->irq[6]);
	}
	mutex_unlock(&info->lock);
	dev_dbg(info->dev, "VBUS output:%d, Charger:%s\n", info->vbus_output,
		(info->online) ? "online" : "N/A");
out:
	set_charging_fsm(info);
}
static int pm860x_charger_suspend(struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct pm860x_charger_info *info = dev_get_drvdata(dev);
	int irq, i;

	mutex_lock(&info->lock);
	info->allowed = 0;
	mutex_unlock(&info->lock);

	set_charging_fsm(info);

	if (device_may_wakeup(dev)) {
		enable_irq_wake(info->chip->core_irq);
		for (i = 0; i < info->irq_nums; i++) {
#if defined(CONFIG_USB_VBUS_88PM860X)
			/* this IRQ should be handled in VBUS driver */
			if (i == 0)
				continue;
#endif
			irq = platform_get_irq(pdev, i);
			enable_irq_wake(irq);
		}
	}
	set_vbatt_threshold(info, PM860X_POWER_OFF, 0);
	return 0;
}
static irqreturn_t pm860x_exception_handler(int irq, void *data)
{
	struct pm860x_charger_info *info = data;

	mutex_lock(&info->lock);
	info->allowed = 0;
	mutex_unlock(&info->lock);
	dev_dbg(info->dev, "%s, irq: %d\n", __func__, irq);

	set_charging_fsm(info);
	return IRQ_HANDLED;
}
Ejemplo n.º 9
0
static irqreturn_t pm860x_done_handler(int irq, void *data)
{
    struct pm860x_charger_info *info = data;
    struct power_supply *psy;
    union power_supply_propval val;
    int ret;
    int vbatt;

    mutex_lock(&info->lock);
    /* pre-charge done, will transimit to fast-charge stage */
    if (info->state == FSM_PRECHARGE) {
        info->allowed = 1;
        goto out;
    }
    /*
     * Fast charge done, delay to read
     * the correct status of CHG_DET.
     */
    mdelay(5);
    info->allowed = 0;
    psy = power_supply_get_by_name(pm860x_supplied_to[0]);
    if (!psy)
        goto out;
    ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW,
                                    &val);
    if (ret)
        goto out_psy_put;
    vbatt = val.intval / 1000;
    /*
     * CHG_DONE interrupt is faster than CHG_DET interrupt when
     * plug in/out usb, So we can not rely on info->online, we
     * need check pm8607 status register to check usb is online
     * or not, then we can decide it is real charge done
     * automatically or it is triggered by usb plug out;
     */
    ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
    if (ret < 0)
        goto out_psy_put;
    if (vbatt > CHARGE_THRESHOLD && ret & STATUS2_CHG)
        power_supply_set_property(psy, POWER_SUPPLY_PROP_CHARGE_FULL,
                                  &val);

out_psy_put:
    power_supply_put(psy);
out:
    mutex_unlock(&info->lock);
    dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed);
    set_charging_fsm(info);

    return IRQ_HANDLED;
}
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;
}
static int pm860x_init_charger(struct pm860x_charger_info *info)
{
	int ret;

	mutex_lock(&info->lock);
	info->state = FSM_INIT;

	ret = pm860x_set_bits(info->i2c,
							PM8607_CHG_CTRL6,
							CC6_BAT_DET_GPADC1,
							CC6_BAT_DET_GPADC1);
	if (ret < 0)
		goto out;		
	ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);

	if (ret < 0)
		goto out;


	if (ret & STATUS2_CHG) {
		info->online = 1;
		info->allowed = 1;
	} else {
		info->online = 0;
		info->allowed = 0;
	}

        if (info->batdet == 1) {
                if (ret & STATUS2_BAT) {
                        info->present = 1;
                } else {
                        info->present = 0;
                }
        }

	info->charge_type = USB_CHARGER;

	mutex_unlock(&info->lock);

	pr_info( \
	"charger:pm860x_init_charger:info->present=%d,reg02=[%x]\n",
	info->present, ret);

	set_charging_fsm(info);
	return 0;
out:
	return ret;
}
static ssize_t stop_charging(struct device *dev, struct device_attribute *attr,
			     const char *buf, size_t count)
{
	struct pm860x_charger_info *info = dev_get_drvdata(dev);
	int disable;

	sscanf(buf, "%d", &disable);
	if (disable && info) {
		dev_dbg(info->dev, "stop charging by manual\n");

		mutex_lock(&info->lock);
		info->allowed = 0;
		mutex_unlock(&info->lock);
		set_charging_fsm(info);
	}
	return strnlen(buf, PAGE_SIZE);
}
irqreturn_t pm860x_charger_handler(int irq, void *data)
{
	struct pm860x_charger_info *info = ginfo;
	int ret;

	mutex_lock(&info->lock);
	ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
	if (ret < 0) {
		mutex_unlock(&info->lock);
		goto out;
	}

	if (ret & STATUS2_CHG) {
		info->online = 1;
		info->allowed = 1;
	} else {
		info->online = 0;
		info->allowed = 0;
	}

	if (info->batdet == 1) {
		if (ret & STATUS2_BAT) {
			info->present = 1;
		} else { 
			info->present = 0;
		}
	}

	mutex_unlock(&info->lock);
	dev_dbg(info->dev, "%s, Charger:%s, Allowed:%d\n", __func__,
		(info->online) ? "online" : "N/A", info->allowed);

	pr_info( \
	"charger:pm860x_charger_handler:allowed[%d]online[%d]present[%d]reg02[%x]\n",
	info->allowed, info->online, info->present, ret);

	set_charging_fsm(info);
#ifndef ALTERNATE_CHARGER
	if (info->charge_type == USB_CHARGER)
		power_supply_changed(&info->usb);
	else
		power_supply_changed(&info->ac);
#endif
out:
	return IRQ_HANDLED;
}
static irqreturn_t pm860x_temp_handler(int irq, void *data)
{
	struct power_supply *psy;
	struct pm860x_charger_info *info = data;
	union power_supply_propval state;
	int ret = -EINVAL;

	
	pr_info( \
	"charger:pm860x_temp_handler:enter\n");
	
	psy = power_supply_get_by_name(pm860x_supplied_to[0]);
	if (!psy)
		goto out;
	ret = psy->get_property(psy, POWER_SUPPLY_PROP_HEALTH, &state);
	if (ret)
		goto out;

	mutex_lock(&info->lock);
	switch (state.intval) {
	case POWER_SUPPLY_HEALTH_GOOD:
		info->allowed = 1;
		pr_info( \
		"charger:pm860x_temp_handler:POWER_SUPPLY_HEALTH_GOOD\n");
		break;
	case POWER_SUPPLY_HEALTH_OVERHEAT:
	case POWER_SUPPLY_HEALTH_COLD:
		info->allowed = 0;
		pr_info( \
		"charger:pm860x_temp_handler:POWER_SUPPLY_HEALTH_NOT_GOOD\n");
		break;
	case POWER_SUPPLY_HEALTH_DEAD:
		info->allowed = 0;
		pr_info( \
		"charger:pm860x_temp_handler:POWER_SUPPLY_HEALTH_DEAD\n");
		break;
	}
	dev_dbg(info->dev, "%s, Allowed:%d\n", __func__, info->allowed);
	mutex_unlock(&info->lock);

	set_charging_fsm(info);
out:
	return IRQ_HANDLED;
}
static irqreturn_t pm860x_vbattery_handler(int irq, void *data)
{
	struct pm860x_charger_info *info = data;

	mutex_lock(&info->lock);

	set_vbatt_threshold(info, 0, 0);

	if (info->present && info->online)
		info->allowed = 1;
	else
		info->allowed = 0;
	mutex_unlock(&info->lock);
	dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed);

	set_charging_fsm(info);

	return IRQ_HANDLED;
}
static int pm860x_init_charger(struct pm860x_charger_info *info)
{
	int ret;

	ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
	if (ret < 0)
		return ret;

	mutex_lock(&info->lock);
	info->state = FSM_INIT;
	if (ret & STATUS2_CHG) {
		info->online = 1;
		info->allowed = 1;
	} else {
		info->online = 0;
		info->allowed = 0;
	}
	mutex_unlock(&info->lock);

	set_charging_fsm(info);
	return 0;
}