linux/drivers/hwmon/da9052-hwmon.c
<<
>>
Prefs
   1/*
   2 * HWMON Driver for Dialog DA9052
   3 *
   4 * Copyright(c) 2012 Dialog Semiconductor Ltd.
   5 *
   6 * Author: David Dajun Chen <dchen@diasemi.com>
   7 *
   8 *  This program is free software; you can redistribute  it and/or modify it
   9 *  under  the terms of  the GNU General  Public License as published by the
  10 *  Free Software Foundation;  either version 2 of the  License, or (at your
  11 *  option) any later version.
  12 *
  13 */
  14
  15#include <linux/delay.h>
  16#include <linux/err.h>
  17#include <linux/hwmon.h>
  18#include <linux/hwmon-sysfs.h>
  19#include <linux/init.h>
  20#include <linux/kernel.h>
  21#include <linux/module.h>
  22#include <linux/slab.h>
  23#include <linux/platform_device.h>
  24
  25#include <linux/mfd/da9052/da9052.h>
  26#include <linux/mfd/da9052/reg.h>
  27
  28struct da9052_hwmon {
  29        struct da9052   *da9052;
  30        struct device   *class_device;
  31        struct mutex    hwmon_lock;
  32};
  33
  34static const char * const input_names[] = {
  35        [DA9052_ADC_VDDOUT]     =       "VDDOUT",
  36        [DA9052_ADC_ICH]        =       "CHARGING CURRENT",
  37        [DA9052_ADC_TBAT]       =       "BATTERY TEMP",
  38        [DA9052_ADC_VBAT]       =       "BATTERY VOLTAGE",
  39        [DA9052_ADC_IN4]        =       "ADC IN4",
  40        [DA9052_ADC_IN5]        =       "ADC IN5",
  41        [DA9052_ADC_IN6]        =       "ADC IN6",
  42        [DA9052_ADC_TJUNC]      =       "BATTERY JUNCTION TEMP",
  43        [DA9052_ADC_VBBAT]      =       "BACK-UP BATTERY VOLTAGE",
  44};
  45
  46/* Conversion function for VDDOUT and VBAT */
  47static inline int volt_reg_to_mV(int value)
  48{
  49        return DIV_ROUND_CLOSEST(value * 1000, 512) + 2500;
  50}
  51
  52/* Conversion function for ADC channels 4, 5 and 6 */
  53static inline int input_reg_to_mV(int value)
  54{
  55        return DIV_ROUND_CLOSEST(value * 2500, 1023);
  56}
  57
  58/* Conversion function for VBBAT */
  59static inline int vbbat_reg_to_mV(int value)
  60{
  61        return DIV_ROUND_CLOSEST(value * 2500, 512);
  62}
  63
  64static int da9052_enable_vddout_channel(struct da9052 *da9052)
  65{
  66        int ret;
  67
  68        ret = da9052_reg_read(da9052, DA9052_ADC_CONT_REG);
  69        if (ret < 0)
  70                return ret;
  71
  72        ret |= DA9052_ADCCONT_AUTOVDDEN;
  73
  74        return da9052_reg_write(da9052, DA9052_ADC_CONT_REG, ret);
  75}
  76
  77static int da9052_disable_vddout_channel(struct da9052 *da9052)
  78{
  79        int ret;
  80
  81        ret = da9052_reg_read(da9052, DA9052_ADC_CONT_REG);
  82        if (ret < 0)
  83                return ret;
  84
  85        ret &= ~DA9052_ADCCONT_AUTOVDDEN;
  86
  87        return da9052_reg_write(da9052, DA9052_ADC_CONT_REG, ret);
  88}
  89
  90static ssize_t da9052_read_vddout(struct device *dev,
  91                                  struct device_attribute *devattr, char *buf)
  92{
  93        struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
  94        int ret, vdd;
  95
  96        mutex_lock(&hwmon->hwmon_lock);
  97
  98        ret = da9052_enable_vddout_channel(hwmon->da9052);
  99        if (ret < 0)
 100                goto hwmon_err;
 101
 102        vdd = da9052_reg_read(hwmon->da9052, DA9052_VDD_RES_REG);
 103        if (vdd < 0) {
 104                ret = vdd;
 105                goto hwmon_err_release;
 106        }
 107
 108        ret = da9052_disable_vddout_channel(hwmon->da9052);
 109        if (ret < 0)
 110                goto hwmon_err;
 111
 112        mutex_unlock(&hwmon->hwmon_lock);
 113        return sprintf(buf, "%d\n", volt_reg_to_mV(vdd));
 114
 115hwmon_err_release:
 116        da9052_disable_vddout_channel(hwmon->da9052);
 117hwmon_err:
 118        mutex_unlock(&hwmon->hwmon_lock);
 119        return ret;
 120}
 121
 122static ssize_t da9052_read_ich(struct device *dev,
 123                               struct device_attribute *devattr, char *buf)
 124{
 125        struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
 126        int ret;
 127
 128        ret = da9052_reg_read(hwmon->da9052, DA9052_ICHG_AV_REG);
 129        if (ret < 0)
 130                return ret;
 131
 132        /* Equivalent to 3.9mA/bit in register ICHG_AV */
 133        return sprintf(buf, "%d\n", DIV_ROUND_CLOSEST(ret * 39, 10));
 134}
 135
 136static ssize_t da9052_read_tbat(struct device *dev,
 137                                struct device_attribute *devattr, char *buf)
 138{
 139        struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
 140
 141        return sprintf(buf, "%d\n", da9052_adc_read_temp(hwmon->da9052));
 142}
 143
 144static ssize_t da9052_read_vbat(struct device *dev,
 145                                struct device_attribute *devattr, char *buf)
 146{
 147        struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
 148        int ret;
 149
 150        ret = da9052_adc_manual_read(hwmon->da9052, DA9052_ADC_VBAT);
 151        if (ret < 0)
 152                return ret;
 153
 154        return sprintf(buf, "%d\n", volt_reg_to_mV(ret));
 155}
 156
 157static ssize_t da9052_read_misc_channel(struct device *dev,
 158                                        struct device_attribute *devattr,
 159                                        char *buf)
 160{
 161        struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
 162        int channel = to_sensor_dev_attr(devattr)->index;
 163        int ret;
 164
 165        ret = da9052_adc_manual_read(hwmon->da9052, channel);
 166        if (ret < 0)
 167                return ret;
 168
 169        return sprintf(buf, "%d\n", input_reg_to_mV(ret));
 170}
 171
 172static ssize_t da9052_read_tjunc(struct device *dev,
 173                                 struct device_attribute *devattr, char *buf)
 174{
 175        struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
 176        int tjunc;
 177        int toffset;
 178
 179        tjunc = da9052_reg_read(hwmon->da9052, DA9052_TJUNC_RES_REG);
 180        if (tjunc < 0)
 181                return tjunc;
 182
 183        toffset = da9052_reg_read(hwmon->da9052, DA9052_T_OFFSET_REG);
 184        if (toffset < 0)
 185                return toffset;
 186
 187        /*
 188         * Degrees celsius = 1.708 * (TJUNC_RES - T_OFFSET) - 108.8
 189         * T_OFFSET is a trim value used to improve accuracy of the result
 190         */
 191        return sprintf(buf, "%d\n", 1708 * (tjunc - toffset) - 108800);
 192}
 193
 194static ssize_t da9052_read_vbbat(struct device *dev,
 195                                 struct device_attribute *devattr, char *buf)
 196{
 197        struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
 198        int ret;
 199
 200        ret = da9052_adc_manual_read(hwmon->da9052, DA9052_ADC_VBBAT);
 201        if (ret < 0)
 202                return ret;
 203
 204        return sprintf(buf, "%d\n", vbbat_reg_to_mV(ret));
 205}
 206
 207static ssize_t da9052_hwmon_show_name(struct device *dev,
 208                                      struct device_attribute *devattr,
 209                                      char *buf)
 210{
 211        return sprintf(buf, "da9052-hwmon\n");
 212}
 213
 214static ssize_t show_label(struct device *dev,
 215                          struct device_attribute *devattr, char *buf)
 216{
 217        return sprintf(buf, "%s\n",
 218                       input_names[to_sensor_dev_attr(devattr)->index]);
 219}
 220
 221static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, da9052_read_vddout, NULL,
 222                          DA9052_ADC_VDDOUT);
 223static SENSOR_DEVICE_ATTR(in0_label, S_IRUGO, show_label, NULL,
 224                          DA9052_ADC_VDDOUT);
 225static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, da9052_read_vbat, NULL,
 226                          DA9052_ADC_VBAT);
 227static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL,
 228                          DA9052_ADC_VBAT);
 229static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, da9052_read_misc_channel, NULL,
 230                          DA9052_ADC_IN4);
 231static SENSOR_DEVICE_ATTR(in4_label, S_IRUGO, show_label, NULL,
 232                          DA9052_ADC_IN4);
 233static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, da9052_read_misc_channel, NULL,
 234                          DA9052_ADC_IN5);
 235static SENSOR_DEVICE_ATTR(in5_label, S_IRUGO, show_label, NULL,
 236                          DA9052_ADC_IN5);
 237static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, da9052_read_misc_channel, NULL,
 238                          DA9052_ADC_IN6);
 239static SENSOR_DEVICE_ATTR(in6_label, S_IRUGO, show_label, NULL,
 240                          DA9052_ADC_IN6);
 241static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, da9052_read_vbbat, NULL,
 242                          DA9052_ADC_VBBAT);
 243static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_label, NULL,
 244                          DA9052_ADC_VBBAT);
 245
 246static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, da9052_read_ich, NULL,
 247                          DA9052_ADC_ICH);
 248static SENSOR_DEVICE_ATTR(curr1_label, S_IRUGO, show_label, NULL,
 249                          DA9052_ADC_ICH);
 250
 251static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, da9052_read_tbat, NULL,
 252                          DA9052_ADC_TBAT);
 253static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_label, NULL,
 254                          DA9052_ADC_TBAT);
 255static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, da9052_read_tjunc, NULL,
 256                          DA9052_ADC_TJUNC);
 257static SENSOR_DEVICE_ATTR(temp8_label, S_IRUGO, show_label, NULL,
 258                          DA9052_ADC_TJUNC);
 259
 260static DEVICE_ATTR(name, S_IRUGO, da9052_hwmon_show_name, NULL);
 261
 262static struct attribute *da9052_attr[] = {
 263        &dev_attr_name.attr,
 264        &sensor_dev_attr_in0_input.dev_attr.attr,
 265        &sensor_dev_attr_in0_label.dev_attr.attr,
 266        &sensor_dev_attr_in3_input.dev_attr.attr,
 267        &sensor_dev_attr_in3_label.dev_attr.attr,
 268        &sensor_dev_attr_in4_input.dev_attr.attr,
 269        &sensor_dev_attr_in4_label.dev_attr.attr,
 270        &sensor_dev_attr_in5_input.dev_attr.attr,
 271        &sensor_dev_attr_in5_label.dev_attr.attr,
 272        &sensor_dev_attr_in6_input.dev_attr.attr,
 273        &sensor_dev_attr_in6_label.dev_attr.attr,
 274        &sensor_dev_attr_in9_input.dev_attr.attr,
 275        &sensor_dev_attr_in9_label.dev_attr.attr,
 276        &sensor_dev_attr_curr1_input.dev_attr.attr,
 277        &sensor_dev_attr_curr1_label.dev_attr.attr,
 278        &sensor_dev_attr_temp2_input.dev_attr.attr,
 279        &sensor_dev_attr_temp2_label.dev_attr.attr,
 280        &sensor_dev_attr_temp8_input.dev_attr.attr,
 281        &sensor_dev_attr_temp8_label.dev_attr.attr,
 282        NULL
 283};
 284
 285static const struct attribute_group da9052_attr_group = {.attrs = da9052_attr};
 286
 287static int __devinit da9052_hwmon_probe(struct platform_device *pdev)
 288{
 289        struct da9052_hwmon *hwmon;
 290        int ret;
 291
 292        hwmon = devm_kzalloc(&pdev->dev, sizeof(struct da9052_hwmon),
 293                             GFP_KERNEL);
 294        if (!hwmon)
 295                return -ENOMEM;
 296
 297        mutex_init(&hwmon->hwmon_lock);
 298        hwmon->da9052 = dev_get_drvdata(pdev->dev.parent);
 299
 300        platform_set_drvdata(pdev, hwmon);
 301
 302        ret = sysfs_create_group(&pdev->dev.kobj, &da9052_attr_group);
 303        if (ret)
 304                goto err_mem;
 305
 306        hwmon->class_device = hwmon_device_register(&pdev->dev);
 307        if (IS_ERR(hwmon->class_device)) {
 308                ret = PTR_ERR(hwmon->class_device);
 309                goto err_sysfs;
 310        }
 311
 312        return 0;
 313
 314err_sysfs:
 315        sysfs_remove_group(&pdev->dev.kobj, &da9052_attr_group);
 316err_mem:
 317        return ret;
 318}
 319
 320static int __devexit da9052_hwmon_remove(struct platform_device *pdev)
 321{
 322        struct da9052_hwmon *hwmon = platform_get_drvdata(pdev);
 323
 324        hwmon_device_unregister(hwmon->class_device);
 325        sysfs_remove_group(&pdev->dev.kobj, &da9052_attr_group);
 326
 327        return 0;
 328}
 329
 330static struct platform_driver da9052_hwmon_driver = {
 331        .probe = da9052_hwmon_probe,
 332        .remove = __devexit_p(da9052_hwmon_remove),
 333        .driver = {
 334                .name = "da9052-hwmon",
 335                .owner = THIS_MODULE,
 336        },
 337};
 338
 339module_platform_driver(da9052_hwmon_driver);
 340
 341MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
 342MODULE_DESCRIPTION("DA9052 HWMON driver");
 343MODULE_LICENSE("GPL");
 344MODULE_ALIAS("platform:da9052-hwmon");
 345
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.