xref: /openbmc/linux/drivers/hwmon/thmc50.c (revision 84f768c1633cfc547d82b9dc671ffea2f3785542)
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);
39add77c64SKrzysztof Helt I2C_CLIENT_MODULE_PARM(adm1022_temp3, "List of adapter,address pairs "
40add77c64SKrzysztof Helt 			"to enable 3rd temperature (ADM1022 only)");
41add77c64SKrzysztof Helt 
42add77c64SKrzysztof Helt /* Many THMC50 constants specified below */
43add77c64SKrzysztof Helt 
44add77c64SKrzysztof Helt /* The THMC50 registers */
45add77c64SKrzysztof Helt #define THMC50_REG_CONF				0x40
46add77c64SKrzysztof Helt #define THMC50_REG_COMPANY_ID			0x3E
47add77c64SKrzysztof Helt #define THMC50_REG_DIE_CODE			0x3F
48add77c64SKrzysztof Helt #define THMC50_REG_ANALOG_OUT			0x19
49dcf3b5fbSKrzysztof Helt /*
50bba891c2SKrzysztof Helt  * The mirror status register cannot be used as
51bba891c2SKrzysztof Helt  * reading it does not clear alarms.
52dcf3b5fbSKrzysztof Helt  */
53bba891c2SKrzysztof Helt #define THMC50_REG_INTR				0x41
54add77c64SKrzysztof Helt 
555910a9b2STobias Klauser static const u8 THMC50_REG_TEMP[] = { 0x27, 0x26, 0x20 };
565910a9b2STobias Klauser static const u8 THMC50_REG_TEMP_MIN[] = { 0x3A, 0x38, 0x2C };
575910a9b2STobias Klauser static const u8 THMC50_REG_TEMP_MAX[] = { 0x39, 0x37, 0x2B };
58*84f768c1SKrzysztof Helt static const u8 THMC50_REG_TEMP_CRITICAL[] = { 0x13, 0x14, 0x14 };
59*84f768c1SKrzysztof Helt static const u8 THMC50_REG_TEMP_DEFAULT[] = { 0x17, 0x18, 0x18 };
60add77c64SKrzysztof Helt 
61add77c64SKrzysztof Helt #define THMC50_REG_CONF_nFANOFF			0x20
62*84f768c1SKrzysztof Helt #define THMC50_REG_CONF_PROGRAMMED		0x08
63add77c64SKrzysztof Helt 
64add77c64SKrzysztof Helt /* Each client has this additional data */
65add77c64SKrzysztof Helt struct thmc50_data {
661beeffe4STony Jones 	struct device *hwmon_dev;
67add77c64SKrzysztof Helt 
68add77c64SKrzysztof Helt 	struct mutex update_lock;
69add77c64SKrzysztof Helt 	enum chips type;
70add77c64SKrzysztof Helt 	unsigned long last_updated;	/* In jiffies */
71add77c64SKrzysztof Helt 	char has_temp3;		/* !=0 if it is ADM1022 in temp3 mode */
72add77c64SKrzysztof Helt 	char valid;		/* !=0 if following fields are valid */
73add77c64SKrzysztof Helt 
74add77c64SKrzysztof Helt 	/* Register values */
75add77c64SKrzysztof Helt 	s8 temp_input[3];
76add77c64SKrzysztof Helt 	s8 temp_max[3];
77add77c64SKrzysztof Helt 	s8 temp_min[3];
78*84f768c1SKrzysztof Helt 	s8 temp_critical[3];
79add77c64SKrzysztof Helt 	u8 analog_out;
80dcf3b5fbSKrzysztof Helt 	u8 alarms;
81add77c64SKrzysztof Helt };
82add77c64SKrzysztof Helt 
83ccf37488SJean Delvare static int thmc50_detect(struct i2c_client *client, int kind,
84ccf37488SJean Delvare 			 struct i2c_board_info *info);
85ccf37488SJean Delvare static int thmc50_probe(struct i2c_client *client,
86ccf37488SJean Delvare 			const struct i2c_device_id *id);
87ccf37488SJean Delvare static int thmc50_remove(struct i2c_client *client);
88add77c64SKrzysztof Helt static void thmc50_init_client(struct i2c_client *client);
89add77c64SKrzysztof Helt static struct thmc50_data *thmc50_update_device(struct device *dev);
90add77c64SKrzysztof Helt 
91ccf37488SJean Delvare static const struct i2c_device_id thmc50_id[] = {
92ccf37488SJean Delvare 	{ "adm1022", adm1022 },
93ccf37488SJean Delvare 	{ "thmc50", thmc50 },
94ccf37488SJean Delvare 	{ }
95ccf37488SJean Delvare };
96ccf37488SJean Delvare MODULE_DEVICE_TABLE(i2c, thmc50_id);
97ccf37488SJean Delvare 
98add77c64SKrzysztof Helt static struct i2c_driver thmc50_driver = {
99ccf37488SJean Delvare 	.class = I2C_CLASS_HWMON,
100add77c64SKrzysztof Helt 	.driver = {
101add77c64SKrzysztof Helt 		.name = "thmc50",
102add77c64SKrzysztof Helt 	},
103ccf37488SJean Delvare 	.probe = thmc50_probe,
104ccf37488SJean Delvare 	.remove = thmc50_remove,
105ccf37488SJean Delvare 	.id_table = thmc50_id,
106ccf37488SJean Delvare 	.detect = thmc50_detect,
107ccf37488SJean Delvare 	.address_data = &addr_data,
108add77c64SKrzysztof Helt };
109add77c64SKrzysztof Helt 
110add77c64SKrzysztof Helt static ssize_t show_analog_out(struct device *dev,
111add77c64SKrzysztof Helt 			       struct device_attribute *attr, char *buf)
112add77c64SKrzysztof Helt {
113add77c64SKrzysztof Helt 	struct thmc50_data *data = thmc50_update_device(dev);
114add77c64SKrzysztof Helt 	return sprintf(buf, "%d\n", data->analog_out);
115add77c64SKrzysztof Helt }
116add77c64SKrzysztof Helt 
117add77c64SKrzysztof Helt static ssize_t set_analog_out(struct device *dev,
118add77c64SKrzysztof Helt 			      struct device_attribute *attr,
119add77c64SKrzysztof Helt 			      const char *buf, size_t count)
120add77c64SKrzysztof Helt {
121add77c64SKrzysztof Helt 	struct i2c_client *client = to_i2c_client(dev);
122add77c64SKrzysztof Helt 	struct thmc50_data *data = i2c_get_clientdata(client);
123add77c64SKrzysztof Helt 	int tmp = simple_strtoul(buf, NULL, 10);
124add77c64SKrzysztof Helt 	int config;
125add77c64SKrzysztof Helt 
126add77c64SKrzysztof Helt 	mutex_lock(&data->update_lock);
127add77c64SKrzysztof Helt 	data->analog_out = SENSORS_LIMIT(tmp, 0, 255);
128add77c64SKrzysztof Helt 	i2c_smbus_write_byte_data(client, THMC50_REG_ANALOG_OUT,
129add77c64SKrzysztof Helt 				  data->analog_out);
130add77c64SKrzysztof Helt 
131add77c64SKrzysztof Helt 	config = i2c_smbus_read_byte_data(client, THMC50_REG_CONF);
132add77c64SKrzysztof Helt 	if (data->analog_out == 0)
133add77c64SKrzysztof Helt 		config &= ~THMC50_REG_CONF_nFANOFF;
134add77c64SKrzysztof Helt 	else
135add77c64SKrzysztof Helt 		config |= THMC50_REG_CONF_nFANOFF;
136add77c64SKrzysztof Helt 	i2c_smbus_write_byte_data(client, THMC50_REG_CONF, config);
137add77c64SKrzysztof Helt 
138add77c64SKrzysztof Helt 	mutex_unlock(&data->update_lock);
139add77c64SKrzysztof Helt 	return count;
140add77c64SKrzysztof Helt }
141add77c64SKrzysztof Helt 
142add77c64SKrzysztof Helt /* There is only one PWM mode = DC */
143add77c64SKrzysztof Helt static ssize_t show_pwm_mode(struct device *dev, struct device_attribute *attr,
144add77c64SKrzysztof Helt 			     char *buf)
145add77c64SKrzysztof Helt {
146add77c64SKrzysztof Helt 	return sprintf(buf, "0\n");
147add77c64SKrzysztof Helt }
148add77c64SKrzysztof Helt 
149add77c64SKrzysztof Helt /* Temperatures */
150add77c64SKrzysztof Helt static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
151add77c64SKrzysztof Helt 			 char *buf)
152add77c64SKrzysztof Helt {
153add77c64SKrzysztof Helt 	int nr = to_sensor_dev_attr(attr)->index;
154add77c64SKrzysztof Helt 	struct thmc50_data *data = thmc50_update_device(dev);
155add77c64SKrzysztof Helt 	return sprintf(buf, "%d\n", data->temp_input[nr] * 1000);
156add77c64SKrzysztof Helt }
157add77c64SKrzysztof Helt 
158add77c64SKrzysztof Helt static ssize_t show_temp_min(struct device *dev, struct device_attribute *attr,
159add77c64SKrzysztof Helt 			     char *buf)
160add77c64SKrzysztof Helt {
161add77c64SKrzysztof Helt 	int nr = to_sensor_dev_attr(attr)->index;
162add77c64SKrzysztof Helt 	struct thmc50_data *data = thmc50_update_device(dev);
163add77c64SKrzysztof Helt 	return sprintf(buf, "%d\n", data->temp_min[nr] * 1000);
164add77c64SKrzysztof Helt }
165add77c64SKrzysztof Helt 
166add77c64SKrzysztof Helt static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr,
167add77c64SKrzysztof Helt 			    const char *buf, size_t count)
168add77c64SKrzysztof Helt {
169add77c64SKrzysztof Helt 	int nr = to_sensor_dev_attr(attr)->index;
170add77c64SKrzysztof Helt 	struct i2c_client *client = to_i2c_client(dev);
171add77c64SKrzysztof Helt 	struct thmc50_data *data = i2c_get_clientdata(client);
172add77c64SKrzysztof Helt 	int val = simple_strtol(buf, NULL, 10);
173add77c64SKrzysztof Helt 
174add77c64SKrzysztof Helt 	mutex_lock(&data->update_lock);
175add77c64SKrzysztof Helt 	data->temp_min[nr] = SENSORS_LIMIT(val / 1000, -128, 127);
176add77c64SKrzysztof Helt 	i2c_smbus_write_byte_data(client, THMC50_REG_TEMP_MIN[nr],
177add77c64SKrzysztof Helt 				  data->temp_min[nr]);
178add77c64SKrzysztof Helt 	mutex_unlock(&data->update_lock);
179add77c64SKrzysztof Helt 	return count;
180add77c64SKrzysztof Helt }
181add77c64SKrzysztof Helt 
182add77c64SKrzysztof Helt static ssize_t show_temp_max(struct device *dev, struct device_attribute *attr,
183add77c64SKrzysztof Helt 			     char *buf)
184add77c64SKrzysztof Helt {
185add77c64SKrzysztof Helt 	int nr = to_sensor_dev_attr(attr)->index;
186add77c64SKrzysztof Helt 	struct thmc50_data *data = thmc50_update_device(dev);
187add77c64SKrzysztof Helt 	return sprintf(buf, "%d\n", data->temp_max[nr] * 1000);
188add77c64SKrzysztof Helt }
189add77c64SKrzysztof Helt 
190add77c64SKrzysztof Helt static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr,
191add77c64SKrzysztof Helt 			    const char *buf, size_t count)
192add77c64SKrzysztof Helt {
193add77c64SKrzysztof Helt 	int nr = to_sensor_dev_attr(attr)->index;
194add77c64SKrzysztof Helt 	struct i2c_client *client = to_i2c_client(dev);
195add77c64SKrzysztof Helt 	struct thmc50_data *data = i2c_get_clientdata(client);
196add77c64SKrzysztof Helt 	int val = simple_strtol(buf, NULL, 10);
197add77c64SKrzysztof Helt 
198add77c64SKrzysztof Helt 	mutex_lock(&data->update_lock);
199add77c64SKrzysztof Helt 	data->temp_max[nr] = SENSORS_LIMIT(val / 1000, -128, 127);
200add77c64SKrzysztof Helt 	i2c_smbus_write_byte_data(client, THMC50_REG_TEMP_MAX[nr],
201add77c64SKrzysztof Helt 				  data->temp_max[nr]);
202add77c64SKrzysztof Helt 	mutex_unlock(&data->update_lock);
203add77c64SKrzysztof Helt 	return count;
204add77c64SKrzysztof Helt }
205add77c64SKrzysztof Helt 
206*84f768c1SKrzysztof Helt static ssize_t show_temp_critical(struct device *dev,
207*84f768c1SKrzysztof Helt 				  struct device_attribute *attr,
208*84f768c1SKrzysztof Helt 				  char *buf)
209*84f768c1SKrzysztof Helt {
210*84f768c1SKrzysztof Helt 	int nr = to_sensor_dev_attr(attr)->index;
211*84f768c1SKrzysztof Helt 	struct thmc50_data *data = thmc50_update_device(dev);
212*84f768c1SKrzysztof Helt 	return sprintf(buf, "%d\n", data->temp_critical[nr] * 1000);
213*84f768c1SKrzysztof Helt }
214*84f768c1SKrzysztof Helt 
215dcf3b5fbSKrzysztof Helt static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
216dcf3b5fbSKrzysztof Helt 			  char *buf)
217dcf3b5fbSKrzysztof Helt {
218dcf3b5fbSKrzysztof Helt 	int index = to_sensor_dev_attr(attr)->index;
219dcf3b5fbSKrzysztof Helt 	struct thmc50_data *data = thmc50_update_device(dev);
220dcf3b5fbSKrzysztof Helt 
221dcf3b5fbSKrzysztof Helt 	return sprintf(buf, "%u\n", (data->alarms >> index) & 1);
222dcf3b5fbSKrzysztof Helt }
223dcf3b5fbSKrzysztof Helt 
224add77c64SKrzysztof Helt #define temp_reg(offset)						\
225add77c64SKrzysztof Helt static SENSOR_DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp,	\
226add77c64SKrzysztof Helt 			NULL, offset - 1);				\
227add77c64SKrzysztof Helt static SENSOR_DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR,	\
228add77c64SKrzysztof Helt 			show_temp_min, set_temp_min, offset - 1);	\
229add77c64SKrzysztof Helt static SENSOR_DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR,	\
230*84f768c1SKrzysztof Helt 			show_temp_max, set_temp_max, offset - 1);	\
231*84f768c1SKrzysztof Helt static SENSOR_DEVICE_ATTR(temp##offset##_crit, S_IRUGO,			\
232*84f768c1SKrzysztof Helt 			show_temp_critical, NULL, offset - 1);
233add77c64SKrzysztof Helt 
234add77c64SKrzysztof Helt temp_reg(1);
235add77c64SKrzysztof Helt temp_reg(2);
236add77c64SKrzysztof Helt temp_reg(3);
237add77c64SKrzysztof Helt 
238dcf3b5fbSKrzysztof Helt static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 0);
239dcf3b5fbSKrzysztof Helt static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 5);
240dcf3b5fbSKrzysztof Helt static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 1);
241dcf3b5fbSKrzysztof Helt static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 7);
242dcf3b5fbSKrzysztof Helt static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL, 2);
243dcf3b5fbSKrzysztof Helt 
244add77c64SKrzysztof Helt static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_analog_out,
245add77c64SKrzysztof Helt 			  set_analog_out, 0);
246add77c64SKrzysztof Helt static SENSOR_DEVICE_ATTR(pwm1_mode, S_IRUGO, show_pwm_mode, NULL, 0);
247add77c64SKrzysztof Helt 
248add77c64SKrzysztof Helt static struct attribute *thmc50_attributes[] = {
249add77c64SKrzysztof Helt 	&sensor_dev_attr_temp1_max.dev_attr.attr,
250add77c64SKrzysztof Helt 	&sensor_dev_attr_temp1_min.dev_attr.attr,
251add77c64SKrzysztof Helt 	&sensor_dev_attr_temp1_input.dev_attr.attr,
252*84f768c1SKrzysztof Helt 	&sensor_dev_attr_temp1_crit.dev_attr.attr,
253dcf3b5fbSKrzysztof Helt 	&sensor_dev_attr_temp1_alarm.dev_attr.attr,
254add77c64SKrzysztof Helt 	&sensor_dev_attr_temp2_max.dev_attr.attr,
255add77c64SKrzysztof Helt 	&sensor_dev_attr_temp2_min.dev_attr.attr,
256add77c64SKrzysztof Helt 	&sensor_dev_attr_temp2_input.dev_attr.attr,
257*84f768c1SKrzysztof Helt 	&sensor_dev_attr_temp2_crit.dev_attr.attr,
258dcf3b5fbSKrzysztof Helt 	&sensor_dev_attr_temp2_alarm.dev_attr.attr,
259dcf3b5fbSKrzysztof Helt 	&sensor_dev_attr_temp2_fault.dev_attr.attr,
260add77c64SKrzysztof Helt 	&sensor_dev_attr_pwm1.dev_attr.attr,
261add77c64SKrzysztof Helt 	&sensor_dev_attr_pwm1_mode.dev_attr.attr,
262add77c64SKrzysztof Helt 	NULL
263add77c64SKrzysztof Helt };
264add77c64SKrzysztof Helt 
265add77c64SKrzysztof Helt static const struct attribute_group thmc50_group = {
266add77c64SKrzysztof Helt 	.attrs = thmc50_attributes,
267add77c64SKrzysztof Helt };
268add77c64SKrzysztof Helt 
269add77c64SKrzysztof Helt /* for ADM1022 3rd temperature mode */
270894c00cfSJean Delvare static struct attribute *temp3_attributes[] = {
271add77c64SKrzysztof Helt 	&sensor_dev_attr_temp3_max.dev_attr.attr,
272add77c64SKrzysztof Helt 	&sensor_dev_attr_temp3_min.dev_attr.attr,
273add77c64SKrzysztof Helt 	&sensor_dev_attr_temp3_input.dev_attr.attr,
274*84f768c1SKrzysztof Helt 	&sensor_dev_attr_temp3_crit.dev_attr.attr,
275dcf3b5fbSKrzysztof Helt 	&sensor_dev_attr_temp3_alarm.dev_attr.attr,
276dcf3b5fbSKrzysztof Helt 	&sensor_dev_attr_temp3_fault.dev_attr.attr,
277add77c64SKrzysztof Helt 	NULL
278add77c64SKrzysztof Helt };
279add77c64SKrzysztof Helt 
280894c00cfSJean Delvare static const struct attribute_group temp3_group = {
281894c00cfSJean Delvare 	.attrs = temp3_attributes,
282add77c64SKrzysztof Helt };
283add77c64SKrzysztof Helt 
284ccf37488SJean Delvare /* Return 0 if detection is successful, -ENODEV otherwise */
285ccf37488SJean Delvare static int thmc50_detect(struct i2c_client *client, int kind,
286ccf37488SJean Delvare 			 struct i2c_board_info *info)
287add77c64SKrzysztof Helt {
288add77c64SKrzysztof Helt 	unsigned company;
289add77c64SKrzysztof Helt 	unsigned revision;
290add77c64SKrzysztof Helt 	unsigned config;
291ccf37488SJean Delvare 	struct i2c_adapter *adapter = client->adapter;
292add77c64SKrzysztof Helt 	int err = 0;
293cc28a610SJean Delvare 	const char *type_name;
294add77c64SKrzysztof Helt 
295add77c64SKrzysztof Helt 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
296add77c64SKrzysztof Helt 		pr_debug("thmc50: detect failed, "
297add77c64SKrzysztof Helt 			 "smbus byte data not supported!\n");
298ccf37488SJean Delvare 		return -ENODEV;
299add77c64SKrzysztof Helt 	}
300add77c64SKrzysztof Helt 
301add77c64SKrzysztof Helt 	pr_debug("thmc50: Probing for THMC50 at 0x%2X on bus %d\n",
302add77c64SKrzysztof Helt 		 client->addr, i2c_adapter_id(client->adapter));
303add77c64SKrzysztof Helt 
304add77c64SKrzysztof Helt 	/* Now, we do the remaining detection. */
305add77c64SKrzysztof Helt 	company = i2c_smbus_read_byte_data(client, THMC50_REG_COMPANY_ID);
306add77c64SKrzysztof Helt 	revision = i2c_smbus_read_byte_data(client, THMC50_REG_DIE_CODE);
307add77c64SKrzysztof Helt 	config = i2c_smbus_read_byte_data(client, THMC50_REG_CONF);
308add77c64SKrzysztof Helt 
309add77c64SKrzysztof Helt 	if (kind == 0)
310add77c64SKrzysztof Helt 		kind = thmc50;
311add77c64SKrzysztof Helt 	else if (kind < 0) {
312add77c64SKrzysztof Helt 		err = -ENODEV;
313add77c64SKrzysztof Helt 		if (revision >= 0xc0 && ((config & 0x10) == 0)) {
314add77c64SKrzysztof Helt 			if (company == 0x49) {
315add77c64SKrzysztof Helt 				kind = thmc50;
316add77c64SKrzysztof Helt 				err = 0;
317add77c64SKrzysztof Helt 			} else if (company == 0x41) {
318add77c64SKrzysztof Helt 				kind = adm1022;
319add77c64SKrzysztof Helt 				err = 0;
320add77c64SKrzysztof Helt 			}
321add77c64SKrzysztof Helt 		}
322add77c64SKrzysztof Helt 	}
323add77c64SKrzysztof Helt 	if (err == -ENODEV) {
324add77c64SKrzysztof Helt 		pr_debug("thmc50: Detection of THMC50/ADM1022 failed\n");
325ccf37488SJean Delvare 		return err;
326add77c64SKrzysztof Helt 	}
327add77c64SKrzysztof Helt 
328cc28a610SJean Delvare 	if (kind == adm1022) {
329add77c64SKrzysztof Helt 		int id = i2c_adapter_id(client->adapter);
330add77c64SKrzysztof Helt 		int i;
331add77c64SKrzysztof Helt 
332add77c64SKrzysztof Helt 		type_name = "adm1022";
333add77c64SKrzysztof Helt 		for (i = 0; i + 1 < adm1022_temp3_num; i += 2)
334add77c64SKrzysztof Helt 			if (adm1022_temp3[i] == id &&
335ccf37488SJean Delvare 			    adm1022_temp3[i + 1] == client->addr) {
336add77c64SKrzysztof Helt 				/* enable 2nd remote temp */
337ccf37488SJean Delvare 				config |= (1 << 7);
338ccf37488SJean Delvare 				i2c_smbus_write_byte_data(client,
339ccf37488SJean Delvare 							  THMC50_REG_CONF,
340ccf37488SJean Delvare 							  config);
341add77c64SKrzysztof Helt 				break;
342add77c64SKrzysztof Helt 			}
343cc28a610SJean Delvare 	} else {
344cc28a610SJean Delvare 		type_name = "thmc50";
345add77c64SKrzysztof Helt 	}
346cc28a610SJean Delvare 	pr_debug("thmc50: Detected %s (version %x, revision %x)\n",
347cc28a610SJean Delvare 		 type_name, (revision >> 4) - 0xc, revision & 0xf);
348add77c64SKrzysztof Helt 
349ccf37488SJean Delvare 	strlcpy(info->type, type_name, I2C_NAME_SIZE);
350add77c64SKrzysztof Helt 
351ccf37488SJean Delvare 	return 0;
352ccf37488SJean Delvare }
353ccf37488SJean Delvare 
354ccf37488SJean Delvare static int thmc50_probe(struct i2c_client *client,
355ccf37488SJean Delvare 			const struct i2c_device_id *id)
356ccf37488SJean Delvare {
357ccf37488SJean Delvare 	struct thmc50_data *data;
358ccf37488SJean Delvare 	int err;
359ccf37488SJean Delvare 
360ccf37488SJean Delvare 	data = kzalloc(sizeof(struct thmc50_data), GFP_KERNEL);
361ccf37488SJean Delvare 	if (!data) {
362ccf37488SJean Delvare 		pr_debug("thmc50: detect failed, kzalloc failed!\n");
363ccf37488SJean Delvare 		err = -ENOMEM;
364ccf37488SJean Delvare 		goto exit;
365ccf37488SJean Delvare 	}
366ccf37488SJean Delvare 
367ccf37488SJean Delvare 	i2c_set_clientdata(client, data);
368ccf37488SJean Delvare 	data->type = id->driver_data;
369ccf37488SJean Delvare 	mutex_init(&data->update_lock);
370add77c64SKrzysztof Helt 
371add77c64SKrzysztof Helt 	thmc50_init_client(client);
372add77c64SKrzysztof Helt 
373add77c64SKrzysztof Helt 	/* Register sysfs hooks */
374add77c64SKrzysztof Helt 	if ((err = sysfs_create_group(&client->dev.kobj, &thmc50_group)))
375ccf37488SJean Delvare 		goto exit_free;
376add77c64SKrzysztof Helt 
377add77c64SKrzysztof Helt 	/* Register ADM1022 sysfs hooks */
378894c00cfSJean Delvare 	if (data->has_temp3)
379add77c64SKrzysztof Helt 		if ((err = sysfs_create_group(&client->dev.kobj,
380894c00cfSJean Delvare 					      &temp3_group)))
381add77c64SKrzysztof Helt 			goto exit_remove_sysfs_thmc50;
382add77c64SKrzysztof Helt 
383add77c64SKrzysztof Helt 	/* Register a new directory entry with module sensors */
3841beeffe4STony Jones 	data->hwmon_dev = hwmon_device_register(&client->dev);
3851beeffe4STony Jones 	if (IS_ERR(data->hwmon_dev)) {
3861beeffe4STony Jones 		err = PTR_ERR(data->hwmon_dev);
387add77c64SKrzysztof Helt 		goto exit_remove_sysfs;
388add77c64SKrzysztof Helt 	}
389add77c64SKrzysztof Helt 
390add77c64SKrzysztof Helt 	return 0;
391add77c64SKrzysztof Helt 
392add77c64SKrzysztof Helt exit_remove_sysfs:
393894c00cfSJean Delvare 	if (data->has_temp3)
394894c00cfSJean Delvare 		sysfs_remove_group(&client->dev.kobj, &temp3_group);
395add77c64SKrzysztof Helt exit_remove_sysfs_thmc50:
396add77c64SKrzysztof Helt 	sysfs_remove_group(&client->dev.kobj, &thmc50_group);
397add77c64SKrzysztof Helt exit_free:
398add77c64SKrzysztof Helt 	kfree(data);
399add77c64SKrzysztof Helt exit:
400add77c64SKrzysztof Helt 	return err;
401add77c64SKrzysztof Helt }
402add77c64SKrzysztof Helt 
403ccf37488SJean Delvare static int thmc50_remove(struct i2c_client *client)
404add77c64SKrzysztof Helt {
405add77c64SKrzysztof Helt 	struct thmc50_data *data = i2c_get_clientdata(client);
406add77c64SKrzysztof Helt 
4071beeffe4STony Jones 	hwmon_device_unregister(data->hwmon_dev);
408add77c64SKrzysztof Helt 	sysfs_remove_group(&client->dev.kobj, &thmc50_group);
409894c00cfSJean Delvare 	if (data->has_temp3)
410894c00cfSJean Delvare 		sysfs_remove_group(&client->dev.kobj, &temp3_group);
411add77c64SKrzysztof Helt 
412add77c64SKrzysztof Helt 	kfree(data);
413add77c64SKrzysztof Helt 
414add77c64SKrzysztof Helt 	return 0;
415add77c64SKrzysztof Helt }
416add77c64SKrzysztof Helt 
417add77c64SKrzysztof Helt static void thmc50_init_client(struct i2c_client *client)
418add77c64SKrzysztof Helt {
419add77c64SKrzysztof Helt 	struct thmc50_data *data = i2c_get_clientdata(client);
420add77c64SKrzysztof Helt 	int config;
421add77c64SKrzysztof Helt 
422add77c64SKrzysztof Helt 	data->analog_out = i2c_smbus_read_byte_data(client,
423add77c64SKrzysztof Helt 						    THMC50_REG_ANALOG_OUT);
424add77c64SKrzysztof Helt 	/* set up to at least 1 */
425add77c64SKrzysztof Helt 	if (data->analog_out == 0) {
426add77c64SKrzysztof Helt 		data->analog_out = 1;
427add77c64SKrzysztof Helt 		i2c_smbus_write_byte_data(client, THMC50_REG_ANALOG_OUT,
428add77c64SKrzysztof Helt 					  data->analog_out);
429add77c64SKrzysztof Helt 	}
430add77c64SKrzysztof Helt 	config = i2c_smbus_read_byte_data(client, THMC50_REG_CONF);
431add77c64SKrzysztof Helt 	config |= 0x1;	/* start the chip if it is in standby mode */
432ccf37488SJean Delvare 	if (data->type == adm1022 && (config & (1 << 7)))
433ccf37488SJean Delvare 		data->has_temp3 = 1;
434add77c64SKrzysztof Helt 	i2c_smbus_write_byte_data(client, THMC50_REG_CONF, config);
435add77c64SKrzysztof Helt }
436add77c64SKrzysztof Helt 
437add77c64SKrzysztof Helt static struct thmc50_data *thmc50_update_device(struct device *dev)
438add77c64SKrzysztof Helt {
439add77c64SKrzysztof Helt 	struct i2c_client *client = to_i2c_client(dev);
440add77c64SKrzysztof Helt 	struct thmc50_data *data = i2c_get_clientdata(client);
441add77c64SKrzysztof Helt 	int timeout = HZ / 5 + (data->type == thmc50 ? HZ : 0);
442add77c64SKrzysztof Helt 
443add77c64SKrzysztof Helt 	mutex_lock(&data->update_lock);
444add77c64SKrzysztof Helt 
445add77c64SKrzysztof Helt 	if (time_after(jiffies, data->last_updated + timeout)
446add77c64SKrzysztof Helt 	    || !data->valid) {
447add77c64SKrzysztof Helt 
448add77c64SKrzysztof Helt 		int temps = data->has_temp3 ? 3 : 2;
449add77c64SKrzysztof Helt 		int i;
450*84f768c1SKrzysztof Helt 		int prog = i2c_smbus_read_byte_data(client, THMC50_REG_CONF);
451*84f768c1SKrzysztof Helt 
452*84f768c1SKrzysztof Helt 		prog &= THMC50_REG_CONF_PROGRAMMED;
453*84f768c1SKrzysztof Helt 
454add77c64SKrzysztof Helt 		for (i = 0; i < temps; i++) {
455add77c64SKrzysztof Helt 			data->temp_input[i] = i2c_smbus_read_byte_data(client,
456add77c64SKrzysztof Helt 						THMC50_REG_TEMP[i]);
457add77c64SKrzysztof Helt 			data->temp_max[i] = i2c_smbus_read_byte_data(client,
458add77c64SKrzysztof Helt 						THMC50_REG_TEMP_MAX[i]);
459add77c64SKrzysztof Helt 			data->temp_min[i] = i2c_smbus_read_byte_data(client,
460add77c64SKrzysztof Helt 						THMC50_REG_TEMP_MIN[i]);
461*84f768c1SKrzysztof Helt 			data->temp_critical[i] =
462*84f768c1SKrzysztof Helt 				i2c_smbus_read_byte_data(client,
463*84f768c1SKrzysztof Helt 					prog ? THMC50_REG_TEMP_CRITICAL[i]
464*84f768c1SKrzysztof Helt 					     : THMC50_REG_TEMP_DEFAULT[i]);
465add77c64SKrzysztof Helt 		}
466add77c64SKrzysztof Helt 		data->analog_out =
467add77c64SKrzysztof Helt 		    i2c_smbus_read_byte_data(client, THMC50_REG_ANALOG_OUT);
468dcf3b5fbSKrzysztof Helt 		data->alarms =
469bba891c2SKrzysztof Helt 		    i2c_smbus_read_byte_data(client, THMC50_REG_INTR);
470add77c64SKrzysztof Helt 		data->last_updated = jiffies;
471add77c64SKrzysztof Helt 		data->valid = 1;
472add77c64SKrzysztof Helt 	}
473add77c64SKrzysztof Helt 
474add77c64SKrzysztof Helt 	mutex_unlock(&data->update_lock);
475add77c64SKrzysztof Helt 
476add77c64SKrzysztof Helt 	return data;
477add77c64SKrzysztof Helt }
478add77c64SKrzysztof Helt 
479add77c64SKrzysztof Helt static int __init sm_thmc50_init(void)
480add77c64SKrzysztof Helt {
481add77c64SKrzysztof Helt 	return i2c_add_driver(&thmc50_driver);
482add77c64SKrzysztof Helt }
483add77c64SKrzysztof Helt 
484add77c64SKrzysztof Helt static void __exit sm_thmc50_exit(void)
485add77c64SKrzysztof Helt {
486add77c64SKrzysztof Helt 	i2c_del_driver(&thmc50_driver);
487add77c64SKrzysztof Helt }
488add77c64SKrzysztof Helt 
489add77c64SKrzysztof Helt MODULE_AUTHOR("Krzysztof Helt <krzysztof.h1@wp.pl>");
490add77c64SKrzysztof Helt MODULE_DESCRIPTION("THMC50 driver");
491add77c64SKrzysztof Helt 
492add77c64SKrzysztof Helt module_init(sm_thmc50_init);
493add77c64SKrzysztof Helt module_exit(sm_thmc50_exit);
494