Rev 420 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
420 | giacomo | 1 | /* |
2 | lm75.c - Part of lm_sensors, Linux kernel modules for hardware |
||
3 | monitoring |
||
4 | Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> |
||
5 | |||
6 | This program is free software; you can redistribute it and/or modify |
||
7 | it under the terms of the GNU General Public License as published by |
||
8 | the Free Software Foundation; either version 2 of the License, or |
||
9 | (at your option) any later version. |
||
10 | |||
11 | This program is distributed in the hope that it will be useful, |
||
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
14 | GNU General Public License for more details. |
||
15 | |||
16 | You should have received a copy of the GNU General Public License |
||
17 | along with this program; if not, write to the Free Software |
||
18 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
||
19 | */ |
||
20 | |||
21 | /* #define DEBUG 1 */ |
||
22 | |||
23 | #include <linux/module.h> |
||
24 | #include <linux/init.h> |
||
25 | #include <linux/slab.h> |
||
26 | #include <linux/i2c.h> |
||
27 | #include <linux/i2c-sensor.h> |
||
28 | |||
29 | |||
30 | /* Addresses to scan */ |
||
31 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; |
||
32 | static unsigned short normal_i2c_range[] = { 0x48, 0x4f, I2C_CLIENT_END }; |
||
33 | static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; |
||
34 | static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END }; |
||
35 | |||
36 | /* Insmod parameters */ |
||
37 | SENSORS_INSMOD_1(lm75); |
||
38 | |||
39 | /* Many LM75 constants specified below */ |
||
40 | |||
41 | /* The LM75 registers */ |
||
42 | #define LM75_REG_TEMP 0x00 |
||
43 | #define LM75_REG_CONF 0x01 |
||
44 | #define LM75_REG_TEMP_HYST 0x02 |
||
45 | #define LM75_REG_TEMP_OS 0x03 |
||
46 | |||
47 | /* Conversions. Rounding and limit checking is only done on the TO_REG |
||
48 | variants. Note that you should be a bit careful with which arguments |
||
49 | these macros are called: arguments may be evaluated more than once. |
||
50 | Fixing this is just not worth it. */ |
||
51 | #define TEMP_FROM_REG(val) ((((val & 0x7fff) >> 7) * 5) | ((val & 0x8000)?-256:0)) |
||
52 | #define TEMP_TO_REG(val) (SENSORS_LIMIT((val<0?(0x200+((val)/5))<<7:(((val) + 2) / 5) << 7),0,0xffff)) |
||
53 | |||
54 | /* Initial values */ |
||
55 | #define LM75_INIT_TEMP_OS 600 |
||
56 | #define LM75_INIT_TEMP_HYST 500 |
||
57 | |||
58 | /* Each client has this additional data */ |
||
59 | struct lm75_data { |
||
60 | struct semaphore update_lock; |
||
61 | char valid; /* !=0 if following fields are valid */ |
||
62 | unsigned long last_updated; /* In jiffies */ |
||
63 | u16 temp_input; /* Register values */ |
||
64 | u16 temp_max; |
||
65 | u16 temp_hyst; |
||
66 | }; |
||
67 | |||
68 | static int lm75_attach_adapter(struct i2c_adapter *adapter); |
||
69 | static int lm75_detect(struct i2c_adapter *adapter, int address, int kind); |
||
70 | static void lm75_init_client(struct i2c_client *client); |
||
71 | static int lm75_detach_client(struct i2c_client *client); |
||
72 | static int lm75_read_value(struct i2c_client *client, u8 reg); |
||
73 | static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value); |
||
74 | static void lm75_update_client(struct i2c_client *client); |
||
75 | |||
76 | |||
77 | /* This is the driver that will be inserted */ |
||
78 | static struct i2c_driver lm75_driver = { |
||
79 | .owner = THIS_MODULE, |
||
80 | .name = "lm75", |
||
81 | .id = I2C_DRIVERID_LM75, |
||
82 | .flags = I2C_DF_NOTIFY, |
||
83 | .attach_adapter = lm75_attach_adapter, |
||
84 | .detach_client = lm75_detach_client, |
||
85 | }; |
||
86 | |||
87 | static int lm75_id = 0; |
||
88 | |||
89 | #define show(value) \ |
||
90 | static ssize_t show_##value(struct device *dev, char *buf) \ |
||
91 | { \ |
||
92 | struct i2c_client *client = to_i2c_client(dev); \ |
||
93 | struct lm75_data *data = i2c_get_clientdata(client); \ |
||
94 | int temp; \ |
||
95 | \ |
||
96 | lm75_update_client(client); \ |
||
97 | temp = TEMP_FROM_REG(data->value); \ |
||
98 | return sprintf(buf, "%d\n", temp * 100); \ |
||
99 | } |
||
100 | show(temp_max); |
||
101 | show(temp_hyst); |
||
102 | show(temp_input); |
||
103 | |||
104 | #define set(value, reg) \ |
||
105 | static ssize_t set_##value(struct device *dev, const char *buf, size_t count) \ |
||
106 | { \ |
||
107 | struct i2c_client *client = to_i2c_client(dev); \ |
||
108 | struct lm75_data *data = i2c_get_clientdata(client); \ |
||
109 | int temp = simple_strtoul(buf, NULL, 10) / 100; \ |
||
110 | \ |
||
111 | data->value = TEMP_TO_REG(temp); \ |
||
112 | lm75_write_value(client, reg, data->value); \ |
||
113 | return count; \ |
||
114 | } |
||
115 | set(temp_max, LM75_REG_TEMP_OS); |
||
116 | set(temp_hyst, LM75_REG_TEMP_HYST); |
||
117 | |||
118 | static DEVICE_ATTR(temp_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max); |
||
119 | static DEVICE_ATTR(temp_min, S_IWUSR | S_IRUGO, show_temp_hyst, set_temp_hyst); |
||
120 | static DEVICE_ATTR(temp_input, S_IRUGO, show_temp_input, NULL); |
||
121 | |||
122 | static int lm75_attach_adapter(struct i2c_adapter *adapter) |
||
123 | { |
||
124 | if (!(adapter->class & I2C_ADAP_CLASS_SMBUS)) |
||
125 | return 0; |
||
126 | return i2c_detect(adapter, &addr_data, lm75_detect); |
||
127 | } |
||
128 | |||
129 | /* This function is called by i2c_detect */ |
||
130 | static int lm75_detect(struct i2c_adapter *adapter, int address, int kind) |
||
131 | { |
||
132 | int i, cur, conf, hyst, os; |
||
133 | struct i2c_client *new_client; |
||
134 | struct lm75_data *data; |
||
135 | int err = 0; |
||
136 | const char *name; |
||
137 | |||
138 | /* Make sure we aren't probing the ISA bus!! This is just a safety check |
||
139 | at this moment; i2c_detect really won't call us. */ |
||
140 | #ifdef DEBUG |
||
141 | if (i2c_is_isa_adapter(adapter)) { |
||
142 | dev_dbg(&adapter->dev, |
||
143 | "lm75_detect called for an ISA bus adapter?!?\n"); |
||
144 | goto exit; |
||
145 | } |
||
146 | #endif |
||
147 | |||
148 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | |
||
149 | I2C_FUNC_SMBUS_WORD_DATA)) |
||
150 | goto exit; |
||
151 | |||
152 | /* OK. For now, we presume we have a valid client. We now create the |
||
153 | client structure, even though we cannot fill it completely yet. |
||
154 | But it allows us to access lm75_{read,write}_value. */ |
||
155 | if (!(new_client = kmalloc(sizeof(struct i2c_client) + |
||
156 | sizeof(struct lm75_data), |
||
157 | GFP_KERNEL))) { |
||
158 | err = -ENOMEM; |
||
159 | goto exit; |
||
160 | } |
||
161 | memset(new_client, 0x00, sizeof(struct i2c_client) + |
||
162 | sizeof(struct lm75_data)); |
||
163 | |||
164 | data = (struct lm75_data *) (new_client + 1); |
||
165 | i2c_set_clientdata(new_client, data); |
||
166 | new_client->addr = address; |
||
167 | new_client->adapter = adapter; |
||
168 | new_client->driver = &lm75_driver; |
||
169 | new_client->flags = 0; |
||
170 | |||
171 | /* Now, we do the remaining detection. It is lousy. */ |
||
172 | if (kind < 0) { |
||
173 | cur = i2c_smbus_read_word_data(new_client, 0); |
||
174 | conf = i2c_smbus_read_byte_data(new_client, 1); |
||
175 | hyst = i2c_smbus_read_word_data(new_client, 2); |
||
176 | os = i2c_smbus_read_word_data(new_client, 3); |
||
177 | for (i = 0; i <= 0x1f; i++) |
||
178 | if ((i2c_smbus_read_byte_data(new_client, i * 8 + 1) != conf) || |
||
179 | (i2c_smbus_read_word_data(new_client, i * 8 + 2) != hyst) || |
||
180 | (i2c_smbus_read_word_data(new_client, i * 8 + 3) != os)) |
||
181 | goto exit_free; |
||
182 | } |
||
183 | |||
184 | /* Determine the chip type - only one kind supported! */ |
||
185 | if (kind <= 0) |
||
186 | kind = lm75; |
||
187 | |||
188 | if (kind == lm75) { |
||
189 | name = "lm75"; |
||
190 | } else { |
||
191 | dev_dbg(&adapter->dev, "Internal error: unknown kind (%d)?!?", |
||
192 | kind); |
||
193 | goto exit_free; |
||
194 | } |
||
195 | |||
196 | /* Fill in the remaining client fields and put it into the global list */ |
||
197 | strlcpy(new_client->name, name, I2C_NAME_SIZE); |
||
198 | |||
199 | new_client->id = lm75_id++; |
||
200 | data->valid = 0; |
||
201 | init_MUTEX(&data->update_lock); |
||
202 | |||
203 | /* Tell the I2C layer a new client has arrived */ |
||
204 | if ((err = i2c_attach_client(new_client))) |
||
205 | goto exit_free; |
||
206 | |||
207 | /* Initialize the LM75 chip */ |
||
208 | lm75_init_client(new_client); |
||
209 | |||
210 | /* Register sysfs hooks */ |
||
211 | device_create_file(&new_client->dev, &dev_attr_temp_max); |
||
212 | device_create_file(&new_client->dev, &dev_attr_temp_min); |
||
213 | device_create_file(&new_client->dev, &dev_attr_temp_input); |
||
214 | |||
215 | return 0; |
||
216 | |||
217 | exit_free: |
||
218 | kfree(new_client); |
||
219 | exit: |
||
220 | return err; |
||
221 | } |
||
222 | |||
223 | static int lm75_detach_client(struct i2c_client *client) |
||
224 | { |
||
225 | i2c_detach_client(client); |
||
226 | kfree(client); |
||
227 | return 0; |
||
228 | } |
||
229 | |||
230 | static u16 swap_bytes(u16 val) |
||
231 | { |
||
232 | return (val >> 8) | (val << 8); |
||
233 | } |
||
234 | |||
235 | /* All registers are word-sized, except for the configuration register. |
||
236 | LM75 uses a high-byte first convention, which is exactly opposite to |
||
237 | the usual practice. */ |
||
238 | static int lm75_read_value(struct i2c_client *client, u8 reg) |
||
239 | { |
||
240 | if (reg == LM75_REG_CONF) |
||
241 | return i2c_smbus_read_byte_data(client, reg); |
||
242 | else |
||
243 | return swap_bytes(i2c_smbus_read_word_data(client, reg)); |
||
244 | } |
||
245 | |||
246 | /* All registers are word-sized, except for the configuration register. |
||
247 | LM75 uses a high-byte first convention, which is exactly opposite to |
||
248 | the usual practice. */ |
||
249 | static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value) |
||
250 | { |
||
251 | if (reg == LM75_REG_CONF) |
||
252 | return i2c_smbus_write_byte_data(client, reg, value); |
||
253 | else |
||
254 | return i2c_smbus_write_word_data(client, reg, |
||
255 | swap_bytes(value)); |
||
256 | } |
||
257 | |||
258 | static void lm75_init_client(struct i2c_client *client) |
||
259 | { |
||
260 | /* Initialize the LM75 chip */ |
||
261 | lm75_write_value(client, LM75_REG_TEMP_OS, |
||
262 | TEMP_TO_REG(LM75_INIT_TEMP_OS)); |
||
263 | lm75_write_value(client, LM75_REG_TEMP_HYST, |
||
264 | TEMP_TO_REG(LM75_INIT_TEMP_HYST)); |
||
265 | lm75_write_value(client, LM75_REG_CONF, 0); |
||
266 | } |
||
267 | |||
268 | static void lm75_update_client(struct i2c_client *client) |
||
269 | { |
||
270 | struct lm75_data *data = i2c_get_clientdata(client); |
||
271 | |||
272 | down(&data->update_lock); |
||
273 | |||
274 | if ((jiffies - data->last_updated > HZ + HZ / 2) || |
||
275 | (jiffies < data->last_updated) || !data->valid) { |
||
276 | dev_dbg(&client->dev, "Starting lm75 update\n"); |
||
277 | |||
278 | data->temp_input = lm75_read_value(client, LM75_REG_TEMP); |
||
279 | data->temp_max = lm75_read_value(client, LM75_REG_TEMP_OS); |
||
280 | data->temp_hyst = lm75_read_value(client, LM75_REG_TEMP_HYST); |
||
281 | data->last_updated = jiffies; |
||
282 | data->valid = 1; |
||
283 | } |
||
284 | |||
285 | up(&data->update_lock); |
||
286 | } |
||
287 | |||
288 | static int __init sensors_lm75_init(void) |
||
289 | { |
||
290 | return i2c_add_driver(&lm75_driver); |
||
291 | } |
||
292 | |||
293 | static void __exit sensors_lm75_exit(void) |
||
294 | { |
||
295 | i2c_del_driver(&lm75_driver); |
||
296 | } |
||
297 | |||
298 | MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>"); |
||
299 | MODULE_DESCRIPTION("LM75 driver"); |
||
300 | MODULE_LICENSE("GPL"); |
||
301 | |||
302 | module_init(sensors_lm75_init); |
||
303 | module_exit(sensors_lm75_exit); |