12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2761c1770SMaciej S. Szmigiero /* 3761c1770SMaciej S. Szmigiero * An hwmon driver for the Microchip TC74 4761c1770SMaciej S. Szmigiero * 5761c1770SMaciej S. Szmigiero * Copyright 2015 Maciej Szmigiero <mail@maciej.szmigiero.name> 6761c1770SMaciej S. Szmigiero * 7761c1770SMaciej S. Szmigiero * Based on ad7414.c: 8761c1770SMaciej S. Szmigiero * Copyright 2006 Stefan Roese, DENX Software Engineering 9761c1770SMaciej S. Szmigiero * Copyright 2008 Sean MacLennan, PIKA Technologies 10761c1770SMaciej S. Szmigiero * Copyright 2008 Frank Edelhaeuser, Spansion Inc. 11761c1770SMaciej S. Szmigiero */ 12761c1770SMaciej S. Szmigiero 13761c1770SMaciej S. Szmigiero #include <linux/bitops.h> 14761c1770SMaciej S. Szmigiero #include <linux/err.h> 15761c1770SMaciej S. Szmigiero #include <linux/hwmon.h> 16761c1770SMaciej S. Szmigiero #include <linux/hwmon-sysfs.h> 17761c1770SMaciej S. Szmigiero #include <linux/i2c.h> 18761c1770SMaciej S. Szmigiero #include <linux/jiffies.h> 19761c1770SMaciej S. Szmigiero #include <linux/module.h> 20761c1770SMaciej S. Szmigiero #include <linux/mutex.h> 21761c1770SMaciej S. Szmigiero #include <linux/slab.h> 22761c1770SMaciej S. Szmigiero #include <linux/sysfs.h> 23761c1770SMaciej S. Szmigiero 24761c1770SMaciej S. Szmigiero /* TC74 registers */ 25761c1770SMaciej S. Szmigiero #define TC74_REG_TEMP 0x00 26761c1770SMaciej S. Szmigiero #define TC74_REG_CONFIG 0x01 27761c1770SMaciej S. Szmigiero 28761c1770SMaciej S. Szmigiero struct tc74_data { 29761c1770SMaciej S. Szmigiero struct i2c_client *client; 30761c1770SMaciej S. Szmigiero struct mutex lock; /* atomic read data updates */ 31761c1770SMaciej S. Szmigiero bool valid; /* validity of fields below */ 32761c1770SMaciej S. Szmigiero unsigned long next_update; /* In jiffies */ 33761c1770SMaciej S. Szmigiero s8 temp_input; /* Temp value in dC */ 34761c1770SMaciej S. Szmigiero }; 35761c1770SMaciej S. Szmigiero 36761c1770SMaciej S. Szmigiero static int tc74_update_device(struct device *dev) 37761c1770SMaciej S. Szmigiero { 38761c1770SMaciej S. Szmigiero struct tc74_data *data = dev_get_drvdata(dev); 39761c1770SMaciej S. Szmigiero struct i2c_client *client = data->client; 40761c1770SMaciej S. Szmigiero int ret; 41761c1770SMaciej S. Szmigiero 42761c1770SMaciej S. Szmigiero ret = mutex_lock_interruptible(&data->lock); 43761c1770SMaciej S. Szmigiero if (ret) 44761c1770SMaciej S. Szmigiero return ret; 45761c1770SMaciej S. Szmigiero 46761c1770SMaciej S. Szmigiero if (time_after(jiffies, data->next_update) || !data->valid) { 47761c1770SMaciej S. Szmigiero s32 value; 48761c1770SMaciej S. Szmigiero 49761c1770SMaciej S. Szmigiero value = i2c_smbus_read_byte_data(client, TC74_REG_CONFIG); 50761c1770SMaciej S. Szmigiero if (value < 0) { 51761c1770SMaciej S. Szmigiero dev_dbg(&client->dev, "TC74_REG_CONFIG read err %d\n", 52761c1770SMaciej S. Szmigiero (int)value); 53761c1770SMaciej S. Szmigiero 54761c1770SMaciej S. Szmigiero ret = value; 55761c1770SMaciej S. Szmigiero goto ret_unlock; 56761c1770SMaciej S. Szmigiero } 57761c1770SMaciej S. Szmigiero 58761c1770SMaciej S. Szmigiero if (!(value & BIT(6))) { 59761c1770SMaciej S. Szmigiero /* not ready yet */ 60761c1770SMaciej S. Szmigiero 61761c1770SMaciej S. Szmigiero ret = -EAGAIN; 62761c1770SMaciej S. Szmigiero goto ret_unlock; 63761c1770SMaciej S. Szmigiero } 64761c1770SMaciej S. Szmigiero 65761c1770SMaciej S. Szmigiero value = i2c_smbus_read_byte_data(client, TC74_REG_TEMP); 66761c1770SMaciej S. Szmigiero if (value < 0) { 67761c1770SMaciej S. Szmigiero dev_dbg(&client->dev, "TC74_REG_TEMP read err %d\n", 68761c1770SMaciej S. Szmigiero (int)value); 69761c1770SMaciej S. Szmigiero 70761c1770SMaciej S. Szmigiero ret = value; 71761c1770SMaciej S. Szmigiero goto ret_unlock; 72761c1770SMaciej S. Szmigiero } 73761c1770SMaciej S. Szmigiero 74761c1770SMaciej S. Szmigiero data->temp_input = value; 75761c1770SMaciej S. Szmigiero data->next_update = jiffies + HZ / 4; 76761c1770SMaciej S. Szmigiero data->valid = true; 77761c1770SMaciej S. Szmigiero } 78761c1770SMaciej S. Szmigiero 79761c1770SMaciej S. Szmigiero ret_unlock: 80761c1770SMaciej S. Szmigiero mutex_unlock(&data->lock); 81761c1770SMaciej S. Szmigiero 82761c1770SMaciej S. Szmigiero return ret; 83761c1770SMaciej S. Szmigiero } 84761c1770SMaciej S. Szmigiero 855abcbc7bSGuenter Roeck static ssize_t temp_input_show(struct device *dev, 86761c1770SMaciej S. Szmigiero struct device_attribute *attr, char *buf) 87761c1770SMaciej S. Szmigiero { 88761c1770SMaciej S. Szmigiero struct tc74_data *data = dev_get_drvdata(dev); 89761c1770SMaciej S. Szmigiero int ret; 90761c1770SMaciej S. Szmigiero 91761c1770SMaciej S. Szmigiero ret = tc74_update_device(dev); 92761c1770SMaciej S. Szmigiero if (ret) 93761c1770SMaciej S. Szmigiero return ret; 94761c1770SMaciej S. Szmigiero 95761c1770SMaciej S. Szmigiero return sprintf(buf, "%d\n", data->temp_input * 1000); 96761c1770SMaciej S. Szmigiero } 975abcbc7bSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_input, 0); 98761c1770SMaciej S. Szmigiero 99761c1770SMaciej S. Szmigiero static struct attribute *tc74_attrs[] = { 100761c1770SMaciej S. Szmigiero &sensor_dev_attr_temp1_input.dev_attr.attr, 101761c1770SMaciej S. Szmigiero NULL 102761c1770SMaciej S. Szmigiero }; 103761c1770SMaciej S. Szmigiero 104761c1770SMaciej S. Szmigiero ATTRIBUTE_GROUPS(tc74); 105761c1770SMaciej S. Szmigiero 106761c1770SMaciej S. Szmigiero static int tc74_probe(struct i2c_client *client, 107761c1770SMaciej S. Szmigiero const struct i2c_device_id *dev_id) 108761c1770SMaciej S. Szmigiero { 109761c1770SMaciej S. Szmigiero struct device *dev = &client->dev; 110761c1770SMaciej S. Szmigiero struct tc74_data *data; 111761c1770SMaciej S. Szmigiero struct device *hwmon_dev; 112761c1770SMaciej S. Szmigiero s32 conf; 113761c1770SMaciej S. Szmigiero 114761c1770SMaciej S. Szmigiero if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 115761c1770SMaciej S. Szmigiero return -EOPNOTSUPP; 116761c1770SMaciej S. Szmigiero 117761c1770SMaciej S. Szmigiero data = devm_kzalloc(dev, sizeof(struct tc74_data), GFP_KERNEL); 118761c1770SMaciej S. Szmigiero if (!data) 119761c1770SMaciej S. Szmigiero return -ENOMEM; 120761c1770SMaciej S. Szmigiero 121761c1770SMaciej S. Szmigiero data->client = client; 122761c1770SMaciej S. Szmigiero mutex_init(&data->lock); 123761c1770SMaciej S. Szmigiero 124761c1770SMaciej S. Szmigiero /* Make sure the chip is powered up. */ 125761c1770SMaciej S. Szmigiero conf = i2c_smbus_read_byte_data(client, TC74_REG_CONFIG); 126761c1770SMaciej S. Szmigiero if (conf < 0) { 127761c1770SMaciej S. Szmigiero dev_err(dev, "unable to read config register\n"); 128761c1770SMaciej S. Szmigiero 129761c1770SMaciej S. Szmigiero return conf; 130761c1770SMaciej S. Szmigiero } 131761c1770SMaciej S. Szmigiero 132761c1770SMaciej S. Szmigiero if (conf & 0x3f) { 133761c1770SMaciej S. Szmigiero dev_err(dev, "invalid config register value\n"); 134761c1770SMaciej S. Szmigiero 135761c1770SMaciej S. Szmigiero return -ENODEV; 136761c1770SMaciej S. Szmigiero } 137761c1770SMaciej S. Szmigiero 138761c1770SMaciej S. Szmigiero if (conf & BIT(7)) { 139761c1770SMaciej S. Szmigiero s32 ret; 140761c1770SMaciej S. Szmigiero 141761c1770SMaciej S. Szmigiero conf &= ~BIT(7); 142761c1770SMaciej S. Szmigiero 143761c1770SMaciej S. Szmigiero ret = i2c_smbus_write_byte_data(client, TC74_REG_CONFIG, conf); 144761c1770SMaciej S. Szmigiero if (ret) 145761c1770SMaciej S. Szmigiero dev_warn(dev, "unable to disable STANDBY\n"); 146761c1770SMaciej S. Szmigiero } 147761c1770SMaciej S. Szmigiero 148761c1770SMaciej S. Szmigiero hwmon_dev = devm_hwmon_device_register_with_groups(dev, 149761c1770SMaciej S. Szmigiero client->name, 150761c1770SMaciej S. Szmigiero data, tc74_groups); 151761c1770SMaciej S. Szmigiero return PTR_ERR_OR_ZERO(hwmon_dev); 152761c1770SMaciej S. Szmigiero } 153761c1770SMaciej S. Szmigiero 154761c1770SMaciej S. Szmigiero static const struct i2c_device_id tc74_id[] = { 155761c1770SMaciej S. Szmigiero { "tc74", 0 }, 156761c1770SMaciej S. Szmigiero {} 157761c1770SMaciej S. Szmigiero }; 158761c1770SMaciej S. Szmigiero MODULE_DEVICE_TABLE(i2c, tc74_id); 159761c1770SMaciej S. Szmigiero 160761c1770SMaciej S. Szmigiero static struct i2c_driver tc74_driver = { 161761c1770SMaciej S. Szmigiero .driver = { 162761c1770SMaciej S. Szmigiero .name = "tc74", 163761c1770SMaciej S. Szmigiero }, 164761c1770SMaciej S. Szmigiero .probe = tc74_probe, 165761c1770SMaciej S. Szmigiero .id_table = tc74_id, 166761c1770SMaciej S. Szmigiero }; 167761c1770SMaciej S. Szmigiero 168761c1770SMaciej S. Szmigiero module_i2c_driver(tc74_driver); 169761c1770SMaciej S. Szmigiero 170761c1770SMaciej S. Szmigiero MODULE_AUTHOR("Maciej Szmigiero <mail@maciej.szmigiero.name>"); 171761c1770SMaciej S. Szmigiero 172761c1770SMaciej S. Szmigiero MODULE_DESCRIPTION("TC74 driver"); 173761c1770SMaciej S. Szmigiero MODULE_LICENSE("GPL"); 174