linux/drivers/hwmon/w83773g.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (C) 2017 IBM Corp.
   4 *
   5 * Driver for the Nuvoton W83773G SMBus temperature sensor IC.
   6 * Supported models: W83773G
   7 */
   8
   9#include <linux/module.h>
  10#include <linux/init.h>
  11#include <linux/i2c.h>
  12#include <linux/hwmon.h>
  13#include <linux/hwmon-sysfs.h>
  14#include <linux/err.h>
  15#include <linux/of_device.h>
  16#include <linux/regmap.h>
  17
  18/* W83773 has 3 channels */
  19#define W83773_CHANNELS                         3
  20
  21/* The W83773 registers */
  22#define W83773_CONVERSION_RATE_REG_READ         0x04
  23#define W83773_CONVERSION_RATE_REG_WRITE        0x0A
  24#define W83773_MANUFACTURER_ID_REG              0xFE
  25#define W83773_LOCAL_TEMP                       0x00
  26
  27static const u8 W83773_STATUS[2] = { 0x02, 0x17 };
  28
  29static const u8 W83773_TEMP_LSB[2] = { 0x10, 0x25 };
  30static const u8 W83773_TEMP_MSB[2] = { 0x01, 0x24 };
  31
  32static const u8 W83773_OFFSET_LSB[2] = { 0x12, 0x16 };
  33static const u8 W83773_OFFSET_MSB[2] = { 0x11, 0x15 };
  34
  35/* this is the number of sensors in the device */
  36static const struct i2c_device_id w83773_id[] = {
  37        { "w83773g" },
  38        { }
  39};
  40
  41MODULE_DEVICE_TABLE(i2c, w83773_id);
  42
  43static const struct of_device_id __maybe_unused w83773_of_match[] = {
  44        {
  45                .compatible = "nuvoton,w83773g"
  46        },
  47        { },
  48};
  49MODULE_DEVICE_TABLE(of, w83773_of_match);
  50
  51static inline long temp_of_local(s8 reg)
  52{
  53        return reg * 1000;
  54}
  55
  56static inline long temp_of_remote(s8 hb, u8 lb)
  57{
  58        return (hb << 3 | lb >> 5) * 125;
  59}
  60
  61static int get_local_temp(struct regmap *regmap, long *val)
  62{
  63        unsigned int regval;
  64        int ret;
  65
  66        ret = regmap_read(regmap, W83773_LOCAL_TEMP, &regval);
  67        if (ret < 0)
  68                return ret;
  69
  70        *val = temp_of_local(regval);
  71        return 0;
  72}
  73
  74static int get_remote_temp(struct regmap *regmap, int index, long *val)
  75{
  76        unsigned int regval_high;
  77        unsigned int regval_low;
  78        int ret;
  79
  80        ret = regmap_read(regmap, W83773_TEMP_MSB[index], &regval_high);
  81        if (ret < 0)
  82                return ret;
  83
  84        ret = regmap_read(regmap, W83773_TEMP_LSB[index], &regval_low);
  85        if (ret < 0)
  86                return ret;
  87
  88        *val = temp_of_remote(regval_high, regval_low);
  89        return 0;
  90}
  91
  92static int get_fault(struct regmap *regmap, int index, long *val)
  93{
  94        unsigned int regval;
  95        int ret;
  96
  97        ret = regmap_read(regmap, W83773_STATUS[index], &regval);
  98        if (ret < 0)
  99                return ret;
 100
 101        *val = (regval & 0x04) >> 2;
 102        return 0;
 103}
 104
 105static int get_offset(struct regmap *regmap, int index, long *val)
 106{
 107        unsigned int regval_high;
 108        unsigned int regval_low;
 109        int ret;
 110
 111        ret = regmap_read(regmap, W83773_OFFSET_MSB[index], &regval_high);
 112        if (ret < 0)
 113                return ret;
 114
 115        ret = regmap_read(regmap, W83773_OFFSET_LSB[index], &regval_low);
 116        if (ret < 0)
 117                return ret;
 118
 119        *val = temp_of_remote(regval_high, regval_low);
 120        return 0;
 121}
 122
 123static int set_offset(struct regmap *regmap, int index, long val)
 124{
 125        int ret;
 126        u8 high_byte;
 127        u8 low_byte;
 128
 129        val = clamp_val(val, -127825, 127825);
 130        /* offset value equals to (high_byte << 3 | low_byte >> 5) * 125 */
 131        val /= 125;
 132        high_byte = val >> 3;
 133        low_byte = (val & 0x07) << 5;
 134
 135        ret = regmap_write(regmap, W83773_OFFSET_MSB[index], high_byte);
 136        if (ret < 0)
 137                return ret;
 138
 139        return regmap_write(regmap, W83773_OFFSET_LSB[index], low_byte);
 140}
 141
 142static int get_update_interval(struct regmap *regmap, long *val)
 143{
 144        unsigned int regval;
 145        int ret;
 146
 147        ret = regmap_read(regmap, W83773_CONVERSION_RATE_REG_READ, &regval);
 148        if (ret < 0)
 149                return ret;
 150
 151        *val = 16000 >> regval;
 152        return 0;
 153}
 154
 155static int set_update_interval(struct regmap *regmap, long val)
 156{
 157        int rate;
 158
 159        /*
 160         * For valid rates, interval can be calculated as
 161         *      interval = (1 << (8 - rate)) * 62.5;
 162         * Rounded rate is therefore
 163         *      rate = 8 - __fls(interval * 8 / (62.5 * 7));
 164         * Use clamp_val() to avoid overflows, and to ensure valid input
 165         * for __fls.
 166         */
 167        val = clamp_val(val, 62, 16000) * 10;
 168        rate = 8 - __fls((val * 8 / (625 * 7)));
 169        return regmap_write(regmap, W83773_CONVERSION_RATE_REG_WRITE, rate);
 170}
 171
 172static int w83773_read(struct device *dev, enum hwmon_sensor_types type,
 173                       u32 attr, int channel, long *val)
 174{
 175        struct regmap *regmap = dev_get_drvdata(dev);
 176
 177        if (type == hwmon_chip) {
 178                if (attr == hwmon_chip_update_interval)
 179                        return get_update_interval(regmap, val);
 180                return -EOPNOTSUPP;
 181        }
 182
 183        switch (attr) {
 184        case hwmon_temp_input:
 185                if (channel == 0)
 186                        return get_local_temp(regmap, val);
 187                return get_remote_temp(regmap, channel - 1, val);
 188        case hwmon_temp_fault:
 189                return get_fault(regmap, channel - 1, val);
 190        case hwmon_temp_offset:
 191                return get_offset(regmap, channel - 1, val);
 192        default:
 193                return -EOPNOTSUPP;
 194        }
 195}
 196
 197static int w83773_write(struct device *dev, enum hwmon_sensor_types type,
 198                        u32 attr, int channel, long val)
 199{
 200        struct regmap *regmap = dev_get_drvdata(dev);
 201
 202        if (type == hwmon_chip && attr == hwmon_chip_update_interval)
 203                return set_update_interval(regmap, val);
 204
 205        if (type == hwmon_temp && attr == hwmon_temp_offset)
 206                return set_offset(regmap, channel - 1, val);
 207
 208        return -EOPNOTSUPP;
 209}
 210
 211static umode_t w83773_is_visible(const void *data, enum hwmon_sensor_types type,
 212                                 u32 attr, int channel)
 213{
 214        switch (type) {
 215        case hwmon_chip:
 216                switch (attr) {
 217                case hwmon_chip_update_interval:
 218                        return 0644;
 219                }
 220                break;
 221        case hwmon_temp:
 222                switch (attr) {
 223                case hwmon_temp_input:
 224                case hwmon_temp_fault:
 225                        return 0444;
 226                case hwmon_temp_offset:
 227                        return 0644;
 228                }
 229                break;
 230        default:
 231                break;
 232        }
 233        return 0;
 234}
 235
 236static const struct hwmon_channel_info *w83773_info[] = {
 237        HWMON_CHANNEL_INFO(chip,
 238                           HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL),
 239        HWMON_CHANNEL_INFO(temp,
 240                           HWMON_T_INPUT,
 241                           HWMON_T_INPUT | HWMON_T_FAULT | HWMON_T_OFFSET,
 242                           HWMON_T_INPUT | HWMON_T_FAULT | HWMON_T_OFFSET),
 243        NULL
 244};
 245
 246static const struct hwmon_ops w83773_ops = {
 247        .is_visible = w83773_is_visible,
 248        .read = w83773_read,
 249        .write = w83773_write,
 250};
 251
 252static const struct hwmon_chip_info w83773_chip_info = {
 253        .ops = &w83773_ops,
 254        .info = w83773_info,
 255};
 256
 257static const struct regmap_config w83773_regmap_config = {
 258        .reg_bits = 8,
 259        .val_bits = 8,
 260};
 261
 262static int w83773_probe(struct i2c_client *client)
 263{
 264        struct device *dev = &client->dev;
 265        struct device *hwmon_dev;
 266        struct regmap *regmap;
 267        int ret;
 268
 269        regmap = devm_regmap_init_i2c(client, &w83773_regmap_config);
 270        if (IS_ERR(regmap)) {
 271                dev_err(dev, "failed to allocate register map\n");
 272                return PTR_ERR(regmap);
 273        }
 274
 275        /* Set the conversion rate to 2 Hz */
 276        ret = regmap_write(regmap, W83773_CONVERSION_RATE_REG_WRITE, 0x05);
 277        if (ret < 0) {
 278                dev_err(&client->dev, "error writing config rate register\n");
 279                return ret;
 280        }
 281
 282        i2c_set_clientdata(client, regmap);
 283
 284        hwmon_dev = devm_hwmon_device_register_with_info(dev,
 285                                                         client->name,
 286                                                         regmap,
 287                                                         &w83773_chip_info,
 288                                                         NULL);
 289        return PTR_ERR_OR_ZERO(hwmon_dev);
 290}
 291
 292static struct i2c_driver w83773_driver = {
 293        .class = I2C_CLASS_HWMON,
 294        .driver = {
 295                .name   = "w83773g",
 296                .of_match_table = of_match_ptr(w83773_of_match),
 297        },
 298        .probe_new = w83773_probe,
 299        .id_table = w83773_id,
 300};
 301
 302module_i2c_driver(w83773_driver);
 303
 304MODULE_AUTHOR("Lei YU <mine260309@gmail.com>");
 305MODULE_DESCRIPTION("W83773G temperature sensor driver");
 306MODULE_LICENSE("GPL");
 307