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; }
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; }
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); }
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); }
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; }
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); }
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); }
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; }
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; }
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); }
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; }
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; }
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, ¶m); //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, };