1 /* 2 * An hwmon driver for the Microchip TC74 3 * 4 * Copyright 2015 Maciej Szmigiero <mail@maciej.szmigiero.name> 5 * 6 * Based on ad7414.c: 7 * Copyright 2006 Stefan Roese, DENX Software Engineering 8 * Copyright 2008 Sean MacLennan, PIKA Technologies 9 * Copyright 2008 Frank Edelhaeuser, Spansion Inc. 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License as published by 13 * the Free Software Foundation; either version 2 of the License, or 14 * (at your option) any later version. 15 */ 16 17 #include <linux/bitops.h> 18 #include <linux/err.h> 19 #include <linux/hwmon.h> 20 #include <linux/hwmon-sysfs.h> 21 #include <linux/i2c.h> 22 #include <linux/jiffies.h> 23 #include <linux/module.h> 24 #include <linux/mutex.h> 25 #include <linux/slab.h> 26 #include <linux/sysfs.h> 27 28 /* TC74 registers */ 29 #define TC74_REG_TEMP 0x00 30 #define TC74_REG_CONFIG 0x01 31 32 struct tc74_data { 33 struct i2c_client *client; 34 struct mutex lock; /* atomic read data updates */ 35 bool valid; /* validity of fields below */ 36 unsigned long next_update; /* In jiffies */ 37 s8 temp_input; /* Temp value in dC */ 38 }; 39 40 static int tc74_update_device(struct device *dev) 41 { 42 struct tc74_data *data = dev_get_drvdata(dev); 43 struct i2c_client *client = data->client; 44 int ret; 45 46 ret = mutex_lock_interruptible(&data->lock); 47 if (ret) 48 return ret; 49 50 if (time_after(jiffies, data->next_update) || !data->valid) { 51 s32 value; 52 53 value = i2c_smbus_read_byte_data(client, TC74_REG_CONFIG); 54 if (value < 0) { 55 dev_dbg(&client->dev, "TC74_REG_CONFIG read err %d\n", 56 (int)value); 57 58 ret = value; 59 goto ret_unlock; 60 } 61 62 if (!(value & BIT(6))) { 63 /* not ready yet */ 64 65 ret = -EAGAIN; 66 goto ret_unlock; 67 } 68 69 value = i2c_smbus_read_byte_data(client, TC74_REG_TEMP); 70 if (value < 0) { 71 dev_dbg(&client->dev, "TC74_REG_TEMP read err %d\n", 72 (int)value); 73 74 ret = value; 75 goto ret_unlock; 76 } 77 78 data->temp_input = value; 79 data->next_update = jiffies + HZ / 4; 80 data->valid = true; 81 } 82 83 ret_unlock: 84 mutex_unlock(&data->lock); 85 86 return ret; 87 } 88 89 static ssize_t show_temp_input(struct device *dev, 90 struct device_attribute *attr, char *buf) 91 { 92 struct tc74_data *data = dev_get_drvdata(dev); 93 int ret; 94 95 ret = tc74_update_device(dev); 96 if (ret) 97 return ret; 98 99 return sprintf(buf, "%d\n", data->temp_input * 1000); 100 } 101 static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL, 0); 102 103 static struct attribute *tc74_attrs[] = { 104 &sensor_dev_attr_temp1_input.dev_attr.attr, 105 NULL 106 }; 107 108 ATTRIBUTE_GROUPS(tc74); 109 110 static int tc74_probe(struct i2c_client *client, 111 const struct i2c_device_id *dev_id) 112 { 113 struct device *dev = &client->dev; 114 struct tc74_data *data; 115 struct device *hwmon_dev; 116 s32 conf; 117 118 if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 119 return -EOPNOTSUPP; 120 121 data = devm_kzalloc(dev, sizeof(struct tc74_data), GFP_KERNEL); 122 if (!data) 123 return -ENOMEM; 124 125 data->client = client; 126 mutex_init(&data->lock); 127 128 /* Make sure the chip is powered up. */ 129 conf = i2c_smbus_read_byte_data(client, TC74_REG_CONFIG); 130 if (conf < 0) { 131 dev_err(dev, "unable to read config register\n"); 132 133 return conf; 134 } 135 136 if (conf & 0x3f) { 137 dev_err(dev, "invalid config register value\n"); 138 139 return -ENODEV; 140 } 141 142 if (conf & BIT(7)) { 143 s32 ret; 144 145 conf &= ~BIT(7); 146 147 ret = i2c_smbus_write_byte_data(client, TC74_REG_CONFIG, conf); 148 if (ret) 149 dev_warn(dev, "unable to disable STANDBY\n"); 150 } 151 152 hwmon_dev = devm_hwmon_device_register_with_groups(dev, 153 client->name, 154 data, tc74_groups); 155 return PTR_ERR_OR_ZERO(hwmon_dev); 156 } 157 158 static const struct i2c_device_id tc74_id[] = { 159 { "tc74", 0 }, 160 {} 161 }; 162 MODULE_DEVICE_TABLE(i2c, tc74_id); 163 164 static struct i2c_driver tc74_driver = { 165 .driver = { 166 .name = "tc74", 167 }, 168 .probe = tc74_probe, 169 .id_table = tc74_id, 170 }; 171 172 module_i2c_driver(tc74_driver); 173 174 MODULE_AUTHOR("Maciej Szmigiero <mail@maciej.szmigiero.name>"); 175 176 MODULE_DESCRIPTION("TC74 driver"); 177 MODULE_LICENSE("GPL"); 178