xref: /openbmc/linux/drivers/hwmon/thmc50.c (revision c3813d6af177fab19e322f3114b1f64fbcf08d71)
1add77c64SKrzysztof Helt /*
2add77c64SKrzysztof Helt     thmc50.c - Part of lm_sensors, Linux kernel modules for hardware
3add77c64SKrzysztof Helt              monitoring
4add77c64SKrzysztof Helt     Copyright (C) 2007 Krzysztof Helt <krzysztof.h1@wp.pl>
5add77c64SKrzysztof Helt     Based on 2.4 driver by Frodo Looijaard <frodol@dds.nl> and
6add77c64SKrzysztof Helt     Philip Edelbrock <phil@netroedge.com>
7add77c64SKrzysztof Helt 
8add77c64SKrzysztof Helt     This program is free software; you can redistribute it and/or modify
9add77c64SKrzysztof Helt     it under the terms of the GNU General Public License as published by
10add77c64SKrzysztof Helt     the Free Software Foundation; either version 2 of the License, or
11add77c64SKrzysztof Helt     (at your option) any later version.
12add77c64SKrzysztof Helt 
13add77c64SKrzysztof Helt     This program is distributed in the hope that it will be useful,
14add77c64SKrzysztof Helt     but WITHOUT ANY WARRANTY; without even the implied warranty of
15add77c64SKrzysztof Helt     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16add77c64SKrzysztof Helt     GNU General Public License for more details.
17add77c64SKrzysztof Helt 
18add77c64SKrzysztof Helt     You should have received a copy of the GNU General Public License
19add77c64SKrzysztof Helt     along with this program; if not, write to the Free Software
20add77c64SKrzysztof Helt     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21add77c64SKrzysztof Helt */
22add77c64SKrzysztof Helt 
23add77c64SKrzysztof Helt #include <linux/module.h>
24add77c64SKrzysztof Helt #include <linux/init.h>
25add77c64SKrzysztof Helt #include <linux/slab.h>
26add77c64SKrzysztof Helt #include <linux/i2c.h>
27add77c64SKrzysztof Helt #include <linux/hwmon.h>
28add77c64SKrzysztof Helt #include <linux/hwmon-sysfs.h>
29add77c64SKrzysztof Helt #include <linux/err.h>
30add77c64SKrzysztof Helt #include <linux/mutex.h>
31add77c64SKrzysztof Helt 
32add77c64SKrzysztof Helt MODULE_LICENSE("GPL");
33add77c64SKrzysztof Helt 
34add77c64SKrzysztof Helt /* Addresses to scan */
3525e9c86dSMark M. Hoffman static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
36add77c64SKrzysztof Helt 
37add77c64SKrzysztof Helt /* Insmod parameters */
38add77c64SKrzysztof Helt I2C_CLIENT_INSMOD_2(thmc50, adm1022);
39f95f0b4cSJean Delvare 
40f95f0b4cSJean Delvare static unsigned short adm1022_temp3[16];
41f95f0b4cSJean Delvare static unsigned int adm1022_temp3_num;
42f95f0b4cSJean Delvare module_param_array(adm1022_temp3, ushort, &adm1022_temp3_num, 0);
43f95f0b4cSJean Delvare MODULE_PARM_DESC(adm1022_temp3, "List of adapter,address pairs "
44add77c64SKrzysztof Helt 			"to enable 3rd temperature (ADM1022 only)");
45add77c64SKrzysztof Helt 
46add77c64SKrzysztof Helt /* Many THMC50 constants specified below */
47add77c64SKrzysztof Helt 
48add77c64SKrzysztof Helt /* The THMC50 registers */
49add77c64SKrzysztof Helt #define THMC50_REG_CONF				0x40
50add77c64SKrzysztof Helt #define THMC50_REG_COMPANY_ID			0x3E
51add77c64SKrzysztof Helt #define THMC50_REG_DIE_CODE			0x3F
52add77c64SKrzysztof Helt #define THMC50_REG_ANALOG_OUT			0x19
53dcf3b5fbSKrzysztof Helt /*
54bba891c2SKrzysztof Helt  * The mirror status register cannot be used as
55bba891c2SKrzysztof Helt  * reading it does not clear alarms.
56dcf3b5fbSKrzysztof Helt  */
57bba891c2SKrzysztof Helt #define THMC50_REG_INTR				0x41
58add77c64SKrzysztof Helt 
595910a9b2STobias Klauser static const u8 THMC50_REG_TEMP[] = { 0x27, 0x26, 0x20 };
605910a9b2STobias Klauser static const u8 THMC50_REG_TEMP_MIN[] = { 0x3A, 0x38, 0x2C };
615910a9b2STobias Klauser static const u8 THMC50_REG_TEMP_MAX[] = { 0x39, 0x37, 0x2B };
6284f768c1SKrzysztof Helt static const u8 THMC50_REG_TEMP_CRITICAL[] = { 0x13, 0x14, 0x14 };
6384f768c1SKrzysztof Helt static const u8 THMC50_REG_TEMP_DEFAULT[] = { 0x17, 0x18, 0x18 };
64add77c64SKrzysztof Helt 
65add77c64SKrzysztof Helt #define THMC50_REG_CONF_nFANOFF			0x20
6684f768c1SKrzysztof Helt #define THMC50_REG_CONF_PROGRAMMED		0x08
67add77c64SKrzysztof Helt 
68add77c64SKrzysztof Helt /* Each client has this additional data */
69add77c64SKrzysztof Helt struct thmc50_data {
701beeffe4STony Jones 	struct device *hwmon_dev;
71add77c64SKrzysztof Helt 
72add77c64SKrzysztof Helt 	struct mutex update_lock;
73add77c64SKrzysztof Helt 	enum chips type;
74add77c64SKrzysztof Helt 	unsigned long last_updated;	/* In jiffies */
75add77c64SKrzysztof Helt 	char has_temp3;		/* !=0 if it is ADM1022 in temp3 mode */
76add77c64SKrzysztof Helt 	char valid;		/* !=0 if following fields are valid */
77add77c64SKrzysztof Helt 
78add77c64SKrzysztof Helt 	/* Register values */
79add77c64SKrzysztof Helt 	s8 temp_input[3];
80add77c64SKrzysztof Helt 	s8 temp_max[3];
81add77c64SKrzysztof Helt 	s8 temp_min[3];
8284f768c1SKrzysztof Helt 	s8 temp_critical[3];
83add77c64SKrzysztof Helt 	u8 analog_out;
84dcf3b5fbSKrzysztof Helt 	u8 alarms;
85add77c64SKrzysztof Helt };
86add77c64SKrzysztof Helt 
87310ec792SJean Delvare static int thmc50_detect(struct i2c_client *client,
88ccf37488SJean Delvare 			 struct i2c_board_info *info);
89ccf37488SJean Delvare static int thmc50_probe(struct i2c_client *client,
90ccf37488SJean Delvare 			const struct i2c_device_id *id);
91ccf37488SJean Delvare static int thmc50_remove(struct i2c_client *client);
92add77c64SKrzysztof Helt static void thmc50_init_client(struct i2c_client *client);
93add77c64SKrzysztof Helt static struct thmc50_data *thmc50_update_device(struct device *dev);
94add77c64SKrzysztof Helt 
95ccf37488SJean Delvare static const struct i2c_device_id thmc50_id[] = {
96ccf37488SJean Delvare 	{ "adm1022", adm1022 },
97ccf37488SJean Delvare 	{ "thmc50", thmc50 },
98ccf37488SJean Delvare 	{ }
99ccf37488SJean Delvare };
100ccf37488SJean Delvare MODULE_DEVICE_TABLE(i2c, thmc50_id);
101ccf37488SJean Delvare 
102add77c64SKrzysztof Helt static struct i2c_driver thmc50_driver = {
103ccf37488SJean Delvare 	.class = I2C_CLASS_HWMON,
104add77c64SKrzysztof Helt 	.driver = {
105add77c64SKrzysztof Helt 		.name = "thmc50",
106add77c64SKrzysztof Helt 	},
107ccf37488SJean Delvare 	.probe = thmc50_probe,
108ccf37488SJean Delvare 	.remove = thmc50_remove,
109ccf37488SJean Delvare 	.id_table = thmc50_id,
110ccf37488SJean Delvare 	.detect = thmc50_detect,
111*c3813d6aSJean Delvare 	.address_list = normal_i2c,
112add77c64SKrzysztof Helt };
113add77c64SKrzysztof Helt 
114add77c64SKrzysztof Helt static ssize_t show_analog_out(struct device *dev,
115add77c64SKrzysztof Helt 			       struct device_attribute *attr, char *buf)
116add77c64SKrzysztof Helt {
117add77c64SKrzysztof Helt 	struct thmc50_data *data = thmc50_update_device(dev);
118add77c64SKrzysztof Helt 	return sprintf(buf, "%d\n", data->analog_out);
119add77c64SKrzysztof Helt }
120add77c64SKrzysztof Helt 
121add77c64SKrzysztof Helt static ssize_t set_analog_out(struct device *dev,
122add77c64SKrzysztof Helt 			      struct device_attribute *attr,
123add77c64SKrzysztof Helt 			      const char *buf, size_t count)
124add77c64SKrzysztof Helt {
125add77c64SKrzysztof Helt 	struct i2c_client *client = to_i2c_client(dev);
126add77c64SKrzysztof Helt 	struct thmc50_data *data = i2c_get_clientdata(client);
127add77c64SKrzysztof Helt 	int tmp = simple_strtoul(buf, NULL, 10);
128add77c64SKrzysztof Helt 	int config;
129add77c64SKrzysztof Helt 
130add77c64SKrzysztof Helt 	mutex_lock(&data->update_lock);
131add77c64SKrzysztof Helt 	data->analog_out = SENSORS_LIMIT(tmp, 0, 255);
132add77c64SKrzysztof Helt 	i2c_smbus_write_byte_data(client, THMC50_REG_ANALOG_OUT,
133add77c64SKrzysztof Helt 				  data->analog_out);
134add77c64SKrzysztof Helt 
135add77c64SKrzysztof Helt 	config = i2c_smbus_read_byte_data(client, THMC50_REG_CONF);
136add77c64SKrzysztof Helt 	if (data->analog_out == 0)
137add77c64SKrzysztof Helt 		config &= ~THMC50_REG_CONF_nFANOFF;
138add77c64SKrzysztof Helt 	else
139add77c64SKrzysztof Helt 		config |= THMC50_REG_CONF_nFANOFF;
140add77c64SKrzysztof Helt 	i2c_smbus_write_byte_data(client, THMC50_REG_CONF, config);
141add77c64SKrzysztof Helt 
142add77c64SKrzysztof Helt 	mutex_unlock(&data->update_lock);
143add77c64SKrzysztof Helt 	return count;
144add77c64SKrzysztof Helt }
145add77c64SKrzysztof Helt 
146add77c64SKrzysztof Helt /* There is only one PWM mode = DC */
147add77c64SKrzysztof Helt static ssize_t show_pwm_mode(struct device *dev, struct device_attribute *attr,
148add77c64SKrzysztof Helt 			     char *buf)
149add77c64SKrzysztof Helt {
150add77c64SKrzysztof Helt 	return sprintf(buf, "0\n");
151add77c64SKrzysztof Helt }
152add77c64SKrzysztof Helt 
153add77c64SKrzysztof Helt /* Temperatures */
154add77c64SKrzysztof Helt static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
155add77c64SKrzysztof Helt 			 char *buf)
156add77c64SKrzysztof Helt {
157add77c64SKrzysztof Helt 	int nr = to_sensor_dev_attr(attr)->index;
158add77c64SKrzysztof Helt 	struct thmc50_data *data = thmc50_update_device(dev);
159add77c64SKrzysztof Helt 	return sprintf(buf, "%d\n", data->temp_input[nr] * 1000);
160add77c64SKrzysztof Helt }
161add77c64SKrzysztof Helt 
162add77c64SKrzysztof Helt static ssize_t show_temp_min(struct device *dev, struct device_attribute *attr,
163add77c64SKrzysztof Helt 			     char *buf)
164add77c64SKrzysztof Helt {
165add77c64SKrzysztof Helt 	int nr = to_sensor_dev_attr(attr)->index;
166add77c64SKrzysztof Helt 	struct thmc50_data *data = thmc50_update_device(dev);
167add77c64SKrzysztof Helt 	return sprintf(buf, "%d\n", data->temp_min[nr] * 1000);
168add77c64SKrzysztof Helt }
169add77c64SKrzysztof Helt 
170add77c64SKrzysztof Helt static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr,
171add77c64SKrzysztof Helt 			    const char *buf, size_t count)
172add77c64SKrzysztof Helt {
173add77c64SKrzysztof Helt 	int nr = to_sensor_dev_attr(attr)->index;
174add77c64SKrzysztof Helt 	struct i2c_client *client = to_i2c_client(dev);
175add77c64SKrzysztof Helt 	struct thmc50_data *data = i2c_get_clientdata(client);
176add77c64SKrzysztof Helt 	int val = simple_strtol(buf, NULL, 10);
177add77c64SKrzysztof Helt 
178add77c64SKrzysztof Helt 	mutex_lock(&data->update_lock);
179add77c64SKrzysztof Helt 	data->temp_min[nr] = SENSORS_LIMIT(val / 1000, -128, 127);
180add77c64SKrzysztof Helt 	i2c_smbus_write_byte_data(client, THMC50_REG_TEMP_MIN[nr],
181add77c64SKrzysztof Helt 				  data->temp_min[nr]);
182add77c64SKrzysztof Helt 	mutex_unlock(&data->update_lock);
183add77c64SKrzysztof Helt 	return count;
184add77c64SKrzysztof Helt }
185add77c64SKrzysztof Helt 
186add77c64SKrzysztof Helt static ssize_t show_temp_max(struct device *dev, struct device_attribute *attr,
187add77c64SKrzysztof Helt 			     char *buf)
188add77c64SKrzysztof Helt {
189add77c64SKrzysztof Helt 	int nr = to_sensor_dev_attr(attr)->index;
190add77c64SKrzysztof Helt 	struct thmc50_data *data = thmc50_update_device(dev);
191add77c64SKrzysztof Helt 	return sprintf(buf, "%d\n", data->temp_max[nr] * 1000);
192add77c64SKrzysztof Helt }
193add77c64SKrzysztof Helt 
194add77c64SKrzysztof Helt static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr,
195add77c64SKrzysztof Helt 			    const char *buf, size_t count)
196add77c64SKrzysztof Helt {
197add77c64SKrzysztof Helt 	int nr = to_sensor_dev_attr(attr)->index;
198add77c64SKrzysztof Helt 	struct i2c_client *client = to_i2c_client(dev);
199add77c64SKrzysztof Helt 	struct thmc50_data *data = i2c_get_clientdata(client);
200add77c64SKrzysztof Helt 	int val = simple_strtol(buf, NULL, 10);
201add77c64SKrzysztof Helt 
202add77c64SKrzysztof Helt 	mutex_lock(&data->update_lock);
203add77c64SKrzysztof Helt 	data->temp_max[nr] = SENSORS_LIMIT(val / 1000, -128, 127);
204add77c64SKrzysztof Helt 	i2c_smbus_write_byte_data(client, THMC50_REG_TEMP_MAX[nr],
205add77c64SKrzysztof Helt 				  data->temp_max[nr]);
206add77c64SKrzysztof Helt 	mutex_unlock(&data->update_lock);
207add77c64SKrzysztof Helt 	return count;
208add77c64SKrzysztof Helt }
209add77c64SKrzysztof Helt 
21084f768c1SKrzysztof Helt static ssize_t show_temp_critical(struct device *dev,
21184f768c1SKrzysztof Helt 				  struct device_attribute *attr,
21284f768c1SKrzysztof Helt 				  char *buf)
21384f768c1SKrzysztof Helt {
21484f768c1SKrzysztof Helt 	int nr = to_sensor_dev_attr(attr)->index;
21584f768c1SKrzysztof Helt 	struct thmc50_data *data = thmc50_update_device(dev);
21684f768c1SKrzysztof Helt 	return sprintf(buf, "%d\n", data->temp_critical[nr] * 1000);
21784f768c1SKrzysztof Helt }
21884f768c1SKrzysztof Helt 
219dcf3b5fbSKrzysztof Helt static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
220dcf3b5fbSKrzysztof Helt 			  char *buf)
221dcf3b5fbSKrzysztof Helt {
222dcf3b5fbSKrzysztof Helt 	int index = to_sensor_dev_attr(attr)->index;
223dcf3b5fbSKrzysztof Helt 	struct thmc50_data *data = thmc50_update_device(dev);
224dcf3b5fbSKrzysztof Helt 
225dcf3b5fbSKrzysztof Helt 	return sprintf(buf, "%u\n", (data->alarms >> index) & 1);
226dcf3b5fbSKrzysztof Helt }
227dcf3b5fbSKrzysztof Helt 
228add77c64SKrzysztof Helt #define temp_reg(offset)						\
229add77c64SKrzysztof Helt static SENSOR_DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp,	\
230add77c64SKrzysztof Helt 			NULL, offset - 1);				\
231add77c64SKrzysztof Helt static SENSOR_DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR,	\
232add77c64SKrzysztof Helt 			show_temp_min, set_temp_min, offset - 1);	\
233add77c64SKrzysztof Helt static SENSOR_DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR,	\
23484f768c1SKrzysztof Helt 			show_temp_max, set_temp_max, offset - 1);	\
23584f768c1SKrzysztof Helt static SENSOR_DEVICE_ATTR(temp##offset##_crit, S_IRUGO,			\
23684f768c1SKrzysztof Helt 			show_temp_critical, NULL, offset - 1);
237add77c64SKrzysztof Helt 
238add77c64SKrzysztof Helt temp_reg(1);
239add77c64SKrzysztof Helt temp_reg(2);
240add77c64SKrzysztof Helt temp_reg(3);
241add77c64SKrzysztof Helt 
242dcf3b5fbSKrzysztof Helt static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 0);
243dcf3b5fbSKrzysztof Helt static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 5);
244dcf3b5fbSKrzysztof Helt static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 1);
245dcf3b5fbSKrzysztof Helt static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 7);
246dcf3b5fbSKrzysztof Helt static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL, 2);
247dcf3b5fbSKrzysztof Helt 
248add77c64SKrzysztof Helt static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_analog_out,
249add77c64SKrzysztof Helt 			  set_analog_out, 0);
250add77c64SKrzysztof Helt static SENSOR_DEVICE_ATTR(pwm1_mode, S_IRUGO, show_pwm_mode, NULL, 0);
251add77c64SKrzysztof Helt 
252add77c64SKrzysztof Helt static struct attribute *thmc50_attributes[] = {
253add77c64SKrzysztof Helt 	&sensor_dev_attr_temp1_max.dev_attr.attr,
254add77c64SKrzysztof Helt 	&sensor_dev_attr_temp1_min.dev_attr.attr,
255add77c64SKrzysztof Helt 	&sensor_dev_attr_temp1_input.dev_attr.attr,
25684f768c1SKrzysztof Helt 	&sensor_dev_attr_temp1_crit.dev_attr.attr,
257dcf3b5fbSKrzysztof Helt 	&sensor_dev_attr_temp1_alarm.dev_attr.attr,
258add77c64SKrzysztof Helt 	&sensor_dev_attr_temp2_max.dev_attr.attr,
259add77c64SKrzysztof Helt 	&sensor_dev_attr_temp2_min.dev_attr.attr,
260add77c64SKrzysztof Helt 	&sensor_dev_attr_temp2_input.dev_attr.attr,
26184f768c1SKrzysztof Helt 	&sensor_dev_attr_temp2_crit.dev_attr.attr,
262dcf3b5fbSKrzysztof Helt 	&sensor_dev_attr_temp2_alarm.dev_attr.attr,
263dcf3b5fbSKrzysztof Helt 	&sensor_dev_attr_temp2_fault.dev_attr.attr,
264add77c64SKrzysztof Helt 	&sensor_dev_attr_pwm1.dev_attr.attr,
265add77c64SKrzysztof Helt 	&sensor_dev_attr_pwm1_mode.dev_attr.attr,
266add77c64SKrzysztof Helt 	NULL
267add77c64SKrzysztof Helt };
268add77c64SKrzysztof Helt 
269add77c64SKrzysztof Helt static const struct attribute_group thmc50_group = {
270add77c64SKrzysztof Helt 	.attrs = thmc50_attributes,
271add77c64SKrzysztof Helt };
272add77c64SKrzysztof Helt 
273add77c64SKrzysztof Helt /* for ADM1022 3rd temperature mode */
274894c00cfSJean Delvare static struct attribute *temp3_attributes[] = {
275add77c64SKrzysztof Helt 	&sensor_dev_attr_temp3_max.dev_attr.attr,
276add77c64SKrzysztof Helt 	&sensor_dev_attr_temp3_min.dev_attr.attr,
277add77c64SKrzysztof Helt 	&sensor_dev_attr_temp3_input.dev_attr.attr,
27884f768c1SKrzysztof Helt 	&sensor_dev_attr_temp3_crit.dev_attr.attr,
279dcf3b5fbSKrzysztof Helt 	&sensor_dev_attr_temp3_alarm.dev_attr.attr,
280dcf3b5fbSKrzysztof Helt 	&sensor_dev_attr_temp3_fault.dev_attr.attr,
281add77c64SKrzysztof Helt 	NULL
282add77c64SKrzysztof Helt };
283add77c64SKrzysztof Helt 
284894c00cfSJean Delvare static const struct attribute_group temp3_group = {
285894c00cfSJean Delvare 	.attrs = temp3_attributes,
286add77c64SKrzysztof Helt };
287add77c64SKrzysztof Helt 
288ccf37488SJean Delvare /* Return 0 if detection is successful, -ENODEV otherwise */
289310ec792SJean Delvare static int thmc50_detect(struct i2c_client *client,
290ccf37488SJean Delvare 			 struct i2c_board_info *info)
291add77c64SKrzysztof Helt {
292add77c64SKrzysztof Helt 	unsigned company;
293add77c64SKrzysztof Helt 	unsigned revision;
294add77c64SKrzysztof Helt 	unsigned config;
295ccf37488SJean Delvare 	struct i2c_adapter *adapter = client->adapter;
296cc28a610SJean Delvare 	const char *type_name;
297add77c64SKrzysztof Helt 
298add77c64SKrzysztof Helt 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
299add77c64SKrzysztof Helt 		pr_debug("thmc50: detect failed, "
300add77c64SKrzysztof Helt 			 "smbus byte data not supported!\n");
301ccf37488SJean Delvare 		return -ENODEV;
302add77c64SKrzysztof Helt 	}
303add77c64SKrzysztof Helt 
304add77c64SKrzysztof Helt 	pr_debug("thmc50: Probing for THMC50 at 0x%2X on bus %d\n",
305add77c64SKrzysztof Helt 		 client->addr, i2c_adapter_id(client->adapter));
306add77c64SKrzysztof Helt 
307add77c64SKrzysztof Helt 	company = i2c_smbus_read_byte_data(client, THMC50_REG_COMPANY_ID);
308add77c64SKrzysztof Helt 	revision = i2c_smbus_read_byte_data(client, THMC50_REG_DIE_CODE);
309add77c64SKrzysztof Helt 	config = i2c_smbus_read_byte_data(client, THMC50_REG_CONF);
31052df6440SJean Delvare 	if (revision < 0xc0 || (config & 0x10))
31152df6440SJean Delvare 		return -ENODEV;
312add77c64SKrzysztof Helt 
31352df6440SJean Delvare 	if (company == 0x41) {
314add77c64SKrzysztof Helt 		int id = i2c_adapter_id(client->adapter);
315add77c64SKrzysztof Helt 		int i;
316add77c64SKrzysztof Helt 
317add77c64SKrzysztof Helt 		type_name = "adm1022";
318add77c64SKrzysztof Helt 		for (i = 0; i + 1 < adm1022_temp3_num; i += 2)
319add77c64SKrzysztof Helt 			if (adm1022_temp3[i] == id &&
320ccf37488SJean Delvare 			    adm1022_temp3[i + 1] == client->addr) {
321add77c64SKrzysztof Helt 				/* enable 2nd remote temp */
322ccf37488SJean Delvare 				config |= (1 << 7);
323ccf37488SJean Delvare 				i2c_smbus_write_byte_data(client,
324ccf37488SJean Delvare 							  THMC50_REG_CONF,
325ccf37488SJean Delvare 							  config);
326add77c64SKrzysztof Helt 				break;
327add77c64SKrzysztof Helt 			}
32852df6440SJean Delvare 	} else if (company == 0x49) {
329cc28a610SJean Delvare 		type_name = "thmc50";
33052df6440SJean Delvare 	} else {
33152df6440SJean Delvare 		pr_debug("thmc50: Detection of THMC50/ADM1022 failed\n");
33252df6440SJean Delvare 		return -ENODEV;
333add77c64SKrzysztof Helt 	}
33452df6440SJean Delvare 
335cc28a610SJean Delvare 	pr_debug("thmc50: Detected %s (version %x, revision %x)\n",
336cc28a610SJean Delvare 		 type_name, (revision >> 4) - 0xc, revision & 0xf);
337add77c64SKrzysztof Helt 
338ccf37488SJean Delvare 	strlcpy(info->type, type_name, I2C_NAME_SIZE);
339add77c64SKrzysztof Helt 
340ccf37488SJean Delvare 	return 0;
341ccf37488SJean Delvare }
342ccf37488SJean Delvare 
343ccf37488SJean Delvare static int thmc50_probe(struct i2c_client *client,
344ccf37488SJean Delvare 			const struct i2c_device_id *id)
345ccf37488SJean Delvare {
346ccf37488SJean Delvare 	struct thmc50_data *data;
347ccf37488SJean Delvare 	int err;
348ccf37488SJean Delvare 
349ccf37488SJean Delvare 	data = kzalloc(sizeof(struct thmc50_data), GFP_KERNEL);
350ccf37488SJean Delvare 	if (!data) {
351ccf37488SJean Delvare 		pr_debug("thmc50: detect failed, kzalloc failed!\n");
352ccf37488SJean Delvare 		err = -ENOMEM;
353ccf37488SJean Delvare 		goto exit;
354ccf37488SJean Delvare 	}
355ccf37488SJean Delvare 
356ccf37488SJean Delvare 	i2c_set_clientdata(client, data);
357ccf37488SJean Delvare 	data->type = id->driver_data;
358ccf37488SJean Delvare 	mutex_init(&data->update_lock);
359add77c64SKrzysztof Helt 
360add77c64SKrzysztof Helt 	thmc50_init_client(client);
361add77c64SKrzysztof Helt 
362add77c64SKrzysztof Helt 	/* Register sysfs hooks */
363add77c64SKrzysztof Helt 	if ((err = sysfs_create_group(&client->dev.kobj, &thmc50_group)))
364ccf37488SJean Delvare 		goto exit_free;
365add77c64SKrzysztof Helt 
366add77c64SKrzysztof Helt 	/* Register ADM1022 sysfs hooks */
367894c00cfSJean Delvare 	if (data->has_temp3)
368add77c64SKrzysztof Helt 		if ((err = sysfs_create_group(&client->dev.kobj,
369894c00cfSJean Delvare 					      &temp3_group)))
370add77c64SKrzysztof Helt 			goto exit_remove_sysfs_thmc50;
371add77c64SKrzysztof Helt 
372add77c64SKrzysztof Helt 	/* Register a new directory entry with module sensors */
3731beeffe4STony Jones 	data->hwmon_dev = hwmon_device_register(&client->dev);
3741beeffe4STony Jones 	if (IS_ERR(data->hwmon_dev)) {
3751beeffe4STony Jones 		err = PTR_ERR(data->hwmon_dev);
376add77c64SKrzysztof Helt 		goto exit_remove_sysfs;
377add77c64SKrzysztof Helt 	}
378add77c64SKrzysztof Helt 
379add77c64SKrzysztof Helt 	return 0;
380add77c64SKrzysztof Helt 
381add77c64SKrzysztof Helt exit_remove_sysfs:
382894c00cfSJean Delvare 	if (data->has_temp3)
383894c00cfSJean Delvare 		sysfs_remove_group(&client->dev.kobj, &temp3_group);
384add77c64SKrzysztof Helt exit_remove_sysfs_thmc50:
385add77c64SKrzysztof Helt 	sysfs_remove_group(&client->dev.kobj, &thmc50_group);
386add77c64SKrzysztof Helt exit_free:
387add77c64SKrzysztof Helt 	kfree(data);
388add77c64SKrzysztof Helt exit:
389add77c64SKrzysztof Helt 	return err;
390add77c64SKrzysztof Helt }
391add77c64SKrzysztof Helt 
392ccf37488SJean Delvare static int thmc50_remove(struct i2c_client *client)
393add77c64SKrzysztof Helt {
394add77c64SKrzysztof Helt 	struct thmc50_data *data = i2c_get_clientdata(client);
395add77c64SKrzysztof Helt 
3961beeffe4STony Jones 	hwmon_device_unregister(data->hwmon_dev);
397add77c64SKrzysztof Helt 	sysfs_remove_group(&client->dev.kobj, &thmc50_group);
398894c00cfSJean Delvare 	if (data->has_temp3)
399894c00cfSJean Delvare 		sysfs_remove_group(&client->dev.kobj, &temp3_group);
400add77c64SKrzysztof Helt 
401add77c64SKrzysztof Helt 	kfree(data);
402add77c64SKrzysztof Helt 
403add77c64SKrzysztof Helt 	return 0;
404add77c64SKrzysztof Helt }
405add77c64SKrzysztof Helt 
406add77c64SKrzysztof Helt static void thmc50_init_client(struct i2c_client *client)
407add77c64SKrzysztof Helt {
408add77c64SKrzysztof Helt 	struct thmc50_data *data = i2c_get_clientdata(client);
409add77c64SKrzysztof Helt 	int config;
410add77c64SKrzysztof Helt 
411add77c64SKrzysztof Helt 	data->analog_out = i2c_smbus_read_byte_data(client,
412add77c64SKrzysztof Helt 						    THMC50_REG_ANALOG_OUT);
413add77c64SKrzysztof Helt 	/* set up to at least 1 */
414add77c64SKrzysztof Helt 	if (data->analog_out == 0) {
415add77c64SKrzysztof Helt 		data->analog_out = 1;
416add77c64SKrzysztof Helt 		i2c_smbus_write_byte_data(client, THMC50_REG_ANALOG_OUT,
417add77c64SKrzysztof Helt 					  data->analog_out);
418add77c64SKrzysztof Helt 	}
419add77c64SKrzysztof Helt 	config = i2c_smbus_read_byte_data(client, THMC50_REG_CONF);
420add77c64SKrzysztof Helt 	config |= 0x1;	/* start the chip if it is in standby mode */
421ccf37488SJean Delvare 	if (data->type == adm1022 && (config & (1 << 7)))
422ccf37488SJean Delvare 		data->has_temp3 = 1;
423add77c64SKrzysztof Helt 	i2c_smbus_write_byte_data(client, THMC50_REG_CONF, config);
424add77c64SKrzysztof Helt }
425add77c64SKrzysztof Helt 
426add77c64SKrzysztof Helt static struct thmc50_data *thmc50_update_device(struct device *dev)
427add77c64SKrzysztof Helt {
428add77c64SKrzysztof Helt 	struct i2c_client *client = to_i2c_client(dev);
429add77c64SKrzysztof Helt 	struct thmc50_data *data = i2c_get_clientdata(client);
430add77c64SKrzysztof Helt 	int timeout = HZ / 5 + (data->type == thmc50 ? HZ : 0);
431add77c64SKrzysztof Helt 
432add77c64SKrzysztof Helt 	mutex_lock(&data->update_lock);
433add77c64SKrzysztof Helt 
434add77c64SKrzysztof Helt 	if (time_after(jiffies, data->last_updated + timeout)
435add77c64SKrzysztof Helt 	    || !data->valid) {
436add77c64SKrzysztof Helt 
437add77c64SKrzysztof Helt 		int temps = data->has_temp3 ? 3 : 2;
438add77c64SKrzysztof Helt 		int i;
43984f768c1SKrzysztof Helt 		int prog = i2c_smbus_read_byte_data(client, THMC50_REG_CONF);
44084f768c1SKrzysztof Helt 
44184f768c1SKrzysztof Helt 		prog &= THMC50_REG_CONF_PROGRAMMED;
44284f768c1SKrzysztof Helt 
443add77c64SKrzysztof Helt 		for (i = 0; i < temps; i++) {
444add77c64SKrzysztof Helt 			data->temp_input[i] = i2c_smbus_read_byte_data(client,
445add77c64SKrzysztof Helt 						THMC50_REG_TEMP[i]);
446add77c64SKrzysztof Helt 			data->temp_max[i] = i2c_smbus_read_byte_data(client,
447add77c64SKrzysztof Helt 						THMC50_REG_TEMP_MAX[i]);
448add77c64SKrzysztof Helt 			data->temp_min[i] = i2c_smbus_read_byte_data(client,
449add77c64SKrzysztof Helt 						THMC50_REG_TEMP_MIN[i]);
45084f768c1SKrzysztof Helt 			data->temp_critical[i] =
45184f768c1SKrzysztof Helt 				i2c_smbus_read_byte_data(client,
45284f768c1SKrzysztof Helt 					prog ? THMC50_REG_TEMP_CRITICAL[i]
45384f768c1SKrzysztof Helt 					     : THMC50_REG_TEMP_DEFAULT[i]);
454add77c64SKrzysztof Helt 		}
455add77c64SKrzysztof Helt 		data->analog_out =
456add77c64SKrzysztof Helt 		    i2c_smbus_read_byte_data(client, THMC50_REG_ANALOG_OUT);
457dcf3b5fbSKrzysztof Helt 		data->alarms =
458bba891c2SKrzysztof Helt 		    i2c_smbus_read_byte_data(client, THMC50_REG_INTR);
459add77c64SKrzysztof Helt 		data->last_updated = jiffies;
460add77c64SKrzysztof Helt 		data->valid = 1;
461add77c64SKrzysztof Helt 	}
462add77c64SKrzysztof Helt 
463add77c64SKrzysztof Helt 	mutex_unlock(&data->update_lock);
464add77c64SKrzysztof Helt 
465add77c64SKrzysztof Helt 	return data;
466add77c64SKrzysztof Helt }
467add77c64SKrzysztof Helt 
468add77c64SKrzysztof Helt static int __init sm_thmc50_init(void)
469add77c64SKrzysztof Helt {
470add77c64SKrzysztof Helt 	return i2c_add_driver(&thmc50_driver);
471add77c64SKrzysztof Helt }
472add77c64SKrzysztof Helt 
473add77c64SKrzysztof Helt static void __exit sm_thmc50_exit(void)
474add77c64SKrzysztof Helt {
475add77c64SKrzysztof Helt 	i2c_del_driver(&thmc50_driver);
476add77c64SKrzysztof Helt }
477add77c64SKrzysztof Helt 
478add77c64SKrzysztof Helt MODULE_AUTHOR("Krzysztof Helt <krzysztof.h1@wp.pl>");
479add77c64SKrzysztof Helt MODULE_DESCRIPTION("THMC50 driver");
480add77c64SKrzysztof Helt 
481add77c64SKrzysztof Helt module_init(sm_thmc50_init);
482add77c64SKrzysztof Helt module_exit(sm_thmc50_exit);
483