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