diff options
Diffstat (limited to 'drivers/power/cpcap-battery.c')
| -rw-r--r-- | drivers/power/cpcap-battery.c | 823 | 
1 files changed, 823 insertions, 0 deletions
| diff --git a/drivers/power/cpcap-battery.c b/drivers/power/cpcap-battery.c new file mode 100644 index 00000000000..517d41bb919 --- /dev/null +++ b/drivers/power/cpcap-battery.c @@ -0,0 +1,823 @@ +/* + * Copyright (C) 2007-2009 Motorola, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include <asm/div64.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/poll.h> +#include <linux/power_supply.h> +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/spi/cpcap.h> +#include <linux/spi/cpcap-regbits.h> +#include <linux/spi/spi.h> +#include <linux/time.h> +#include <linux/miscdevice.h> +#include <linux/debugfs.h> + +#define CPCAP_BATT_IRQ_BATTDET 0x01 +#define CPCAP_BATT_IRQ_OV      0x02 +#define CPCAP_BATT_IRQ_CC_CAL  0x04 +#define CPCAP_BATT_IRQ_ADCDONE 0x08 +#define CPCAP_BATT_IRQ_MACRO   0x10 + +static long cpcap_batt_ioctl( +			    struct file *file, +			    unsigned int cmd, +			    unsigned long arg); +static unsigned int cpcap_batt_poll(struct file *file, poll_table *wait); +static int cpcap_batt_open(struct inode *inode, struct file *file); +static ssize_t cpcap_batt_read(struct file *file, char *buf, size_t count, +			       loff_t *ppos); +static int cpcap_batt_probe(struct platform_device *pdev); +static int cpcap_batt_remove(struct platform_device *pdev); +static int cpcap_batt_resume(struct platform_device *pdev); + +struct cpcap_batt_ps { +	struct power_supply batt; +	struct power_supply ac; +	struct power_supply usb; +	struct cpcap_device *cpcap; +	struct cpcap_batt_data batt_state; +	struct cpcap_batt_ac_data ac_state; +	struct cpcap_batt_usb_data usb_state; +	struct cpcap_adc_request req; +	struct mutex lock; +	char irq_status; +	char data_pending; +	wait_queue_head_t wait; +	char async_req_pending; +	unsigned long last_run_time; +	bool no_update; +}; + +static const struct file_operations batt_fops = { +	.owner = THIS_MODULE, +	.open = cpcap_batt_open, +	.unlocked_ioctl = cpcap_batt_ioctl, +	.read = cpcap_batt_read, +	.poll = cpcap_batt_poll, +}; + +static struct miscdevice batt_dev = { +	.minor	= MISC_DYNAMIC_MINOR, +	.name	= "cpcap_batt", +	.fops	= &batt_fops, +}; + +static enum power_supply_property cpcap_batt_props[] = { +	POWER_SUPPLY_PROP_STATUS, +	POWER_SUPPLY_PROP_HEALTH, +	POWER_SUPPLY_PROP_PRESENT, +	POWER_SUPPLY_PROP_TECHNOLOGY, +	POWER_SUPPLY_PROP_CAPACITY, +	POWER_SUPPLY_PROP_VOLTAGE_NOW, +	POWER_SUPPLY_PROP_TEMP, +	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, +	POWER_SUPPLY_PROP_CHARGE_COUNTER +}; + +static enum power_supply_property cpcap_batt_ac_props[] = +{ +	POWER_SUPPLY_PROP_ONLINE +}; + +static enum power_supply_property cpcap_batt_usb_props[] = +{ +	POWER_SUPPLY_PROP_ONLINE, +	POWER_SUPPLY_PROP_CURRENT_NOW, +	POWER_SUPPLY_PROP_MODEL_NAME +}; + +static struct platform_driver cpcap_batt_driver = { +	.probe		= cpcap_batt_probe, +	.remove		= cpcap_batt_remove, +	.resume		= cpcap_batt_resume, +	.driver		= { +		.name	= "cpcap_battery", +		.owner	= THIS_MODULE, +	}, +}; + +static struct cpcap_batt_ps *cpcap_batt_sply; + +void cpcap_batt_irq_hdlr(enum cpcap_irqs irq, void *data) +{ +	struct cpcap_batt_ps *sply = data; + +	mutex_lock(&sply->lock); +	sply->data_pending = 1; + +	switch (irq) { +	case CPCAP_IRQ_BATTDETB: +		sply->irq_status |= CPCAP_BATT_IRQ_BATTDET; +		cpcap_irq_unmask(sply->cpcap, irq); +		break; + +	case CPCAP_IRQ_VBUSOV: +		sply->irq_status |=  CPCAP_BATT_IRQ_OV; +		cpcap_irq_unmask(sply->cpcap, irq); +		break; + +	case CPCAP_IRQ_CC_CAL: +		sply->irq_status |= CPCAP_BATT_IRQ_CC_CAL; +		cpcap_irq_unmask(sply->cpcap, irq); +		break; + +	case CPCAP_IRQ_UC_PRIMACRO_7: +	case CPCAP_IRQ_UC_PRIMACRO_8: +	case CPCAP_IRQ_UC_PRIMACRO_9: +	case CPCAP_IRQ_UC_PRIMACRO_10: +	case CPCAP_IRQ_UC_PRIMACRO_11: +		sply->irq_status |= CPCAP_BATT_IRQ_MACRO; +		break; +	default: +		break; +	} + +	mutex_unlock(&sply->lock); + +	wake_up_interruptible(&sply->wait); +} + +void cpcap_batt_adc_hdlr(struct cpcap_device *cpcap, void *data) +{ +	struct cpcap_batt_ps *sply = data; +	mutex_lock(&sply->lock); + +	sply->async_req_pending = 0; + +	sply->data_pending = 1; + +	sply->irq_status |= CPCAP_BATT_IRQ_ADCDONE; + +	mutex_unlock(&sply->lock); + +	wake_up_interruptible(&sply->wait); +} + +static int cpcap_batt_open(struct inode *inode, struct file *file) +{ +	file->private_data = cpcap_batt_sply; +	return 0; +} + +static unsigned int cpcap_batt_poll(struct file *file, poll_table *wait) +{ +	struct cpcap_batt_ps *sply = file->private_data; +	unsigned int ret = 0; + +	poll_wait(file, &sply->wait, wait); + +	if (sply->data_pending) +		ret = (POLLIN | POLLRDNORM); + +	return ret; +} + +static ssize_t cpcap_batt_read(struct file *file, +			       char *buf, size_t count, loff_t *ppos) +{ +	struct cpcap_batt_ps *sply = file->private_data; +	int ret = -EFBIG; +	unsigned long long temp; + +	if (count >= sizeof(char)) { +		mutex_lock(&sply->lock); +		if (!copy_to_user((void *)buf, (void *)&sply->irq_status, +				  sizeof(sply->irq_status))) +			ret = sizeof(sply->irq_status); +		else +			ret = -EFAULT; +		sply->data_pending = 0; +		temp = sched_clock(); +		do_div(temp, NSEC_PER_SEC); +		sply->last_run_time = (unsigned long)temp; + +		sply->irq_status = 0; +		mutex_unlock(&sply->lock); +	} + +	return ret; +} + +static long cpcap_batt_ioctl( +			    struct file *file, +			    unsigned int cmd, +			    unsigned long arg) +{ +	long ret = 0; +	int i; +	struct cpcap_batt_ps *sply = file->private_data; +	struct cpcap_adc_request *req_async = &sply->req; +	struct cpcap_adc_request req; +	struct cpcap_adc_us_request req_us; +	struct spi_device *spi = sply->cpcap->spi; +	struct cpcap_platform_data *data = spi->controller_data; + +	switch (cmd) { +	case CPCAP_IOCTL_BATT_DISPLAY_UPDATE: +		if (sply->no_update) +			return 0; +		if (copy_from_user((void *)&sply->batt_state, +				   (void *)arg, sizeof(struct cpcap_batt_data))) +			return -EFAULT; +		power_supply_changed(&sply->batt); + +		if (data->batt_changed) +			data->batt_changed(&sply->batt, &sply->batt_state); +		break; + +	case CPCAP_IOCTL_BATT_ATOD_ASYNC: +		mutex_lock(&sply->lock); +		if (!sply->async_req_pending) { +			if (copy_from_user((void *)&req_us, (void *)arg, +					   sizeof(struct cpcap_adc_us_request) +					   )) { +				mutex_unlock(&sply->lock); +				return -EFAULT; +			} + +			req_async->format = req_us.format; +			req_async->timing = req_us.timing; +			req_async->type = req_us.type; +			req_async->callback = cpcap_batt_adc_hdlr; +			req_async->callback_param = sply; + +			ret = cpcap_adc_async_read(sply->cpcap, req_async); +			if (!ret) +				sply->async_req_pending = 1; +		} else { +			ret = -EAGAIN; +		} +		mutex_unlock(&sply->lock); + +		break; + +	case CPCAP_IOCTL_BATT_ATOD_SYNC: +		if (copy_from_user((void *)&req_us, (void *)arg, +				   sizeof(struct cpcap_adc_us_request))) +			return -EFAULT; + +		req.format = req_us.format; +		req.timing = req_us.timing; +		req.type = req_us.type; + +		ret = cpcap_adc_sync_read(sply->cpcap, &req); + +		if (ret) +			return ret; + +		req_us.status = req.status; +		for (i = 0; i < CPCAP_ADC_BANK0_NUM; i++) +			req_us.result[i] = req.result[i]; + +		if (copy_to_user((void *)arg, (void *)&req_us, +				 sizeof(struct cpcap_adc_us_request))) +			return -EFAULT; +		break; + +	case CPCAP_IOCTL_BATT_ATOD_READ: +		req_us.format = req_async->format; +		req_us.timing = req_async->timing; +		req_us.type = req_async->type; +		req_us.status = req_async->status; +		for (i = 0; i < CPCAP_ADC_BANK0_NUM; i++) +			req_us.result[i] = req_async->result[i]; + +		if (copy_to_user((void *)arg, (void *)&req_us, +				 sizeof(struct cpcap_adc_us_request))) +			return -EFAULT; +		break; + +	default: +		return -ENOTTY; +		break; +	} + +	return ret; +} + +static int cpcap_batt_ac_get_property(struct power_supply *psy, +				      enum power_supply_property psp, +				      union power_supply_propval *val) +{ +	int ret = 0; +	struct cpcap_batt_ps *sply = container_of(psy, struct cpcap_batt_ps, +						 ac); + +	switch (psp) { +	case POWER_SUPPLY_PROP_ONLINE: +		val->intval = sply->ac_state.online; +		break; +	default: +		ret = -EINVAL; +		break; +	} + +	return ret; +} + +static char *cpcap_batt_usb_models[] = { +	"none", "usb", "factory" +}; + +static int cpcap_batt_usb_get_property(struct power_supply *psy, +				       enum power_supply_property psp, +				       union power_supply_propval *val) +{ +	int ret = 0; +	struct cpcap_batt_ps *sply = container_of(psy, struct cpcap_batt_ps, +						 usb); + +	switch (psp) { +	case POWER_SUPPLY_PROP_ONLINE: +		val->intval = sply->usb_state.online; +		break; +	case POWER_SUPPLY_PROP_CURRENT_NOW: +		val->intval = sply->usb_state.current_now; +		break; +	case POWER_SUPPLY_PROP_MODEL_NAME: +		val->strval = cpcap_batt_usb_models[sply->usb_state.model]; +		break; +	default: +		ret = -EINVAL; +		break; +	} + +	return ret; +} + +static int cpcap_batt_get_property(struct power_supply *psy, +				   enum power_supply_property psp, +				   union power_supply_propval *val) +{ +	int ret = 0; +	struct cpcap_batt_ps *sply = container_of(psy, struct cpcap_batt_ps, +						  batt); + +	switch (psp) { +	case POWER_SUPPLY_PROP_STATUS: +		val->intval = sply->batt_state.status; +		break; + +	case POWER_SUPPLY_PROP_HEALTH: +		val->intval = sply->batt_state.health; +		break; + +	case POWER_SUPPLY_PROP_PRESENT: +		val->intval = sply->batt_state.present; +		break; + +	case POWER_SUPPLY_PROP_TECHNOLOGY: +		val->intval = POWER_SUPPLY_TECHNOLOGY_LION; +		break; + +	case POWER_SUPPLY_PROP_CAPACITY: +		val->intval = sply->batt_state.capacity; +		break; + +	case POWER_SUPPLY_PROP_VOLTAGE_NOW: +		val->intval = sply->batt_state.batt_volt; +		break; + +	case POWER_SUPPLY_PROP_TEMP: +		val->intval = sply->batt_state.batt_temp; +		break; + +	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: +		val->intval = sply->batt_state.batt_full_capacity; +		break; + +	case POWER_SUPPLY_PROP_CHARGE_COUNTER: +		val->intval = sply->batt_state.batt_capacity_one; +		break; + +	default: +		ret = -EINVAL; +		break; +	} + +	return ret; +} + +static int cpcap_batt_probe(struct platform_device *pdev) +{ +	int ret = 0; +	struct cpcap_batt_ps *sply; + +	if (pdev->dev.platform_data == NULL) { +		dev_err(&pdev->dev, "no platform_data\n"); +		ret = -EINVAL; +		goto prb_exit; +	} + +	sply = kzalloc(sizeof(struct cpcap_batt_ps), GFP_KERNEL); +	if (sply == NULL) { +		ret = -ENOMEM; +		goto prb_exit; +	} + +	sply->cpcap = pdev->dev.platform_data; +	mutex_init(&sply->lock); +	init_waitqueue_head(&sply->wait); + +	sply->batt_state.status	= POWER_SUPPLY_STATUS_UNKNOWN; +	sply->batt_state.health	= POWER_SUPPLY_HEALTH_GOOD; +	sply->batt_state.present = 1; +	sply->batt_state.capacity = 100;	/* Percentage */ +	sply->batt_state.batt_volt = 4200000;	/* uV */ +	sply->batt_state.batt_temp = 230;	/* tenths of degrees Celsius */ +	sply->batt_state.batt_full_capacity = 0; +	sply->batt_state.batt_capacity_one = 100; + +	sply->ac_state.online = 0; + +	sply->usb_state.online = 0; +	sply->usb_state.current_now = 0; +	sply->usb_state.model = CPCAP_BATT_USB_MODEL_NONE; + +	sply->batt.properties = cpcap_batt_props; +	sply->batt.num_properties = ARRAY_SIZE(cpcap_batt_props); +	sply->batt.get_property = cpcap_batt_get_property; +	sply->batt.name = "battery"; +	sply->batt.type = POWER_SUPPLY_TYPE_BATTERY; + +	sply->ac.properties = cpcap_batt_ac_props; +	sply->ac.num_properties = ARRAY_SIZE(cpcap_batt_ac_props); +	sply->ac.get_property = cpcap_batt_ac_get_property; +	sply->ac.name = "ac"; +	sply->ac.type = POWER_SUPPLY_TYPE_MAINS; + +	sply->usb.properties = cpcap_batt_usb_props; +	sply->usb.num_properties = ARRAY_SIZE(cpcap_batt_usb_props); +	sply->usb.get_property = cpcap_batt_usb_get_property; +	sply->usb.name = "usb"; +	sply->usb.type = POWER_SUPPLY_TYPE_USB; + +	sply->no_update = false; + +	ret = power_supply_register(&pdev->dev, &sply->ac); +	if (ret) +		goto prb_exit; +	ret = power_supply_register(&pdev->dev, &sply->batt); +	if (ret) +		goto unregac_exit; +	ret = power_supply_register(&pdev->dev, &sply->usb); +	if (ret) +		goto unregbatt_exit; +	platform_set_drvdata(pdev, sply); +	sply->cpcap->battdata = sply; +	cpcap_batt_sply = sply; + +	ret = misc_register(&batt_dev); +	if (ret) +		goto unregusb_exit; + +	ret = cpcap_irq_register(sply->cpcap, CPCAP_IRQ_VBUSOV, +				 cpcap_batt_irq_hdlr, sply); +	if (ret) +		goto unregmisc_exit; +	ret = cpcap_irq_register(sply->cpcap, CPCAP_IRQ_BATTDETB, +				 cpcap_batt_irq_hdlr, sply); +	if (ret) +		goto unregirq_exit; +	ret = cpcap_irq_register(sply->cpcap, CPCAP_IRQ_CC_CAL, +				 cpcap_batt_irq_hdlr, sply); +	if (ret) +		goto unregirq_exit; + +	ret = cpcap_irq_register(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_7, +				 cpcap_batt_irq_hdlr, sply); +	cpcap_irq_mask(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_7); + +	if (ret) +		goto unregirq_exit; + +	ret = cpcap_irq_register(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_8, +				 cpcap_batt_irq_hdlr, sply); +	cpcap_irq_mask(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_8); + +	if (ret) +		goto unregirq_exit; + +	ret = cpcap_irq_register(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_9, +				 cpcap_batt_irq_hdlr, sply); +	cpcap_irq_mask(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_9); + +	if (ret) +		goto unregirq_exit; + +	ret = cpcap_irq_register(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_10, +				 cpcap_batt_irq_hdlr, sply); +	cpcap_irq_mask(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_10); + +	if (ret) +		goto unregirq_exit; + +	ret = cpcap_irq_register(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_11, +				 cpcap_batt_irq_hdlr, sply); +	cpcap_irq_mask(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_11); + +	if (ret) +		goto unregirq_exit; + +	goto prb_exit; + +unregirq_exit: +	cpcap_irq_free(sply->cpcap, CPCAP_IRQ_VBUSOV); +	cpcap_irq_free(sply->cpcap, CPCAP_IRQ_BATTDETB); +	cpcap_irq_free(sply->cpcap, CPCAP_IRQ_CC_CAL); +	cpcap_irq_free(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_7); +	cpcap_irq_free(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_8); +	cpcap_irq_free(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_9); +	cpcap_irq_free(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_10); +	cpcap_irq_free(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_11); +unregmisc_exit: +	misc_deregister(&batt_dev); +unregusb_exit: +	power_supply_unregister(&sply->usb); +unregbatt_exit: +	power_supply_unregister(&sply->batt); +unregac_exit: +	power_supply_unregister(&sply->ac); + +prb_exit: +	return ret; +} + +static int cpcap_batt_remove(struct platform_device *pdev) +{ +	struct cpcap_batt_ps *sply = platform_get_drvdata(pdev); + +	power_supply_unregister(&sply->batt); +	power_supply_unregister(&sply->ac); +	power_supply_unregister(&sply->usb); +	misc_deregister(&batt_dev); +	cpcap_irq_free(sply->cpcap, CPCAP_IRQ_VBUSOV); +	cpcap_irq_free(sply->cpcap, CPCAP_IRQ_BATTDETB); +	cpcap_irq_free(sply->cpcap, CPCAP_IRQ_CC_CAL); +	cpcap_irq_free(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_7); +	cpcap_irq_free(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_8); +	cpcap_irq_free(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_9); +	cpcap_irq_free(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_10); +	cpcap_irq_free(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_11); +	sply->cpcap->battdata = NULL; +	kfree(sply); + +	return 0; +} + +static int cpcap_batt_resume(struct platform_device *pdev) +{ +	struct cpcap_batt_ps *sply = platform_get_drvdata(pdev); +	unsigned long cur_time; +	unsigned long long temp; + +	temp = sched_clock(); +	do_div(temp, NSEC_PER_SEC); +	cur_time = ((unsigned long)temp); +	if ((cur_time - sply->last_run_time) < 0) +		sply->last_run_time = 0; + +	if ((cur_time - sply->last_run_time) > 50) { +		mutex_lock(&sply->lock); +		sply->data_pending = 1; +		sply->irq_status |= CPCAP_BATT_IRQ_MACRO; + +		mutex_unlock(&sply->lock); + +		wake_up_interruptible(&sply->wait); +	} + +	return 0; +} + +void cpcap_batt_set_ac_prop(struct cpcap_device *cpcap, int online) +{ +	struct cpcap_batt_ps *sply = cpcap->battdata; +	struct spi_device *spi = cpcap->spi; +	struct cpcap_platform_data *data = spi->controller_data; + +	if (sply != NULL) { +		sply->ac_state.online = online; +		power_supply_changed(&sply->ac); + +		if (data->ac_changed) +			data->ac_changed(&sply->ac, &sply->ac_state); +	} +} +EXPORT_SYMBOL(cpcap_batt_set_ac_prop); + +void cpcap_batt_set_usb_prop_online(struct cpcap_device *cpcap, int online, +				    enum cpcap_batt_usb_model model) +{ +	struct cpcap_batt_ps *sply = cpcap->battdata; +	struct spi_device *spi = cpcap->spi; +	struct cpcap_platform_data *data = spi->controller_data; + +	if (sply != NULL) { +		sply->usb_state.online = online; +		sply->usb_state.model = model; +		power_supply_changed(&sply->usb); + +		if (data->usb_changed) +			data->usb_changed(&sply->usb, &sply->usb_state); +	} +} +EXPORT_SYMBOL(cpcap_batt_set_usb_prop_online); + +void cpcap_batt_set_usb_prop_curr(struct cpcap_device *cpcap, unsigned int curr) +{ +	struct cpcap_batt_ps *sply = cpcap->battdata; +	struct spi_device *spi = cpcap->spi; +	struct cpcap_platform_data *data = spi->controller_data; + +	if (sply != NULL) { +		sply->usb_state.current_now = curr; +		power_supply_changed(&sply->usb); + +		if (data->usb_changed) +			data->usb_changed(&sply->usb, &sply->usb_state); +	} +} +EXPORT_SYMBOL(cpcap_batt_set_usb_prop_curr); + +/* + * Debugfs interface to test how system works with different values of + * the battery properties. Once the propety value is set through the + * debugfs, updtes from the drivers will be discarded. + */ +#ifdef CONFIG_DEBUG_FS + +static int cpcap_batt_debug_set(void *prop, u64 val) +{ +	int data = (int)val; +	enum power_supply_property psp = (enum power_supply_property)prop; +	struct cpcap_batt_ps *sply = cpcap_batt_sply; +	bool changed = true; +	sply->no_update = true; + +	switch (psp) { +	case POWER_SUPPLY_PROP_STATUS: +		sply->batt_state.status = data; +		break; + +	case POWER_SUPPLY_PROP_HEALTH: +		sply->batt_state.health = data; +		break; + +	case POWER_SUPPLY_PROP_PRESENT: +		sply->batt_state.present = data; +		break; + +	case POWER_SUPPLY_PROP_CAPACITY: +		sply->batt_state.capacity = data; +		break; + +	case POWER_SUPPLY_PROP_VOLTAGE_NOW: +		sply->batt_state.batt_volt = data; +		break; + +	case POWER_SUPPLY_PROP_TEMP: +		sply->batt_state.batt_temp = data; +		break; + +	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: +		sply->batt_state.batt_full_capacity = data; +		break; + +	case POWER_SUPPLY_PROP_CHARGE_COUNTER: +		sply->batt_state.batt_capacity_one = data; +		break; + +	default: +		changed = false; +		break; +	} + +	if (changed) +		power_supply_changed(&sply->batt); + +	return 0; +} + +static int cpcap_batt_debug_get(void *prop, u64 *val) +{ +	enum power_supply_property psp = (enum power_supply_property)prop; +	struct cpcap_batt_ps *sply = cpcap_batt_sply; + +	switch (psp) { +	case POWER_SUPPLY_PROP_STATUS: +		*val = sply->batt_state.status; +		break; + +	case POWER_SUPPLY_PROP_HEALTH: +		*val = sply->batt_state.health; +		break; + +	case POWER_SUPPLY_PROP_PRESENT: +		*val = sply->batt_state.present; +		break; + +	case POWER_SUPPLY_PROP_CAPACITY: +		*val = sply->batt_state.capacity; +		break; + +	case POWER_SUPPLY_PROP_VOLTAGE_NOW: +		*val = sply->batt_state.batt_volt; +		break; + +	case POWER_SUPPLY_PROP_TEMP: +		*val = sply->batt_state.batt_temp; +		break; + +	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: +		*val = sply->batt_state.batt_full_capacity; +		break; + +	case POWER_SUPPLY_PROP_CHARGE_COUNTER: +		*val = sply->batt_state.batt_capacity_one; +		break; + +	default: +		break; +	} + +	return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(cpcap_battery_fops, cpcap_batt_debug_get, +			cpcap_batt_debug_set, "%llu\n"); + +static int __init cpcap_batt_debug_init(void) +{ +	struct dentry *dent = debugfs_create_dir("battery", 0); +	int            ret  = 0; + +	if (!IS_ERR(dent)) { +		debugfs_create_file("status", 0666, dent, +		  (void *)POWER_SUPPLY_PROP_STATUS, &cpcap_battery_fops); +		debugfs_create_file("health", 0666, dent, +		  (void *)POWER_SUPPLY_PROP_HEALTH, &cpcap_battery_fops); +		debugfs_create_file("present", 0666, dent, +		  (void *)POWER_SUPPLY_PROP_PRESENT, &cpcap_battery_fops); +		debugfs_create_file("voltage", 0666, dent, +		  (void *)POWER_SUPPLY_PROP_VOLTAGE_NOW, &cpcap_battery_fops); +		debugfs_create_file("capacity", 0666, dent, +		  (void *)POWER_SUPPLY_PROP_CAPACITY, &cpcap_battery_fops); +		debugfs_create_file("temp", 0666, dent, +		  (void *)POWER_SUPPLY_PROP_TEMP, &cpcap_battery_fops); +		debugfs_create_file("charge_full_design", 0666, dent, +		  (void *)POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, +		  &cpcap_battery_fops); +		debugfs_create_file("charge_counter", 0666, dent, +		  (void *)POWER_SUPPLY_PROP_CHARGE_COUNTER, +		  &cpcap_battery_fops); +	} else { +		ret = PTR_ERR(dent); +	} + +	return ret; +} + +late_initcall(cpcap_batt_debug_init); + +#endif /* CONFIG_DEBUG_FS */ + +static int __init cpcap_batt_init(void) +{ +	return platform_driver_register(&cpcap_batt_driver); +} +subsys_initcall(cpcap_batt_init); + +static void __exit cpcap_batt_exit(void) +{ +	platform_driver_unregister(&cpcap_batt_driver); +} +module_exit(cpcap_batt_exit); + +MODULE_ALIAS("platform:cpcap_batt"); +MODULE_DESCRIPTION("CPCAP BATTERY driver"); +MODULE_AUTHOR("Motorola"); +MODULE_LICENSE("GPL"); |