/* Detects a suspend and clears all the previous wake up reasons*/
static int wakeup_reason_pm_event(struct notifier_block *notifier,
		unsigned long pm_event, void *unused)
{
	switch (pm_event) {
	case PM_SUSPEND_PREPARE:
		spin_lock(&resume_reason_lock);
		irqcount = 0;
		suspend_abort = false;
		mbox_wakeup = false;
		spin_unlock(&resume_reason_lock);
		/* monotonic time since boot */
		last_monotime = ktime_get();
		/* monotonic time since boot including the time spent in suspend */
		last_stime = ktime_get_boottime();
		break;
	case PM_POST_SUSPEND:
		/* monotonic time since boot */
		curr_monotime = ktime_get();
		/* monotonic time since boot including the time spent in suspend */
		curr_stime = ktime_get_boottime();
		break;
	default:
		break;
	}
	return NOTIFY_DONE;
}
static void xyref5260_lcd_on(void)
{
	s64 us = ktime_us_delta(lcd_on_time, ktime_get_boottime());

	if (us > LCD_POWER_OFF_TIME_US) {
		pr_warn("lcd on sleep time too long\n");
		us = LCD_POWER_OFF_TIME_US;
	}

	if (us > 0)
		usleep_range(us, us);

	s3c_gpio_setpull(EXYNOS5260_GPB2(0), S3C_GPIO_PULL_NONE);

	s3c_gpio_setpull(EXYNOS5260_GPD2(2), S3C_GPIO_PULL_NONE);
	gpio_request_one(EXYNOS5260_GPD2(2),
			GPIOF_OUT_INIT_HIGH, "GPD2");
	usleep_range(5000, 6000);
	gpio_free(EXYNOS5260_GPD2(2));

#ifndef CONFIG_BACKLIGHT_PWM
	s3c_gpio_setpull(EXYNOS5260_GPD2(1), S3C_GPIO_PULL_NONE);
	gpio_request_one(EXYNOS5260_GPD2(1),
			GPIOF_OUT_INIT_HIGH, "GPD2");
	usleep_range(5000, 6000);
	gpio_free(EXYNOS5260_GPD2(1));
#endif
}
static irqreturn_t sensordata_irq_thread_fn(int iIrq, void *dev_id)
{
	struct ssp_data *data = dev_id;
	struct timespec ts;

	ts = ktime_to_timespec(ktime_get_boottime());
	data->timestamp = ts.tv_sec * 1000000000ULL + ts.tv_nsec;

#if defined SSP_IRQ_EDGE_PROTECT
	if (prevent_irq != 0) {
		select_irq_msg(data);
		data->uIrqCnt++;
#if SSP_STATUS_MONITOR
		data->uSubIrqCnt++;
#endif
	} else
		pr_err("[SSP] %s, irq Occured while Boot.\n", __func__);
#else
	if (gpio_get_value(data->mcu_int1)) {
		pr_info("[SSP] MCU int HIGH");
		return IRQ_HANDLED;
	}
	select_irq_msg(data);
	data->uIrqCnt++;
#if SSP_STATUS_MONITOR
	data->uSubIrqCnt++;
#endif
#endif

	return IRQ_HANDLED;
}
static inline s64 yas_iio_get_boottime_ns(void)
{
	struct timespec ts;

	ts = ktime_to_timespec(ktime_get_boottime());

	return timespec_to_ns(&ts);
}
Example #5
0
u64 fimc_is_get_timestamp_boot(void)
{
	struct timespec curtime;

	curtime = ktime_to_timespec(ktime_get_boottime());

	return (u64)curtime.tv_sec*1000000000 + curtime.tv_nsec;
}
static s64 _get_current_time_sec(void)
{
	s64 msecs;
	ktime_t now;
	struct timeval tv;

	now = ktime_get_boottime();
	tv = ktime_to_timeval(now);
	return (s64) tv.tv_sec;
}
static irqreturn_t sensordata_irq_thread_fn(int iIrq, void *dev_id)
{
	struct ssp_data *data = dev_id;
	struct timespec ts;

	ts = ktime_to_timespec(ktime_get_boottime());
	data->timestamp = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
	select_irq_msg(data);
	data->uIrqCnt++;

	return IRQ_HANDLED;
}
static void manta_lcd_on(void)
{
	s64 us = ktime_us_delta(lcd_on_time, ktime_get_boottime());
	if (us > LCD_POWER_OFF_TIME_US) {
		pr_warn("lcd on sleep time too long\n");
		us = LCD_POWER_OFF_TIME_US;
	}

	if (us > 0)
		usleep_range(us, us);

	gpio_set_value(GPIO_LCD_EN, 1);
	usleep_range(200000, 200000);
}
static void xyref5260_lcd_off(void)
{
	gpio_request_one(EXYNOS5260_GPD2(2),
			GPIOF_OUT_INIT_LOW, "GPD2");
	gpio_free(EXYNOS5260_GPD2(2));
	usleep_range(5000, 6000);

#ifndef CONFIG_BACKLIGHT_PWM
	gpio_request_one(EXYNOS5260_GPD2(1),
			GPIOF_OUT_INIT_LOW, "GPD2");
	usleep_range(5000, 6000);
	gpio_free(EXYNOS5260_GPD2(1));
#endif
	lcd_on_time = ktime_add_us(ktime_get_boottime(), LCD_POWER_OFF_TIME_US);
}
Example #10
0
static irqreturn_t sensordata_irq_thread_fn(int iIrq, void *dev_id)
{
	struct ssp_data *data = dev_id;
	struct timespec ts;

	ts = ktime_to_timespec(ktime_get_boottime());
	data->timestamp = ts.tv_sec * 1000000000ULL + ts.tv_nsec;

	if (gpio_get_value(data->mcu_int1)) {
		ssp_info("MCU int HIGH");
		return IRQ_HANDLED;
	}
	select_irq_msg(data);
	data->uIrqCnt++;

	return IRQ_HANDLED;
}
static irqreturn_t yas_trigger_handler(int irq, void *p)
{
	struct iio_poll_func *pf = p;
	struct iio_dev *indio_dev = pf->indio_dev;
	struct iio_buffer *buffer = indio_dev->buffer;
	struct yas_state *st = iio_priv(indio_dev);
	int len = 0, i, j;
	size_t datasize = buffer->access->get_bytes_per_datum(buffer);
	int32_t *mag;
	struct timespec ts;
	s64 timestamp;

	mag = (int32_t *) kmalloc(datasize, GFP_KERNEL);
	if (mag == NULL)
		goto done;
	if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength)) {
		j = 0;
		for (i = 0; i < 3; i++) {
			if (test_bit(i, indio_dev->active_scan_mask)) {
				mag[j] = st->compass_data[i];
				j++;
			}
		}
		len = j * 4;
	}

	/* Guaranteed to be aligned with 8 byte boundary */
	//if (indio_dev->scan_timestamp)
	// *(s64 *)((u8 *)mag + ALIGN(len, sizeof(s64))) = pf->timestamp;

	ts = ktime_to_timespec(ktime_get_boottime());
	timestamp = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
	*(s64 *)((u8 *)mag + ALIGN(len, sizeof(s64))) = timestamp;
	if (timestamp <= 0)
		pr_err("[%s] invalid time = %lld\n", __func__, timestamp);
	iio_push_to_buffer(indio_dev->buffer, (u8 *)mag, 0);
	kfree(mag);
done:
	iio_trigger_notify_done(indio_dev->trig);
	return IRQ_HANDLED;
}
Example #12
0
static int instinctq_battery_probe(struct platform_device *pdev)
{
	struct s3c_adc_client	*client;
	struct instinctq_battery_pdata *pdata = pdev->dev.platform_data;
	struct instinctq_battery *bat;
	int ret, i, irq;

	/* Platform data is required */
	if (!pdata) {
		dev_err(&pdev->dev, "no platform data supplied\n");
		return -ENODEV;
	}

	/* Check GPIOs */
	if (!gpio_is_valid(pdata->gpio_pok)) {
		dev_err(&pdev->dev, "Invalid gpio pin for POK line\n");
		return -EINVAL;
	}

	if (!gpio_is_valid(pdata->gpio_chg)) {
		dev_err(&pdev->dev, "Invalid gpio pin for CHG line\n");
		return -EINVAL;
	}

	if (!gpio_is_valid(pdata->gpio_en)) {
		dev_err(&pdev->dev, "Invalid gpio pin for EN line\n");
		return -EINVAL;
	}

	if (!pdata->supply_detect_init) {
		dev_err(&pdev->dev, "Supply detection is required\n");
		return -EINVAL;
	}

	/* Register ADC client */
	client = s3c_adc_register(pdev, NULL, NULL, 0);
	if (IS_ERR(client)) {
		dev_err(&pdev->dev, "could not register adc\n");
		return PTR_ERR(client);
	}

	/* Allocate driver data */
	bat = kzalloc(sizeof(struct instinctq_battery), GFP_KERNEL);
	if (!bat) {
		dev_err(&pdev->dev, "could not allocate driver data\n");
		ret = -ENOMEM;
		goto err_free_adc;
	}

	/* Claim and setup GPIOs */
	ret = gpio_request(pdata->gpio_pok, dev_name(&pdev->dev));
	if (ret) {
		dev_err(&pdev->dev, "Failed to request POK pin: %d\n", ret);
		goto err_free;
	}

	ret = gpio_direction_input(pdata->gpio_pok);
	if (ret) {
		dev_err(&pdev->dev, "Failed to set POK to input: %d\n", ret);
		goto err_gpio_pok_free;
	}

	ret = gpio_request(pdata->gpio_chg, dev_name(&pdev->dev));
	if (ret) {
		dev_err(&pdev->dev, "Failed to request CHG pin: %d\n", ret);
		goto err_gpio_pok_free;
	}

	ret = gpio_direction_input(pdata->gpio_chg);
	if (ret) {
		dev_err(&pdev->dev, "Failed to set CHG to input: %d\n", ret);
		goto err_gpio_chg_free;
	}

	ret = gpio_request(pdata->gpio_en, dev_name(&pdev->dev));
	if (ret) {
		dev_err(&pdev->dev, "Failed to request EN pin: %d\n", ret);
		goto err_gpio_chg_free;
	}

	ret = gpio_direction_output(pdata->gpio_en, pdata->gpio_en_inverted);
	if (ret) {
		dev_err(&pdev->dev, "Failed to set EN to output: %d\n", ret);
		goto err_gpio_en_free;
	}

	platform_set_drvdata(pdev, bat);

	bat->dev = &pdev->dev;
	bat->client = client;
	bat->pdata = pdata;
	bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
	bat->health = POWER_SUPPLY_HEALTH_GOOD;
	bat->supply = instinctq_BATTERY_NONE;
	bat->interval = BAT_POLL_INTERVAL;
	bat->calibration = pdata->calibration;

	ret = create_lookup_table(pdata->percent_lut,
				pdata->percent_lut_cnt, &bat->percent_lookup);
	if (ret) {
		dev_err(&pdev->dev, "could not get create percentage lookup table");
		goto err_gpio_en_free;
	}

	ret = create_lookup_table(pdata->volt_lut,
				pdata->volt_lut_cnt, &bat->volt_lookup);
	if (ret) {
		dev_err(&pdev->dev, "could not get create voltage lookup table");
		goto err_percent_free;
	}

	ret = create_lookup_table(pdata->temp_lut,
				pdata->temp_lut_cnt, &bat->temp_lookup);
	if (ret) {
		dev_err(&pdev->dev, "could not get create temperature lookup table");
		goto err_volt_free;
	}

	INIT_WORK(&bat->work, instinctq_battery_work);
	INIT_DELAYED_WORK(&bat->poll_work, instinctq_battery_poll);
	mutex_init(&bat->mutex);
#ifdef CONFIG_HAS_WAKELOCK
	wake_lock_init(&bat->wakelock, WAKE_LOCK_SUSPEND, "battery");
	wake_lock_init(&bat->chg_wakelock, WAKE_LOCK_SUSPEND, "charger");
	wake_lock_init(&bat->fault_wakelock,
					WAKE_LOCK_SUSPEND, "battery fault");
	wake_lock_init(&bat->suspend_lock, WAKE_LOCK_SUSPEND, "suspend_lock");
#endif
#ifdef CONFIG_RTC_INTF_ALARM
	alarm_init(&bat->alarm, ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
							instinctq_battery_alarm);
#endif
	/* Get some initial data for averaging */
	for (i = 0; i < NUM_SAMPLES; ++i) {
		int sample;
		/* Get a voltage sample from the ADC */
		sample = instinctq_battery_adc_read(bat->client, bat->pdata->volt_channel);
		if (sample < 0) {
			dev_warn(&pdev->dev, "Failed to get ADC sample.\n");
			continue;
		}
		sample += bat->compensation;
		bat->vol_adc = sample;
		/* Put the sample and get the new average */
		bat->volt_value = put_sample_get_avg(&bat->volt_avg, sample);
		/* Get a temperature sample from the ADC */
		sample = instinctq_battery_adc_read(bat->client, bat->pdata->temp_channel);
		if (sample < 0) {
			dev_warn(&pdev->dev, "Failed to get ADC sample.\n");
			continue;
		}
		bat->temp_adc = sample;
		/* Put the sample and get the new average */
		bat->temp_value = put_sample_get_avg(&bat->temp_avg, sample);
	}

	bat->percent_value = lookup_value(&bat->percent_lookup, bat->volt_value);
	bat->volt_value = lookup_value(&bat->volt_lookup, bat->volt_value);
	bat->temp_value = lookup_value(&bat->temp_lookup, bat->temp_value);
	bat->last_sample = ktime_get_boottime();

	/* Register the power supplies */
	for (i = 0; i < instinctq_BATTERY_NUM; ++i) {
		bat->psy[i] = instinctq_chg_templates[i];
		ret = power_supply_register(&pdev->dev, &bat->psy[i]);
		if (ret < 0) {
			dev_err(&pdev->dev,
				"Failed to register power supply %s (%d)\n",
							bat->psy[i].name, ret);
			break;
		}
	}

	/* Undo the loop on error */
	if (i-- != instinctq_BATTERY_NUM) {
		for (; i >= 0; --i)
			power_supply_unregister(&bat->psy[i]);
		goto err_temp_free;
	}
#ifdef CONFIG_HAS_WAKELOCK
	ret = device_create_file(bat->psy[instinctq_BATTERY_AC].dev,
							&dev_attr_suspend_lock);
	if (ret < 0) {
		dev_err(&pdev->dev,
			"Failed to register device attribute.\n");
		goto err_psy_unreg;
	}
#endif
	/* Register the battery */
	bat->bat = instinctq_bat_template;
	ret = power_supply_register(&pdev->dev, &bat->bat);
	if (ret < 0) {
		dev_err(&pdev->dev,
			"Failed to register battery power supply: %d\n", ret);
		goto err_attr_unreg;
	}

	for (i = 0; i < ARRAY_SIZE(battery_attrs); ++i) {
		ret = device_create_file(bat->bat.dev, battery_attrs[i]);
		if (ret < 0)
			break;
	}

	if (ret < 0) {
		for (; i >= 0; --i)
			device_remove_file(bat->bat.dev, battery_attrs[i]);
		goto err_bat_unreg;
	}

	bat->workqueue = create_freezable_workqueue(dev_name(&pdev->dev));
	if (!bat->workqueue) {
		dev_err(&pdev->dev, "Failed to create freezeable workqueue\n");
		ret = -ENOMEM;
		goto err_remove_bat_attr;
	}

	/* Claim IRQs */
	irq = gpio_to_irq(pdata->gpio_pok);
	if (irq <= 0) {
		dev_err(&pdev->dev, "POK irq invalid.\n");
		goto err_destroy_workqueue;
	}
	bat->irq_pok = irq;

	ret = request_irq(irq, instinctq_charger_irq,
				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
				dev_name(&pdev->dev), bat);
	if (ret) {
		dev_err(&pdev->dev, "Failed to request POK irq (%d)\n", ret);
		goto err_destroy_workqueue;
	}

	ret = request_irq(IRQ_BATF, instinctq_battery_fault_irq,
						0, dev_name(&pdev->dev), bat);
	if (ret) {
		dev_err(&pdev->dev,
			"Failed to request battery fault irq (%d)\n", ret);
		goto err_pok_irq_free;
	}

	enable_irq_wake(bat->irq_pok);

	instinctq_battery_set_fault_enable(1);

	/* Finish */
	dev_info(&pdev->dev, "successfully loaded\n");
	device_init_wakeup(&pdev->dev, 1);

	pdata->supply_detect_init(instinctq_battery_supply_notify);

	/* Schedule work to check current status */
#ifdef CONFIG_HAS_WAKELOCK
	wake_lock(&bat->wakelock);
#endif
	queue_work(bat->workqueue, &bat->work);

	return 0;

err_pok_irq_free:
	free_irq(bat->irq_pok, bat);
err_destroy_workqueue:
	destroy_workqueue(bat->workqueue);
err_remove_bat_attr:
	for (i = 0; i < ARRAY_SIZE(battery_attrs); ++i)
		device_remove_file(bat->bat.dev, battery_attrs[i]);
err_bat_unreg:
	power_supply_unregister(&bat->bat);
err_attr_unreg:
#ifdef CONFIG_HAS_WAKELOCK
	device_remove_file(bat->psy[instinctq_BATTERY_AC].dev,
							&dev_attr_suspend_lock);
err_psy_unreg:
#endif
	for (i = 0; i < instinctq_BATTERY_NUM; ++i)
		power_supply_unregister(&bat->psy[i]);
err_temp_free:
#ifdef CONFIG_HAS_WAKELOCK
	wake_lock_destroy(&bat->wakelock);
	wake_lock_destroy(&bat->chg_wakelock);
	wake_lock_destroy(&bat->fault_wakelock);
	wake_lock_destroy(&bat->suspend_lock);
#endif
	kfree(bat->temp_lookup.table);
err_volt_free:
	kfree(bat->volt_lookup.table);
err_percent_free:
	kfree(bat->percent_lookup.table);
err_gpio_en_free:
	gpio_free(pdata->gpio_en);
err_gpio_chg_free:
	gpio_free(pdata->gpio_chg);
err_gpio_pok_free:
	gpio_free(pdata->gpio_pok);
err_free:
	kfree(bat);
err_free_adc:
	s3c_adc_release(client);
	return ret;
}
Example #13
0
static void instinctq_battery_work(struct work_struct *work)
{
	struct instinctq_battery *bat =
			container_of(work, struct instinctq_battery, work);
	struct instinctq_battery_pdata *pdata = bat->pdata;
	int is_plugged, is_healthy, chg_enable;
	enum instinctq_battery_supply type;
	int i;
	ktime_t now, diff;

	/* Cancel any pending works */
	cancel_delayed_work_sync(&bat->poll_work);

	/* Check for external power supply connection */
	is_plugged = gpio_get_value(pdata->gpio_pok) ^ pdata->gpio_pok_inverted;

	/* We're going to access shared data */
	mutex_lock(&bat->mutex);

	type = bat->supply;
	if (type == instinctq_BATTERY_NONE)
		is_plugged = 0;

	for (i = 0; i < instinctq_BATTERY_NUM; ++i)
		bat->online[i] = (type == i);

	is_healthy = (bat->health == POWER_SUPPLY_HEALTH_GOOD);
	if (!is_healthy) {
		if (bat->health == POWER_SUPPLY_HEALTH_OVERHEAT)
			dev_warn(bat->dev,
				 "Battery overheated, disabling charger.\n");

		if (bat->health == POWER_SUPPLY_HEALTH_COLD);
			dev_warn(bat->dev,
				 "Battery temperature too low, disabling charger.\n");
	}

	/* Update charging status and polling interval */
	chg_enable = is_plugged && is_healthy;

	if (bat->chg_enable == chg_enable)
		goto no_change;

	if (chg_enable) {
		bat->status = POWER_SUPPLY_STATUS_CHARGING;
#ifdef CONFIG_HAS_WAKELOCK
		wake_lock(&bat->chg_wakelock);
#endif
		/* Enable the charger */
		gpio_set_value(pdata->gpio_en, !pdata->gpio_en_inverted);

		bat->fault = 0;
#ifdef CONFIG_HAS_WAKELOCK
		wake_unlock(&bat->fault_wakelock);
#endif
	} else {
		bat->status = POWER_SUPPLY_STATUS_DISCHARGING;

		/* Disable the charger */
		gpio_set_value(pdata->gpio_en, pdata->gpio_en_inverted);

		/* Enable battery fault interrupt */
		instinctq_battery_set_fault_enable(1);
#ifdef CONFIG_HAS_WAKELOCK
		wake_lock_timeout(&bat->chg_wakelock, HZ / 2);
#endif
	}

	bat->chg_enable = chg_enable;

no_change:
	/* We're no longer accessing shared data */
	mutex_unlock(&bat->mutex);

	/* Update the values and spin the polling loop */
	now = ktime_get_boottime();
	diff = ktime_sub(now, bat->last_sample);
	if (ktime_to_ms(diff) > bat->interval)
		instinctq_battery_poll(&bat->poll_work.work);
	else
		queue_delayed_work(bat->workqueue, &bat->poll_work,
					msecs_to_jiffies(bat->interval));

	/* Notify anyone interested */
	power_supply_changed(&bat->bat);
	for (i = 0; i < instinctq_BATTERY_NUM; ++i)
		power_supply_changed(&bat->psy[i]);
#ifdef CONFIG_HAS_WAKELOCK
	wake_unlock(&bat->wakelock);
#endif
}
Example #14
0
static void instinctq_battery_poll(struct work_struct *work)
{
	struct delayed_work *dwrk = to_delayed_work(work);
	struct instinctq_battery *bat =
			container_of(dwrk, struct instinctq_battery, poll_work);
	struct instinctq_battery_pdata *pdata = bat->pdata;
	int volt_sample, volt_value, temp_sample, temp_value, percent_value;
	int health, update = 0;

	mutex_lock(&bat->mutex);

	/* Get a voltage sample from the ADC */
	volt_sample = instinctq_battery_adc_read(bat->client, bat->pdata->volt_channel);
	if (volt_sample < 0) {
		dev_warn(bat->dev, "Failed to get ADC sample.\n");
		bat->health = POWER_SUPPLY_HEALTH_UNKNOWN;
		goto error;
	}
	volt_sample += bat->compensation;
	bat->vol_adc = volt_sample;
	volt_sample = put_sample_get_avg(&bat->volt_avg, volt_sample);
	volt_value = lookup_value(&bat->volt_lookup, volt_sample);
	percent_value = lookup_value(&bat->percent_lookup, volt_sample);

	/* Get a temperature sample from the ADC */
	temp_sample = instinctq_battery_adc_read(bat->client, bat->pdata->temp_channel);
	if (temp_sample < 0) {
		dev_warn(bat->dev, "Failed to get ADC sample.\n");
		bat->health = POWER_SUPPLY_HEALTH_UNKNOWN;
		goto error;
	}
	bat->temp_adc = temp_sample;
	temp_sample = put_sample_get_avg(&bat->temp_avg, temp_sample);
	temp_value = lookup_value(&bat->temp_lookup, temp_sample);

	if (bat->health == POWER_SUPPLY_HEALTH_UNKNOWN)
		bat->health = POWER_SUPPLY_HEALTH_GOOD;

	bat->volt_value = volt_value;

	if (bat->percent_value >= percent_value
	    || bat->status != POWER_SUPPLY_STATUS_DISCHARGING)
		bat->percent_value = percent_value;

	bat->temp_value = temp_value;
	bat->last_sample = ktime_get_boottime();

	health = bat->health;

	if (temp_value <= pdata->low_temp_enter)
		health = POWER_SUPPLY_HEALTH_COLD;

	if (temp_value >= pdata->high_temp_enter)
		health = POWER_SUPPLY_HEALTH_OVERHEAT;

	if (temp_value >= pdata->low_temp_exit
	    && temp_value <= pdata->high_temp_exit)
		health = POWER_SUPPLY_HEALTH_GOOD;

	if (bat->health != health) {
		bat->health = health;
		update = 1;
	}

	if (bat->chg_enable) {
		if (gpio_get_value(pdata->gpio_chg) ^ pdata->gpio_chg_inverted)
			bat->status = POWER_SUPPLY_STATUS_CHARGING;
		else
			bat->status = POWER_SUPPLY_STATUS_FULL;
	}

error:
	mutex_unlock(&bat->mutex);

	/* Schedule next poll */
	if (update) {
		queue_work(bat->workqueue, &bat->work);
	} else {
		queue_delayed_work(bat->workqueue, &bat->poll_work,
					msecs_to_jiffies(bat->interval));
		power_supply_changed(&bat->bat);
	}
}
static __devinit int android_bat_probe(struct platform_device *pdev)
{
	struct android_bat_platform_data *pdata = dev_get_platdata(&pdev->dev);
	struct android_bat_data *battery;
	int ret = 0;

	dev_info(&pdev->dev, "Android Battery Driver\n");
	battery = kzalloc(sizeof(*battery), GFP_KERNEL);
	if (!battery)
		return -ENOMEM;

	battery->pdata = pdata;
	if (!battery->pdata) {
		pr_err("%s : No platform data\n", __func__);
		ret = -EINVAL;
		goto err_pdata;
	}

	battery->dev = &pdev->dev;
	platform_set_drvdata(pdev, battery);
	battery->batt_health = POWER_SUPPLY_HEALTH_GOOD;

	battery->psy_bat.name = "android-battery",
	battery->psy_bat.type = POWER_SUPPLY_TYPE_BATTERY,
	battery->psy_bat.properties = android_battery_props,
	battery->psy_bat.num_properties = ARRAY_SIZE(android_battery_props),
	battery->psy_bat.get_property = android_bat_get_property,

	battery->psy_usb.name = "android-usb",
	battery->psy_usb.type = POWER_SUPPLY_TYPE_USB,
	battery->psy_usb.supplied_to = supply_list,
	battery->psy_usb.num_supplicants = ARRAY_SIZE(supply_list),
	battery->psy_usb.properties = android_power_props,
	battery->psy_usb.num_properties = ARRAY_SIZE(android_power_props),
	battery->psy_usb.get_property = android_usb_get_property,

	battery->psy_ac.name = "android-ac",
	battery->psy_ac.type = POWER_SUPPLY_TYPE_MAINS,
	battery->psy_ac.supplied_to = supply_list,
	battery->psy_ac.num_supplicants = ARRAY_SIZE(supply_list),
	battery->psy_ac.properties = android_power_props,
	battery->psy_ac.num_properties = ARRAY_SIZE(android_power_props),
	battery->psy_ac.get_property = android_ac_get_property;

	battery->batt_vcell = -1;
	battery->batt_soc = -1;

	wake_lock_init(&battery->monitor_wake_lock, WAKE_LOCK_SUSPEND,
			"android-battery-monitor");
	wake_lock_init(&battery->charger_wake_lock, WAKE_LOCK_SUSPEND,
			"android-chargerdetect");

	ret = power_supply_register(&pdev->dev, &battery->psy_bat);
	if (ret) {
		dev_err(battery->dev, "%s: failed to register psy_bat\n",
			__func__);
		goto err_psy_bat_reg;
	}

	ret = power_supply_register(&pdev->dev, &battery->psy_usb);
	if (ret) {
		dev_err(battery->dev, "%s: failed to register psy_usb\n",
				__func__);
		goto err_psy_usb_reg;
	}

	ret = power_supply_register(&pdev->dev, &battery->psy_ac);
	if (ret) {
		dev_err(battery->dev, "%s: failed to register psy_ac\n",
				__func__);
		goto err_psy_ac_reg;
	}

	battery->monitor_wqueue =
		alloc_workqueue(dev_name(&pdev->dev), WQ_FREEZABLE, 1);
	if (!battery->monitor_wqueue) {
		dev_err(battery->dev, "%s: fail to create workqueue\n",
				__func__);
		goto err_wq;
	}

	INIT_WORK(&battery->monitor_work, android_bat_monitor_work);
	INIT_WORK(&battery->charger_work, android_bat_charger_work);

	battery->callbacks.charge_source_changed =
		android_bat_charge_source_changed;
	battery->callbacks.battery_set_full =
		android_bat_set_full_status;
	if (battery->pdata && battery->pdata->register_callbacks)
		battery->pdata->register_callbacks(&battery->callbacks);

	/* get initial charger status */
	if (battery->pdata->poll_charge_source)
		battery->charge_source = battery->pdata->poll_charge_source();

	wake_lock(&battery->charger_wake_lock);
	queue_work(battery->monitor_wqueue, &battery->charger_work);

	wake_lock(&battery->monitor_wake_lock);
	battery->last_poll = ktime_get_boottime();
	alarm_init(&battery->monitor_alarm, ALARM_BOOTTIME,
		   android_bat_monitor_alarm);
	queue_work(battery->monitor_wqueue, &battery->monitor_work);

	battery->debugfs_entry =
		debugfs_create_file("android-power", S_IRUGO, NULL,
				    battery, &android_power_debug_fops);
	if (!battery->debugfs_entry)
		pr_err("failed to create android-power debugfs entry\n");

	return 0;

err_wq:
	power_supply_unregister(&battery->psy_ac);
err_psy_ac_reg:
	power_supply_unregister(&battery->psy_usb);
err_psy_usb_reg:
	power_supply_unregister(&battery->psy_bat);
err_psy_bat_reg:
	wake_lock_destroy(&battery->monitor_wake_lock);
	wake_lock_destroy(&battery->charger_wake_lock);
err_pdata:
	kfree(battery);

	return ret;
}
static void android_bat_monitor_work(struct work_struct *work)
{
	struct android_bat_data *battery =
		container_of(work, struct android_bat_data, monitor_work);
	struct timespec cur_time;

	wake_lock(&battery->monitor_wake_lock);
	android_bat_update_data(battery);
	mutex_lock(&android_bat_state_lock);

	switch (battery->charging_status) {
	case POWER_SUPPLY_STATUS_FULL:
		if (battery->batt_vcell < battery->pdata->recharging_voltage &&
		    !battery->recharging) {
			battery->recharging = true;
			android_bat_enable_charging(battery, true);
			pr_info("battery: start recharging, v=%d\n",
				battery->batt_vcell/1000);
		}
		break;
	case POWER_SUPPLY_STATUS_DISCHARGING:
		break;
	case POWER_SUPPLY_STATUS_CHARGING:
		switch (battery->batt_health) {
		case POWER_SUPPLY_HEALTH_OVERHEAT:
		case POWER_SUPPLY_HEALTH_COLD:
		case POWER_SUPPLY_HEALTH_OVERVOLTAGE:
		case POWER_SUPPLY_HEALTH_DEAD:
		case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE:
			battery->charging_status =
				POWER_SUPPLY_STATUS_NOT_CHARGING;
			android_bat_enable_charging(battery, false);

			pr_info("battery: Not charging, health=%d\n",
				battery->batt_health);
			break;
		default:
			break;
		}
		break;
	case POWER_SUPPLY_STATUS_NOT_CHARGING:
		if (battery->batt_health == POWER_SUPPLY_HEALTH_GOOD) {
			pr_info("battery: battery health recovered\n");
			if (battery->charge_source != CHARGE_SOURCE_NONE) {
				android_bat_enable_charging(battery, true);
				battery->charging_status
					= POWER_SUPPLY_STATUS_CHARGING;
			} else {
				battery->charging_status
					= POWER_SUPPLY_STATUS_DISCHARGING;
			}
		}
		break;
	default:
		pr_err("%s: Undefined battery status: %d\n", __func__,
		       battery->charging_status);
		break;
	}

	android_bat_charging_timer(battery);
	get_monotonic_boottime(&cur_time);
	pr_info("battery: l=%d v=%d c=%d temp=%s%ld.%ld h=%d st=%d%s ct=%lu type=%s\n",
		battery->batt_soc, battery->batt_vcell/1000,
		battery->batt_current, battery->batt_temp < 0 ? "-" : "",
		abs(battery->batt_temp / 10), abs(battery->batt_temp % 10),
		battery->batt_health, battery->charging_status,
		   battery->recharging ? "r" : "",
		   battery->charging_start_time ?
		   cur_time.tv_sec - battery->charging_start_time : 0,
		charge_source_str(battery->charge_source));
	mutex_unlock(&android_bat_state_lock);
	power_supply_changed(&battery->psy_bat);
	battery->last_poll = ktime_get_boottime();
	android_bat_monitor_set_alarm(battery, FAST_POLL);
	wake_unlock(&battery->monitor_wake_lock);
	return;
}
static void manta_lcd_off(void)
{
	gpio_set_value(GPIO_LCD_EN, 0);

	lcd_on_time = ktime_add_us(ktime_get_boottime(), LCD_POWER_OFF_TIME_US);
}