256 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * ds620.c - Support for temperature sensor and thermostat DS6204 *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 below24 * 15 14 13 12 11 10 09 0825 * |Done|NVB |THF |TLF |R1 |R0 |AUTOC|1SHOT|26 *27 * 07 06 05 04 03 02 01 0028 * |PO2 |PO1 |A2 |A1 |A0 | | | |29 */30#define DS620_REG_CONFIG_DONE 0x800031#define DS620_REG_CONFIG_NVB 0x400032#define DS620_REG_CONFIG_THF 0x200033#define DS620_REG_CONFIG_TLF 0x100034#define DS620_REG_CONFIG_R1 0x080035#define DS620_REG_CONFIG_R0 0x040036#define DS620_REG_CONFIG_AUTOC 0x020037#define DS620_REG_CONFIG_1SHOT 0x010038#define DS620_REG_CONFIG_PO2 0x008039#define DS620_REG_CONFIG_PO1 0x004040#define DS620_REG_CONFIG_A2 0x002041#define DS620_REG_CONFIG_A1 0x001042#define DS620_REG_CONFIG_A0 0x000843 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 bool valid; /* true 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 else83 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 = true;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 NULL210};211 212ATTRIBUTE_GROUPS(ds620);213 214static int ds620_probe(struct i2c_client *client)215{216 struct device *dev = &client->dev;217 struct device *hwmon_dev;218 struct ds620_data *data;219 220 data = devm_kzalloc(dev, sizeof(struct ds620_data), GFP_KERNEL);221 if (!data)222 return -ENOMEM;223 224 data->client = client;225 mutex_init(&data->update_lock);226 227 /* Initialize the DS620 chip */228 ds620_init_client(client);229 230 hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,231 data, ds620_groups);232 return PTR_ERR_OR_ZERO(hwmon_dev);233}234 235static const struct i2c_device_id ds620_id[] = {236 {"ds620"},237 {}238};239 240MODULE_DEVICE_TABLE(i2c, ds620_id);241 242/* This is the driver that will be inserted */243static struct i2c_driver ds620_driver = {244 .driver = {245 .name = "ds620",246 },247 .probe = ds620_probe,248 .id_table = ds620_id,249};250 251module_i2c_driver(ds620_driver);252 253MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");254MODULE_DESCRIPTION("DS620 driver");255MODULE_LICENSE("GPL");256