static void bq2419x_work_thread(struct kthread_work *work) { struct bq2419x_chip *bq2419x = container_of(work, struct bq2419x_chip, bq_wdt_work); int ret; for (;;) { if (bq2419x->stop_thread) return; if (bq2419x->chg_restart_timeout) { mutex_lock(&bq2419x->mutex); bq2419x->chg_restart_timeout--; if (!bq2419x->chg_restart_timeout) { ret = bq2419x_charger_enable(bq2419x); if (ret < 0) dev_err(bq2419x->dev, "Charger enable failed %d", ret); } if (bq2419x->suspended) bq2419x->chg_restart_timeout = 0; mutex_unlock(&bq2419x->mutex); } ret = bq2419x_reset_wdt(bq2419x, "THREAD"); if (ret < 0) dev_err(bq2419x->dev, "bq2419x_reset_wdt failed: %d\n", ret); msleep(bq2419x->wdt_refresh_timeout * 1000); } }
static void bq2419x_work_thread(struct kthread_work *work) { struct bq2419x_chip *bq2419x = container_of(work, struct bq2419x_chip, bq_wdt_work); int ret; int val = 0; for (;;) { if (bq2419x->stop_thread) return; if (bq2419x->chg_restart_timeout) { mutex_lock(&bq2419x->mutex); bq2419x->chg_restart_timeout--; if (!bq2419x->chg_restart_timeout) { ret = bq2419x_charger_enable(bq2419x); if (ret < 0) dev_err(bq2419x->dev, "Charger enable failed %d", ret); ret = regmap_read(bq2419x->regmap, BQ2419X_SYS_STAT_REG, &val); if (ret < 0) dev_err(bq2419x->dev, "SYS_STAT_REG read failed %d\n", ret); /* * Update Charging status based on STAT register */ if ((val & BQ2419x_CHRG_STATE_MASK) == BQ2419x_CHRG_STATE_NOTCHARGING) { bq2419x->status = 0; if (bq2419x->update_status) bq2419x->update_status (bq2419x->status, 0); bq2419x->chg_restart_timeout = bq2419x->chg_restart_time / bq2419x->wdt_refresh_timeout; } else { bq2419x->status = 1; if (bq2419x->update_status) bq2419x->update_status (bq2419x->status, 0); } } if (bq2419x->suspended) bq2419x->chg_restart_timeout = 0; mutex_unlock(&bq2419x->mutex); } ret = bq2419x_reset_wdt(bq2419x, "THREAD"); if (ret < 0) dev_err(bq2419x->dev, "bq2419x_reset_wdt failed: %d\n", ret); msleep(bq2419x->wdt_refresh_timeout * 1000); } }
static int bq2419x_vbus_disable(struct regulator_dev *rdev) { struct bq2419x_chip *bq2419x = rdev_get_drvdata(rdev); int ret; dev_info(bq2419x->dev, "VBUS disabled, charging enabled\n"); ret = bq2419x_charger_enable(bq2419x); if (ret < 0) { dev_err(bq2419x->dev, "Charger enable failed %d", ret); return ret; } return ret; }
static int bq2419x_vbus_disable(struct regulator_dev *rdev) { struct bq2419x_chip *bq2419x = rdev_get_drvdata(rdev); int ret; dev_info(bq2419x->dev, "VBUS disabled, charging enabled\n"); mutex_lock(&bq2419x->otg_mutex); bq2419x->is_otg_connected = false; ret = bq2419x_charger_enable(bq2419x); if (ret < 0) dev_err(bq2419x->dev, "Charger enable failed %d", ret); mutex_unlock(&bq2419x->otg_mutex); return ret; }
static void bq2419x_shutdown(struct i2c_client *client) { int ret = 0; struct bq2419x_chip *bq2419x = i2c_get_clientdata(client); int alarm_time = bq2419x->rtc_alarm_time; if (bq2419x->irq) disable_irq(bq2419x->irq); if (!bq2419x->rtc) bq2419x->rtc = alarmtimer_get_rtcdev(); if (bq2419x->in_current_limit > 500) { dev_info(bq2419x->dev, "HighCurrent %dmA charger is connectd\n", bq2419x->in_current_limit); ret = bq2419x_reset_wdt(bq2419x, "shutdown"); if (ret < 0) dev_err(bq2419x->dev, "bq2419x_reset_wdt failed: %d\n", ret); alarm_time = 20; } mutex_lock(&bq2419x->mutex); bq2419x->suspended = 1; mutex_unlock(&bq2419x->mutex); ret = bq2419x_charger_enable(bq2419x); if (ret < 0) dev_err(bq2419x->dev, "Charger enable failed %d", ret); if (bq2419x->in_current_limit <= 500) { /* Configure charging current to 500mA */ ret = regmap_write(bq2419x->regmap, BQ2419X_INPUT_SRC_REG, 0x32); if (ret < 0) dev_err(bq2419x->dev, "INPUT_SRC_REG write failed %d\n", ret); } ret = bq2419x_wakealarm(bq2419x, alarm_time); if (ret < 0) dev_err(bq2419x->dev, "RTC wake alarm config failed %d\n", ret); cancel_delayed_work_sync(&bq2419x->otg_reset_work); }
static int bq2419x_set_charging_current(struct regulator_dev *rdev, int min_uA, int max_uA) { struct bq2419x_chip *bq_charger = rdev_get_drvdata(rdev); int ret = 0; int val; dev_info(bq_charger->dev, "Setting charging current %d\n", max_uA/1000); msleep(200); bq_charger->status = 0; ret = bq2419x_charger_enable(bq_charger); if (ret < 0) { dev_err(bq_charger->dev, "Charger enable failed %d", ret); return ret; } ret = regmap_read(bq_charger->regmap, BQ2419X_SYS_STAT_REG, &val); if (ret < 0) dev_err(bq_charger->dev, "error reading reg: 0x%x\n", BQ2419X_SYS_STAT_REG); if (max_uA == 0 && val != 0) return ret; bq_charger->in_current_limit = max_uA/1000; if ((val & BQ2419x_VBUS_STAT) == BQ2419x_VBUS_UNKNOWN) { bq_charger->in_current_limit = 500; bq_charger->status = 0; } else { bq_charger->status = 1; } ret = bq2419x_init(bq_charger); if (ret < 0) goto error; if (bq_charger->update_status) bq_charger->update_status(bq_charger->status, 0); return 0; error: dev_err(bq_charger->dev, "Charger enable failed, err = %d\n", ret); return ret; }
static int bq2419x_suspend(struct device *dev) { int ret = 0; struct bq2419x_chip *bq2419x = dev_get_drvdata(dev); mutex_lock(&bq2419x->mutex); bq2419x->suspended = 1; mutex_unlock(&bq2419x->mutex); disable_irq(bq2419x->irq); ret = bq2419x_charger_enable(bq2419x); if (ret < 0) { dev_err(bq2419x->dev, "Charger enable failed %d", ret); return ret; } /* Configure charging current to 500mA */ ret = regmap_write(bq2419x->regmap, BQ2419X_INPUT_SRC_REG, 0x32); if (ret < 0) { dev_err(bq2419x->dev, "INPUT_SRC_REG write failed %d\n", ret); return ret; } return 0; }
static int __devinit bq2419x_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct bq2419x_chip *bq2419x; struct bq2419x_platform_data *pdata; int ret = 0; pdata = client->dev.platform_data; if (!pdata) { dev_err(&client->dev, "No Platform data"); return -EINVAL; } bq2419x = devm_kzalloc(&client->dev, sizeof(*bq2419x), GFP_KERNEL); if (!bq2419x) { dev_err(&client->dev, "Memory allocation failed\n"); return -ENOMEM; } bq2419x->regmap = devm_regmap_init_i2c(client, &bq2419x_regmap_config); if (IS_ERR(bq2419x->regmap)) { ret = PTR_ERR(bq2419x->regmap); dev_err(&client->dev, "regmap init failed with err %d\n", ret); return ret; } bq2419x->dev = &client->dev; if (pdata->bcharger_pdata) { bq2419x->update_status = pdata->bcharger_pdata->update_status; bq2419x->rtc_alarm_time = pdata->bcharger_pdata->rtc_alarm_time; bq2419x->wdt_time_sec = pdata->bcharger_pdata->wdt_timeout; bq2419x->chg_restart_time = pdata->bcharger_pdata->chg_restart_time; bq2419x->chg_enable = true; } bq2419x->wdt_refresh_timeout = 25; i2c_set_clientdata(client, bq2419x); bq2419x->irq = client->irq; if (bq2419x->rtc_alarm_time) bq2419x->rtc = alarmtimer_get_rtcdev(); mutex_init(&bq2419x->mutex); bq2419x->suspended = 0; bq2419x->chg_restart_timeout = 0; ret = bq2419x_show_chip_version(bq2419x); if (ret < 0) { dev_err(&client->dev, "version read failed %d\n", ret); return ret; } ret = bq2419x_charger_init(bq2419x); if (ret < 0) { dev_err(bq2419x->dev, "Charger init failed: %d\n", ret); return ret; } ret = bq2419x_init_charger_regulator(bq2419x, pdata); if (ret < 0) { dev_err(&client->dev, "Charger regualtor init failed %d\n", ret); return ret; } ret = bq2419x_init_vbus_regulator(bq2419x, pdata); if (ret < 0) { dev_err(&client->dev, "VBUS regualtor init failed %d\n", ret); goto scrub_chg_reg; } init_kthread_worker(&bq2419x->bq_kworker); bq2419x->bq_kworker_task = kthread_run(kthread_worker_fn, &bq2419x->bq_kworker, dev_name(bq2419x->dev)); if (IS_ERR(bq2419x->bq_kworker_task)) { ret = PTR_ERR(bq2419x->bq_kworker_task); dev_err(&client->dev, "Kworker task creation failed %d\n", ret); goto scrub_vbus_reg; } init_kthread_work(&bq2419x->bq_wdt_work, bq2419x_work_thread); sched_setscheduler(bq2419x->bq_kworker_task, SCHED_FIFO, &bq2419x_param); queue_kthread_work(&bq2419x->bq_kworker, &bq2419x->bq_wdt_work); ret = bq2419x_watchdog_init(bq2419x, bq2419x->wdt_time_sec, "PROBE"); if (ret < 0) { dev_err(bq2419x->dev, "BQWDT init failed %d\n", ret); goto scrub_kthread; } ret = bq2419x_fault_clear_sts(bq2419x); if (ret < 0) { dev_err(bq2419x->dev, "fault clear status failed %d\n", ret); goto scrub_kthread; } ret = request_threaded_irq(bq2419x->irq, NULL, bq2419x_irq, IRQF_TRIGGER_FALLING, dev_name(bq2419x->dev), bq2419x); if (ret < 0) { dev_err(bq2419x->dev, "request IRQ %d fail, err = %d\n", bq2419x->irq, ret); goto scrub_kthread; } /* enable charging */ ret = bq2419x_charger_enable(bq2419x); if (ret < 0) goto scrub_irq; return 0; scrub_irq: free_irq(bq2419x->irq, bq2419x); scrub_kthread: bq2419x->stop_thread = true; flush_kthread_worker(&bq2419x->bq_kworker); kthread_stop(bq2419x->bq_kworker_task); scrub_vbus_reg: regulator_unregister(bq2419x->vbus_rdev); scrub_chg_reg: regulator_unregister(bq2419x->chg_rdev); mutex_destroy(&bq2419x->mutex); return ret; }
static int bq2419x_init_vbus_regulator(struct bq2419x_chip *bq2419x, struct bq2419x_platform_data *pdata) { int ret = 0; if (!pdata->vbus_pdata) { dev_err(bq2419x->dev, "No vbus platform data\n"); return 0; } bq2419x->gpio_otg_iusb = pdata->vbus_pdata->gpio_otg_iusb; bq2419x->vbus_reg_desc.name = "bq2419x-vbus"; bq2419x->vbus_reg_desc.ops = &bq2419x_vbus_ops; bq2419x->vbus_reg_desc.type = REGULATOR_VOLTAGE; bq2419x->vbus_reg_desc.owner = THIS_MODULE; bq2419x->vbus_reg_init_data.supply_regulator = NULL; bq2419x->vbus_reg_init_data.regulator_init = NULL; bq2419x->vbus_reg_init_data.num_consumer_supplies = pdata->vbus_pdata->num_consumer_supplies; bq2419x->vbus_reg_init_data.consumer_supplies = pdata->vbus_pdata->consumer_supplies; bq2419x->vbus_reg_init_data.driver_data = bq2419x; bq2419x->vbus_reg_init_data.constraints.name = "bq2419x-vbus"; bq2419x->vbus_reg_init_data.constraints.min_uV = 0; bq2419x->vbus_reg_init_data.constraints.max_uV = 5000000, bq2419x->vbus_reg_init_data.constraints.valid_modes_mask = REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY; bq2419x->vbus_reg_init_data.constraints.valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE; if (gpio_is_valid(bq2419x->gpio_otg_iusb)) { ret = gpio_request_one(bq2419x->gpio_otg_iusb, GPIOF_OUT_INIT_HIGH, dev_name(bq2419x->dev)); if (ret < 0) { dev_err(bq2419x->dev, "gpio request failed %d\n", ret); return ret; } } /* Register the regulators */ bq2419x->vbus_rdev = regulator_register(&bq2419x->vbus_reg_desc, bq2419x->dev, &bq2419x->vbus_reg_init_data, bq2419x, NULL); if (IS_ERR(bq2419x->vbus_rdev)) { ret = PTR_ERR(bq2419x->vbus_rdev); dev_err(bq2419x->dev, "VBUS regulator register failed %d\n", ret); goto scrub; } /* Disable the VBUS regulator and enable charging */ ret = bq2419x_charger_enable(bq2419x); if (ret < 0) { dev_err(bq2419x->dev, "Charging enable failed %d", ret); goto scrub_reg; } return ret; scrub_reg: regulator_unregister(bq2419x->vbus_rdev); bq2419x->vbus_rdev = NULL; scrub: if (gpio_is_valid(bq2419x->gpio_otg_iusb)) gpio_free(bq2419x->gpio_otg_iusb); return ret; }
static irqreturn_t bq2419x_irq(int irq, void *data) { struct bq2419x_chip *bq2419x = data; int ret; unsigned int val; int check_chg_state = 0; int type; ret = regmap_read(bq2419x->regmap, BQ2419X_FAULT_REG, &val); if (ret < 0) { dev_err(bq2419x->dev, "FAULT_REG read failed %d\n", ret); return ret; } dev_info(bq2419x->dev, "%s() Irq %d status 0x%02x\n", __func__, irq, val); if (val & BQ2419x_FAULT_WATCHDOG_FAULT) { dev_err(bq2419x->dev, "Charging Fault: Watchdog Timer Expired\n"); ret = bq2419x_watchdog_init(bq2419x, bq2419x->wdt_time_sec, "ISR"); if (ret < 0) { dev_err(bq2419x->dev, "BQWDT init failed %d\n", ret); return ret; } ret = bq2419x_charger_init(bq2419x); if (ret < 0) { dev_err(bq2419x->dev, "Charger init failed: %d\n", ret); return ret; } ret = bq2419x_init(bq2419x); if (ret < 0) { dev_err(bq2419x->dev, "bq2419x init failed: %d\n", ret); return ret; } } if (val & BQ2419x_FAULT_BOOST_FAULT) { dev_err(bq2419x->dev, "Charging Fault: VBUS Overloaded\n"); mutex_lock(&bq2419x->otg_mutex); if (bq2419x->is_otg_connected) { ret = bq2419x_charger_enable(bq2419x); if (ret < 0) { dev_err(bq2419x->dev, "Charger enable failed %d", ret); return ret; } schedule_delayed_work(&bq2419x->otg_reset_work, BQ2419x_OTG_ENABLE_TIME); } mutex_unlock(&bq2419x->otg_mutex); } switch (val & BQ2419x_FAULT_CHRG_FAULT_MASK) { case BQ2419x_FAULT_CHRG_INPUT: dev_err(bq2419x->dev, "Charging Fault: " "Input Fault (VBUS OVP or VBAT<VBUS<3.8V)\n"); break; case BQ2419x_FAULT_CHRG_THERMAL: dev_err(bq2419x->dev, "Charging Fault: Thermal shutdown\n"); check_chg_state = 1; mutex_lock(&bq2419x->otg_mutex); if (bq2419x->is_otg_connected) { ret = bq2419x_charger_enable(bq2419x); if (ret < 0) { dev_err(bq2419x->dev, "Charger enable failed %d", ret); return ret; } schedule_delayed_work(&bq2419x->otg_reset_work, BQ2419x_OTG_ENABLE_TIME); } mutex_unlock(&bq2419x->otg_mutex); break; case BQ2419x_FAULT_CHRG_SAFTY: dev_err(bq2419x->dev, "Charging Fault: Safety timer expiration\n"); bq2419x->chg_restart_timeout = bq2419x->chg_restart_time / bq2419x->wdt_refresh_timeout; ret = bq2419x_reset_safety_timer(bq2419x); if (ret < 0) { dev_err(bq2419x->dev, "Reset safety timer failed %d\n", ret); return ret; } check_chg_state = 1; break; default: break; } if (val & BQ2419x_FAULT_NTC_FAULT) { dev_err(bq2419x->dev, "Charging Fault: NTC fault %d\n", val & BQ2419x_FAULT_NTC_FAULT); check_chg_state = 1; } ret = bq2419x_fault_clear_sts(bq2419x); if (ret < 0) { dev_err(bq2419x->dev, "fault clear status failed %d\n", ret); return ret; } ret = regmap_read(bq2419x->regmap, BQ2419X_SYS_STAT_REG, &val); if (ret < 0) { dev_err(bq2419x->dev, "SYS_STAT_REG read failed %d\n", ret); return ret; } if ((val & BQ2419x_CHRG_STATE_MASK) == BQ2419x_CHRG_STATE_CHARGE_DONE) { bq2419x->chg_restart_timeout = bq2419x->chg_restart_time / bq2419x->wdt_refresh_timeout; dev_info(bq2419x->dev, "Charging completed\n"); bq2419x->status = 4; if (bq2419x->update_status) bq2419x->update_status (bq2419x->status, 2); } /* * Update Charging status based on STAT register */ if (check_chg_state) { if ((val & BQ2419x_CHRG_STATE_MASK) == BQ2419x_CHRG_STATE_NOTCHARGING) { type = bq2419x->ac_online ? 1 : 2; bq2419x->status = 0; if (bq2419x->update_status) bq2419x->update_status (bq2419x->status, type); } } return IRQ_HANDLED; }
static int bq2419x_set_charging_current(struct regulator_dev *rdev, int min_uA, int max_uA) { struct bq2419x_chip *bq_charger = rdev_get_drvdata(rdev); int ret = 0; int val; dev_info(bq_charger->dev, "Setting charging current %d\n", max_uA/1000); msleep(200); bq_charger->usb_online = 0; bq_charger->ac_online = 0; bq_charger->status = 0; ret = bq2419x_charger_enable(bq_charger); if (ret < 0) { dev_err(bq_charger->dev, "Charger enable failed %d", ret); return ret; } ret = regmap_read(bq_charger->regmap, BQ2419X_SYS_STAT_REG, &val); if (ret < 0) dev_err(bq_charger->dev, "error reading reg: 0x%x\n", BQ2419X_SYS_STAT_REG); if (max_uA == 0 && val != 0) return ret; bq_charger->in_current_limit = max_uA/1000; if ((val & BQ2419x_VBUS_STAT) == BQ2419x_VBUS_UNKNOWN) { bq_charger->status = 0; bq_charger->usb_online = 0; bq_charger->in_current_limit = 500; ret = bq2419x_init(bq_charger); if (ret < 0) goto error; if (bq_charger->update_status) bq_charger->update_status (bq_charger->status, 0); } else if (bq_charger->in_current_limit == 500) { bq_charger->status = 1; bq_charger->usb_online = 1; ret = bq2419x_init(bq_charger); if (ret < 0) goto error; if (bq_charger->update_status) bq_charger->update_status (bq_charger->status, 2); } else if (bq_charger->in_current_limit > 500) { bq_charger->status = 1; bq_charger->ac_online = 1; ret = bq2419x_init(bq_charger); if (ret < 0) goto error; if (bq_charger->update_status) bq_charger->update_status (bq_charger->status, 1); } if (bq_charger->ac_online) { if ((bq_charger->in_current_limit == 1500)) bq_charger->ac.type = POWER_SUPPLY_TYPE_USB_CDP; else bq_charger->ac.type = POWER_SUPPLY_TYPE_MAINS; } if (ret == 0) { if (bq_charger->use_mains) power_supply_changed(&bq_charger->ac); if (bq_charger->use_usb) power_supply_changed(&bq_charger->usb); } return 0; error: dev_err(bq_charger->dev, "Charger enable failed, err = %d\n", ret); return ret; }