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);
	}
}
예제 #2
0
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);
	}
}
예제 #3
0
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);
}
예제 #6
0
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;
}
예제 #7
0
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;
}
예제 #8
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;
}
예제 #9
0
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;
}