blob: 6803fc2ccea33c41584a3b4fabc12cd4b9c55cac [file] [log] [blame]
/*
* da9052-adc.c -- ADC Driver for Dialog DA9052
*
* Copyright(c) 2009 Dialog Semiconductor Ltd.
*
* Author: Dialog Semiconductor Ltd <dchen@diasemi.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/platform_device.h>
#include <linux/hwmon-sysfs.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/hwmon.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/mfd/da9052/da9052.h>
#include <linux/mfd/da9052/reg.h>
#include <linux/mfd/da9052/adc.h>
#define DRIVER_NAME "da9052-adc"
static const char *input_names[] = {
[DA9052_ADC_VDDOUT] = "VDDOUT",
[DA9052_ADC_ICH] = "CHARGING CURRENT",
[DA9052_ADC_TBAT] = "BATTERY TEMP",
[DA9052_ADC_VBAT] = "BATTERY VOLTAGE",
[DA9052_ADC_ADCIN4] = "ADC INPUT 4",
[DA9052_ADC_ADCIN5] = "ADC INPUT 5",
[DA9052_ADC_ADCIN6] = "ADC INPUT 6",
[DA9052_ADC_TSI] = "TSI",
[DA9052_ADC_TJUNC] = "BATTERY JUNCTION TEMP",
[DA9052_ADC_VBBAT] = "BACK-UP BATTERY TEMP",
};
int da9052_manual_read(struct da9052 *da9052,
unsigned char channel)
{
unsigned char man_timeout_cnt = DA9052_ADC_MAX_MANCONV_RETRY_COUNT;
struct da9052_ssc_msg msg;
unsigned short calc_data;
unsigned int ret;
u16 data = 0;
msg.addr = DA9052_ADCMAN_REG;
msg.data = channel;
msg.data = (msg.data | DA9052_ADCMAN_MANCONV);
mutex_lock(&da9052->manconv_lock);
da9052_lock(da9052);
ret = da9052->write(da9052, &msg);
if (ret)
goto err_ssc_comm;
da9052_unlock(da9052);
/* Wait for the event */
do {
msg.addr = DA9052_ADCCONT_REG;
msg.data = 0;
da9052_lock(da9052);
ret = da9052->read(da9052, &msg);
if (ret)
goto err_ssc_comm;
da9052_unlock(da9052);
if (DA9052_ADCCONT_ADCMODE & msg.data)
msleep(1);
else
msleep(10);
msg.addr = DA9052_ADCMAN_REG;
msg.data = 0;
da9052_lock(da9052);
ret = da9052->read(da9052, &msg);
if (ret)
goto err_ssc_comm;
da9052_unlock(da9052);
/* Counter to avoid endless while loop */
man_timeout_cnt--;
if (man_timeout_cnt == 1) {
if (!(msg.data & DA9052_ADCMAN_MANCONV))
break;
else
goto err_ssc_comm;
}
/* Wait until the MAN_CONV bit is cleared to zero */
} while (msg.data & DA9052_ADCMAN_MANCONV);
msg.addr = DA9052_ADCRESH_REG;
msg.data = 0;
da9052_lock(da9052);
ret = da9052->read(da9052, &msg);
if (ret)
goto err_ssc_comm;
da9052_unlock(da9052);
calc_data = (unsigned short)msg.data;
data = (calc_data << 2);
msg.addr = DA9052_ADCRESL_REG;
msg.data = 0;
da9052_lock(da9052);
ret = da9052->read(da9052, &msg);
if (ret)
goto err_ssc_comm;
da9052_unlock(da9052);
/* Clear first 14 bits before ORing */
calc_data = (unsigned short)msg.data & 0x0003;
data |= calc_data;
mutex_unlock(&da9052->manconv_lock);
return data;
err_ssc_comm:
mutex_unlock(&da9052->manconv_lock);
da9052_unlock(da9052);
return -EIO;
}
EXPORT_SYMBOL(da9052_manual_read);
int da9052_read_tjunc(struct da9052 *da9052, char *buf)
{
struct da9052_ssc_msg msg;
unsigned char temp;
int ret;
msg.addr = DA9052_TJUNCRES_REG;
msg.data = 0;
da9052_lock(da9052);
ret = da9052->read(da9052, &msg);
if (ret)
goto err_ssc_comm;
temp = msg.data;
msg.addr = DA9052_TOFFSET_REG;
msg.data = 0;
ret = da9052->read(da9052, &msg);
if (ret)
goto err_ssc_comm;
da9052_unlock(da9052);
/* Calculate Junction temperature */
temp = (temp - msg.data);
*buf = temp;
return 0;
err_ssc_comm:
da9052_unlock(da9052);
return -EIO;
}
EXPORT_SYMBOL(da9052_read_tjunc);
int da9052_read_tbat_ich(struct da9052 *da9052, char *data, int channel_no)
{
struct da9052_ssc_msg msg;
int ret;
/* Read TBAT conversion result */
switch (channel_no) {
case DA9052_ADC_TBAT:
msg.addr = DA9052_TBATRES_REG;
break;
case DA9052_ADC_ICH:
msg.addr = DA9052_ICHGAV_REG;
break;
default:
return -EINVAL;
}
msg.data = 0;
da9052_lock(da9052);
ret = da9052->read(da9052, &msg);
if (ret)
goto err_ssc_comm;
da9052_unlock(da9052);
*data = msg.data;
printk(KERN_INFO"msg.data 1= %d\n", msg.data);
msg.data = 28;
da9052_lock(da9052);
ret = da9052->write(da9052, &msg);
if (ret)
goto err_ssc_comm;
da9052_unlock(da9052);
printk(KERN_INFO"msg.data2 = %d\n", msg.data);
msg.data = 0;
da9052_lock(da9052);
ret = da9052->read(da9052, &msg);
if (ret)
goto err_ssc_comm;
da9052_unlock(da9052);
printk(KERN_INFO"msg.data3 = %d\n", msg.data);
return 0;
err_ssc_comm:
da9052_unlock(da9052);
return ret;
}
EXPORT_SYMBOL(da9052_read_tbat_ich);
static int da9052_start_adc(struct da9052 *da9052, unsigned channel)
{
struct da9052_ssc_msg msg;
int ret;
msg.addr = DA9052_ADCCONT_REG;
msg.data = 0;
da9052_lock(da9052);
ret = da9052->read(da9052, &msg);
if (ret != 0)
goto err_ssc_comm;
if (channel == DA9052_ADC_VDDOUT)
msg.data = (msg.data | DA9052_ADCCONT_AUTOVDDEN);
else if (channel == DA9052_ADC_ADCIN4)
msg.data = (msg.data | DA9052_ADCCONT_AUTOAD4EN);
else if (channel == DA9052_ADC_ADCIN5)
msg.data = (msg.data | DA9052_ADCCONT_AUTOAD5EN);
else if (channel == DA9052_ADC_ADCIN6)
msg.data = (msg.data | DA9052_ADCCONT_AUTOAD6EN);
else
return -EINVAL;
ret = da9052->write(da9052, &msg);
if (ret != 0)
goto err_ssc_comm;
da9052_unlock(da9052);
return 0;
err_ssc_comm:
da9052_unlock(da9052);
return -EIO;
}
static int da9052_stop_adc(struct da9052 *da9052, unsigned channel)
{
int ret;
struct da9052_ssc_msg msg;
msg.addr = DA9052_ADCCONT_REG;
msg.data = 0;
da9052_lock(da9052);
ret = da9052->read(da9052, &msg);
if (ret != 0)
goto err_ssc_comm;
if (channel == DA9052_ADC_VDDOUT)
msg.data = (msg.data & ~(DA9052_ADCCONT_AUTOVDDEN));
else if (channel == DA9052_ADC_ADCIN4)
msg.data = (msg.data & ~(DA9052_ADCCONT_AUTOAD4EN));
else if (channel == DA9052_ADC_ADCIN5)
msg.data = (msg.data & ~(DA9052_ADCCONT_AUTOAD5EN));
else if (channel == DA9052_ADC_ADCIN6)
msg.data = (msg.data & ~(DA9052_ADCCONT_AUTOAD6EN));
else
return -EINVAL;
ret = da9052->write(da9052, &msg);
if (ret != 0)
goto err_ssc_comm;
da9052_unlock(da9052);
return 0;
err_ssc_comm:
da9052_unlock(da9052);
return -EIO;
}
static ssize_t da9052_adc_read_start_stop(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct da9052_adc_priv *priv = platform_get_drvdata(pdev);
struct da9052_ssc_msg msg;
int channel = to_sensor_dev_attr(devattr)->index;
int ret;
ret = da9052_start_adc(priv->da9052, channel);
if (ret < 0)
return ret;
/* Read the ADC converted value */
switch (channel) {
case DA9052_ADC_VDDOUT:
msg.addr = DA9052_VDDRES_REG;
break;
#if (DA9052_ADC_CONF_ADC4 == 1)
case DA9052_ADC_ADCIN4:
msg.addr = DA9052_ADCIN4RES_REG;
break;
#endif
#if (DA9052_ADC_CONF_ADC5 == 1)
case DA9052_ADC_ADCIN5:
msg.addr = DA9052_ADCIN5RES_REG;
break;
#endif
#if (DA9052_ADC_CONF_ADC6 == 1)
case DA9052_ADC_ADCIN6:
msg.addr = DA9052_ADCIN6RES_REG;
break;
#endif
default:
return -EINVAL;
}
msg.data = 0;
da9052_lock(priv->da9052);
ret = priv->da9052->read(priv->da9052, &msg);
if (ret != 0)
goto err_ssc_comm;
da9052_unlock(priv->da9052);
ret = da9052_stop_adc(priv->da9052, channel);
if (ret < 0)
return ret;
return sprintf(buf, "%u\n", msg.data);
err_ssc_comm:
da9052_unlock(priv->da9052);
return ret;
}
static ssize_t da9052_adc_read_ich(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct da9052_adc_priv *priv = platform_get_drvdata(pdev);
int ret;
ret = da9052_read_tbat_ich(priv->da9052, buf, DA9052_ADC_ICH);
if (ret < 0)
return ret;
return sprintf(buf, "%u\n", *buf);
}
static ssize_t da9052_adc_read_tbat(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct da9052_adc_priv *priv = platform_get_drvdata(pdev);
int ret;
ret = da9052_read_tbat_ich(priv->da9052, buf, DA9052_ADC_TBAT);
if (ret < 0)
return ret;
return sprintf(buf, "%u\n", *buf);
}
static ssize_t da9052_adc_read_vbat(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct da9052_adc_priv *priv = platform_get_drvdata(pdev);
s32 ret;
ret = da9052_manual_read(priv->da9052, DA9052_ADC_VBAT);
if (ret < 0)
return ret;
return sprintf(buf, "%u\n", ret);
}
static ssize_t da9052_adc_read_tjunc(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct da9052_adc_priv *priv = platform_get_drvdata(pdev);
int ret;
ret = da9052_read_tjunc(priv->da9052, buf);
if (ret < 0)
return ret;
return sprintf(buf, "%u\n", *buf);
}
static ssize_t da9052_adc_read_vbbat(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct da9052_adc_priv *priv = platform_get_drvdata(pdev);
s32 ret;
ret = da9052_manual_read(priv->da9052, DA9052_ADC_VBBAT);
if (ret < 0)
return ret;
return sprintf(buf, "%u\n", ret);
}
static int da9052_adc_hw_init(struct da9052 *da9052)
{
struct da9052_ssc_msg msg;
int ret;
/* ADC channel 4 and 5 are by default enabled */
#if (DA9052_ADC_CONF_ADC4 == 1)
msg.addr = DA9052_GPIO0001_REG;
msg.data = 0;
da9052_lock(da9052);
ret = da9052->read(da9052, &msg);
if (ret)
goto err_ssc_comm;
msg.data = (msg.data & ~(DA9052_GPIO0001_GPIO0PIN));
ret = da9052->write(da9052, &msg);
if (ret != 0)
goto err_ssc_comm;
da9052_unlock(da9052);
#endif
#if (DA9052_ADC_CONF_ADC5 == 1)
msg.addr = DA9052_GPIO0001_REG;
msg.data = 0;
da9052_lock(da9052);
ret = da9052->read(da9052, &msg);
if (ret)
goto err_ssc_comm;
msg.data = (msg.data & ~(DA9052_GPIO0001_GPIO0PIN));
ret = da9052->write(da9052, &msg);
if (ret != 0)
goto err_ssc_comm;
da9052_unlock(da9052);
#endif
#if (DA9052_ADC_CONF_ADC6 == 1)
msg.addr = DA9052_GPIO0203_REG;
msg.data = 0;
da9052_lock(da9052);
ret = da9052->read(da9052, &msg);
if (ret)
goto err_ssc_comm;
msg.data = (msg.data & ~(DA9052_GPIO0203_GPIO2PIN));
ret = da9052->write(da9052, &msg);
if (ret != 0)
goto err_ssc_comm;
da9052_unlock(da9052);
#endif
#if 0
/* By default configure the Measurement sequence interval to 10ms */
msg.addr = DA9052_ADCCONT_REG;
msg.data = 0;
da9052_lock(da9052);
ret = da9052->read(da9052, &msg);
if (ret != 0)
goto err_ssc_comm;
/* Set the ADC MODE bit for 10msec sampling timer */
msg.data = (msg.data & ~(DA9052_ADCCONT_ADCMODE));
ret = da9052->write(da9052, &msg);
if (ret != 0)
goto err_ssc_comm;
da9052_unlock(da9052);
#endif
return 0;
err_ssc_comm:
da9052_unlock(da9052);
return -EIO;
}
static ssize_t da9052_adc_show_name(struct device *dev,
struct device_attribute *devattr, char *buf)
{
return sprintf(buf, "da9052-adc\n");
}
static ssize_t show_label(struct device *dev,
struct device_attribute *devattr, char *buf)
{
int channel = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%s\n", input_names[channel]);
}
#define DA9052_ADC_CHANNELS(id, name) \
static SENSOR_DEVICE_ATTR(in##id##_label, S_IRUGO, show_label, \
NULL, name)
DA9052_ADC_CHANNELS(0, DA9052_ADC_VDDOUT);
DA9052_ADC_CHANNELS(1, DA9052_ADC_ICH);
DA9052_ADC_CHANNELS(2, DA9052_ADC_TBAT);
DA9052_ADC_CHANNELS(3, DA9052_ADC_VBAT);
#if (DA9052_ADC_CONF_ADC4 == 1)
DA9052_ADC_CHANNELS(4, DA9052_ADC_ADCIN4);
#endif
#if (DA9052_ADC_CONF_ADC5 == 1)
DA9052_ADC_CHANNELS(5, DA9052_ADC_ADCIN5);
#endif
#if (DA9052_ADC_CONF_ADC6 == 1)
DA9052_ADC_CHANNELS(6, DA9052_ADC_ADCIN6);
#endif
DA9052_ADC_CHANNELS(7, DA9052_ADC_TSI);
DA9052_ADC_CHANNELS(8, DA9052_ADC_TJUNC);
DA9052_ADC_CHANNELS(9, DA9052_ADC_VBBAT);
static DEVICE_ATTR(name, S_IRUGO, da9052_adc_show_name, NULL);
static SENSOR_DEVICE_ATTR(read_vddout, S_IRUGO,
da9052_adc_read_start_stop, NULL,
DA9052_ADC_VDDOUT);
static SENSOR_DEVICE_ATTR(read_ich, S_IRUGO, da9052_adc_read_ich, NULL,
DA9052_ADC_ICH);
static SENSOR_DEVICE_ATTR(read_tbat, S_IRUGO, da9052_adc_read_tbat, NULL,
DA9052_ADC_TBAT);
static SENSOR_DEVICE_ATTR(read_vbat, S_IRUGO, da9052_adc_read_vbat, NULL,
DA9052_ADC_VBAT);
#if (DA9052_ADC_CONF_ADC4 == 1)
static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, da9052_adc_read_start_stop, NULL,
DA9052_ADC_ADCIN4);
#endif
#if (DA9052_ADC_CONF_ADC5 == 1)
static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, da9052_adc_read_start_stop, NULL,
DA9052_ADC_ADCIN5);
#endif
#if (DA9052_ADC_CONF_ADC6 == 1)
static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, da9052_adc_read_start_stop, NULL,
DA9052_ADC_ADCIN6);
#endif
static SENSOR_DEVICE_ATTR(read_tjunc, S_IRUGO, da9052_adc_read_tjunc, NULL,
DA9052_ADC_TJUNC);
static SENSOR_DEVICE_ATTR(read_vbbat, S_IRUGO, da9052_adc_read_vbbat, NULL,
DA9052_ADC_VBBAT);
static struct attribute *da9052_attr[] = {
&dev_attr_name.attr,
&sensor_dev_attr_read_vddout.dev_attr.attr,
&sensor_dev_attr_in0_label.dev_attr.attr,
&sensor_dev_attr_read_ich.dev_attr.attr,
&sensor_dev_attr_in1_label.dev_attr.attr,
&sensor_dev_attr_read_tbat.dev_attr.attr,
&sensor_dev_attr_in2_label.dev_attr.attr,
&sensor_dev_attr_read_vbat.dev_attr.attr,
&sensor_dev_attr_in3_label.dev_attr.attr,
#if (DA9052_ADC_CONF_ADC4 == 1)
&sensor_dev_attr_in4_input.dev_attr.attr,
&sensor_dev_attr_in4_label.dev_attr.attr,
#endif
#if (DA9052_ADC_CONF_ADC5 == 1)
&sensor_dev_attr_in5_input.dev_attr.attr,
&sensor_dev_attr_in5_label.dev_attr.attr,
#endif
#if (DA9052_ADC_CONF_ADC6 == 1)
&sensor_dev_attr_in6_input.dev_attr.attr,
&sensor_dev_attr_in6_label.dev_attr.attr,
#endif
&sensor_dev_attr_in7_label.dev_attr.attr,
&sensor_dev_attr_read_tjunc.dev_attr.attr,
&sensor_dev_attr_in8_label.dev_attr.attr,
&sensor_dev_attr_read_vbbat.dev_attr.attr,
&sensor_dev_attr_in9_label.dev_attr.attr,
NULL
};
static const struct attribute_group da9052_group = {
.attrs = da9052_attr,
};
static int __init da9052_adc_probe(struct platform_device *pdev)
{
struct da9052_adc_priv *priv;
int ret;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->da9052 = dev_get_drvdata(pdev->dev.parent);
platform_set_drvdata(pdev, priv);
/* Register sysfs hooks */
ret = sysfs_create_group(&pdev->dev.kobj, &da9052_group);
if (ret)
goto out_err_create1;
priv->hwmon_dev = hwmon_device_register(&pdev->dev);
if (IS_ERR(priv->hwmon_dev)) {
ret = PTR_ERR(priv->hwmon_dev);
goto out_err_create2;
}
/* Initializes the hardware for ADC module */
da9052_adc_hw_init(priv->da9052);
/* Initialize mutex required for ADC Manual read */
mutex_init(&priv->da9052->manconv_lock);
return 0;
out_err_create2:
sysfs_remove_group(&pdev->dev.kobj, &da9052_group);
out_err_create1:
platform_set_drvdata(pdev, NULL);
kfree(priv);
return ret;
}
static int __devexit da9052_adc_remove(struct platform_device *pdev)
{
struct da9052_adc_priv *priv = platform_get_drvdata(pdev);
mutex_destroy(&priv->da9052->manconv_lock);
hwmon_device_unregister(priv->hwmon_dev);
sysfs_remove_group(&pdev->dev.kobj, &da9052_group);
platform_set_drvdata(pdev, NULL);
kfree(priv);
return 0;
}
static struct platform_driver da9052_adc_driver = {
.remove = __devexit_p(da9052_adc_remove),
.driver = {
.owner = THIS_MODULE,
.name = DRIVER_NAME,
},
};
static int __init da9052_adc_init(void)
{
return platform_driver_probe(&da9052_adc_driver, da9052_adc_probe);
}
module_init(da9052_adc_init);
static void __exit da9052_adc_exit(void)
{
platform_driver_unregister(&da9052_adc_driver);
}
module_exit(da9052_adc_exit);
MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
MODULE_DESCRIPTION("DA9052 ADC driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRIVER_NAME);