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 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;
}
Esempio n. 4
0
static void hook_switch_work(struct work_struct *work)
{
	struct pm860x_headset_info	*info =
		container_of(work, struct pm860x_headset_info, work_hook);
	struct headset_switch_data	*switch_data;
	unsigned char value;

	if (info == NULL) {
		pr_debug("Invalid headset info!\n");
		return;
		}
	switch_data = info->psw_data_hook;
	if (switch_data == NULL) {
		pr_debug("Invalid hook switch data!\n");
		return;
	}

	value = (unsigned char)pm860x_reg_read(info->i2c, PM8607_STATUS_1);
	value &= PM8607_STATUS_HOOK;

	/* hook pressed */
	if (value) {
		switch_data->state = PM860X_HOOKSWITCH_PRESSED;
	} else {
	/* hook released */
		switch_data->state = PM860X_HOOKSWITCH_RELEASED;
	}

	pr_info("hook state switch to %d \n", switch_data->state);
	switch_set_state(&switch_data->sdev, switch_data->state);
}
Esempio n. 5
0
static int pm860x_led_suspend(struct pm860x_led *data)
{
	int value, blink_period, blink_on_time;
	int ret;

	if(data->port == PM8606_LED1_GREEN)
	{
		if(0)//data->blink_time == 1)
		{
			blink_period = data->color_green_blink_on + data->color_green_blink_off;
			blink_period = (blink_period > 930? blink_period - 930 : 930-blink_period) / 930;
			blink_on_time = data->color_green_blink_on / 66;
			pm860x_reg_write(data->i2c, PM8606_RGB1A, (blink_period << 3) | blink_on_time);
			pm860x_reg_write(data->i2c, PM8606_RGB1C, 0x5F);
		}
		else 
		{
			pm860x_reg_write(data->i2c, PM8606_RGB1A, 0);
			pm860x_reg_write(data->i2c, PM8606_RGB1C, 0);
			/* Disable reference OSC */
			ret = pm860x_reg_read(data->i2c, PM8606_MISC);
			if (ret < 0)
				goto bl_suspend;
			if (ret & PM8606_MISC_OSC_EN) {
				value = ret & (~PM8606_MISC_OSC_EN);
				ret = pm860x_reg_write(data->i2c, PM8606_MISC, value);
				if (ret < 0)
					goto bl_suspend;
			}
			/* Disable reference VSYS */
			ret = pm860x_reg_read(data->i2c, PM8606_VSYS);
			if (ret < 0)
				goto bl_suspend;
			if (ret & PM8606_VSYS_EN) {
				value = ret & (~PM8606_VSYS_EN);
				ret = pm860x_reg_write(data->i2c, PM8606_VSYS, value);
				if (ret < 0)
					goto bl_suspend;
			}
		}
	}

bl_suspend:
	pm860x_reg_write(data->i2c, PM8606_RGB1B, 0);
	pm860x_reg_write(data->i2c, PM8606_RGB1D, 0);
	return 0;
}
Esempio n. 6
0
static int pm8607_is_enabled(struct regulator_dev *rdev)
{
	struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
	int ret;

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

	return !!((unsigned char)ret & (1 << info->enable_bit));
}
void pm860x_get_general_user(int *data)
{
	struct pm860x_charger_info *info = ginfo;
	*data = pm860x_reg_read(info->i2c, PM8607_GENERAL_USE);
	if (*data < 0)
		printk(KERN_ALERT \
		"pm860x_get_general_user->fail!!!\n");
	else
		pr_info( \
		"[%s][%s]data[0x%x]\n",
		__FILE__, __func__, *data);

} EXPORT_SYMBOL(pm860x_get_general_user);
Esempio n. 8
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;
}
/* 88PM860x gives us an interrupt when ONKEY is held */
static irqreturn_t pm860x_onkey_handler(int irq, void *data)
{
    struct pm860x_onkey_info *info = data;
    int ret;

    ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
    ret &= ONKEY_STATUS;
    input_report_key(info->idev, KEY_POWER, ret);
    input_sync(info->idev);

    /* Enable 8-second long onkey detection */
    pm860x_set_bits(info->i2c, PM8607_WAKEUP, 3, LONG_ONKEY_EN);
    return IRQ_HANDLED;
}
static ssize_t pm860x_power_proc_read(char *page, char **start, off_t off,
		int count, int *eof, void *data)
{
	u8 reg_val;
	int len=0;
	struct pm860x_charger_info *info = ginfo;

	if (index == 0xffff) {
		int i;
		printk(KERN_INFO "pm8607:sanremo reg dump\n");
		for (i = 0; i < PM860X_POWER_REG_NUM; i++) {
			reg_val = pm860x_reg_read(info->i2c, i);
			printk(KERN_INFO "[0x%02x]=0x%02x\n", i, reg_val);
		}
		return 0;
	}
	if((index < 0) || (index > PM860X_POWER_REG_NUM))
		return 0;
	reg_val = pm860x_reg_read(info->i2c, index);
	len = sprintf(page, "0x%x:0x%x\n",index,reg_val);

	return len;
}
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;
}
Esempio n. 12
0
static int pm860x_backlight_get_brightness(struct backlight_device *bl)
{
	struct pm860x_backlight_data *data = bl_get_data(bl);
	struct pm860x_chip *chip = data->chip;
	int ret;

	ret = pm860x_reg_read(data->i2c, wled_a(data->port));
	if (ret < 0)
		goto out;
	data->current_brightness = ret;
	dev_dbg(chip->dev, "get brightness %d\n", data->current_brightness);
	return data->current_brightness;
out:
	return -EINVAL;
}
Esempio n. 13
0
static int pm8607_get_voltage(struct regulator_dev *rdev)
{
	struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
	uint8_t val, mask;
	int ret;

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

	mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
	val = ((unsigned char)ret & mask) >> info->vol_shift;

	return pm8607_list_voltage(rdev, val);
}
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;
}
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 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;
}
Esempio n. 17
0
static int pm860x_backlight_probe(struct platform_device *pdev)
{
	struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
	struct pm860x_backlight_pdata *pdata = NULL;
	struct pm860x_backlight_data *data;
	struct backlight_device *bl;
	struct resource *res;
	struct backlight_properties props;
	unsigned char value;
	char name[MFD_NAME_SIZE];
	int ret;

	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
	if (res == NULL) {
		dev_err(&pdev->dev, "No I/O resource!\n");
		return -EINVAL;
	}

	pdata = pdev->dev.platform_data;
	if (pdata == NULL) {
		dev_err(&pdev->dev, "platform data isn't assigned to "
			"backlight\n");
		return -EINVAL;
	}

	data = kzalloc(sizeof(struct pm860x_backlight_data), GFP_KERNEL);
	if (data == NULL)
		return -ENOMEM;
	strncpy(name, res->name, MFD_NAME_SIZE);
	data->chip = chip;
	data->i2c = (chip->id == CHIP_PM8606) ? chip->client	\
			: chip->companion;
	data->current_brightness = MAX_BRIGHTNESS;
	data->pwm = pdata->pwm;
	data->iset = pdata->iset;
	data->port = pdata->flags;
	if (data->port < 0) {
		dev_err(&pdev->dev, "wrong platform data is assigned");
		kfree(data);
		return -EINVAL;
	}

	memset(&props, 0, sizeof(struct backlight_properties));
	props.type = BACKLIGHT_RAW;
	props.max_brightness = MAX_BRIGHTNESS;
	bl = backlight_device_register(name, &pdev->dev, data,
					&pm860x_backlight_ops, &props);
	if (IS_ERR(bl)) {
		dev_err(&pdev->dev, "failed to register backlight\n");
		kfree(data);
		return PTR_ERR(bl);
	}
	bl->props.brightness = MAX_BRIGHTNESS;

	platform_set_drvdata(pdev, bl);

	/* Enable reference VSYS */
	ret = pm860x_reg_read(data->i2c, PM8606_VSYS);
	if (ret < 0)
		goto out;
	if ((ret & PM8606_VSYS_EN) == 0) {
		value = ret | PM8606_VSYS_EN;
		ret = pm860x_reg_write(data->i2c, PM8606_VSYS, value);
		if (ret < 0)
			goto out;
	}
	/* Enable reference OSC */
	ret = pm860x_reg_read(data->i2c, PM8606_MISC);
	if (ret < 0)
		goto out;
	if ((ret & PM8606_MISC_OSC_EN) == 0) {
		value = ret | PM8606_MISC_OSC_EN;
		ret = pm860x_reg_write(data->i2c, PM8606_MISC, value);
		if (ret < 0)
			goto out;
	}
	/* read current backlight */
	ret = pm860x_backlight_get_brightness(bl);
	if (ret < 0)
		goto out;

	backlight_update_status(bl);
	return 0;
out:
	backlight_device_unregister(bl);
	kfree(data);
	return ret;
}
Esempio n. 18
0
static void headset_switch_work(struct work_struct *work)
{
	struct pm860x_headset_info	*info =
		container_of(work, struct pm860x_headset_info, work_headset);
	struct headset_switch_data	*switch_data;
	unsigned char value;

	if (info == NULL) {
		pr_debug("Invalid headset info!\n");
		return;
	}
	switch_data = info->psw_data_headset;
	if (switch_data == NULL) {
		pr_debug("Invalid headset switch data!\n");
		return;
	}

	pm860x_reg_write(info->i2c, 0xd3, 0x3);	

	value = (unsigned char)pm860x_reg_read(info->i2c, PM8607_STATUS_1);
	value &= PM8607_STATUS_HEADSET;
	/* on TD_DKB, the headset jack circuit logic is opposite to the
	 * design of levante spec, so if headset status is connected in
	 * levante spec, it's actually disconnected. */
//	value = info->headset_flag? !value : value;
	value = info->headset_flag? value : !value;
	/* headset detected */
	if (value) {
		switch_data->state = PM860X_HEADSET_ADD;
	
		/* enable MIC bias to enable hook detection, we must enable mic bias first
		* otherwise we may get false hook detection */
		pm860x_set_bits(info->i2c, PM8607_AUDIO_REG_BASE + PM8607_AUDIO_ADC_ANALOG_PROGRAM1, PM8607_ADC_EN_MIC2_BIAS, 0x60);
		/* enable MIC detection to detect hook press*/
		pm860x_set_bits(info->i2c, PM8607_MIC_DECTION, PM8607_MIC_DET_EN_MIC_DET, 1);

		/* we need to wait some time before the status register goes stable */
		msleep(1500);		

		value = (unsigned char)pm860x_reg_read(info->i2c, PM8607_STATUS_1);
		
		printk("register 0x01  is 0x%x  .\n",value);

		/* Levante issue: use hook status to detect MIC, if detected hook, it's
		 * without MIC, headphone; otherwise, it's headset. */
		value &= PM8607_STATUS_HOOK;
		if (value)
			switch_data->state = PM860X_HEADPHONE_ADD;

		/* unmask hook interrupt only if the headset has a Mic */
		if (switch_data->state == PM860X_HEADSET_ADD) {
			pm860x_set_bits(info->i2c, PM8607_INT_MASK_3, PM8607_INT_EN_HOOK, PM8607_INT_EN_HOOK);
		} else {
			/* disable MIC/hook detection if headset does not have a Mic */
			pm860x_set_bits(info->i2c, PM8607_MIC_DECTION, PM8607_MIC_DET_EN_MIC_DET, 0);
	        }

	} 
	else {
		/* headset removed disable MIC/hook detection when headset is */
		pm860x_set_bits(info->i2c, PM8607_MIC_DECTION, PM8607_MIC_DET_EN_MIC_DET, 0);
		/* disable hook interrupt */
		pm860x_set_bits(info->i2c, PM8607_INT_MASK_3, PM8607_INT_EN_HOOK, 0);
		/* disable mic bias */
		pm860x_set_bits(info->i2c, PM8607_AUDIO_REG_BASE + PM8607_AUDIO_ADC_ANALOG_PROGRAM1, PM8607_ADC_EN_MIC2_BIAS, 0);

		switch_data->state = PM860X_HEADSET_REMOVE;
	}

	pr_info("headset_switch_work to %d \n", switch_data->state);
	switch_set_state(&switch_data->sdev, switch_data->state);
}