diff options
author | Alex Gonzalez <alex.gonzalez@digi.com> | 2011-12-19 11:59:42 +0100 |
---|---|---|
committer | Alex Gonzalez <alex.gonzalez@digi.com> | 2011-12-19 11:59:42 +0100 |
commit | f92cadcfab15388520fd241062d7b5618a4bee19 (patch) | |
tree | d68256c8dae63e8e5882d83d7c851c066297969c | |
parent | 02448c5ada2d2de22acd74152b69bb2a815b5f79 (diff) |
ccxmx53: Add ADC chardev interface support.
A character device interface is needed for backwards compatibility with
the adc_test application.
Signed-off-by: Alex Gonzalez <alex.gonzalez@digi.com>
-rw-r--r-- | arch/arm/configs/imx5_ccwmx53js_defconfig | 83 | ||||
-rw-r--r-- | drivers/hwmon/Kconfig | 10 | ||||
-rw-r--r-- | drivers/hwmon/da9052-adc.c | 253 |
3 files changed, 330 insertions, 16 deletions
diff --git a/arch/arm/configs/imx5_ccwmx53js_defconfig b/arch/arm/configs/imx5_ccwmx53js_defconfig index 969437651c25..4d3d684a9baf 100644 --- a/arch/arm/configs/imx5_ccwmx53js_defconfig +++ b/arch/arm/configs/imx5_ccwmx53js_defconfig @@ -1105,7 +1105,88 @@ CONFIG_GPIO_SYSFS=y # CONFIG_GENERIC_PWM is not set # CONFIG_W1 is not set # CONFIG_POWER_SUPPLY is not set -# CONFIG_HWMON is not set +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Native drivers +# +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADCXX is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADT7411 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_ASC7621 is not set +# CONFIG_SENSORS_ATXP1 is not set +CONFIG_SENSORS_DA9052=y +CONFIG_SENSORS_DA9052_CHARDEV=y +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_G760A is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM70 is not set +# CONFIG_SENSORS_LM73 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LTC4215 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_LM95241 is not set +# CONFIG_SENSORS_MAX1111 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX17135 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_SHT15 is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_EMC1403 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_ADS7871 is not set +# CONFIG_SENSORS_AMC6821 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TMP102 is not set +# CONFIG_SENSORS_TMP401 is not set +# CONFIG_SENSORS_TMP421 is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set +# CONFIG_SENSORS_MAG3110 is not set +# CONFIG_MXC_MMA8450 is not set +# CONFIG_MXC_MMA8451 is not set # CONFIG_THERMAL is not set CONFIG_WATCHDOG=y CONFIG_WATCHDOG_NOWAYOUT=y diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 735d451b4945..7bf690ec5743 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -310,6 +310,16 @@ config SENSORS_DA9052 This driver can also be built as a module. If so, the module will be called da9052_hwmon. +config SENSORS_DA9052_CHARDEV + tristate "Dialog DA9052 character device interface" + depends on SENSORS_DA9052 + help + Say y here to access the Dialog Semiconductor DA9052 PMIC + via a /dev/pmic_adc char device. + + This driver can also be built as a module. If so, the module + will be called da9052_hwmon. + config SENSORS_DS1621 tristate "Dallas Semiconductor DS1621 and DS1625" depends on I2C 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; } |