summaryrefslogtreecommitdiff
path: root/drivers/hwmon/da9052-adc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hwmon/da9052-adc.c')
-rw-r--r--drivers/hwmon/da9052-adc.c253
1 files changed, 238 insertions, 15 deletions
diff --git a/drivers/hwmon/da9052-adc.c b/drivers/hwmon/da9052-adc.c
index b8b6e88f1d2d..85472fbc8813 100644
--- a/drivers/hwmon/da9052-adc.c
+++ b/drivers/hwmon/da9052-adc.c
@@ -23,9 +23,21 @@
#include <linux/mfd/da9052/da9052.h>
#include <linux/mfd/da9052/reg.h>
#include <linux/mfd/da9052/adc.h>
+#ifdef CONFIG_SENSORS_DA9052_CHARDEV
+#include <linux/pmic_status.h>
+#include <linux/pmic_adc.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#endif
#define DRIVER_NAME "da9052-adc"
+#ifdef CONFIG_SENSORS_DA9052_CHARDEV
+static int da9052_adc_major;
+static struct class *da9052_adc_class;
+#endif
+
static const char *input_names[] = {
[DA9052_ADC_VDDOUT] = "VDDOUT",
[DA9052_ADC_ICH] = "CHARGING CURRENT",
@@ -193,21 +205,18 @@ int da9052_read_tbat_ich(struct da9052 *da9052, char *data, int channel_no)
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:
@@ -285,16 +294,12 @@ err_ssc_comm:
return -EIO;
}
-static ssize_t da9052_adc_read_start_stop(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static unsigned char da9052_adc_read_start_stop_internal( struct da9052 *da9052 , int channel )
{
- 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);
+ ret = da9052_start_adc(da9052, channel);
if (ret < 0)
return ret;
@@ -322,23 +327,37 @@ static ssize_t da9052_adc_read_start_stop(struct device *dev,
return -EINVAL;
}
msg.data = 0;
- da9052_lock(priv->da9052);
- ret = priv->da9052->read(priv->da9052, &msg);
+ da9052_lock(da9052);
+ ret = da9052->read(da9052, &msg);
if (ret != 0)
goto err_ssc_comm;
- da9052_unlock(priv->da9052);
+ da9052_unlock(da9052);
- ret = da9052_stop_adc(priv->da9052, channel);
+ ret = da9052_stop_adc(da9052, channel);
if (ret < 0)
return ret;
- return sprintf(buf, "%u\n", msg.data);
+ return msg.data;
err_ssc_comm:
- da9052_unlock(priv->da9052);
+ da9052_unlock(da9052);
return ret;
}
+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);
+ int channel = to_sensor_dev_attr(devattr)->index;
+ int ret = da9052_adc_read_start_stop_internal( priv->da9052 , channel );
+
+ if( ret < 0 )
+ return ret;
+
+ return sprintf(buf, "%u\n", ret );
+}
+
static ssize_t da9052_adc_read_ich(struct device *dev,
struct device_attribute *devattr, char *buf)
{
@@ -571,11 +590,208 @@ static const struct attribute_group da9052_group = {
.attrs = da9052_attr,
};
+#ifdef CONFIG_SENSORS_DA9052_CHARDEV
+/*!
+ * This function implements the open method on a DA9052 ADC device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ * @return This function returns 0.
+ */
+static int da9052_adc_open(struct inode *inode, struct file *file)
+{
+ pr_debug("da9052_adc : da9052_adc_open()\n");
+ return 0;
+}
+
+/*!
+ * This function implements the release method on a DA9052 ADC device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ * @return This function returns 0.
+ */
+static int da9052_adc_free(struct inode *inode, struct file *file)
+{
+ pr_debug("da9052_adc : da9052_adc_free()\n");
+ return 0;
+}
+
+/*!
+ * This function implements IOCTL controls on a DA9052 ADC device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ * @param cmd the command
+ * @param arg the parameter
+ * @return This function returns 0 if successful.
+ */
+static int da9052_adc_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ t_adc_convert_param *convert_param;
+
+ if ((_IOC_TYPE(cmd) != 'p') && (_IOC_TYPE(cmd) != 'D'))
+ return -ENOTTY;
+
+ switch (cmd) {
+ case PMIC_ADC_CONVERT:
+ if ((convert_param = kmalloc(sizeof(t_adc_convert_param),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ if (copy_from_user(convert_param, (t_adc_convert_param *) arg,
+ sizeof(t_adc_convert_param))) {
+ kfree(convert_param);
+ return -EFAULT;
+ }
+
+ switch (convert_param->channel){
+ case DA9052_ADC_VDDOUT:
+ convert_param->result[0] =
+ da9052_adc_read_start_stop_internal(da9052_local,
+ convert_param->channel);
+ break;
+ case DA9052_ADC_ICH:
+ da9052_read_tbat_ich(da9052_local,
+ (char *)&convert_param->result[0] , convert_param->channel);
+ break;
+ case DA9052_ADC_TBAT:
+ da9052_read_tbat_ich(da9052_local,
+ (char *)&convert_param->result[0] ,
+ convert_param->channel);
+ break;
+ case DA9052_ADC_VBAT:
+ convert_param->result[0] = da9052_manual_read(da9052_local,
+ convert_param->channel);
+ break;
+ case DA9052_ADC_ADCIN4:
+ convert_param->result[0] =
+ da9052_adc_read_start_stop_internal(da9052_local,
+ convert_param->channel);
+ break;
+ case DA9052_ADC_ADCIN5:
+ convert_param->result[0] =
+ da9052_adc_read_start_stop_internal(da9052_local,
+ convert_param->channel);
+ break;
+ case DA9052_ADC_ADCIN6:
+ convert_param->result[0] =
+ da9052_adc_read_start_stop_internal(da9052_local,
+ convert_param->channel);
+ break;
+ case DA9052_ADC_TSI:
+ pr_info("TSI not availale through chardev driver.\n");
+ return -EFAULT;
+ case DA9052_ADC_TJUNC:
+ da9052_read_tjunc(da9052_local,
+ (char *)&convert_param->result[0]);
+ break;
+ case DA9052_ADC_VBBAT:
+ convert_param->result[0] = da9052_manual_read(da9052_local,
+ convert_param->channel);
+ break;
+ default:
+ pr_err("Invalid channel %d\n",convert_param->channel);
+ return -EFAULT;
+ }
+
+ if( convert_param->result[0] < 0 ){
+ kfree(convert_param);
+ return -EFAULT;
+ }
+
+ if (copy_to_user((t_adc_convert_param *) arg, convert_param,
+ sizeof(t_adc_convert_param))) {
+ kfree(convert_param);
+ return -EFAULT;
+ }
+ kfree(convert_param);
+ break;
+
+ default:
+ pr_debug("da9052_adc_ioctl: unsupported ioctl command 0x%x\n", cmd);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static struct file_operations da9052_adc_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = da9052_adc_ioctl,
+ .open = da9052_adc_open,
+ .release = da9052_adc_free,
+};
+
+static struct cdev da9052_adc_cdev;
+static DEVICE_ATTR(adc, 0644, da9052_adc_show_name, NULL);
+
+static int __init da9052_adc_chardev(struct platform_device *pdev)
+{
+ int ret = 0;
+ dev_t devid;
+ struct device * sdev;
+
+ if( (ret = alloc_chrdev_region(&devid, 0, 8, "pmic_adc")) < 0 ) {
+ pr_err(KERN_ERR "Unable to allocate device range for da9052_adc\n");
+ return ret;
+ }
+ da9052_adc_major = MAJOR(devid);
+ if (da9052_adc_major < 0) {
+ pr_err(KERN_ERR "Unable to get a major for pmic_adc\n");
+ ret = da9052_adc_major;
+ goto unreg_char;
+ }
+
+ cdev_init(&da9052_adc_cdev, &da9052_adc_fops);
+ ret =cdev_add(&da9052_adc_cdev, devid, 8);
+ if (ret < 0) {
+ pr_err("pmic_adc: cannot add character device\n");
+ goto unreg_char;
+ }
+
+ da9052_adc_class = class_create(THIS_MODULE, "pmic_adc");
+ if (IS_ERR(da9052_adc_class)) {
+ pr_err(KERN_ERR "Error creating pmic_adc class.\n");
+ ret = PTR_ERR(da9052_adc_class);
+ goto unreg_char;
+ }
+
+ sdev = device_create(da9052_adc_class, NULL, devid, NULL, "pmic_adc");
+ if (IS_ERR(sdev) ) {
+ pr_err(KERN_ERR "Error creating pmic_adc class device.\n");
+ ret = PTR_ERR(sdev);
+ goto cl_destroy;
+ }
+
+ ret = device_create_file(&(pdev->dev), &dev_attr_adc);
+ if (ret) {
+ pr_err("Can't create device file!\n");
+ ret = -ENODEV;
+ goto dev_destroy;
+ }
+
+ return ret;
+
+dev_destroy:
+ device_destroy(da9052_adc_class, MKDEV(da9052_adc_major, 0));
+cl_destroy:
+ class_destroy(da9052_adc_class);
+unreg_char:
+ unregister_chrdev(da9052_adc_major, "da9052_adc");
+ return ret;
+}
+#endif
+
static int __init da9052_adc_probe(struct platform_device *pdev)
{
struct da9052_adc_priv *priv;
int ret;
+#ifdef CONFIG_SENSORS_DA9052_CHARDEV
+ da9052_adc_chardev(pdev);
+#endif
+
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -625,6 +841,13 @@ static int __devexit da9052_adc_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL);
kfree(priv);
+#ifdef CONFIG_SENSORS_DA9052_CHARDEV
+ device_remove_file(&(pdev->dev), &dev_attr_adc);
+ device_destroy(da9052_adc_class, MKDEV(da9052_adc_major, 0));
+ class_destroy(da9052_adc_class);
+ unregister_chrdev(da9052_adc_major, "da9052_adc");
+#endif
+
return 0;
}