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