160994698SRoland Stigge /* 260994698SRoland Stigge * ds620.c - Support for temperature sensor and thermostat DS620 360994698SRoland Stigge * 460994698SRoland Stigge * Copyright (C) 2010, 2011 Roland Stigge <stigge@antcom.de> 560994698SRoland Stigge * 660994698SRoland Stigge * based on ds1621.c by Christian W. Zuckschwerdt <zany@triq.net> 760994698SRoland Stigge * 860994698SRoland Stigge * This program is free software; you can redistribute it and/or modify 960994698SRoland Stigge * it under the terms of the GNU General Public License as published by 1060994698SRoland Stigge * the Free Software Foundation; either version 2 of the License, or 1160994698SRoland Stigge * (at your option) any later version. 1260994698SRoland Stigge * 1360994698SRoland Stigge * This program is distributed in the hope that it will be useful, 1460994698SRoland Stigge * but WITHOUT ANY WARRANTY; without even the implied warranty of 1560994698SRoland Stigge * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1660994698SRoland Stigge * GNU General Public License for more details. 1760994698SRoland Stigge * 1860994698SRoland Stigge * You should have received a copy of the GNU General Public License 1960994698SRoland Stigge * along with this program; if not, write to the Free Software 2060994698SRoland Stigge * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 2160994698SRoland Stigge */ 2260994698SRoland Stigge 2360994698SRoland Stigge #include <linux/module.h> 2460994698SRoland Stigge #include <linux/init.h> 2560994698SRoland Stigge #include <linux/slab.h> 2660994698SRoland Stigge #include <linux/jiffies.h> 2760994698SRoland Stigge #include <linux/i2c.h> 2860994698SRoland Stigge #include <linux/hwmon.h> 2960994698SRoland Stigge #include <linux/hwmon-sysfs.h> 3060994698SRoland Stigge #include <linux/err.h> 3160994698SRoland Stigge #include <linux/mutex.h> 3260994698SRoland Stigge #include <linux/sysfs.h> 3360994698SRoland Stigge #include <linux/i2c/ds620.h> 3460994698SRoland Stigge 3560994698SRoland Stigge /* 3660994698SRoland Stigge * Many DS620 constants specified below 3760994698SRoland Stigge * 15 14 13 12 11 10 09 08 3860994698SRoland Stigge * |Done|NVB |THF |TLF |R1 |R0 |AUTOC|1SHOT| 3960994698SRoland Stigge * 4060994698SRoland Stigge * 07 06 05 04 03 02 01 00 4160994698SRoland Stigge * |PO2 |PO1 |A2 |A1 |A0 | | | | 4260994698SRoland Stigge */ 4360994698SRoland Stigge #define DS620_REG_CONFIG_DONE 0x8000 4460994698SRoland Stigge #define DS620_REG_CONFIG_NVB 0x4000 4560994698SRoland Stigge #define DS620_REG_CONFIG_THF 0x2000 4660994698SRoland Stigge #define DS620_REG_CONFIG_TLF 0x1000 4760994698SRoland Stigge #define DS620_REG_CONFIG_R1 0x0800 4860994698SRoland Stigge #define DS620_REG_CONFIG_R0 0x0400 4960994698SRoland Stigge #define DS620_REG_CONFIG_AUTOC 0x0200 5060994698SRoland Stigge #define DS620_REG_CONFIG_1SHOT 0x0100 5160994698SRoland Stigge #define DS620_REG_CONFIG_PO2 0x0080 5260994698SRoland Stigge #define DS620_REG_CONFIG_PO1 0x0040 5360994698SRoland Stigge #define DS620_REG_CONFIG_A2 0x0020 5460994698SRoland Stigge #define DS620_REG_CONFIG_A1 0x0010 5560994698SRoland Stigge #define DS620_REG_CONFIG_A0 0x0008 5660994698SRoland Stigge 5760994698SRoland Stigge /* The DS620 registers */ 5860994698SRoland Stigge static const u8 DS620_REG_TEMP[3] = { 5960994698SRoland Stigge 0xAA, /* input, word, RO */ 6060994698SRoland Stigge 0xA2, /* min, word, RW */ 6160994698SRoland Stigge 0xA0, /* max, word, RW */ 6260994698SRoland Stigge }; 6360994698SRoland Stigge 6460994698SRoland Stigge #define DS620_REG_CONF 0xAC /* word, RW */ 6560994698SRoland Stigge #define DS620_COM_START 0x51 /* no data */ 6660994698SRoland Stigge #define DS620_COM_STOP 0x22 /* no data */ 6760994698SRoland Stigge 6860994698SRoland Stigge /* Each client has this additional data */ 6960994698SRoland Stigge struct ds620_data { 7060994698SRoland Stigge struct device *hwmon_dev; 7160994698SRoland Stigge struct mutex update_lock; 7260994698SRoland Stigge char valid; /* !=0 if following fields are valid */ 7360994698SRoland Stigge unsigned long last_updated; /* In jiffies */ 7460994698SRoland Stigge 75*cc41d586SRoland Stigge s16 temp[3]; /* Register values, word */ 7660994698SRoland Stigge }; 7760994698SRoland Stigge 7860994698SRoland Stigge /* 7960994698SRoland Stigge * Temperature registers are word-sized. 8060994698SRoland Stigge * DS620 uses a high-byte first convention, which is exactly opposite to 8160994698SRoland Stigge * the SMBus standard. 8260994698SRoland Stigge */ 8360994698SRoland Stigge static int ds620_read_temp(struct i2c_client *client, u8 reg) 8460994698SRoland Stigge { 8560994698SRoland Stigge int ret; 8660994698SRoland Stigge 8760994698SRoland Stigge ret = i2c_smbus_read_word_data(client, reg); 8860994698SRoland Stigge if (ret < 0) 8960994698SRoland Stigge return ret; 9060994698SRoland Stigge return swab16(ret); 9160994698SRoland Stigge } 9260994698SRoland Stigge 9360994698SRoland Stigge static int ds620_write_temp(struct i2c_client *client, u8 reg, u16 value) 9460994698SRoland Stigge { 9560994698SRoland Stigge return i2c_smbus_write_word_data(client, reg, swab16(value)); 9660994698SRoland Stigge } 9760994698SRoland Stigge 9860994698SRoland Stigge static void ds620_init_client(struct i2c_client *client) 9960994698SRoland Stigge { 10060994698SRoland Stigge struct ds620_platform_data *ds620_info = client->dev.platform_data; 10160994698SRoland Stigge u16 conf, new_conf; 10260994698SRoland Stigge 10360994698SRoland Stigge new_conf = conf = 10460994698SRoland Stigge swab16(i2c_smbus_read_word_data(client, DS620_REG_CONF)); 10560994698SRoland Stigge 10660994698SRoland Stigge /* switch to continuous conversion mode */ 10760994698SRoland Stigge new_conf &= ~DS620_REG_CONFIG_1SHOT; 10860994698SRoland Stigge /* already high at power-on, but don't trust the BIOS! */ 10960994698SRoland Stigge new_conf |= DS620_REG_CONFIG_PO2; 11060994698SRoland Stigge /* thermostat mode according to platform data */ 11160994698SRoland Stigge if (ds620_info && ds620_info->pomode == 1) 11260994698SRoland Stigge new_conf &= ~DS620_REG_CONFIG_PO1; /* PO_LOW */ 11360994698SRoland Stigge else if (ds620_info && ds620_info->pomode == 2) 11460994698SRoland Stigge new_conf |= DS620_REG_CONFIG_PO1; /* PO_HIGH */ 11560994698SRoland Stigge else 11660994698SRoland Stigge new_conf &= ~DS620_REG_CONFIG_PO2; /* always low */ 11760994698SRoland Stigge /* with highest precision */ 11860994698SRoland Stigge new_conf |= DS620_REG_CONFIG_R1 | DS620_REG_CONFIG_R0; 11960994698SRoland Stigge 12060994698SRoland Stigge if (conf != new_conf) 12160994698SRoland Stigge i2c_smbus_write_word_data(client, DS620_REG_CONF, 12260994698SRoland Stigge swab16(new_conf)); 12360994698SRoland Stigge 12460994698SRoland Stigge /* start conversion */ 12560994698SRoland Stigge i2c_smbus_write_byte(client, DS620_COM_START); 12660994698SRoland Stigge } 12760994698SRoland Stigge 12860994698SRoland Stigge static struct ds620_data *ds620_update_client(struct device *dev) 12960994698SRoland Stigge { 13060994698SRoland Stigge struct i2c_client *client = to_i2c_client(dev); 13160994698SRoland Stigge struct ds620_data *data = i2c_get_clientdata(client); 13260994698SRoland Stigge struct ds620_data *ret = data; 13360994698SRoland Stigge 13460994698SRoland Stigge mutex_lock(&data->update_lock); 13560994698SRoland Stigge 13660994698SRoland Stigge if (time_after(jiffies, data->last_updated + HZ + HZ / 2) 13760994698SRoland Stigge || !data->valid) { 13860994698SRoland Stigge int i; 13960994698SRoland Stigge int res; 14060994698SRoland Stigge 14160994698SRoland Stigge dev_dbg(&client->dev, "Starting ds620 update\n"); 14260994698SRoland Stigge 14360994698SRoland Stigge for (i = 0; i < ARRAY_SIZE(data->temp); i++) { 14460994698SRoland Stigge res = ds620_read_temp(client, 14560994698SRoland Stigge DS620_REG_TEMP[i]); 14660994698SRoland Stigge if (res < 0) { 14760994698SRoland Stigge ret = ERR_PTR(res); 14860994698SRoland Stigge goto abort; 14960994698SRoland Stigge } 15060994698SRoland Stigge 15160994698SRoland Stigge data->temp[i] = res; 15260994698SRoland Stigge } 15360994698SRoland Stigge 15460994698SRoland Stigge data->last_updated = jiffies; 15560994698SRoland Stigge data->valid = 1; 15660994698SRoland Stigge } 15760994698SRoland Stigge abort: 15860994698SRoland Stigge mutex_unlock(&data->update_lock); 15960994698SRoland Stigge 16060994698SRoland Stigge return ret; 16160994698SRoland Stigge } 16260994698SRoland Stigge 16360994698SRoland Stigge static ssize_t show_temp(struct device *dev, struct device_attribute *da, 16460994698SRoland Stigge char *buf) 16560994698SRoland Stigge { 16660994698SRoland Stigge struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 16760994698SRoland Stigge struct ds620_data *data = ds620_update_client(dev); 16860994698SRoland Stigge 16960994698SRoland Stigge if (IS_ERR(data)) 17060994698SRoland Stigge return PTR_ERR(data); 17160994698SRoland Stigge 17260994698SRoland Stigge return sprintf(buf, "%d\n", ((data->temp[attr->index] / 8) * 625) / 10); 17360994698SRoland Stigge } 17460994698SRoland Stigge 17560994698SRoland Stigge static ssize_t set_temp(struct device *dev, struct device_attribute *da, 17660994698SRoland Stigge const char *buf, size_t count) 17760994698SRoland Stigge { 17860994698SRoland Stigge int res; 17960994698SRoland Stigge long val; 18060994698SRoland Stigge 18160994698SRoland Stigge struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 18260994698SRoland Stigge struct i2c_client *client = to_i2c_client(dev); 18360994698SRoland Stigge struct ds620_data *data = i2c_get_clientdata(client); 18460994698SRoland Stigge 18560994698SRoland Stigge res = strict_strtol(buf, 10, &val); 18660994698SRoland Stigge 18760994698SRoland Stigge if (res) 18860994698SRoland Stigge return res; 18960994698SRoland Stigge 19060994698SRoland Stigge val = (val * 10 / 625) * 8; 19160994698SRoland Stigge 19260994698SRoland Stigge mutex_lock(&data->update_lock); 19360994698SRoland Stigge data->temp[attr->index] = val; 19460994698SRoland Stigge ds620_write_temp(client, DS620_REG_TEMP[attr->index], 19560994698SRoland Stigge data->temp[attr->index]); 19660994698SRoland Stigge mutex_unlock(&data->update_lock); 19760994698SRoland Stigge return count; 19860994698SRoland Stigge } 19960994698SRoland Stigge 20060994698SRoland Stigge static ssize_t show_alarm(struct device *dev, struct device_attribute *da, 20160994698SRoland Stigge char *buf) 20260994698SRoland Stigge { 20360994698SRoland Stigge struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 20460994698SRoland Stigge struct ds620_data *data = ds620_update_client(dev); 20560994698SRoland Stigge struct i2c_client *client = to_i2c_client(dev); 20660994698SRoland Stigge u16 conf, new_conf; 20760994698SRoland Stigge int res; 20860994698SRoland Stigge 20960994698SRoland Stigge if (IS_ERR(data)) 21060994698SRoland Stigge return PTR_ERR(data); 21160994698SRoland Stigge 21260994698SRoland Stigge /* reset alarms if necessary */ 21360994698SRoland Stigge res = i2c_smbus_read_word_data(client, DS620_REG_CONF); 21460994698SRoland Stigge if (res < 0) 21560994698SRoland Stigge return res; 21660994698SRoland Stigge 21760994698SRoland Stigge conf = swab16(res); 21860994698SRoland Stigge new_conf = conf; 21960994698SRoland Stigge new_conf &= ~attr->index; 22060994698SRoland Stigge if (conf != new_conf) { 22160994698SRoland Stigge res = i2c_smbus_write_word_data(client, DS620_REG_CONF, 22260994698SRoland Stigge swab16(new_conf)); 22360994698SRoland Stigge if (res < 0) 22460994698SRoland Stigge return res; 22560994698SRoland Stigge } 22660994698SRoland Stigge 22760994698SRoland Stigge return sprintf(buf, "%d\n", !!(conf & attr->index)); 22860994698SRoland Stigge } 22960994698SRoland Stigge 23060994698SRoland Stigge static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); 23160994698SRoland Stigge static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp, set_temp, 1); 23260994698SRoland Stigge static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp, set_temp, 2); 23360994698SRoland Stigge static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 23460994698SRoland Stigge DS620_REG_CONFIG_TLF); 23560994698SRoland Stigge static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 23660994698SRoland Stigge DS620_REG_CONFIG_THF); 23760994698SRoland Stigge 23860994698SRoland Stigge static struct attribute *ds620_attributes[] = { 23960994698SRoland Stigge &sensor_dev_attr_temp1_input.dev_attr.attr, 24060994698SRoland Stigge &sensor_dev_attr_temp1_min.dev_attr.attr, 24160994698SRoland Stigge &sensor_dev_attr_temp1_max.dev_attr.attr, 24260994698SRoland Stigge &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, 24360994698SRoland Stigge &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, 24460994698SRoland Stigge NULL 24560994698SRoland Stigge }; 24660994698SRoland Stigge 24760994698SRoland Stigge static const struct attribute_group ds620_group = { 24860994698SRoland Stigge .attrs = ds620_attributes, 24960994698SRoland Stigge }; 25060994698SRoland Stigge 25160994698SRoland Stigge static int ds620_probe(struct i2c_client *client, 25260994698SRoland Stigge const struct i2c_device_id *id) 25360994698SRoland Stigge { 25460994698SRoland Stigge struct ds620_data *data; 25560994698SRoland Stigge int err; 25660994698SRoland Stigge 25760994698SRoland Stigge data = kzalloc(sizeof(struct ds620_data), GFP_KERNEL); 25860994698SRoland Stigge if (!data) { 25960994698SRoland Stigge err = -ENOMEM; 26060994698SRoland Stigge goto exit; 26160994698SRoland Stigge } 26260994698SRoland Stigge 26360994698SRoland Stigge i2c_set_clientdata(client, data); 26460994698SRoland Stigge mutex_init(&data->update_lock); 26560994698SRoland Stigge 26660994698SRoland Stigge /* Initialize the DS620 chip */ 26760994698SRoland Stigge ds620_init_client(client); 26860994698SRoland Stigge 26960994698SRoland Stigge /* Register sysfs hooks */ 27060994698SRoland Stigge err = sysfs_create_group(&client->dev.kobj, &ds620_group); 27160994698SRoland Stigge if (err) 27260994698SRoland Stigge goto exit_free; 27360994698SRoland Stigge 27460994698SRoland Stigge data->hwmon_dev = hwmon_device_register(&client->dev); 27560994698SRoland Stigge if (IS_ERR(data->hwmon_dev)) { 27660994698SRoland Stigge err = PTR_ERR(data->hwmon_dev); 27760994698SRoland Stigge goto exit_remove_files; 27860994698SRoland Stigge } 27960994698SRoland Stigge 28060994698SRoland Stigge dev_info(&client->dev, "temperature sensor found\n"); 28160994698SRoland Stigge 28260994698SRoland Stigge return 0; 28360994698SRoland Stigge 28460994698SRoland Stigge exit_remove_files: 28560994698SRoland Stigge sysfs_remove_group(&client->dev.kobj, &ds620_group); 28660994698SRoland Stigge exit_free: 28760994698SRoland Stigge kfree(data); 28860994698SRoland Stigge exit: 28960994698SRoland Stigge return err; 29060994698SRoland Stigge } 29160994698SRoland Stigge 29260994698SRoland Stigge static int ds620_remove(struct i2c_client *client) 29360994698SRoland Stigge { 29460994698SRoland Stigge struct ds620_data *data = i2c_get_clientdata(client); 29560994698SRoland Stigge 29660994698SRoland Stigge hwmon_device_unregister(data->hwmon_dev); 29760994698SRoland Stigge sysfs_remove_group(&client->dev.kobj, &ds620_group); 29860994698SRoland Stigge 29960994698SRoland Stigge kfree(data); 30060994698SRoland Stigge 30160994698SRoland Stigge return 0; 30260994698SRoland Stigge } 30360994698SRoland Stigge 30460994698SRoland Stigge static const struct i2c_device_id ds620_id[] = { 30560994698SRoland Stigge {"ds620", 0}, 30660994698SRoland Stigge {} 30760994698SRoland Stigge }; 30860994698SRoland Stigge 30960994698SRoland Stigge MODULE_DEVICE_TABLE(i2c, ds620_id); 31060994698SRoland Stigge 31160994698SRoland Stigge /* This is the driver that will be inserted */ 31260994698SRoland Stigge static struct i2c_driver ds620_driver = { 31360994698SRoland Stigge .class = I2C_CLASS_HWMON, 31460994698SRoland Stigge .driver = { 31560994698SRoland Stigge .name = "ds620", 31660994698SRoland Stigge }, 31760994698SRoland Stigge .probe = ds620_probe, 31860994698SRoland Stigge .remove = ds620_remove, 31960994698SRoland Stigge .id_table = ds620_id, 32060994698SRoland Stigge }; 32160994698SRoland Stigge 32260994698SRoland Stigge static int __init ds620_init(void) 32360994698SRoland Stigge { 32460994698SRoland Stigge return i2c_add_driver(&ds620_driver); 32560994698SRoland Stigge } 32660994698SRoland Stigge 32760994698SRoland Stigge static void __exit ds620_exit(void) 32860994698SRoland Stigge { 32960994698SRoland Stigge i2c_del_driver(&ds620_driver); 33060994698SRoland Stigge } 33160994698SRoland Stigge 33260994698SRoland Stigge MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>"); 33360994698SRoland Stigge MODULE_DESCRIPTION("DS620 driver"); 33460994698SRoland Stigge MODULE_LICENSE("GPL"); 33560994698SRoland Stigge 33660994698SRoland Stigge module_init(ds620_init); 33760994698SRoland Stigge module_exit(ds620_exit); 338