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 | |
28 | struct 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 | |
36 | static 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 | |
79 | ret_unlock: |
80 | mutex_unlock(lock: &data->lock); |
81 | |
82 | return ret; |
83 | } |
84 | |
85 | static 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, fmt: "%d\n" , data->temp_input * 1000); |
96 | } |
97 | static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_input, 0); |
98 | |
99 | static struct attribute *tc74_attrs[] = { |
100 | &sensor_dev_attr_temp1_input.dev_attr.attr, |
101 | NULL |
102 | }; |
103 | |
104 | ATTRIBUTE_GROUPS(tc74); |
105 | |
106 | static 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(adap: client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) |
114 | return -EOPNOTSUPP; |
115 | |
116 | data = devm_kzalloc(dev, size: 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, value: 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 | name: client->name, |
149 | drvdata: data, groups: tc74_groups); |
150 | return PTR_ERR_OR_ZERO(ptr: hwmon_dev); |
151 | } |
152 | |
153 | static const struct i2c_device_id tc74_id[] = { |
154 | { "tc74" , 0 }, |
155 | {} |
156 | }; |
157 | MODULE_DEVICE_TABLE(i2c, tc74_id); |
158 | |
159 | static struct i2c_driver tc74_driver = { |
160 | .driver = { |
161 | .name = "tc74" , |
162 | }, |
163 | .probe = tc74_probe, |
164 | .id_table = tc74_id, |
165 | }; |
166 | |
167 | module_i2c_driver(tc74_driver); |
168 | |
169 | MODULE_AUTHOR("Maciej Szmigiero <mail@maciej.szmigiero.name>" ); |
170 | |
171 | MODULE_DESCRIPTION("TC74 driver" ); |
172 | MODULE_LICENSE("GPL" ); |
173 | |