linux/drivers/hwmon/tc74.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * An hwmon driver for the Microchip TC74
   4 *
   5 * Copyright 2015 Maciej Szmigiero <mail@maciej.szmigiero.name>
   6 *
   7 * Based on ad7414.c:
   8 *      Copyright 2006 Stefan Roese, DENX Software Engineering
   9 *      Copyright 2008 Sean MacLennan, PIKA Technologies
  10 *      Copyright 2008 Frank Edelhaeuser, Spansion Inc.
  11 */
  12
  13#include <linux/bitops.h>
  14#include <linux/err.h>
  15#include <linux/hwmon.h>
  16#include <linux/hwmon-sysfs.h>
  17#include <linux/i2c.h>
  18#include <linux/jiffies.h>
  19#include <linux/module.h>
  20#include <linux/mutex.h>
  21#include <linux/slab.h>
  22#include <linux/sysfs.h>
  23
  24/* TC74 registers */
  25#define TC74_REG_TEMP           0x00
  26#define TC74_REG_CONFIG         0x01
  27
  28struct tc74_data {
  29        struct i2c_client       *client;
  30        struct mutex            lock;   /* atomic read data updates */
  31        bool                    valid;  /* validity of fields below */
  32        unsigned long           next_update;    /* In jiffies */
  33        s8                      temp_input;     /* Temp value in dC */
  34};
  35
  36static int tc74_update_device(struct device *dev)
  37{
  38        struct tc74_data *data = dev_get_drvdata(dev);
  39        struct i2c_client *client = data->client;
  40        int ret;
  41
  42        ret = mutex_lock_interruptible(&data->lock);
  43        if (ret)
  44                return ret;
  45
  46        if (time_after(jiffies, data->next_update) || !data->valid) {
  47                s32 value;
  48
  49                value = i2c_smbus_read_byte_data(client, TC74_REG_CONFIG);
  50                if (value < 0) {
  51                        dev_dbg(&client->dev, "TC74_REG_CONFIG read err %d\n",
  52                                (int)value);
  53
  54                        ret = value;
  55                        goto ret_unlock;
  56                }
  57
  58                if (!(value & BIT(6))) {
  59                        /* not ready yet */
  60
  61                        ret = -EAGAIN;
  62                        goto ret_unlock;
  63                }
  64
  65                value = i2c_smbus_read_byte_data(client, TC74_REG_TEMP);
  66                if (value < 0) {
  67                        dev_dbg(&client->dev, "TC74_REG_TEMP read err %d\n",
  68                                (int)value);
  69
  70                        ret = value;
  71                        goto ret_unlock;
  72                }
  73
  74                data->temp_input = value;
  75                data->next_update = jiffies + HZ / 4;
  76                data->valid = true;
  77        }
  78
  79ret_unlock:
  80        mutex_unlock(&data->lock);
  81
  82        return ret;
  83}
  84
  85static ssize_t temp_input_show(struct device *dev,
  86                               struct device_attribute *attr, char *buf)
  87{
  88        struct tc74_data *data = dev_get_drvdata(dev);
  89        int ret;
  90
  91        ret = tc74_update_device(dev);
  92        if (ret)
  93                return ret;
  94
  95        return sprintf(buf, "%d\n", data->temp_input * 1000);
  96}
  97static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_input, 0);
  98
  99static struct attribute *tc74_attrs[] = {
 100        &sensor_dev_attr_temp1_input.dev_attr.attr,
 101        NULL
 102};
 103
 104ATTRIBUTE_GROUPS(tc74);
 105
 106static int tc74_probe(struct i2c_client *client)
 107{
 108        struct device *dev = &client->dev;
 109        struct tc74_data *data;
 110        struct device *hwmon_dev;
 111        s32 conf;
 112
 113        if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
 114                return -EOPNOTSUPP;
 115
 116        data = devm_kzalloc(dev, sizeof(struct tc74_data), GFP_KERNEL);
 117        if (!data)
 118                return -ENOMEM;
 119
 120        data->client = client;
 121        mutex_init(&data->lock);
 122
 123        /* Make sure the chip is powered up. */
 124        conf = i2c_smbus_read_byte_data(client, TC74_REG_CONFIG);
 125        if (conf < 0) {
 126                dev_err(dev, "unable to read config register\n");
 127
 128                return conf;
 129        }
 130
 131        if (conf & 0x3f) {
 132                dev_err(dev, "invalid config register value\n");
 133
 134                return -ENODEV;
 135        }
 136
 137        if (conf & BIT(7)) {
 138                s32 ret;
 139
 140                conf &= ~BIT(7);
 141
 142                ret = i2c_smbus_write_byte_data(client, TC74_REG_CONFIG, conf);
 143                if (ret)
 144                        dev_warn(dev, "unable to disable STANDBY\n");
 145        }
 146
 147        hwmon_dev = devm_hwmon_device_register_with_groups(dev,
 148                                                           client->name,
 149                                                           data, tc74_groups);
 150        return PTR_ERR_OR_ZERO(hwmon_dev);
 151}
 152
 153static const struct i2c_device_id tc74_id[] = {
 154        { "tc74", 0 },
 155        {}
 156};
 157MODULE_DEVICE_TABLE(i2c, tc74_id);
 158
 159static struct i2c_driver tc74_driver = {
 160        .driver = {
 161                .name   = "tc74",
 162        },
 163        .probe_new = tc74_probe,
 164        .id_table = tc74_id,
 165};
 166
 167module_i2c_driver(tc74_driver);
 168
 169MODULE_AUTHOR("Maciej Szmigiero <mail@maciej.szmigiero.name>");
 170
 171MODULE_DESCRIPTION("TC74 driver");
 172MODULE_LICENSE("GPL");
 173