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_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 void set_vbatt_threshold(struct pm860x_charger_info *info, int min, int max) { int data; /* (tmp << 8) * 3 / 1800 */ if (min <= 0) data = 0; else data = (min << 5) / 675; pm860x_reg_write(info->i2c, PM8607_VBAT_LOWTH, data); dev_dbg(info->dev, "VBAT Min:%dmv, LOWTH:0x%x\n", min, data); if (max <= 0) data = 0xff; else data = (max << 5) / 675; pm860x_reg_write(info->i2c, PM8607_VBAT_HIGHTH, data); dev_dbg(info->dev, "VBAT Max:%dmv, HIGHTH:0x%x\n", max, data); }
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 set_vchg_threshold(struct pm860x_charger_info *info, int min, int max) { int data; /* (tmp << 8) * / 5 / 1800 */ if (min <= 0) data = 0; else data = (min << 5) / 1125; pm860x_reg_write(info->i2c, PM8607_VCHG_LOWTH, data); dev_dbg(info->dev, "VCHG_LOWTH:%dmv, 0x%x\n", min, data); if (max <= 0) data = 0xff; else data = (max << 5) / 1125; pm860x_reg_write(info->i2c, PM8607_VCHG_HIGHTH, data); dev_dbg(info->dev, "VCHG_HIGHTH:%dmv, 0x%x\n", max, data); }
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; }
/* SAMSUNG_PPS: i2c can send only 8 bit */ int pm860x_set_general_user(unsigned char data) { int ret; struct pm860x_charger_info *info = ginfo; ret = pm860x_reg_write(info->i2c, PM8607_GENERAL_USE, data); if (ret < 0) printk(KERN_ALERT \ "pm860x_set_general_user[0x%x]->fail!!!\n", data); else pr_info( \ "[%s][%s]data[0x%x]\n", __FILE__, __func__, data); return ret ; } EXPORT_SYMBOL(pm860x_set_general_user);
static ssize_t pm860x_power_proc_write(struct file *filp, const char *buff, size_t len, loff_t * off) { u8 reg_val; char messages[256], vol[256]; int value = 0; u8 temp1,temp2,temp3; struct pm860x_charger_info *info = ginfo; if (len > 256) len = 256; if (copy_from_user(messages, buff, len)) return -EFAULT; if ('-' == messages[0]) { /* set the register index */ memcpy(vol, messages + 1, len - 1); index = (int) simple_strtoul(vol, NULL, 16); printk("index=0x%x\n", index); } else if('d' == messages[0]){ temp1 = pm860x_page_reg_read(info->i2c,0xD4); temp2 = pm860x_page_reg_read(info->i2c,0xD5); temp3 = pm860x_page_reg_read(info->i2c,0xD7); value = temp1 + (temp2<<8) + (temp3<<16); printk("d4=0x%x, d5=0x%x,d7=0x%x value:0x%x,value:%d\n", temp1,temp2,temp3,value,value); } else { /* set the register value */ reg_val = (int) simple_strtoul(messages, NULL, 16); pm860x_reg_write(info->i2c, index, reg_val & 0xFF); } return len; }
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; }
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 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 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; }