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