1dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal/*
2dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal * emc1403.c - SMSC Thermal Driver
3dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal *
4dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal * Copyright (C) 2008 Intel Corp
5dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal *
6dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal *
8dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal * This program is free software; you can redistribute it and/or modify
9dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal * it under the terms of the GNU General Public License as published by
10dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal * the Free Software Foundation; version 2 of the License.
11dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal *
12dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal * This program is distributed in the hope that it will be useful, but
13dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal * WITHOUT ANY WARRANTY; without even the implied warranty of
14dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal * General Public License for more details.
16dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal *
17dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal * You should have received a copy of the GNU General Public License along
18dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal * with this program; if not, write to the Free Software Foundation, Inc.,
19dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
20dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
21dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal *
22dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal * TODO
23dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal *	-	cache alarm and critical limit registers
24dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal *	-	add emc1404 support
25dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal */
26dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
27dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal#include <linux/module.h>
28dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal#include <linux/init.h>
29dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal#include <linux/slab.h>
30dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal#include <linux/i2c.h>
31dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal#include <linux/hwmon.h>
32dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal#include <linux/hwmon-sysfs.h>
33dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal#include <linux/err.h>
34dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal#include <linux/sysfs.h>
35dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal#include <linux/mutex.h>
36dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
37dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal#define THERMAL_PID_REG		0xfd
38dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal#define THERMAL_SMSC_ID_REG	0xfe
39dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal#define THERMAL_REVISION_REG	0xff
40dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
41dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstruct thermal_data {
42dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	struct device *hwmon_dev;
43dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	struct mutex mutex;
444bebced84fb66e8f4c061c5579264b112c39fdecGuenter Roeck	/*
454bebced84fb66e8f4c061c5579264b112c39fdecGuenter Roeck	 * Cache the hyst value so we don't keep re-reading it. In theory
464bebced84fb66e8f4c061c5579264b112c39fdecGuenter Roeck	 * we could cache it forever as nobody else should be writing it.
474bebced84fb66e8f4c061c5579264b112c39fdecGuenter Roeck	 */
48dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	u8 cached_hyst;
49dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	unsigned long hyst_valid;
50dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal};
51dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
52dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic ssize_t show_temp(struct device *dev,
53dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal			struct device_attribute *attr, char *buf)
54dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal{
55dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	struct i2c_client *client = to_i2c_client(dev);
56dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
57dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	int retval = i2c_smbus_read_byte_data(client, sda->index);
58dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
59dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	if (retval < 0)
60dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		return retval;
61dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	return sprintf(buf, "%d000\n", retval);
62dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal}
63dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
64dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic ssize_t show_bit(struct device *dev,
65dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal			struct device_attribute *attr, char *buf)
66dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal{
67dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	struct i2c_client *client = to_i2c_client(dev);
68dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	struct sensor_device_attribute_2 *sda = to_sensor_dev_attr_2(attr);
69dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	int retval = i2c_smbus_read_byte_data(client, sda->nr);
70dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
71dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	if (retval < 0)
72dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		return retval;
73dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	retval &= sda->index;
74dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	return sprintf(buf, "%d\n", retval ? 1 : 0);
75dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal}
76dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
77dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic ssize_t store_temp(struct device *dev,
78dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		struct device_attribute *attr, const char *buf, size_t count)
79dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal{
80dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
81dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	struct i2c_client *client = to_i2c_client(dev);
82dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	unsigned long val;
83dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	int retval;
84dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
85179c4fdb565dd2157e5dfe89318b74868e3b523dFrans Meulenbroeks	if (kstrtoul(buf, 10, &val))
86dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		return -EINVAL;
87dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	retval = i2c_smbus_write_byte_data(client, sda->index,
88dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal					DIV_ROUND_CLOSEST(val, 1000));
89dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	if (retval < 0)
90dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		return retval;
91dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	return count;
92dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal}
93dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
94960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Coxstatic ssize_t store_bit(struct device *dev,
95960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox		struct device_attribute *attr, const char *buf, size_t count)
96960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox{
97960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox	struct i2c_client *client = to_i2c_client(dev);
98960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox	struct thermal_data *data = i2c_get_clientdata(client);
99960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox	struct sensor_device_attribute_2 *sda = to_sensor_dev_attr_2(attr);
100960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox	unsigned long val;
101960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox	int retval;
102960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox
103179c4fdb565dd2157e5dfe89318b74868e3b523dFrans Meulenbroeks	if (kstrtoul(buf, 10, &val))
104960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox		return -EINVAL;
105960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox
106960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox	mutex_lock(&data->mutex);
107960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox	retval = i2c_smbus_read_byte_data(client, sda->nr);
108960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox	if (retval < 0)
109960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox		goto fail;
110960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox
111960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox	retval &= ~sda->index;
112960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox	if (val)
113960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox		retval |= sda->index;
114960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox
115960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox	retval = i2c_smbus_write_byte_data(client, sda->index, retval);
116960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox	if (retval == 0)
117960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox		retval = count;
118960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Coxfail:
119960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox	mutex_unlock(&data->mutex);
120960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox	return retval;
121960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox}
122960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox
123dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic ssize_t show_hyst(struct device *dev,
124dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal			struct device_attribute *attr, char *buf)
125dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal{
126dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	struct i2c_client *client = to_i2c_client(dev);
127dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	struct thermal_data *data = i2c_get_clientdata(client);
128dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
129dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	int retval;
130dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	int hyst;
131dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
132dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	retval = i2c_smbus_read_byte_data(client, sda->index);
133dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	if (retval < 0)
134dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		return retval;
135dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
136dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	if (time_after(jiffies, data->hyst_valid)) {
137dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		hyst = i2c_smbus_read_byte_data(client, 0x21);
138dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		if (hyst < 0)
139dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal			return retval;
140dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		data->cached_hyst = hyst;
141dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		data->hyst_valid = jiffies + HZ;
142dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	}
143dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	return sprintf(buf, "%d000\n", retval - data->cached_hyst);
144dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal}
145dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
146dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic ssize_t store_hyst(struct device *dev,
147dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		struct device_attribute *attr, const char *buf, size_t count)
148dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal{
149dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	struct i2c_client *client = to_i2c_client(dev);
150dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	struct thermal_data *data = i2c_get_clientdata(client);
151dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
152dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	int retval;
153dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	int hyst;
154dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	unsigned long val;
155dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
156179c4fdb565dd2157e5dfe89318b74868e3b523dFrans Meulenbroeks	if (kstrtoul(buf, 10, &val))
157dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		return -EINVAL;
158dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
159dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	mutex_lock(&data->mutex);
160dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	retval = i2c_smbus_read_byte_data(client, sda->index);
161dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	if (retval < 0)
162dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		goto fail;
163dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
164dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	hyst = val - retval * 1000;
165dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	hyst = DIV_ROUND_CLOSEST(hyst, 1000);
166dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	if (hyst < 0 || hyst > 255) {
167dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		retval = -ERANGE;
168dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		goto fail;
169dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	}
170dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
171dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	retval = i2c_smbus_write_byte_data(client, 0x21, hyst);
172dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	if (retval == 0) {
173dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		retval = count;
174dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		data->cached_hyst = hyst;
175dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		data->hyst_valid = jiffies + HZ;
176dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	}
177dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalfail:
178dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	mutex_unlock(&data->mutex);
179dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	return retval;
180dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal}
181dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
182dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal/*
183dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal *	Sensors. We pass the actual i2c register to the methods.
184dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal */
185dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
186dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO | S_IWUSR,
187dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	show_temp, store_temp, 0x06);
188dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR,
189dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	show_temp, store_temp, 0x05);
190dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR,
191dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	show_temp, store_temp, 0x20);
192dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0x00);
193dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic SENSOR_DEVICE_ATTR_2(temp1_min_alarm, S_IRUGO,
194dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	show_bit, NULL, 0x36, 0x01);
195dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic SENSOR_DEVICE_ATTR_2(temp1_max_alarm, S_IRUGO,
196dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	show_bit, NULL, 0x35, 0x01);
197dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic SENSOR_DEVICE_ATTR_2(temp1_crit_alarm, S_IRUGO,
198dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	show_bit, NULL, 0x37, 0x01);
199dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO | S_IWUSR,
200dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	show_hyst, store_hyst, 0x20);
201dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
202dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO | S_IWUSR,
203dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	show_temp, store_temp, 0x08);
204dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO | S_IWUSR,
205dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	show_temp, store_temp, 0x07);
206dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO | S_IWUSR,
207dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	show_temp, store_temp, 0x19);
208dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 0x01);
209dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic SENSOR_DEVICE_ATTR_2(temp2_min_alarm, S_IRUGO,
210dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	show_bit, NULL, 0x36, 0x02);
211dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic SENSOR_DEVICE_ATTR_2(temp2_max_alarm, S_IRUGO,
212dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	show_bit, NULL, 0x35, 0x02);
213dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic SENSOR_DEVICE_ATTR_2(temp2_crit_alarm, S_IRUGO,
214dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	show_bit, NULL, 0x37, 0x02);
215dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO | S_IWUSR,
216dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	show_hyst, store_hyst, 0x19);
217dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
218dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO | S_IWUSR,
219dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	show_temp, store_temp, 0x16);
220dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO | S_IWUSR,
221dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	show_temp, store_temp, 0x15);
222dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic SENSOR_DEVICE_ATTR(temp3_crit, S_IRUGO | S_IWUSR,
223dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	show_temp, store_temp, 0x1A);
224dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 0x23);
225dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic SENSOR_DEVICE_ATTR_2(temp3_min_alarm, S_IRUGO,
226dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	show_bit, NULL, 0x36, 0x04);
227dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic SENSOR_DEVICE_ATTR_2(temp3_max_alarm, S_IRUGO,
228dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	show_bit, NULL, 0x35, 0x04);
229dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic SENSOR_DEVICE_ATTR_2(temp3_crit_alarm, S_IRUGO,
230dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	show_bit, NULL, 0x37, 0x04);
231dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO | S_IWUSR,
232dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	show_hyst, store_hyst, 0x1A);
233dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
234960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Coxstatic SENSOR_DEVICE_ATTR_2(power_state, S_IRUGO | S_IWUSR,
235960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox	show_bit, store_bit, 0x03, 0x40);
236960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox
237dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic struct attribute *mid_att_thermal[] = {
238dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	&sensor_dev_attr_temp1_min.dev_attr.attr,
239dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	&sensor_dev_attr_temp1_max.dev_attr.attr,
240dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	&sensor_dev_attr_temp1_crit.dev_attr.attr,
241dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	&sensor_dev_attr_temp1_input.dev_attr.attr,
242dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
243dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
244dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
245dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
246dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	&sensor_dev_attr_temp2_min.dev_attr.attr,
247dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	&sensor_dev_attr_temp2_max.dev_attr.attr,
248dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	&sensor_dev_attr_temp2_crit.dev_attr.attr,
249dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	&sensor_dev_attr_temp2_input.dev_attr.attr,
250dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	&sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
251dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
252dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	&sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
253dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	&sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
254dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	&sensor_dev_attr_temp3_min.dev_attr.attr,
255dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	&sensor_dev_attr_temp3_max.dev_attr.attr,
256dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	&sensor_dev_attr_temp3_crit.dev_attr.attr,
257dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	&sensor_dev_attr_temp3_input.dev_attr.attr,
258dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	&sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
259dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	&sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
260dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	&sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
261dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	&sensor_dev_attr_temp3_crit_hyst.dev_attr.attr,
262960f12f4d1eb5ba3c76dc6b57a909a65dd59e1c2Alan Cox	&sensor_dev_attr_power_state.dev_attr.attr,
263dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	NULL
264dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal};
265dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
266dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic const struct attribute_group m_thermal_gr = {
267dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	.attrs = mid_att_thermal
268dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal};
269dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
270dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic int emc1403_detect(struct i2c_client *client,
271dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal			struct i2c_board_info *info)
272dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal{
273dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	int id;
2747a1b76f2a46016809c7bcacf81e89948cc306703Jekyll Lai	/* Check if thermal chip is SMSC and EMC1403 or EMC1423 */
275dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
276dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	id = i2c_smbus_read_byte_data(client, THERMAL_SMSC_ID_REG);
277dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	if (id != 0x5d)
278dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		return -ENODEV;
279dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
2807a1b76f2a46016809c7bcacf81e89948cc306703Jekyll Lai	id = i2c_smbus_read_byte_data(client, THERMAL_PID_REG);
2817a1b76f2a46016809c7bcacf81e89948cc306703Jekyll Lai	switch (id) {
2827a1b76f2a46016809c7bcacf81e89948cc306703Jekyll Lai	case 0x21:
2837a1b76f2a46016809c7bcacf81e89948cc306703Jekyll Lai		strlcpy(info->type, "emc1403", I2C_NAME_SIZE);
2847a1b76f2a46016809c7bcacf81e89948cc306703Jekyll Lai		break;
2857a1b76f2a46016809c7bcacf81e89948cc306703Jekyll Lai	case 0x23:
2867a1b76f2a46016809c7bcacf81e89948cc306703Jekyll Lai		strlcpy(info->type, "emc1423", I2C_NAME_SIZE);
2877a1b76f2a46016809c7bcacf81e89948cc306703Jekyll Lai		break;
2884bebced84fb66e8f4c061c5579264b112c39fdecGuenter Roeck	/*
2894bebced84fb66e8f4c061c5579264b112c39fdecGuenter Roeck	 * Note: 0x25 is the 1404 which is very similar and this
2904bebced84fb66e8f4c061c5579264b112c39fdecGuenter Roeck	 * driver could be extended
2914bebced84fb66e8f4c061c5579264b112c39fdecGuenter Roeck	 */
2927a1b76f2a46016809c7bcacf81e89948cc306703Jekyll Lai	default:
293dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		return -ENODEV;
2947a1b76f2a46016809c7bcacf81e89948cc306703Jekyll Lai	}
295dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
296dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	id = i2c_smbus_read_byte_data(client, THERMAL_REVISION_REG);
297dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	if (id != 0x01)
298dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		return -ENODEV;
299dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
300dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	return 0;
301dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal}
302dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
303dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic int emc1403_probe(struct i2c_client *client,
304dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal			const struct i2c_device_id *id)
305dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal{
306dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	int res;
307dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	struct thermal_data *data;
308dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
309dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	data = kzalloc(sizeof(struct thermal_data), GFP_KERNEL);
310dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	if (data == NULL) {
311dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		dev_warn(&client->dev, "out of memory");
312dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		return -ENOMEM;
313dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	}
314dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
315dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	i2c_set_clientdata(client, data);
316dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	mutex_init(&data->mutex);
317dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	data->hyst_valid = jiffies - 1;		/* Expired */
318dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
319dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	res = sysfs_create_group(&client->dev.kobj, &m_thermal_gr);
320dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	if (res) {
321dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		dev_warn(&client->dev, "create group failed\n");
322dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		goto thermal_error1;
323dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	}
324dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	data->hwmon_dev = hwmon_device_register(&client->dev);
325dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	if (IS_ERR(data->hwmon_dev)) {
326dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		res = PTR_ERR(data->hwmon_dev);
327dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		dev_warn(&client->dev, "register hwmon dev failed\n");
328dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		goto thermal_error2;
329dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	}
330dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	dev_info(&client->dev, "EMC1403 Thermal chip found\n");
331dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	return res;
332dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
333dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalthermal_error2:
334dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	sysfs_remove_group(&client->dev.kobj, &m_thermal_gr);
335dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalthermal_error1:
336dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	kfree(data);
337dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	return res;
338dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal}
339dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
340dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic int emc1403_remove(struct i2c_client *client)
341dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal{
342dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	struct thermal_data *data = i2c_get_clientdata(client);
343dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
344dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	hwmon_device_unregister(data->hwmon_dev);
345dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	sysfs_remove_group(&client->dev.kobj, &m_thermal_gr);
346dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	kfree(data);
347dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	return 0;
348dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal}
349dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
350dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic const unsigned short emc1403_address_list[] = {
351bcf721d14d881da86a8defa96bdc9492abe191aeGuenter Roeck	0x18, 0x29, 0x4c, 0x4d, I2C_CLIENT_END
352dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal};
353dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
354dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic const struct i2c_device_id emc1403_idtable[] = {
355dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	{ "emc1403", 0 },
3567a1b76f2a46016809c7bcacf81e89948cc306703Jekyll Lai	{ "emc1423", 0 },
357dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	{ }
358dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal};
359dac6831e67e90d1cee430a66e7390e753c20d835Kalhan TrisalMODULE_DEVICE_TABLE(i2c, emc1403_idtable);
360dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
361dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisalstatic struct i2c_driver sensor_emc1403 = {
362dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	.class = I2C_CLASS_HWMON,
363dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	.driver = {
364dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal		.name = "emc1403",
365dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	},
366dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	.detect = emc1403_detect,
367dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	.probe = emc1403_probe,
368dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	.remove = emc1403_remove,
369dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	.id_table = emc1403_idtable,
370dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal	.address_list = emc1403_address_list,
371dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal};
372dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
373f0967eea80ec2a19a4fe1ad27e3ff1b22c79a3c7Axel Linmodule_i2c_driver(sensor_emc1403);
374dac6831e67e90d1cee430a66e7390e753c20d835Kalhan Trisal
375dac6831e67e90d1cee430a66e7390e753c20d835Kalhan TrisalMODULE_AUTHOR("Kalhan Trisal <kalhan.trisal@intel.com");
376dac6831e67e90d1cee430a66e7390e753c20d835Kalhan TrisalMODULE_DESCRIPTION("emc1403 Thermal Driver");
377dac6831e67e90d1cee430a66e7390e753c20d835Kalhan TrisalMODULE_LICENSE("GPL v2");
378