18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ds620.c - Support for temperature sensor and thermostat DS620 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010, 2011 Roland Stigge <stigge@antcom.de> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * based on ds1621.c by Christian W. Zuckschwerdt <zany@triq.net> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 148c2ecf20Sopenharmony_ci#include <linux/i2c.h> 158c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 168c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 178c2ecf20Sopenharmony_ci#include <linux/err.h> 188c2ecf20Sopenharmony_ci#include <linux/mutex.h> 198c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 208c2ecf20Sopenharmony_ci#include <linux/platform_data/ds620.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* 238c2ecf20Sopenharmony_ci * Many DS620 constants specified below 248c2ecf20Sopenharmony_ci * 15 14 13 12 11 10 09 08 258c2ecf20Sopenharmony_ci * |Done|NVB |THF |TLF |R1 |R0 |AUTOC|1SHOT| 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * 07 06 05 04 03 02 01 00 288c2ecf20Sopenharmony_ci * |PO2 |PO1 |A2 |A1 |A0 | | | | 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci#define DS620_REG_CONFIG_DONE 0x8000 318c2ecf20Sopenharmony_ci#define DS620_REG_CONFIG_NVB 0x4000 328c2ecf20Sopenharmony_ci#define DS620_REG_CONFIG_THF 0x2000 338c2ecf20Sopenharmony_ci#define DS620_REG_CONFIG_TLF 0x1000 348c2ecf20Sopenharmony_ci#define DS620_REG_CONFIG_R1 0x0800 358c2ecf20Sopenharmony_ci#define DS620_REG_CONFIG_R0 0x0400 368c2ecf20Sopenharmony_ci#define DS620_REG_CONFIG_AUTOC 0x0200 378c2ecf20Sopenharmony_ci#define DS620_REG_CONFIG_1SHOT 0x0100 388c2ecf20Sopenharmony_ci#define DS620_REG_CONFIG_PO2 0x0080 398c2ecf20Sopenharmony_ci#define DS620_REG_CONFIG_PO1 0x0040 408c2ecf20Sopenharmony_ci#define DS620_REG_CONFIG_A2 0x0020 418c2ecf20Sopenharmony_ci#define DS620_REG_CONFIG_A1 0x0010 428c2ecf20Sopenharmony_ci#define DS620_REG_CONFIG_A0 0x0008 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* The DS620 registers */ 458c2ecf20Sopenharmony_cistatic const u8 DS620_REG_TEMP[3] = { 468c2ecf20Sopenharmony_ci 0xAA, /* input, word, RO */ 478c2ecf20Sopenharmony_ci 0xA2, /* min, word, RW */ 488c2ecf20Sopenharmony_ci 0xA0, /* max, word, RW */ 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define DS620_REG_CONF 0xAC /* word, RW */ 528c2ecf20Sopenharmony_ci#define DS620_COM_START 0x51 /* no data */ 538c2ecf20Sopenharmony_ci#define DS620_COM_STOP 0x22 /* no data */ 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* Each client has this additional data */ 568c2ecf20Sopenharmony_cistruct ds620_data { 578c2ecf20Sopenharmony_ci struct i2c_client *client; 588c2ecf20Sopenharmony_ci struct mutex update_lock; 598c2ecf20Sopenharmony_ci char valid; /* !=0 if following fields are valid */ 608c2ecf20Sopenharmony_ci unsigned long last_updated; /* In jiffies */ 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci s16 temp[3]; /* Register values, word */ 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic void ds620_init_client(struct i2c_client *client) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct ds620_platform_data *ds620_info = dev_get_platdata(&client->dev); 688c2ecf20Sopenharmony_ci u16 conf, new_conf; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci new_conf = conf = 718c2ecf20Sopenharmony_ci i2c_smbus_read_word_swapped(client, DS620_REG_CONF); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* switch to continuous conversion mode */ 748c2ecf20Sopenharmony_ci new_conf &= ~DS620_REG_CONFIG_1SHOT; 758c2ecf20Sopenharmony_ci /* already high at power-on, but don't trust the BIOS! */ 768c2ecf20Sopenharmony_ci new_conf |= DS620_REG_CONFIG_PO2; 778c2ecf20Sopenharmony_ci /* thermostat mode according to platform data */ 788c2ecf20Sopenharmony_ci if (ds620_info && ds620_info->pomode == 1) 798c2ecf20Sopenharmony_ci new_conf &= ~DS620_REG_CONFIG_PO1; /* PO_LOW */ 808c2ecf20Sopenharmony_ci else if (ds620_info && ds620_info->pomode == 2) 818c2ecf20Sopenharmony_ci new_conf |= DS620_REG_CONFIG_PO1; /* PO_HIGH */ 828c2ecf20Sopenharmony_ci else 838c2ecf20Sopenharmony_ci new_conf &= ~DS620_REG_CONFIG_PO2; /* always low */ 848c2ecf20Sopenharmony_ci /* with highest precision */ 858c2ecf20Sopenharmony_ci new_conf |= DS620_REG_CONFIG_R1 | DS620_REG_CONFIG_R0; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (conf != new_conf) 888c2ecf20Sopenharmony_ci i2c_smbus_write_word_swapped(client, DS620_REG_CONF, new_conf); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* start conversion */ 918c2ecf20Sopenharmony_ci i2c_smbus_write_byte(client, DS620_COM_START); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic struct ds620_data *ds620_update_client(struct device *dev) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct ds620_data *data = dev_get_drvdata(dev); 978c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 988c2ecf20Sopenharmony_ci struct ds620_data *ret = data; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (time_after(jiffies, data->last_updated + HZ + HZ / 2) 1038c2ecf20Sopenharmony_ci || !data->valid) { 1048c2ecf20Sopenharmony_ci int i; 1058c2ecf20Sopenharmony_ci int res; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "Starting ds620 update\n"); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(data->temp); i++) { 1108c2ecf20Sopenharmony_ci res = i2c_smbus_read_word_swapped(client, 1118c2ecf20Sopenharmony_ci DS620_REG_TEMP[i]); 1128c2ecf20Sopenharmony_ci if (res < 0) { 1138c2ecf20Sopenharmony_ci ret = ERR_PTR(res); 1148c2ecf20Sopenharmony_ci goto abort; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci data->temp[i] = res; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci data->last_updated = jiffies; 1218c2ecf20Sopenharmony_ci data->valid = 1; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ciabort: 1248c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return ret; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic ssize_t temp_show(struct device *dev, struct device_attribute *da, 1308c2ecf20Sopenharmony_ci char *buf) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 1338c2ecf20Sopenharmony_ci struct ds620_data *data = ds620_update_client(dev); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (IS_ERR(data)) 1368c2ecf20Sopenharmony_ci return PTR_ERR(data); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", ((data->temp[attr->index] / 8) * 625) / 10); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic ssize_t temp_store(struct device *dev, struct device_attribute *da, 1428c2ecf20Sopenharmony_ci const char *buf, size_t count) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci int res; 1458c2ecf20Sopenharmony_ci long val; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 1488c2ecf20Sopenharmony_ci struct ds620_data *data = dev_get_drvdata(dev); 1498c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci res = kstrtol(buf, 10, &val); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (res) 1548c2ecf20Sopenharmony_ci return res; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci val = (clamp_val(val, -128000, 128000) * 10 / 625) * 8; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci mutex_lock(&data->update_lock); 1598c2ecf20Sopenharmony_ci data->temp[attr->index] = val; 1608c2ecf20Sopenharmony_ci i2c_smbus_write_word_swapped(client, DS620_REG_TEMP[attr->index], 1618c2ecf20Sopenharmony_ci data->temp[attr->index]); 1628c2ecf20Sopenharmony_ci mutex_unlock(&data->update_lock); 1638c2ecf20Sopenharmony_ci return count; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic ssize_t alarm_show(struct device *dev, struct device_attribute *da, 1678c2ecf20Sopenharmony_ci char *buf) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 1708c2ecf20Sopenharmony_ci struct ds620_data *data = ds620_update_client(dev); 1718c2ecf20Sopenharmony_ci struct i2c_client *client; 1728c2ecf20Sopenharmony_ci u16 conf, new_conf; 1738c2ecf20Sopenharmony_ci int res; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (IS_ERR(data)) 1768c2ecf20Sopenharmony_ci return PTR_ERR(data); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci client = data->client; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* reset alarms if necessary */ 1818c2ecf20Sopenharmony_ci res = i2c_smbus_read_word_swapped(client, DS620_REG_CONF); 1828c2ecf20Sopenharmony_ci if (res < 0) 1838c2ecf20Sopenharmony_ci return res; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci new_conf = conf = res; 1868c2ecf20Sopenharmony_ci new_conf &= ~attr->index; 1878c2ecf20Sopenharmony_ci if (conf != new_conf) { 1888c2ecf20Sopenharmony_ci res = i2c_smbus_write_word_swapped(client, DS620_REG_CONF, 1898c2ecf20Sopenharmony_ci new_conf); 1908c2ecf20Sopenharmony_ci if (res < 0) 1918c2ecf20Sopenharmony_ci return res; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", !!(conf & attr->index)); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0); 1988c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp1_min, temp, 1); 1998c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp1_max, temp, 2); 2008c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, alarm, DS620_REG_CONFIG_TLF); 2018c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, DS620_REG_CONFIG_THF); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic struct attribute *ds620_attrs[] = { 2048c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_input.dev_attr.attr, 2058c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_min.dev_attr.attr, 2068c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_max.dev_attr.attr, 2078c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, 2088c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, 2098c2ecf20Sopenharmony_ci NULL 2108c2ecf20Sopenharmony_ci}; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(ds620); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic int ds620_probe(struct i2c_client *client) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 2178c2ecf20Sopenharmony_ci struct device *hwmon_dev; 2188c2ecf20Sopenharmony_ci struct ds620_data *data; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci data = devm_kzalloc(dev, sizeof(struct ds620_data), GFP_KERNEL); 2218c2ecf20Sopenharmony_ci if (!data) 2228c2ecf20Sopenharmony_ci return -ENOMEM; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci data->client = client; 2258c2ecf20Sopenharmony_ci mutex_init(&data->update_lock); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* Initialize the DS620 chip */ 2288c2ecf20Sopenharmony_ci ds620_init_client(client); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, 2318c2ecf20Sopenharmony_ci data, ds620_groups); 2328c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(hwmon_dev); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic const struct i2c_device_id ds620_id[] = { 2368c2ecf20Sopenharmony_ci {"ds620", 0}, 2378c2ecf20Sopenharmony_ci {} 2388c2ecf20Sopenharmony_ci}; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ds620_id); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/* This is the driver that will be inserted */ 2438c2ecf20Sopenharmony_cistatic struct i2c_driver ds620_driver = { 2448c2ecf20Sopenharmony_ci .class = I2C_CLASS_HWMON, 2458c2ecf20Sopenharmony_ci .driver = { 2468c2ecf20Sopenharmony_ci .name = "ds620", 2478c2ecf20Sopenharmony_ci }, 2488c2ecf20Sopenharmony_ci .probe_new = ds620_probe, 2498c2ecf20Sopenharmony_ci .id_table = ds620_id, 2508c2ecf20Sopenharmony_ci}; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cimodule_i2c_driver(ds620_driver); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ciMODULE_AUTHOR("Roland Stigge <stigge@antcom.de>"); 2558c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DS620 driver"); 2568c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 257