Blame view

drivers/hwmon/tmp421.c 7.62 KB
c942fddf8   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
9410700b8   Andre Prendel   hwmon: Add driver...
2
3
4
5
6
  /* tmp421.c
   *
   * Copyright (C) 2009 Andre Prendel <andre.prendel@gmx.de>
   * Preliminary support by:
   * Melvin Rook, Raymond Ng
9410700b8   Andre Prendel   hwmon: Add driver...
7
8
9
10
   */
  
  /*
   * Driver for the Texas Instruments TMP421 SMBus temperature sensor IC.
05c77ab24   Guenter Roeck   hwmon: (tmp421) A...
11
   * Supported models: TMP421, TMP422, TMP423, TMP441, TMP442
9410700b8   Andre Prendel   hwmon: Add driver...
12
13
14
15
16
17
18
19
20
21
22
   */
  
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/slab.h>
  #include <linux/jiffies.h>
  #include <linux/i2c.h>
  #include <linux/hwmon.h>
  #include <linux/hwmon-sysfs.h>
  #include <linux/err.h>
  #include <linux/mutex.h>
a1253027b   Javier Martinez Canillas   hwmon: (tmp421) A...
23
  #include <linux/of_device.h>
9410700b8   Andre Prendel   hwmon: Add driver...
24
25
26
  #include <linux/sysfs.h>
  
  /* Addresses to scan */
918ee91c0   Jean Delvare   hwmon: I2C addres...
27
28
  static const unsigned short normal_i2c[] = { 0x2a, 0x4c, 0x4d, 0x4e, 0x4f,
  					     I2C_CLIENT_END };
9410700b8   Andre Prendel   hwmon: Add driver...
29

05c77ab24   Guenter Roeck   hwmon: (tmp421) A...
30
  enum chips { tmp421, tmp422, tmp423, tmp441, tmp442 };
9410700b8   Andre Prendel   hwmon: Add driver...
31
32
  
  /* The TMP421 registers */
a63ee9d83   Guenter Roeck   hwmon: (tmp421) S...
33
  #define TMP421_STATUS_REG			0x08
9410700b8   Andre Prendel   hwmon: Add driver...
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
  #define TMP421_CONFIG_REG_1			0x09
  #define TMP421_CONVERSION_RATE_REG		0x0B
  #define TMP421_MANUFACTURER_ID_REG		0xFE
  #define TMP421_DEVICE_ID_REG			0xFF
  
  static const u8 TMP421_TEMP_MSB[4]		= { 0x00, 0x01, 0x02, 0x03 };
  static const u8 TMP421_TEMP_LSB[4]		= { 0x10, 0x11, 0x12, 0x13 };
  
  /* Flags */
  #define TMP421_CONFIG_SHUTDOWN			0x40
  #define TMP421_CONFIG_RANGE			0x04
  
  /* Manufacturer / Device ID's */
  #define TMP421_MANUFACTURER_ID			0x55
  #define TMP421_DEVICE_ID			0x21
  #define TMP422_DEVICE_ID			0x22
  #define TMP423_DEVICE_ID			0x23
05c77ab24   Guenter Roeck   hwmon: (tmp421) A...
51
52
  #define TMP441_DEVICE_ID			0x41
  #define TMP442_DEVICE_ID			0x42
9410700b8   Andre Prendel   hwmon: Add driver...
53
54
  
  static const struct i2c_device_id tmp421_id[] = {
8d59582a8   Jean Delvare   hwmon: (tmp421) R...
55
56
57
  	{ "tmp421", 2 },
  	{ "tmp422", 3 },
  	{ "tmp423", 4 },
05c77ab24   Guenter Roeck   hwmon: (tmp421) A...
58
59
  	{ "tmp441", 2 },
  	{ "tmp442", 3 },
9410700b8   Andre Prendel   hwmon: Add driver...
60
61
62
  	{ }
  };
  MODULE_DEVICE_TABLE(i2c, tmp421_id);
bd7d56a70   Guenter Roeck   hwmon: (tmp421) F...
63
  static const struct of_device_id __maybe_unused tmp421_of_match[] = {
a1253027b   Javier Martinez Canillas   hwmon: (tmp421) A...
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
  	{
  		.compatible = "ti,tmp421",
  		.data = (void *)2
  	},
  	{
  		.compatible = "ti,tmp422",
  		.data = (void *)3
  	},
  	{
  		.compatible = "ti,tmp423",
  		.data = (void *)4
  	},
  	{
  		.compatible = "ti,tmp441",
  		.data = (void *)2
  	},
  	{
f422449b5   Cheng-Min Ao   hwmon: (tmp421) C...
81
  		.compatible = "ti,tmp442",
a1253027b   Javier Martinez Canillas   hwmon: (tmp421) A...
82
83
84
85
86
  		.data = (void *)3
  	},
  	{ },
  };
  MODULE_DEVICE_TABLE(of, tmp421_of_match);
9410700b8   Andre Prendel   hwmon: Add driver...
87
  struct tmp421_data {
276dac803   Guenter Roeck   hwmon: (tmp421) C...
88
  	struct i2c_client *client;
9410700b8   Andre Prendel   hwmon: Add driver...
89
  	struct mutex update_lock;
bb43cc45d   Guenter Roeck   hwmon: (tmp421) C...
90
91
92
93
  	u32 temp_config[5];
  	struct hwmon_channel_info temp_info;
  	const struct hwmon_channel_info *info[2];
  	struct hwmon_chip_info chip;
9410700b8   Andre Prendel   hwmon: Add driver...
94
95
  	char valid;
  	unsigned long last_updated;
a1253027b   Javier Martinez Canillas   hwmon: (tmp421) A...
96
  	unsigned long channels;
9410700b8   Andre Prendel   hwmon: Add driver...
97
98
99
100
101
102
  	u8 config;
  	s16 temp[4];
  };
  
  static int temp_from_s16(s16 reg)
  {
a44908d74   Jean Delvare   hwmon: (tmp421) F...
103
104
  	/* Mask out status bits */
  	int temp = reg & ~0xf;
9410700b8   Andre Prendel   hwmon: Add driver...
105
106
107
108
109
110
  
  	return (temp * 1000 + 128) / 256;
  }
  
  static int temp_from_u16(u16 reg)
  {
a44908d74   Jean Delvare   hwmon: (tmp421) F...
111
112
  	/* Mask out status bits */
  	int temp = reg & ~0xf;
9410700b8   Andre Prendel   hwmon: Add driver...
113
114
115
116
117
118
119
120
121
  
  	/* Add offset for extended temperature range. */
  	temp -= 64 * 256;
  
  	return (temp * 1000 + 128) / 256;
  }
  
  static struct tmp421_data *tmp421_update_device(struct device *dev)
  {
276dac803   Guenter Roeck   hwmon: (tmp421) C...
122
123
  	struct tmp421_data *data = dev_get_drvdata(dev);
  	struct i2c_client *client = data->client;
9410700b8   Andre Prendel   hwmon: Add driver...
124
125
126
127
128
129
130
  	int i;
  
  	mutex_lock(&data->update_lock);
  
  	if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) {
  		data->config = i2c_smbus_read_byte_data(client,
  			TMP421_CONFIG_REG_1);
8d59582a8   Jean Delvare   hwmon: (tmp421) R...
131
  		for (i = 0; i < data->channels; i++) {
9410700b8   Andre Prendel   hwmon: Add driver...
132
133
134
135
136
137
138
139
140
141
142
143
144
  			data->temp[i] = i2c_smbus_read_byte_data(client,
  				TMP421_TEMP_MSB[i]) << 8;
  			data->temp[i] |= i2c_smbus_read_byte_data(client,
  				TMP421_TEMP_LSB[i]);
  		}
  		data->last_updated = jiffies;
  		data->valid = 1;
  	}
  
  	mutex_unlock(&data->update_lock);
  
  	return data;
  }
bb43cc45d   Guenter Roeck   hwmon: (tmp421) C...
145
146
  static int tmp421_read(struct device *dev, enum hwmon_sensor_types type,
  		       u32 attr, int channel, long *val)
9410700b8   Andre Prendel   hwmon: Add driver...
147
  {
bb43cc45d   Guenter Roeck   hwmon: (tmp421) C...
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
  	struct tmp421_data *tmp421 = tmp421_update_device(dev);
  
  	switch (attr) {
  	case hwmon_temp_input:
  		if (tmp421->config & TMP421_CONFIG_RANGE)
  			*val = temp_from_u16(tmp421->temp[channel]);
  		else
  			*val = temp_from_s16(tmp421->temp[channel]);
  		return 0;
  	case hwmon_temp_fault:
  		/*
  		 * The OPEN bit signals a fault. This is bit 0 of the temperature
  		 * register (low byte).
  		 */
  		*val = tmp421->temp[channel] & 0x01;
  		return 0;
  	default:
  		return -EOPNOTSUPP;
  	}
9410700b8   Andre Prendel   hwmon: Add driver...
167

9410700b8   Andre Prendel   hwmon: Add driver...
168
  }
bb43cc45d   Guenter Roeck   hwmon: (tmp421) C...
169
170
  static umode_t tmp421_is_visible(const void *data, enum hwmon_sensor_types type,
  				 u32 attr, int channel)
9410700b8   Andre Prendel   hwmon: Add driver...
171
  {
bb43cc45d   Guenter Roeck   hwmon: (tmp421) C...
172
173
174
175
  	switch (attr) {
  	case hwmon_temp_fault:
  		if (channel == 0)
  			return 0;
b626eb22f   Guenter Roeck   hwmon: (tmp421) R...
176
  		return 0444;
bb43cc45d   Guenter Roeck   hwmon: (tmp421) C...
177
  	case hwmon_temp_input:
b626eb22f   Guenter Roeck   hwmon: (tmp421) R...
178
  		return 0444;
bb43cc45d   Guenter Roeck   hwmon: (tmp421) C...
179
180
181
  	default:
  		return 0;
  	}
9410700b8   Andre Prendel   hwmon: Add driver...
182
  }
9410700b8   Andre Prendel   hwmon: Add driver...
183
184
185
186
187
188
189
190
191
192
  static int tmp421_init_client(struct i2c_client *client)
  {
  	int config, config_orig;
  
  	/* Set the conversion rate to 2 Hz */
  	i2c_smbus_write_byte_data(client, TMP421_CONVERSION_RATE_REG, 0x05);
  
  	/* Start conversions (disable shutdown if necessary) */
  	config = i2c_smbus_read_byte_data(client, TMP421_CONFIG_REG_1);
  	if (config < 0) {
b55f37572   Guenter Roeck   hwmon: Fix checkp...
193
194
195
  		dev_err(&client->dev,
  			"Could not read configuration register (%d)
  ", config);
010a166a5   Sachin Kamat   hwmon: (tmp421) F...
196
  		return config;
9410700b8   Andre Prendel   hwmon: Add driver...
197
198
199
200
201
202
203
204
205
206
207
208
209
  	}
  
  	config_orig = config;
  	config &= ~TMP421_CONFIG_SHUTDOWN;
  
  	if (config != config_orig) {
  		dev_info(&client->dev, "Enable monitoring chip
  ");
  		i2c_smbus_write_byte_data(client, TMP421_CONFIG_REG_1, config);
  	}
  
  	return 0;
  }
310ec7921   Jean Delvare   i2c: Drop the kin...
210
  static int tmp421_detect(struct i2c_client *client,
9410700b8   Andre Prendel   hwmon: Add driver...
211
212
  			 struct i2c_board_info *info)
  {
dbe73c8f4   Jean Delvare   hwmon: (tmp401/tm...
213
  	enum chips kind;
9410700b8   Andre Prendel   hwmon: Add driver...
214
  	struct i2c_adapter *adapter = client->adapter;
8b9bf554d   Colin Ian King   hwmon: (tmp421) m...
215
216
217
218
  	static const char * const names[] = {
  		"TMP421", "TMP422", "TMP423",
  		"TMP441", "TMP442"
  	};
a63ee9d83   Guenter Roeck   hwmon: (tmp421) S...
219
  	int addr = client->addr;
dbe73c8f4   Jean Delvare   hwmon: (tmp401/tm...
220
  	u8 reg;
9410700b8   Andre Prendel   hwmon: Add driver...
221
222
223
  
  	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
  		return -ENODEV;
dbe73c8f4   Jean Delvare   hwmon: (tmp401/tm...
224
225
226
  	reg = i2c_smbus_read_byte_data(client, TMP421_MANUFACTURER_ID_REG);
  	if (reg != TMP421_MANUFACTURER_ID)
  		return -ENODEV;
a63ee9d83   Guenter Roeck   hwmon: (tmp421) S...
227
228
229
230
231
232
233
  	reg = i2c_smbus_read_byte_data(client, TMP421_CONVERSION_RATE_REG);
  	if (reg & 0xf8)
  		return -ENODEV;
  
  	reg = i2c_smbus_read_byte_data(client, TMP421_STATUS_REG);
  	if (reg & 0x7f)
  		return -ENODEV;
dbe73c8f4   Jean Delvare   hwmon: (tmp401/tm...
234
235
236
237
238
239
  	reg = i2c_smbus_read_byte_data(client, TMP421_DEVICE_ID_REG);
  	switch (reg) {
  	case TMP421_DEVICE_ID:
  		kind = tmp421;
  		break;
  	case TMP422_DEVICE_ID:
a63ee9d83   Guenter Roeck   hwmon: (tmp421) S...
240
241
  		if (addr == 0x2a)
  			return -ENODEV;
dbe73c8f4   Jean Delvare   hwmon: (tmp401/tm...
242
243
244
  		kind = tmp422;
  		break;
  	case TMP423_DEVICE_ID:
a63ee9d83   Guenter Roeck   hwmon: (tmp421) S...
245
246
  		if (addr != 0x4c && addr != 0x4d)
  			return -ENODEV;
dbe73c8f4   Jean Delvare   hwmon: (tmp401/tm...
247
248
  		kind = tmp423;
  		break;
05c77ab24   Guenter Roeck   hwmon: (tmp421) A...
249
250
251
252
253
254
255
256
  	case TMP441_DEVICE_ID:
  		kind = tmp441;
  		break;
  	case TMP442_DEVICE_ID:
  		if (addr != 0x4c && addr != 0x4d)
  			return -ENODEV;
  		kind = tmp442;
  		break;
dbe73c8f4   Jean Delvare   hwmon: (tmp401/tm...
257
258
  	default:
  		return -ENODEV;
9410700b8   Andre Prendel   hwmon: Add driver...
259
  	}
dbe73c8f4   Jean Delvare   hwmon: (tmp401/tm...
260

dc71afe5a   Jean Delvare   hwmon: Fix off-by...
261
  	strlcpy(info->type, tmp421_id[kind].name, I2C_NAME_SIZE);
9410700b8   Andre Prendel   hwmon: Add driver...
262
263
  	dev_info(&adapter->dev, "Detected TI %s chip at 0x%02x
  ",
dc71afe5a   Jean Delvare   hwmon: Fix off-by...
264
  		 names[kind], client->addr);
9410700b8   Andre Prendel   hwmon: Add driver...
265
266
267
  
  	return 0;
  }
bb43cc45d   Guenter Roeck   hwmon: (tmp421) C...
268
269
270
271
  static const struct hwmon_ops tmp421_ops = {
  	.is_visible = tmp421_is_visible,
  	.read = tmp421_read,
  };
9410700b8   Andre Prendel   hwmon: Add driver...
272
273
274
  static int tmp421_probe(struct i2c_client *client,
  			const struct i2c_device_id *id)
  {
276dac803   Guenter Roeck   hwmon: (tmp421) C...
275
276
  	struct device *dev = &client->dev;
  	struct device *hwmon_dev;
9410700b8   Andre Prendel   hwmon: Add driver...
277
  	struct tmp421_data *data;
bb43cc45d   Guenter Roeck   hwmon: (tmp421) C...
278
  	int i, err;
9410700b8   Andre Prendel   hwmon: Add driver...
279

276dac803   Guenter Roeck   hwmon: (tmp421) C...
280
  	data = devm_kzalloc(dev, sizeof(struct tmp421_data), GFP_KERNEL);
9410700b8   Andre Prendel   hwmon: Add driver...
281
282
  	if (!data)
  		return -ENOMEM;
9410700b8   Andre Prendel   hwmon: Add driver...
283
  	mutex_init(&data->update_lock);
a1253027b   Javier Martinez Canillas   hwmon: (tmp421) A...
284
285
286
287
288
  	if (client->dev.of_node)
  		data->channels = (unsigned long)
  			of_device_get_match_data(&client->dev);
  	else
  		data->channels = id->driver_data;
276dac803   Guenter Roeck   hwmon: (tmp421) C...
289
  	data->client = client;
9410700b8   Andre Prendel   hwmon: Add driver...
290
291
292
  
  	err = tmp421_init_client(client);
  	if (err)
f625e0fd5   Guenter Roeck   hwmon: (tmp421) C...
293
  		return err;
9410700b8   Andre Prendel   hwmon: Add driver...
294

bb43cc45d   Guenter Roeck   hwmon: (tmp421) C...
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
  	for (i = 0; i < data->channels; i++)
  		data->temp_config[i] = HWMON_T_INPUT | HWMON_T_FAULT;
  
  	data->chip.ops = &tmp421_ops;
  	data->chip.info = data->info;
  
  	data->info[0] = &data->temp_info;
  
  	data->temp_info.type = hwmon_temp;
  	data->temp_info.config = data->temp_config;
  
  	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
  							 data,
  							 &data->chip,
  							 NULL);
276dac803   Guenter Roeck   hwmon: (tmp421) C...
310
  	return PTR_ERR_OR_ZERO(hwmon_dev);
9410700b8   Andre Prendel   hwmon: Add driver...
311
312
313
314
315
316
  }
  
  static struct i2c_driver tmp421_driver = {
  	.class = I2C_CLASS_HWMON,
  	.driver = {
  		.name	= "tmp421",
a1253027b   Javier Martinez Canillas   hwmon: (tmp421) A...
317
  		.of_match_table = of_match_ptr(tmp421_of_match),
9410700b8   Andre Prendel   hwmon: Add driver...
318
319
  	},
  	.probe = tmp421_probe,
9410700b8   Andre Prendel   hwmon: Add driver...
320
321
  	.id_table = tmp421_id,
  	.detect = tmp421_detect,
c3813d6af   Jean Delvare   i2c: Get rid of s...
322
  	.address_list = normal_i2c,
9410700b8   Andre Prendel   hwmon: Add driver...
323
  };
f0967eea8   Axel Lin   hwmon: convert dr...
324
  module_i2c_driver(tmp421_driver);
9410700b8   Andre Prendel   hwmon: Add driver...
325
326
  
  MODULE_AUTHOR("Andre Prendel <andre.prendel@gmx.de>");
05c77ab24   Guenter Roeck   hwmon: (tmp421) A...
327
  MODULE_DESCRIPTION("Texas Instruments TMP421/422/423/441/442 temperature sensor driver");
9410700b8   Andre Prendel   hwmon: Add driver...
328
  MODULE_LICENSE("GPL");