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; }
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); }
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; }
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);
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; }
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; }
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; }
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 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); }