linux/drivers/hwmon/ds620.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  ds620.c - Support for temperature sensor and thermostat DS620
   4 *
   5 *  Copyright (C) 2010, 2011 Roland Stigge <stigge@antcom.de>
   6 *
   7 *  based on ds1621.c by Christian W. Zuckschwerdt  <zany@triq.net>
   8 */
   9
  10#include <linux/module.h>
  11#include <linux/init.h>
  12#include <linux/slab.h>
  13#include <linux/jiffies.h>
  14#include <linux/i2c.h>
  15#include <linux/hwmon.h>
  16#include <linux/hwmon-sysfs.h>
  17#include <linux/err.h>
  18#include <linux/mutex.h>
  19#include <linux/sysfs.h>
  20#include <linux/platform_data/ds620.h>
  21
  22/*
  23 * Many DS620 constants specified below
  24 *  15   14   13   12   11   10   09    08
  25 * |Done|NVB |THF |TLF |R1  |R0  |AUTOC|1SHOT|
  26 *
  27 *  07   06   05   04   03   02   01    00
  28 * |PO2 |PO1 |A2  |A1  |A0  |    |     |     |
  29 */
  30#define DS620_REG_CONFIG_DONE           0x8000
  31#define DS620_REG_CONFIG_NVB            0x4000
  32#define DS620_REG_CONFIG_THF            0x2000
  33#define DS620_REG_CONFIG_TLF            0x1000
  34#define DS620_REG_CONFIG_R1             0x0800
  35#define DS620_REG_CONFIG_R0             0x0400
  36#define DS620_REG_CONFIG_AUTOC          0x0200
  37#define DS620_REG_CONFIG_1SHOT          0x0100
  38#define DS620_REG_CONFIG_PO2            0x0080
  39#define DS620_REG_CONFIG_PO1            0x0040
  40#define DS620_REG_CONFIG_A2             0x0020
  41#define DS620_REG_CONFIG_A1             0x0010
  42#define DS620_REG_CONFIG_A0             0x0008
  43
  44/* The DS620 registers */
  45static const u8 DS620_REG_TEMP[3] = {
  46        0xAA,                   /* input, word, RO */
  47        0xA2,                   /* min, word, RW */
  48        0xA0,                   /* max, word, RW */
  49};
  50
  51#define DS620_REG_CONF          0xAC    /* word, RW */
  52#define DS620_COM_START         0x51    /* no data */
  53#define DS620_COM_STOP          0x22    /* no data */
  54
  55/* Each client has this additional data */
  56struct ds620_data {
  57        struct i2c_client *client;
  58        struct mutex update_lock;
  59        char valid;             /* !=0 if following fields are valid */
  60        unsigned long last_updated;     /* In jiffies */
  61
  62        s16 temp[3];            /* Register values, word */
  63};
  64
  65static void ds620_init_client(struct i2c_client *client)
  66{
  67        struct ds620_platform_data *ds620_info = dev_get_platdata(&client->dev);
  68        u16 conf, new_conf;
  69
  70        new_conf = conf =
  71            i2c_smbus_read_word_swapped(client, DS620_REG_CONF);
  72
  73        /* switch to continuous conversion mode */
  74        new_conf &= ~DS620_REG_CONFIG_1SHOT;
  75        /* already high at power-on, but don't trust the BIOS! */
  76        new_conf |= DS620_REG_CONFIG_PO2;
  77        /* thermostat mode according to platform data */
  78        if (ds620_info && ds620_info->pomode == 1)
  79                new_conf &= ~DS620_REG_CONFIG_PO1; /* PO_LOW */
  80        else if (ds620_info && ds620_info->pomode == 2)
  81                new_conf |= DS620_REG_CONFIG_PO1; /* PO_HIGH */
  82        else
  83                new_conf &= ~DS620_REG_CONFIG_PO2; /* always low */
  84        /* with highest precision */
  85        new_conf |= DS620_REG_CONFIG_R1 | DS620_REG_CONFIG_R0;
  86
  87        if (conf != new_conf)
  88                i2c_smbus_write_word_swapped(client, DS620_REG_CONF, new_conf);
  89
  90        /* start conversion */
  91        i2c_smbus_write_byte(client, DS620_COM_START);
  92}
  93
  94static struct ds620_data *ds620_update_client(struct device *dev)
  95{
  96        struct ds620_data *data = dev_get_drvdata(dev);
  97        struct i2c_client *client = data->client;
  98        struct ds620_data *ret = data;
  99
 100        mutex_lock(&data->update_lock);
 101
 102        if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
 103            || !data->valid) {
 104                int i;
 105                int res;
 106
 107                dev_dbg(&client->dev, "Starting ds620 update\n");
 108
 109                for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
 110                        res = i2c_smbus_read_word_swapped(client,
 111                                                          DS620_REG_TEMP[i]);
 112                        if (res < 0) {
 113                                ret = ERR_PTR(res);
 114                                goto abort;
 115                        }
 116
 117                        data->temp[i] = res;
 118                }
 119
 120                data->last_updated = jiffies;
 121                data->valid = 1;
 122        }
 123abort:
 124        mutex_unlock(&data->update_lock);
 125
 126        return ret;
 127}
 128
 129static ssize_t temp_show(struct device *dev, struct device_attribute *da,
 130                         char *buf)
 131{
 132        struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
 133        struct ds620_data *data = ds620_update_client(dev);
 134
 135        if (IS_ERR(data))
 136                return PTR_ERR(data);
 137
 138        return sprintf(buf, "%d\n", ((data->temp[attr->index] / 8) * 625) / 10);
 139}
 140
 141static ssize_t temp_store(struct device *dev, struct device_attribute *da,
 142                          const char *buf, size_t count)
 143{
 144        int res;
 145        long val;
 146
 147        struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
 148        struct ds620_data *data = dev_get_drvdata(dev);
 149        struct i2c_client *client = data->client;
 150
 151        res = kstrtol(buf, 10, &val);
 152
 153        if (res)
 154                return res;
 155
 156        val = (clamp_val(val, -128000, 128000) * 10 / 625) * 8;
 157
 158        mutex_lock(&data->update_lock);
 159        data->temp[attr->index] = val;
 160        i2c_smbus_write_word_swapped(client, DS620_REG_TEMP[attr->index],
 161                                     data->temp[attr->index]);
 162        mutex_unlock(&data->update_lock);
 163        return count;
 164}
 165
 166static ssize_t alarm_show(struct device *dev, struct device_attribute *da,
 167                          char *buf)
 168{
 169        struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
 170        struct ds620_data *data = ds620_update_client(dev);
 171        struct i2c_client *client;
 172        u16 conf, new_conf;
 173        int res;
 174
 175        if (IS_ERR(data))
 176                return PTR_ERR(data);
 177
 178        client = data->client;
 179
 180        /* reset alarms if necessary */
 181        res = i2c_smbus_read_word_swapped(client, DS620_REG_CONF);
 182        if (res < 0)
 183                return res;
 184
 185        new_conf = conf = res;
 186        new_conf &= ~attr->index;
 187        if (conf != new_conf) {
 188                res = i2c_smbus_write_word_swapped(client, DS620_REG_CONF,
 189                                                   new_conf);
 190                if (res < 0)
 191                        return res;
 192        }
 193
 194        return sprintf(buf, "%d\n", !!(conf & attr->index));
 195}
 196
 197static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0);
 198static SENSOR_DEVICE_ATTR_RW(temp1_min, temp, 1);
 199static SENSOR_DEVICE_ATTR_RW(temp1_max, temp, 2);
 200static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, alarm, DS620_REG_CONFIG_TLF);
 201static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, DS620_REG_CONFIG_THF);
 202
 203static struct attribute *ds620_attrs[] = {
 204        &sensor_dev_attr_temp1_input.dev_attr.attr,
 205        &sensor_dev_attr_temp1_min.dev_attr.attr,
 206        &sensor_dev_attr_temp1_max.dev_attr.attr,
 207        &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
 208        &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
 209        NULL
 210};
 211
 212ATTRIBUTE_GROUPS(ds620);
 213
 214static int ds620_probe(struct i2c_client *client,
 215                       const struct i2c_device_id *id)
 216{
 217        struct device *dev = &client->dev;
 218        struct device *hwmon_dev;
 219        struct ds620_data *data;
 220
 221        data = devm_kzalloc(dev, sizeof(struct ds620_data), GFP_KERNEL);
 222        if (!data)
 223                return -ENOMEM;
 224
 225        data->client = client;
 226        mutex_init(&data->update_lock);
 227
 228        /* Initialize the DS620 chip */
 229        ds620_init_client(client);
 230
 231        hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
 232                                                           data, ds620_groups);
 233        return PTR_ERR_OR_ZERO(hwmon_dev);
 234}
 235
 236static const struct i2c_device_id ds620_id[] = {
 237        {"ds620", 0},
 238        {}
 239};
 240
 241MODULE_DEVICE_TABLE(i2c, ds620_id);
 242
 243/* This is the driver that will be inserted */
 244static struct i2c_driver ds620_driver = {
 245        .class = I2C_CLASS_HWMON,
 246        .driver = {
 247                   .name = "ds620",
 248        },
 249        .probe = ds620_probe,
 250        .id_table = ds620_id,
 251};
 252
 253module_i2c_driver(ds620_driver);
 254
 255MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
 256MODULE_DESCRIPTION("DS620 driver");
 257MODULE_LICENSE("GPL");
 258