Exemplo n.º 1
0
static int pm8607_set_voltage(struct regulator_dev *rdev,
			      int min_uV, int max_uV)
{
	struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
	uint8_t val, mask;
	int ret;

	if (check_range(info, min_uV, max_uV)) {
		pr_err("invalid voltage range (%d, %d) uV\n", min_uV, max_uV);
		return -EINVAL;
	}

	ret = choose_voltage(rdev, min_uV, max_uV);
	if (ret < 0)
		return -EINVAL;
	val = (uint8_t)(ret << info->vol_shift);
	mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;

	ret = pm860x_set_bits(info->i2c, info->vol_reg, mask, val);
	if (ret)
		return ret;
	switch (info->desc.id) {
	case PM8607_ID_BUCK1:
	case PM8607_ID_BUCK3:
		ret = pm860x_set_bits(info->i2c, info->update_reg,
				      1 << info->update_bit,
				      1 << info->update_bit);
		break;
	}
	return ret;
}
Exemplo n.º 2
0
static int pm860x_backlight_set(struct backlight_device *bl, int brightness)
{
	struct pm860x_backlight_data *data = bl_get_data(bl);
	struct pm860x_chip *chip = data->chip;
	unsigned char value;
	int ret;

	if (brightness > MAX_BRIGHTNESS)
		value = MAX_BRIGHTNESS;
	else
		value = brightness;

	ret = pm860x_reg_write(data->i2c, wled_a(data->port), value);
	if (ret < 0)
		goto out;

	if ((data->current_brightness == 0) && brightness) {
		if (data->iset) {
			ret = pm860x_set_bits(data->i2c, wled_idc(data->port),
					      CURRENT_BITMASK, data->iset);
			if (ret < 0)
				goto out;
		}
		if (data->pwm) {
			ret = pm860x_set_bits(data->i2c, PM8606_PWM,
					      PM8606_PWM_FREQ_MASK, data->pwm);
			if (ret < 0)
				goto out;
		}
		if (brightness == MAX_BRIGHTNESS) {
			/* set WLED_ON bit as 100% */
			ret = pm860x_set_bits(data->i2c, wled_b(data->port),
					      PM8606_WLED_ON, PM8606_WLED_ON);
		}
	} else {
		if (brightness == MAX_BRIGHTNESS) {
			/* set WLED_ON bit as 100% */
			ret = pm860x_set_bits(data->i2c, wled_b(data->port),
					      PM8606_WLED_ON, PM8606_WLED_ON);
		} else {
			/* clear WLED_ON bit since it's not 100% */
			ret = pm860x_set_bits(data->i2c, wled_b(data->port),
					      PM8606_WLED_ON, 0);
		}
	}
	if (ret < 0)
		goto out;

	dev_dbg(chip->dev, "set brightness %d\n", value);
	data->current_brightness = value;
	return 0;
out:
	dev_dbg(chip->dev, "set brightness %d failure with return "
		"value:%d\n", value, ret);
	return ret;
}
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;
}
Exemplo n.º 4
0
static int headset_switch_resume(struct platform_device *pdev)
{
	struct pm860x_headset_info *info = platform_get_drvdata(pdev);
	struct headset_switch_data *switch_data = info->psw_data_headset;


	/* enable MIC/HOOK detection when headset is connected. */
	if (switch_data->state == PM860X_HEADSET_ADD) {
		pm860x_set_bits(info->i2c, PM8607_MIC_DECTION, PM8607_MIC_DET_EN_MIC_DET, 1);
	}

	pm860x_set_bits(info->i2c, PM8607_HEADSET_DECTION, PM8607_HEADSET_EN_HS_DET, 1);

	return 0;
}
static void stop_charge(struct pm860x_charger_info *info, int vbatt)
{
	dev_dbg(info->dev, "Stop charging!\n");
	pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3, CC1_MODE_OFF);
	if (vbatt > CHARGE_THRESHOLD && info->online)
		set_vbatt_threshold(info, CHARGE_THRESHOLD, 0);
}
Exemplo n.º 6
0
static int pm8607_disable(struct regulator_dev *rdev)
{
	struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);

	return pm860x_set_bits(info->i2c, info->enable_reg,
			       1 << info->enable_bit, 0);
}
Exemplo n.º 7
0
static int headset_switch_suspend(struct platform_device *pdev, pm_message_t state)
{
	struct pm860x_headset_info *info = platform_get_drvdata(pdev);
	struct headset_switch_data *switch_data = info->psw_data_headset;

	/* disable MIC/HOOK detection when headset is connected; no operation is
	 * needed for headphone */
	if (switch_data->state == PM860X_HEADSET_ADD) {
		pm860x_set_bits(info->i2c, PM8607_MIC_DECTION, PM8607_MIC_DET_EN_MIC_DET, 0);
	}
	
	pm860x_set_bits(info->i2c, PM8607_HEADSET_DECTION, PM8607_HEADSET_EN_HS_DET, 0);


	return 0;
}
Exemplo n.º 8
0
static int pm8606_preg_enable(struct regulator_dev *rdev)
{
	struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);

	return pm860x_set_bits(info->i2c, rdev->desc->enable_reg,
			       1 << rdev->desc->enable_mask, 0);
}
Exemplo n.º 9
0
static int __led_set(struct pm860x_led *led, int command)
{
	struct pm860x_chip *chip = led->chip;
	int mask, ret;

	mutex_lock(&led->lock);
	switch (command) {
	case SET_BRIGHTNESS:
		if ((led->current_brightness == 0) && led->brightness) {
			if (led->iset) {
				ret = pm860x_set_bits(led->i2c, led->offset,
						LED_CURRENT_MASK, led->iset);
				if (ret < 0)
					goto out;
			}
		} else if (led->brightness == 0) {
			ret = pm860x_set_bits(led->i2c, led->offset,
						LED_CURRENT_MASK, 0);
			if (ret < 0)
				goto out;
		}
		ret = pm860x_set_bits(led->i2c, led->offset, LED_PWM_MASK,
					led->brightness);
		if (ret < 0)
			goto out;
		led->current_brightness = led->brightness;
		dev_dbg(chip->dev, "Update LED. (reg:%d, brightness:%d)\n",
			led->offset, led->brightness);
		break;
	case SET_BLINK:
		ret = pm860x_set_bits(led->i2c, led->offset,
				LED_BLINK_MASK, led->blink_data);
		if (ret < 0)
			goto out;

		mask = __blink_ctl_mask(led->port);
		ret = pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, mask);
		if (ret < 0)
			goto out;
		dev_dbg(chip->dev, "LED blink delay on:%dms, delay off:%dms\n",
			led->blink_on, led->blink_off);
		break;
	}
out:
	mutex_unlock(&led->lock);
	return 0;
}
static int start_precharge(struct pm860x_charger_info *info)
{
	int ret;

	dev_dbg(info->dev, "Start Pre-charging!\n");
	pr_info( \
	"charger:start_precharge\n");

	set_vbatt_threshold(info, 0, 0);

	ret = pm860x_reg_write(info->i2c_8606, PM8606_PREREGULATORA,
			       PREREG1_1500MA | PREREG1_VSYS_4_5V);
	if (ret < 0)
		goto out;
	/* stop charging */
	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3,
			      CC1_MODE_OFF);
	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 precharge current, termination voltage, IBAT & TBAT monitor */
	ret = pm860x_reg_write(info->i2c, PM8607_CHG_CTRL4,
			       CC4_IPRE_40MA | CC4_VPCHG_3_2V | CC4_IFCHG_MON_EN
			       | CC4_BTEMP_MON_EN);
	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;
	/* trigger precharge */
	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3,
			      CC1_MODE_PRECHARGE);
out:
	return ret;
}
static void pm860x_led_work(struct work_struct *work)
{

	struct pm860x_led *led;
	struct pm860x_chip *chip;
	int mask;

	led = container_of(work, struct pm860x_led, work);
	chip = led->chip;
	mutex_lock(&led->lock);
	if ((led->current_brightness == 0) && led->brightness) {
		led_power_set(chip, led->port, 1);
		if (led->iset) {
			pm860x_set_bits(led->i2c, __led_off(led->port),
					LED_CURRENT_MASK, led->iset);
		}
		pm860x_set_bits(led->i2c, __blink_off(led->port),
				LED_BLINK_MASK, LED_ON_CONTINUOUS);
		mask = __blink_ctl_mask(led->port);
		pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, mask);
	} else if (led->brightness == 0) {
		pm860x_set_bits(led->i2c, __led_off(led->port),
				LED_CURRENT_MASK, 0);
		mask = __blink_ctl_mask(led->port);
		pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, 0);
		led_power_set(chip, led->port, 0);
	}
	pm860x_set_bits(led->i2c, __led_off(led->port), LED_PWM_MASK,
			led->brightness);
	led->current_brightness = led->brightness;
	dev_dbg(chip->dev, "Update LED. (reg:%d, brightness:%d)\n",
		__led_off(led->port), led->brightness);
	mutex_unlock(&led->lock);
}
Exemplo n.º 12
0
static int pm8607_set_voltage_sel(struct regulator_dev *rdev, unsigned selector)
{
	struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
	uint8_t val;
	int ret;

	val = (uint8_t)(selector << (ffs(rdev->desc->vsel_mask) - 1));

	ret = pm860x_set_bits(info->i2c, rdev->desc->vsel_reg,
			      rdev->desc->vsel_mask, val);
	if (ret)
		return ret;
	switch (info->desc.id) {
	case PM8607_ID_BUCK1:
	case PM8607_ID_BUCK3:
		ret = pm860x_set_bits(info->i2c, info->update_reg,
				      1 << info->update_bit,
				      1 << info->update_bit);
		break;
	}
	return ret;
}
/* 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 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 int stop_charge(struct pm860x_charger_info *info, int vbatt)
{
	dev_dbg(info->dev, "Stop charging!\n");
	pr_info( \
	"charger:stop_charge\n");
	pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3, CC1_MODE_OFF);
	if (vbatt > CHARGE_THRESHOLD && info->online) {
		set_vbatt_threshold(info, CHARGE_THRESHOLD, 0);
	} else
		set_vbatt_threshold(info, PM860X_POWER_OFF, 0);
	/*hw fix workaround: enable bc_short again after fast charge finished*/
	if((info->chip->chip_version <= PM8607_CHIP_C1) && info->bc_short){
		info->bc_short = 0;/* enable bc_short mechanism*/
		msleep(2);
		pm860x_page_reg_write(info->i2c, 0xCF, 0x0);
	}
	return 0;
}
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;
}
Exemplo n.º 17
0
static int __devexit headset_switch_remove(struct platform_device *pdev)
{
	struct pm860x_headset_info *info = platform_get_drvdata(pdev);
	struct headset_switch_data *switch_data_headset = info->psw_data_headset;
	struct headset_switch_data *switch_data_hook = info->psw_data_hook;

	/* disable headset detection */
	pm860x_set_bits(info->i2c, PM8607_HEADSET_DECTION, PM8607_HEADSET_EN_HS_DET, 0);

	cancel_work_sync(&info->work_headset);
	cancel_work_sync(&info->work_hook);

	free_irq(info->irq_hook, info);
	free_irq(info->irq_headset, info);

	switch_dev_unregister(&switch_data_hook->sdev);
	switch_dev_unregister(&switch_data_headset->sdev);

	kfree(switch_data_hook);
	kfree(switch_data_headset);
	kfree(info);

	return 0;
}
Exemplo n.º 18
0
static void pm860x_led_work(struct work_struct *work)
{

    struct pm860x_led *led;
    struct pm860x_chip *chip;
    unsigned char buf[3];
    int mask, ret;

    led = container_of(work, struct pm860x_led, work);
    chip = led->chip;
    mutex_lock(&led->lock);
    if ((led->current_brightness == 0) && led->brightness) {
        if (led->iset) {
            pm860x_set_bits(led->i2c, __led_off(led->port),
                            LED_CURRENT_MASK, led->iset);
        }
        pm860x_set_bits(led->i2c, __blink_off(led->port),
                        LED_BLINK_MASK, LED_ON_CONTINUOUS);
        mask = __blink_ctl_mask(led->port);
        pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, mask);
    }
    pm860x_set_bits(led->i2c, __led_off(led->port), LED_PWM_MASK,
                    led->brightness);

    if (led->brightness == 0) {
        pm860x_bulk_read(led->i2c, __led_off(led->port), 3, buf);
        ret = buf[0] & LED_PWM_MASK;
        ret |= buf[1] & LED_PWM_MASK;
        ret |= buf[2] & LED_PWM_MASK;
        if (ret == 0) {
            /* unset current since no led is lighting */
            pm860x_set_bits(led->i2c, __led_off(led->port),
                            LED_CURRENT_MASK, 0);
            mask = __blink_ctl_mask(led->port);
            pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, 0);
        }
    }
    led->current_brightness = led->brightness;
    dev_dbg(chip->dev, "Update LED. (reg:%d, brightness:%d)\n",
            __led_off(led->port), led->brightness);
    mutex_unlock(&led->lock);
}
Exemplo n.º 19
0
static int headset_switch_probe(struct platform_device *pdev)
{
	struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
	struct pm860x_headset_info *info;
	struct pm860x_platform_data *pm860x_pdata;
	struct gpio_switch_platform_data *pdata_headset = pdev->dev.platform_data;
	struct gpio_switch_platform_data *pdata_hook = pdata_headset + 1;
	struct headset_switch_data *switch_data_headset, *switch_data_hook;
	int irq_headset, irq_hook, ret = 0;

	if (pdev->dev.parent->platform_data) {
		pm860x_pdata = pdev->dev.parent->platform_data;
	} else {
		pr_debug("Invalid pm860x platform data!\n");
		return -EINVAL;
	}

	if (pdata_headset == NULL || pdata_hook == NULL) {
		pr_debug("Invalid gpio switch platform data!\n");
		return -EBUSY;
	}

	irq_headset = platform_get_irq(pdev, 0);
	if (irq_headset < 0) {
		dev_err(&pdev->dev, "No IRQ resource for headset!\n");
		return -EINVAL;
	}
	irq_hook = platform_get_irq(pdev, 1);
	if (irq_hook < 0) {
		dev_err(&pdev->dev, "No IRQ resource for hook!\n");
		return -EINVAL;
	}

	info = kzalloc(sizeof(struct pm860x_headset_info), GFP_KERNEL);
	if (!info)
		return -ENOMEM;
	info->chip = chip;
	info->dev = &pdev->dev;
	info->irq_headset = irq_headset + chip->irq_base;
	info->irq_hook = irq_hook + chip->irq_base;
	info->headset_flag = pm860x_pdata->headset_flag;
	info->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;

	switch_data_headset = kzalloc(sizeof(struct headset_switch_data), GFP_KERNEL);
	if (!switch_data_headset)
		return -ENOMEM;
	switch_data_hook = kzalloc(sizeof(struct headset_switch_data), GFP_KERNEL);
	if (!switch_data_hook)
		return -ENOMEM;

	switch_data_headset->sdev.name = pdata_headset->name;
	switch_data_headset->name_on = pdata_headset->name_on;
	switch_data_headset->name_off = pdata_headset->name_off;
	switch_data_headset->state_on = pdata_headset->state_on;
	switch_data_headset->state_off = pdata_headset->state_off;
	switch_data_headset->sdev.print_state = switch_headset_print_state;
	info->psw_data_headset = switch_data_headset;

	switch_data_hook->sdev.name = pdata_hook->name;
	switch_data_hook->name_on = pdata_hook->name_on;
	switch_data_hook->name_off = pdata_hook->name_off;
	switch_data_hook->state_on = pdata_hook->state_on;
	switch_data_hook->state_off = pdata_hook->state_off;
	switch_data_hook->sdev.print_state = switch_headset_print_state;
	info->psw_data_hook = switch_data_hook;

	ret = switch_dev_register(&switch_data_headset->sdev);
	if (ret < 0)
		goto err_switch_dev_register;
	ret = switch_dev_register(&switch_data_hook->sdev);
	if (ret < 0)
		goto err_switch_dev_register;

	ret = request_threaded_irq(info->irq_headset, NULL, pm860x_headset_handler,
				   IRQF_ONESHOT, "headset", info);
	if (ret < 0) {
		dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
			info->irq_headset, ret);
		goto out_irq_headset;
	}
	ret = request_threaded_irq(info->irq_hook, NULL, pm860x_headset_handler,
				   IRQF_ONESHOT, "hook", info);
	if (ret < 0) {
		dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
			info->irq_hook, ret);
		goto out_irq_hook;
	}

	platform_set_drvdata(pdev, info);

	/* set hook detection debounce time to 24ms, it's the best setting we experienced */
	pm860x_set_bits(info->i2c, PM8607_HEADSET_DECTION, PM8607_HEADSET_BTN_DBNC, 0x10);

	//pm860x_set_bits(info->i2c, PM8607_HEADSET_DECTION, PM8607_HEADSET_PERIOD, 0x04);
	
	/* set headset period to continuous detection */
	pm860x_set_bits(info->i2c, PM8607_HEADSET_DECTION, PM8607_HEADSET_PERIOD, 0x06);

	/* set MIC detection parameter: MIC period set to 250msec */
	pm860x_reg_write(info->i2c, PM8607_MIC_DECTION, 0xDC);

	/* mask hook interrupt since we don't want the first false hook press down detection
	when inserting a headset without Mic */
	pm860x_set_bits(info->i2c, PM8607_INT_MASK_3, PM8607_INT_EN_HOOK, 0);

	/* enable headset detection */
	pm860x_set_bits(info->i2c, PM8607_HEADSET_DECTION, PM8607_HEADSET_EN_HS_DET, 1);

	INIT_WORK(&info->work_headset, headset_switch_work);
	INIT_WORK(&info->work_hook, hook_switch_work);

	/* Perform initial detection */
	headset_switch_work(&info->work_headset);
	hook_switch_work(&info->work_hook);

	return 0;

err_switch_dev_register:
	kfree(switch_data_headset);
	kfree(switch_data_hook);

out_irq_hook:
	free_irq(info->irq_headset, info);
out_irq_headset:
	kfree(info);
	return ret;
}
Exemplo n.º 20
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);
}
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;
}
Exemplo n.º 22
0
static int pm860x_led_blink_handler_thread(void *d)
{
	struct pm860x_led *led =(struct pm860x_led *)d;
	struct task_struct *tsk = current;
	struct sched_param param = { .sched_priority = 2 };
	
	DEFINE_WAIT(led_blink_wait);
	/* set up thread context */

	daemonize("pm860x_led_blink_handler_thread");

	/* improve pm860x_led_blink_handler_thread priority */
	sched_setscheduler(tsk, SCHED_FIFO, &param);
	
	//for(;;)
	while(1)
    {
		if (0 == led->blink_time) {
			prepare_to_wait(&led->blink_wait_queue, &led_blink_wait, TASK_INTERRUPTIBLE);

			if (0 == led->blink_time)
				schedule();

			finish_wait(&led->blink_wait_queue, &led_blink_wait);
		}
		try_to_freeze();
		
		if(led->color_green_blink_on && led->color_green_blink_off)
		{
			if (led->iset) {
				pm860x_set_bits(led->i2c, led->color_green_port,
					LED_CURRENT_MASK, led->iset);
			}
			msleep(led->color_green_blink_on);
			pm860x_set_bits(led->i2c, led->color_green_port,
					LED_CURRENT_MASK, 0);
			msleep(led->color_green_blink_off);
		}
		else
		{
			if (led->iset) {
				pm860x_set_bits(led->i2c, led->color_green_port,
					LED_CURRENT_MASK, led->iset);
			}
			led->blink_time = 0;
		}
		
		
    }

	return 0;
}

static void pm860x_led_work(struct work_struct *work)
{
	struct pm860x_led *led;
	struct pm860x_chip *chip;
	int mask;

	led = container_of(work, struct pm860x_led, work);
	chip = led->chip;
	mutex_lock(&led->lock);
	if ((led->current_brightness == 0) && led->brightness) {
		if (led->iset) {
			pm860x_set_bits(led->i2c, __led_off(led->port),
					LED_CURRENT_MASK, led->iset);
		}
		pm860x_set_bits(led->i2c, __blink_off(led->port),
				LED_BLINK_MASK, LED_ON_CONTINUOUS);
		mask = __blink_ctl_mask(led->port);
		pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, mask);
	} else if (led->brightness == 0) {
		pm860x_set_bits(led->i2c, __led_off(led->port),
				LED_CURRENT_MASK, 0);
		mask = __blink_ctl_mask(led->port);
		//pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, 0);
	}
	pm860x_set_bits(led->i2c, __led_off(led->port), LED_PWM_MASK,
			led->brightness);
	led->current_brightness = led->brightness;
	dev_dbg(chip->dev, "Update LED. (reg:%d, brightness:%d)\n",
		__led_off(led->port), led->brightness);
	mutex_unlock(&led->lock);
}

static int pm860x_blink_set(struct led_classdev *cdev,
			   unsigned long *delay_on, unsigned long *delay_off)
{
	struct pm860x_led *data = container_of(cdev, struct pm860x_led, cdev);

	return 1;
	switch (data->port)
	{
		case PM8606_LED1_GREEN:
			data->color_green_blink_on = *delay_on;
			data->color_green_blink_off = *delay_off;
			data->color_green_port = __led_off(data->port);
			break;
		default:
			return 1;
	}
	data->blink_time = 1;
	wake_up_interruptible(&data->blink_wait_queue);
	return 1;
}

static void pm860x_led_set(struct led_classdev *cdev,
			   enum led_brightness value)
{
	struct pm860x_led *data = container_of(cdev, struct pm860x_led, cdev);

	data->brightness = value >> 3;

	if(cdev->flags & LED_SUSPENDED)
	{
		pm860x_led_suspend(data);
		//return;
	}
	schedule_work(&data->work);
}

static int __check_device(struct pm860x_led_pdata *pdata, char *name)
{
	struct pm860x_led_pdata *p = pdata;
	int ret = -EINVAL;

	while (p && p->id) {
		if ((p->id != PM8606_ID_LED) || (p->flags < 0))
			break;

		if (!strncmp(name, pm860x_led_name[p->flags],
			MFD_NAME_SIZE)) {
			ret = (int)p->flags;
			break;
		}
		p++;
	}
	return ret;
}

static int pm860x_led_probe(struct platform_device *pdev)
{
	struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
	struct pm860x_platform_data *pm860x_pdata;
	struct pm860x_led_pdata *pdata;
	struct pm860x_led *data;
	struct resource *res;
	int ret;

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

	if (pdev->dev.parent->platform_data) {
		pm860x_pdata = pdev->dev.parent->platform_data;
		pdata = pm860x_pdata->led;
	} else {
		dev_err(&pdev->dev, "No platform data!\n");
		return -EINVAL;
	}

	data = kzalloc(sizeof(struct pm860x_led), GFP_KERNEL);
	if (data == NULL)
		return -ENOMEM;
	strncpy(data->name, res->name, MFD_NAME_SIZE-1);
	dev_set_drvdata(&pdev->dev, data);
	data->chip = chip;
	data->i2c = (chip->id == CHIP_PM8606) ? chip->client : chip->companion;
	data->iset = pdata->iset;
	data->port = __check_device(pdata, data->name);
	if (data->port < 0) {
		ret = -EINVAL;
		goto out;
	}

	data->current_brightness = 0;
	data->cdev.name = data->name;
	data->cdev.brightness_set = pm860x_led_set;
	data->cdev.blink_set = pm860x_blink_set;	
	mutex_init(&data->lock);
	INIT_WORK(&data->work, pm860x_led_work);

	init_waitqueue_head(&data->blink_wait_queue);
	kernel_thread(pm860x_led_blink_handler_thread, data, 0);
	data->blink_time = 0;
	
	ret = led_classdev_register(chip->dev, &data->cdev);
	if (ret < 0) {
		dev_err(&pdev->dev, "Failed to register LED: %d\n", ret);
		goto out;
	}
	return 0;
out:
	kfree(data);
	return ret;
}

static int pm860x_led_remove(struct platform_device *pdev)
{
	struct pm860x_led *data = platform_get_drvdata(pdev);

	led_classdev_unregister(&data->cdev);
	kfree(data);

	return 0;
}

static struct platform_driver pm860x_led_driver = {
	.driver	= {
		.name	= "88pm860x-led",
		.owner	= THIS_MODULE,
	},
	.probe	= pm860x_led_probe,
	.remove	= pm860x_led_remove,
};