Rev 420 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
420 | giacomo | 1 | /* |
2 | lm78.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 | #include <linux/module.h> |
||
22 | #include <linux/init.h> |
||
23 | #include <linux/slab.h> |
||
24 | #include <linux/i2c.h> |
||
25 | #include <linux/i2c-sensor.h> |
||
26 | #include <asm/io.h> |
||
27 | |||
28 | /* Addresses to scan */ |
||
29 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; |
||
30 | static unsigned short normal_i2c_range[] = { 0x20, 0x2f, I2C_CLIENT_END }; |
||
31 | static unsigned int normal_isa[] = { 0x0290, I2C_CLIENT_ISA_END }; |
||
32 | static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END }; |
||
33 | |||
34 | /* Insmod parameters */ |
||
35 | SENSORS_INSMOD_3(lm78, lm78j, lm79); |
||
36 | |||
37 | /* Many LM78 constants specified below */ |
||
38 | |||
39 | /* Length of ISA address segment */ |
||
40 | #define LM78_EXTENT 8 |
||
41 | |||
42 | /* Where are the ISA address/data registers relative to the base address */ |
||
43 | #define LM78_ADDR_REG_OFFSET 5 |
||
44 | #define LM78_DATA_REG_OFFSET 6 |
||
45 | |||
46 | /* The LM78 registers */ |
||
47 | #define LM78_REG_IN_MAX(nr) (0x2b + (nr) * 2) |
||
48 | #define LM78_REG_IN_MIN(nr) (0x2c + (nr) * 2) |
||
49 | #define LM78_REG_IN(nr) (0x20 + (nr)) |
||
50 | |||
51 | #define LM78_REG_FAN_MIN(nr) (0x3b + (nr)) |
||
52 | #define LM78_REG_FAN(nr) (0x28 + (nr)) |
||
53 | |||
54 | #define LM78_REG_TEMP 0x27 |
||
55 | #define LM78_REG_TEMP_OVER 0x39 |
||
56 | #define LM78_REG_TEMP_HYST 0x3a |
||
57 | |||
58 | #define LM78_REG_ALARM1 0x41 |
||
59 | #define LM78_REG_ALARM2 0x42 |
||
60 | |||
61 | #define LM78_REG_VID_FANDIV 0x47 |
||
62 | |||
63 | #define LM78_REG_CONFIG 0x40 |
||
64 | #define LM78_REG_CHIPID 0x49 |
||
65 | #define LM78_REG_I2C_ADDR 0x48 |
||
66 | |||
67 | |||
68 | /* Conversions. Rounding and limit checking is only done on the TO_REG |
||
69 | variants. */ |
||
70 | |||
71 | /* IN: mV, (0V to 4.08V) |
||
72 | REG: 16mV/bit */ |
||
73 | static inline u8 IN_TO_REG(unsigned long val) |
||
74 | { |
||
75 | unsigned long nval = SENSORS_LIMIT(val, 0, 4080); |
||
76 | return (nval + 8) / 16; |
||
77 | } |
||
78 | #define IN_FROM_REG(val) ((val) * 16) |
||
79 | |||
80 | static inline u8 FAN_TO_REG(long rpm, int div) |
||
81 | { |
||
82 | if (rpm == 0) |
||
83 | return 255; |
||
84 | rpm = SENSORS_LIMIT(rpm, 1, 1000000); |
||
85 | return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254); |
||
86 | } |
||
87 | |||
88 | static inline int FAN_FROM_REG(u8 val, int div) |
||
89 | { |
||
90 | return val==0 ? -1 : val==255 ? 0 : 1350000/(val*div); |
||
91 | } |
||
92 | |||
93 | /* TEMP: mC (-128C to +127C) |
||
94 | REG: 1C/bit, two's complement */ |
||
95 | static inline u8 TEMP_TO_REG(int val) |
||
96 | { |
||
97 | int nval = SENSORS_LIMIT(val, -128000, 127000) ; |
||
98 | return nval<0 ? (nval-500)/1000+0x100 : (nval+500)/1000; |
||
99 | } |
||
100 | |||
101 | static inline int TEMP_FROM_REG(u8 val) |
||
102 | { |
||
103 | return (val>=0x80 ? val-0x100 : val) * 1000; |
||
104 | } |
||
105 | |||
106 | /* VID: mV |
||
107 | REG: (see doc/vid) */ |
||
108 | static inline int VID_FROM_REG(u8 val) |
||
109 | { |
||
110 | return val==0x1f ? 0 : val>=0x10 ? 5100-val*100 : 2050-val*50; |
||
111 | } |
||
112 | |||
113 | /* ALARMS: chip-specific bitmask |
||
114 | REG: (same) */ |
||
115 | #define ALARMS_FROM_REG(val) (val) |
||
116 | |||
117 | /* FAN DIV: 1, 2, 4, or 8 (defaults to 2) |
||
118 | REG: 0, 1, 2, or 3 (respectively) (defaults to 1) */ |
||
119 | static inline u8 DIV_TO_REG(int val) |
||
120 | { |
||
121 | return val==8 ? 3 : val==4 ? 2 : val==1 ? 0 : 1; |
||
122 | } |
||
123 | #define DIV_FROM_REG(val) (1 << (val)) |
||
124 | |||
125 | /* Initial limits. To keep them sane, we use the 'standard' translation as |
||
126 | specified in the LM78 sheet. Use the config file to set better limits. */ |
||
127 | #define LM78_INIT_IN_0(vid) ((vid)==3500 ? 2800 : (vid)) |
||
128 | #define LM78_INIT_IN_1(vid) ((vid)==3500 ? 2800 : (vid)) |
||
129 | #define LM78_INIT_IN_2 3300 |
||
130 | #define LM78_INIT_IN_3 (((5000) * 100)/168) |
||
131 | #define LM78_INIT_IN_4 (((12000) * 10)/38) |
||
132 | #define LM78_INIT_IN_5 (((-12000) * -604)/2100) |
||
133 | #define LM78_INIT_IN_6 (((-5000) * -604)/909) |
||
134 | |||
135 | #define LM78_INIT_IN_PERCENTAGE 10 |
||
136 | |||
137 | #define LM78_INIT_IN_MIN_0(vid) (LM78_INIT_IN_0(vid) - \ |
||
138 | LM78_INIT_IN_0(vid) * LM78_INIT_IN_PERCENTAGE / 100) |
||
139 | #define LM78_INIT_IN_MAX_0(vid) (LM78_INIT_IN_0(vid) + \ |
||
140 | LM78_INIT_IN_0(vid) * LM78_INIT_IN_PERCENTAGE / 100) |
||
141 | #define LM78_INIT_IN_MIN_1(vid) (LM78_INIT_IN_1(vid) - \ |
||
142 | LM78_INIT_IN_1(vid) * LM78_INIT_IN_PERCENTAGE / 100) |
||
143 | #define LM78_INIT_IN_MAX_1(vid) (LM78_INIT_IN_1(vid) + \ |
||
144 | LM78_INIT_IN_1(vid) * LM78_INIT_IN_PERCENTAGE / 100) |
||
145 | |||
146 | #define LM78_INIT_IN_MIN_2 \ |
||
147 | (LM78_INIT_IN_2 - LM78_INIT_IN_2 * LM78_INIT_IN_PERCENTAGE / 100) |
||
148 | #define LM78_INIT_IN_MAX_2 \ |
||
149 | (LM78_INIT_IN_2 + LM78_INIT_IN_2 * LM78_INIT_IN_PERCENTAGE / 100) |
||
150 | #define LM78_INIT_IN_MIN_3 \ |
||
151 | (LM78_INIT_IN_3 - LM78_INIT_IN_3 * LM78_INIT_IN_PERCENTAGE / 100) |
||
152 | #define LM78_INIT_IN_MAX_3 \ |
||
153 | (LM78_INIT_IN_3 + LM78_INIT_IN_3 * LM78_INIT_IN_PERCENTAGE / 100) |
||
154 | #define LM78_INIT_IN_MIN_4 \ |
||
155 | (LM78_INIT_IN_4 - LM78_INIT_IN_4 * LM78_INIT_IN_PERCENTAGE / 100) |
||
156 | #define LM78_INIT_IN_MAX_4 \ |
||
157 | (LM78_INIT_IN_4 + LM78_INIT_IN_4 * LM78_INIT_IN_PERCENTAGE / 100) |
||
158 | #define LM78_INIT_IN_MIN_5 \ |
||
159 | (LM78_INIT_IN_5 - LM78_INIT_IN_5 * LM78_INIT_IN_PERCENTAGE / 100) |
||
160 | #define LM78_INIT_IN_MAX_5 \ |
||
161 | (LM78_INIT_IN_5 + LM78_INIT_IN_5 * LM78_INIT_IN_PERCENTAGE / 100) |
||
162 | #define LM78_INIT_IN_MIN_6 \ |
||
163 | (LM78_INIT_IN_6 - LM78_INIT_IN_6 * LM78_INIT_IN_PERCENTAGE / 100) |
||
164 | #define LM78_INIT_IN_MAX_6 \ |
||
165 | (LM78_INIT_IN_6 + LM78_INIT_IN_6 * LM78_INIT_IN_PERCENTAGE / 100) |
||
166 | |||
167 | #define LM78_INIT_FAN_MIN_1 3000 |
||
168 | #define LM78_INIT_FAN_MIN_2 3000 |
||
169 | #define LM78_INIT_FAN_MIN_3 3000 |
||
170 | |||
171 | #define LM78_INIT_TEMP_OVER 60000 |
||
172 | #define LM78_INIT_TEMP_HYST 50000 |
||
173 | |||
174 | /* There are some complications in a module like this. First off, LM78 chips |
||
175 | may be both present on the SMBus and the ISA bus, and we have to handle |
||
176 | those cases separately at some places. Second, there might be several |
||
177 | LM78 chips available (well, actually, that is probably never done; but |
||
178 | it is a clean illustration of how to handle a case like that). Finally, |
||
179 | a specific chip may be attached to *both* ISA and SMBus, and we would |
||
180 | not like to detect it double. Fortunately, in the case of the LM78 at |
||
181 | least, a register tells us what SMBus address we are on, so that helps |
||
182 | a bit - except if there could be more than one SMBus. Groan. No solution |
||
183 | for this yet. */ |
||
184 | |||
185 | /* This module may seem overly long and complicated. In fact, it is not so |
||
186 | bad. Quite a lot of bookkeeping is done. A real driver can often cut |
||
187 | some corners. */ |
||
188 | |||
189 | /* For each registered LM78, we need to keep some data in memory. That |
||
190 | data is pointed to by lm78_list[NR]->data. The structure itself is |
||
191 | dynamically allocated, at the same time when a new lm78 client is |
||
192 | allocated. */ |
||
193 | struct lm78_data { |
||
194 | struct semaphore lock; |
||
195 | enum chips type; |
||
196 | |||
197 | struct semaphore update_lock; |
||
198 | char valid; /* !=0 if following fields are valid */ |
||
199 | unsigned long last_updated; /* In jiffies */ |
||
200 | |||
201 | u8 in[7]; /* Register value */ |
||
202 | u8 in_max[7]; /* Register value */ |
||
203 | u8 in_min[7]; /* Register value */ |
||
204 | u8 fan[3]; /* Register value */ |
||
205 | u8 fan_min[3]; /* Register value */ |
||
206 | u8 temp; /* Register value */ |
||
207 | u8 temp_over; /* Register value */ |
||
208 | u8 temp_hyst; /* Register value */ |
||
209 | u8 fan_div[3]; /* Register encoding, shifted right */ |
||
210 | u8 vid; /* Register encoding, combined */ |
||
211 | u16 alarms; /* Register encoding, combined */ |
||
212 | }; |
||
213 | |||
214 | |||
215 | static int lm78_attach_adapter(struct i2c_adapter *adapter); |
||
216 | static int lm78_detect(struct i2c_adapter *adapter, int address, int kind); |
||
217 | static int lm78_detach_client(struct i2c_client *client); |
||
218 | |||
219 | static int lm78_read_value(struct i2c_client *client, u8 register); |
||
220 | static int lm78_write_value(struct i2c_client *client, u8 register, u8 value); |
||
221 | static void lm78_update_client(struct i2c_client *client); |
||
222 | static void lm78_init_client(struct i2c_client *client); |
||
223 | |||
224 | |||
225 | static struct i2c_driver lm78_driver = { |
||
226 | .owner = THIS_MODULE, |
||
227 | .name = "lm78", |
||
228 | .id = I2C_DRIVERID_LM78, |
||
229 | .flags = I2C_DF_NOTIFY, |
||
230 | .attach_adapter = lm78_attach_adapter, |
||
231 | .detach_client = lm78_detach_client, |
||
232 | }; |
||
233 | |||
234 | /* 7 Voltages */ |
||
235 | static ssize_t show_in(struct device *dev, char *buf, int nr) |
||
236 | { |
||
237 | struct i2c_client *client = to_i2c_client(dev); |
||
238 | struct lm78_data *data = i2c_get_clientdata(client); |
||
239 | lm78_update_client(client); |
||
240 | return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr])); |
||
241 | } |
||
242 | |||
243 | static ssize_t show_in_min(struct device *dev, char *buf, int nr) |
||
244 | { |
||
245 | struct i2c_client *client = to_i2c_client(dev); |
||
246 | struct lm78_data *data = i2c_get_clientdata(client); |
||
247 | lm78_update_client(client); |
||
248 | return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr])); |
||
249 | } |
||
250 | |||
251 | static ssize_t show_in_max(struct device *dev, char *buf, int nr) |
||
252 | { |
||
253 | struct i2c_client *client = to_i2c_client(dev); |
||
254 | struct lm78_data *data = i2c_get_clientdata(client); |
||
255 | lm78_update_client(client); |
||
256 | return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr])); |
||
257 | } |
||
258 | |||
259 | static ssize_t set_in_min(struct device *dev, const char *buf, |
||
260 | size_t count, int nr) |
||
261 | { |
||
262 | struct i2c_client *client = to_i2c_client(dev); |
||
263 | struct lm78_data *data = i2c_get_clientdata(client); |
||
264 | unsigned long val = simple_strtoul(buf, NULL, 10); |
||
265 | data->in_min[nr] = IN_TO_REG(val); |
||
266 | lm78_write_value(client, LM78_REG_IN_MIN(nr), data->in_min[nr]); |
||
267 | return count; |
||
268 | } |
||
269 | |||
270 | static ssize_t set_in_max(struct device *dev, const char *buf, |
||
271 | size_t count, int nr) |
||
272 | { |
||
273 | struct i2c_client *client = to_i2c_client(dev); |
||
274 | struct lm78_data *data = i2c_get_clientdata(client); |
||
275 | unsigned long val = simple_strtoul(buf, NULL, 10); |
||
276 | data->in_max[nr] = IN_TO_REG(val); |
||
277 | lm78_write_value(client, LM78_REG_IN_MAX(nr), data->in_max[nr]); |
||
278 | return count; |
||
279 | } |
||
280 | |||
281 | #define show_in_offset(offset) \ |
||
282 | static ssize_t \ |
||
283 | show_in##offset (struct device *dev, char *buf) \ |
||
284 | { \ |
||
285 | return show_in(dev, buf, 0x##offset); \ |
||
286 | } \ |
||
287 | static DEVICE_ATTR(in_input##offset, S_IRUGO, \ |
||
288 | show_in##offset, NULL) \ |
||
289 | static ssize_t \ |
||
290 | show_in##offset##_min (struct device *dev, char *buf) \ |
||
291 | { \ |
||
292 | return show_in_min(dev, buf, 0x##offset); \ |
||
293 | } \ |
||
294 | static ssize_t \ |
||
295 | show_in##offset##_max (struct device *dev, char *buf) \ |
||
296 | { \ |
||
297 | return show_in_max(dev, buf, 0x##offset); \ |
||
298 | } \ |
||
299 | static ssize_t set_in##offset##_min (struct device *dev, \ |
||
300 | const char *buf, size_t count) \ |
||
301 | { \ |
||
302 | return set_in_min(dev, buf, count, 0x##offset); \ |
||
303 | } \ |
||
304 | static ssize_t set_in##offset##_max (struct device *dev, \ |
||
305 | const char *buf, size_t count) \ |
||
306 | { \ |
||
307 | return set_in_max(dev, buf, count, 0x##offset); \ |
||
308 | } \ |
||
309 | static DEVICE_ATTR(in_min##offset, S_IRUGO | S_IWUSR, \ |
||
310 | show_in##offset##_min, set_in##offset##_min) \ |
||
311 | static DEVICE_ATTR(in_max##offset, S_IRUGO | S_IWUSR, \ |
||
312 | show_in##offset##_max, set_in##offset##_max) |
||
313 | |||
314 | show_in_offset(0); |
||
315 | show_in_offset(1); |
||
316 | show_in_offset(2); |
||
317 | show_in_offset(3); |
||
318 | show_in_offset(4); |
||
319 | show_in_offset(5); |
||
320 | show_in_offset(6); |
||
321 | |||
322 | /* Temperature */ |
||
323 | static ssize_t show_temp(struct device *dev, char *buf) |
||
324 | { |
||
325 | struct i2c_client *client = to_i2c_client(dev); |
||
326 | struct lm78_data *data = i2c_get_clientdata(client); |
||
327 | lm78_update_client(client); |
||
328 | return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp)); |
||
329 | } |
||
330 | |||
331 | static ssize_t show_temp_over(struct device *dev, char *buf) |
||
332 | { |
||
333 | struct i2c_client *client = to_i2c_client(dev); |
||
334 | struct lm78_data *data = i2c_get_clientdata(client); |
||
335 | lm78_update_client(client); |
||
336 | return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over)); |
||
337 | } |
||
338 | |||
339 | static ssize_t set_temp_over(struct device *dev, const char *buf, size_t count) |
||
340 | { |
||
341 | struct i2c_client *client = to_i2c_client(dev); |
||
342 | struct lm78_data *data = i2c_get_clientdata(client); |
||
343 | long val = simple_strtol(buf, NULL, 10); |
||
344 | data->temp_over = TEMP_TO_REG(val); |
||
345 | lm78_write_value(client, LM78_REG_TEMP_OVER, data->temp_over); |
||
346 | return count; |
||
347 | } |
||
348 | |||
349 | static ssize_t show_temp_hyst(struct device *dev, char *buf) |
||
350 | { |
||
351 | struct i2c_client *client = to_i2c_client(dev); |
||
352 | struct lm78_data *data = i2c_get_clientdata(client); |
||
353 | lm78_update_client(client); |
||
354 | return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_hyst)); |
||
355 | } |
||
356 | |||
357 | static ssize_t set_temp_hyst(struct device *dev, const char *buf, size_t count) |
||
358 | { |
||
359 | struct i2c_client *client = to_i2c_client(dev); |
||
360 | struct lm78_data *data = i2c_get_clientdata(client); |
||
361 | long val = simple_strtol(buf, NULL, 10); |
||
362 | data->temp_hyst = TEMP_TO_REG(val); |
||
363 | lm78_write_value(client, LM78_REG_TEMP_HYST, data->temp_hyst); |
||
364 | return count; |
||
365 | } |
||
366 | |||
367 | static DEVICE_ATTR(temp_input, S_IRUGO, show_temp, NULL) |
||
368 | static DEVICE_ATTR(temp_max, S_IRUGO | S_IWUSR, |
||
369 | show_temp_over, set_temp_over) |
||
370 | static DEVICE_ATTR(temp_min, S_IRUGO | S_IWUSR, |
||
371 | show_temp_hyst, set_temp_hyst) |
||
372 | |||
373 | /* 3 Fans */ |
||
374 | static ssize_t show_fan(struct device *dev, char *buf, int nr) |
||
375 | { |
||
376 | struct i2c_client *client = to_i2c_client(dev); |
||
377 | struct lm78_data *data = i2c_get_clientdata(client); |
||
378 | lm78_update_client(client); |
||
379 | return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr], |
||
380 | DIV_FROM_REG(data->fan_div[nr])) ); |
||
381 | } |
||
382 | |||
383 | static ssize_t show_fan_min(struct device *dev, char *buf, int nr) |
||
384 | { |
||
385 | struct i2c_client *client = to_i2c_client(dev); |
||
386 | struct lm78_data *data = i2c_get_clientdata(client); |
||
387 | lm78_update_client(client); |
||
388 | return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr], |
||
389 | DIV_FROM_REG(data->fan_div[nr])) ); |
||
390 | } |
||
391 | |||
392 | static ssize_t set_fan_min(struct device *dev, const char *buf, |
||
393 | size_t count, int nr) |
||
394 | { |
||
395 | struct i2c_client *client = to_i2c_client(dev); |
||
396 | struct lm78_data *data = i2c_get_clientdata(client); |
||
397 | unsigned long val = simple_strtoul(buf, NULL, 10); |
||
398 | data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); |
||
399 | lm78_write_value(client, LM78_REG_FAN_MIN(nr), data->fan_min[nr]); |
||
400 | return count; |
||
401 | } |
||
402 | |||
403 | static ssize_t show_fan_div(struct device *dev, char *buf, int nr) |
||
404 | { |
||
405 | struct i2c_client *client = to_i2c_client(dev); |
||
406 | struct lm78_data *data = i2c_get_clientdata(client); |
||
407 | lm78_update_client(client); |
||
408 | return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]) ); |
||
409 | } |
||
410 | |||
411 | /* Note: we save and restore the fan minimum here, because its value is |
||
412 | determined in part by the fan divisor. This follows the principle of |
||
413 | least suprise; the user doesn't expect the fan minimum to change just |
||
414 | because the divisor changed. */ |
||
415 | static ssize_t set_fan_div(struct device *dev, const char *buf, |
||
416 | size_t count, int nr) |
||
417 | { |
||
418 | struct i2c_client *client = to_i2c_client(dev); |
||
419 | struct lm78_data *data = i2c_get_clientdata(client); |
||
420 | unsigned long min = FAN_FROM_REG(data->fan_min[nr], |
||
421 | DIV_FROM_REG(data->fan_div[nr])); |
||
422 | unsigned long val = simple_strtoul(buf, NULL, 10); |
||
423 | int reg = lm78_read_value(client, LM78_REG_VID_FANDIV); |
||
424 | data->fan_div[nr] = DIV_TO_REG(val); |
||
425 | switch (nr) { |
||
426 | case 0: |
||
427 | reg = (reg & 0xcf) | (data->fan_div[nr] << 4); |
||
428 | break; |
||
429 | case 1: |
||
430 | reg = (reg & 0x3f) | (data->fan_div[nr] << 6); |
||
431 | break; |
||
432 | } |
||
433 | lm78_write_value(client, LM78_REG_VID_FANDIV, reg); |
||
434 | data->fan_min[nr] = |
||
435 | FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); |
||
436 | lm78_write_value(client, LM78_REG_FAN_MIN(nr), data->fan_min[nr]); |
||
437 | return count; |
||
438 | } |
||
439 | |||
440 | #define show_fan_offset(offset) \ |
||
441 | static ssize_t show_fan_##offset (struct device *dev, char *buf) \ |
||
442 | { \ |
||
443 | return show_fan(dev, buf, 0x##offset - 1); \ |
||
444 | } \ |
||
445 | static ssize_t show_fan_##offset##_min (struct device *dev, char *buf) \ |
||
446 | { \ |
||
447 | return show_fan_min(dev, buf, 0x##offset - 1); \ |
||
448 | } \ |
||
449 | static ssize_t show_fan_##offset##_div (struct device *dev, char *buf) \ |
||
450 | { \ |
||
451 | return show_fan_div(dev, buf, 0x##offset - 1); \ |
||
452 | } \ |
||
453 | static ssize_t set_fan_##offset##_min (struct device *dev, \ |
||
454 | const char *buf, size_t count) \ |
||
455 | { \ |
||
456 | return set_fan_min(dev, buf, count, 0x##offset - 1); \ |
||
457 | } \ |
||
458 | static DEVICE_ATTR(fan_input##offset, S_IRUGO, show_fan_##offset, NULL) \ |
||
459 | static DEVICE_ATTR(fan_min##offset, S_IRUGO | S_IWUSR, \ |
||
460 | show_fan_##offset##_min, set_fan_##offset##_min) |
||
461 | |||
462 | static ssize_t set_fan_1_div(struct device *dev, const char *buf, |
||
463 | size_t count) |
||
464 | { |
||
465 | return set_fan_div(dev, buf, count, 0) ; |
||
466 | } |
||
467 | |||
468 | static ssize_t set_fan_2_div(struct device *dev, const char *buf, |
||
469 | size_t count) |
||
470 | { |
||
471 | return set_fan_div(dev, buf, count, 1) ; |
||
472 | } |
||
473 | |||
474 | show_fan_offset(1); |
||
475 | show_fan_offset(2); |
||
476 | show_fan_offset(3); |
||
477 | |||
478 | /* Fan 3 divisor is locked in H/W */ |
||
479 | static DEVICE_ATTR(fan_div1, S_IRUGO | S_IWUSR, |
||
480 | show_fan_1_div, set_fan_1_div) |
||
481 | static DEVICE_ATTR(fan_div2, S_IRUGO | S_IWUSR, |
||
482 | show_fan_2_div, set_fan_2_div) |
||
483 | static DEVICE_ATTR(fan_div3, S_IRUGO, show_fan_3_div, NULL) |
||
484 | |||
485 | /* VID */ |
||
486 | static ssize_t show_vid(struct device *dev, char *buf) |
||
487 | { |
||
488 | struct i2c_client *client = to_i2c_client(dev); |
||
489 | struct lm78_data *data = i2c_get_clientdata(client); |
||
490 | lm78_update_client(client); |
||
491 | return sprintf(buf, "%d\n", VID_FROM_REG(data->vid)); |
||
492 | } |
||
493 | static DEVICE_ATTR(vid, S_IRUGO, show_vid, NULL); |
||
494 | |||
495 | /* Alarms */ |
||
496 | static ssize_t show_alarms(struct device *dev, char *buf) |
||
497 | { |
||
498 | struct i2c_client *client = to_i2c_client(dev); |
||
499 | struct lm78_data *data = i2c_get_clientdata(client); |
||
500 | lm78_update_client(client); |
||
501 | return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->alarms)); |
||
502 | } |
||
503 | static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); |
||
504 | |||
505 | /* This function is called when: |
||
506 | * lm78_driver is inserted (when this module is loaded), for each |
||
507 | available adapter |
||
508 | * when a new adapter is inserted (and lm78_driver is still present) */ |
||
509 | static int lm78_attach_adapter(struct i2c_adapter *adapter) |
||
510 | { |
||
511 | if (!(adapter->class & I2C_ADAP_CLASS_SMBUS)) |
||
512 | return 0; |
||
513 | return i2c_detect(adapter, &addr_data, lm78_detect); |
||
514 | } |
||
515 | |||
516 | /* This function is called by i2c_detect */ |
||
517 | int lm78_detect(struct i2c_adapter *adapter, int address, int kind) |
||
518 | { |
||
519 | int i, err; |
||
520 | struct i2c_client *new_client; |
||
521 | struct lm78_data *data; |
||
522 | const char *client_name = ""; |
||
523 | int is_isa = i2c_is_isa_adapter(adapter); |
||
524 | |||
525 | if (!is_isa && |
||
526 | !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { |
||
527 | err = -ENODEV; |
||
528 | goto ERROR0; |
||
529 | } |
||
530 | |||
531 | /* Reserve the ISA region */ |
||
532 | if (is_isa) |
||
533 | if (!request_region(address, LM78_EXTENT, "lm78")) { |
||
534 | err = -EBUSY; |
||
535 | goto ERROR0; |
||
536 | } |
||
537 | |||
538 | /* Probe whether there is anything available on this address. Already |
||
539 | done for SMBus clients */ |
||
540 | if (kind < 0) { |
||
541 | if (is_isa) { |
||
542 | |||
543 | #define REALLY_SLOW_IO |
||
544 | /* We need the timeouts for at least some LM78-like |
||
545 | chips. But only if we read 'undefined' registers. */ |
||
546 | i = inb_p(address + 1); |
||
547 | if (inb_p(address + 2) != i) { |
||
548 | err = -ENODEV; |
||
549 | goto ERROR1; |
||
550 | } |
||
551 | if (inb_p(address + 3) != i) { |
||
552 | err = -ENODEV; |
||
553 | goto ERROR1; |
||
554 | } |
||
555 | if (inb_p(address + 7) != i) { |
||
556 | err = -ENODEV; |
||
557 | goto ERROR1; |
||
558 | } |
||
559 | #undef REALLY_SLOW_IO |
||
560 | |||
561 | /* Let's just hope nothing breaks here */ |
||
562 | i = inb_p(address + 5) & 0x7f; |
||
563 | outb_p(~i & 0x7f, address + 5); |
||
564 | if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) { |
||
565 | outb_p(i, address + 5); |
||
566 | err = -ENODEV; |
||
567 | goto ERROR1; |
||
568 | } |
||
569 | } |
||
570 | } |
||
571 | |||
572 | /* OK. For now, we presume we have a valid client. We now create the |
||
573 | client structure, even though we cannot fill it completely yet. |
||
574 | But it allows us to access lm78_{read,write}_value. */ |
||
575 | |||
576 | if (!(new_client = kmalloc((sizeof(struct i2c_client)) + |
||
577 | sizeof(struct lm78_data), |
||
578 | GFP_KERNEL))) { |
||
579 | err = -ENOMEM; |
||
580 | goto ERROR1; |
||
581 | } |
||
582 | memset(new_client, 0, sizeof(struct i2c_client) + |
||
583 | sizeof(struct lm78_data)); |
||
584 | |||
585 | data = (struct lm78_data *) (new_client + 1); |
||
586 | if (is_isa) |
||
587 | init_MUTEX(&data->lock); |
||
588 | i2c_set_clientdata(new_client, data); |
||
589 | new_client->addr = address; |
||
590 | new_client->adapter = adapter; |
||
591 | new_client->driver = &lm78_driver; |
||
592 | new_client->flags = 0; |
||
593 | |||
594 | /* Now, we do the remaining detection. */ |
||
595 | if (kind < 0) { |
||
596 | if (lm78_read_value(new_client, LM78_REG_CONFIG) & 0x80) { |
||
597 | err = -ENODEV; |
||
598 | goto ERROR2; |
||
599 | } |
||
600 | if (!is_isa && (lm78_read_value( |
||
601 | new_client, LM78_REG_I2C_ADDR) != address)) { |
||
602 | err = -ENODEV; |
||
603 | goto ERROR2; |
||
604 | } |
||
605 | } |
||
606 | |||
607 | /* Determine the chip type. */ |
||
608 | if (kind <= 0) { |
||
609 | i = lm78_read_value(new_client, LM78_REG_CHIPID); |
||
610 | if (i == 0x00 || i == 0x20) |
||
611 | kind = lm78; |
||
612 | else if (i == 0x40) |
||
613 | kind = lm78j; |
||
614 | else if ((i & 0xfe) == 0xc0) |
||
615 | kind = lm79; |
||
616 | else { |
||
617 | if (kind == 0) |
||
618 | printk(KERN_WARNING "lm78.o: Ignoring 'force' " |
||
619 | "parameter for unknown chip at " |
||
620 | "adapter %d, address 0x%02x\n", |
||
621 | i2c_adapter_id(adapter), address); |
||
622 | err = -ENODEV; |
||
623 | goto ERROR2; |
||
624 | } |
||
625 | } |
||
626 | |||
627 | if (kind == lm78) { |
||
628 | client_name = "lm78"; |
||
629 | } else if (kind == lm78j) { |
||
630 | client_name = "lm78-j"; |
||
631 | } else if (kind == lm79) { |
||
632 | client_name = "lm79"; |
||
633 | } else { |
||
634 | dev_dbg(&adapter->dev, "Internal error: unknown kind (%d)?!?", |
||
635 | kind); |
||
636 | err = -ENODEV; |
||
637 | goto ERROR2; |
||
638 | } |
||
639 | |||
640 | /* Fill in the remaining client fields and put into the global list */ |
||
641 | strlcpy(new_client->name, client_name, I2C_NAME_SIZE); |
||
642 | data->type = kind; |
||
643 | |||
644 | data->valid = 0; |
||
645 | init_MUTEX(&data->update_lock); |
||
646 | |||
647 | /* Tell the I2C layer a new client has arrived */ |
||
648 | if ((err = i2c_attach_client(new_client))) |
||
649 | goto ERROR2; |
||
650 | |||
651 | /* Initialize the LM78 chip */ |
||
652 | lm78_init_client(new_client); |
||
653 | |||
654 | /* Register sysfs hooks */ |
||
655 | device_create_file(&new_client->dev, &dev_attr_in_input0); |
||
656 | device_create_file(&new_client->dev, &dev_attr_in_min0); |
||
657 | device_create_file(&new_client->dev, &dev_attr_in_max0); |
||
658 | device_create_file(&new_client->dev, &dev_attr_in_input1); |
||
659 | device_create_file(&new_client->dev, &dev_attr_in_min1); |
||
660 | device_create_file(&new_client->dev, &dev_attr_in_max1); |
||
661 | device_create_file(&new_client->dev, &dev_attr_in_input2); |
||
662 | device_create_file(&new_client->dev, &dev_attr_in_min2); |
||
663 | device_create_file(&new_client->dev, &dev_attr_in_max2); |
||
664 | device_create_file(&new_client->dev, &dev_attr_in_input3); |
||
665 | device_create_file(&new_client->dev, &dev_attr_in_min3); |
||
666 | device_create_file(&new_client->dev, &dev_attr_in_max3); |
||
667 | device_create_file(&new_client->dev, &dev_attr_in_input4); |
||
668 | device_create_file(&new_client->dev, &dev_attr_in_min4); |
||
669 | device_create_file(&new_client->dev, &dev_attr_in_max4); |
||
670 | device_create_file(&new_client->dev, &dev_attr_in_input5); |
||
671 | device_create_file(&new_client->dev, &dev_attr_in_min5); |
||
672 | device_create_file(&new_client->dev, &dev_attr_in_max5); |
||
673 | device_create_file(&new_client->dev, &dev_attr_in_input6); |
||
674 | device_create_file(&new_client->dev, &dev_attr_in_min6); |
||
675 | device_create_file(&new_client->dev, &dev_attr_in_max6); |
||
676 | device_create_file(&new_client->dev, &dev_attr_temp_input); |
||
677 | device_create_file(&new_client->dev, &dev_attr_temp_min); |
||
678 | device_create_file(&new_client->dev, &dev_attr_temp_max); |
||
679 | device_create_file(&new_client->dev, &dev_attr_fan_input1); |
||
680 | device_create_file(&new_client->dev, &dev_attr_fan_min1); |
||
681 | device_create_file(&new_client->dev, &dev_attr_fan_div1); |
||
682 | device_create_file(&new_client->dev, &dev_attr_fan_input2); |
||
683 | device_create_file(&new_client->dev, &dev_attr_fan_min2); |
||
684 | device_create_file(&new_client->dev, &dev_attr_fan_div2); |
||
685 | device_create_file(&new_client->dev, &dev_attr_fan_input3); |
||
686 | device_create_file(&new_client->dev, &dev_attr_fan_min3); |
||
687 | device_create_file(&new_client->dev, &dev_attr_fan_div3); |
||
688 | device_create_file(&new_client->dev, &dev_attr_alarms); |
||
689 | device_create_file(&new_client->dev, &dev_attr_vid); |
||
690 | |||
691 | return 0; |
||
692 | |||
693 | ERROR2: |
||
694 | kfree(new_client); |
||
695 | ERROR1: |
||
696 | if (is_isa) |
||
697 | release_region(address, LM78_EXTENT); |
||
698 | ERROR0: |
||
699 | return err; |
||
700 | } |
||
701 | |||
702 | static int lm78_detach_client(struct i2c_client *client) |
||
703 | { |
||
704 | int err; |
||
705 | |||
706 | /* release ISA region first */ |
||
707 | if(i2c_is_isa_client(client)) |
||
708 | release_region(client->addr, LM78_EXTENT); |
||
709 | |||
710 | /* now it's safe to scrap the rest */ |
||
711 | if ((err = i2c_detach_client(client))) { |
||
712 | dev_err(&client->dev, |
||
713 | "Client deregistration failed, client not detached.\n"); |
||
714 | return err; |
||
715 | } |
||
716 | |||
717 | kfree(client); |
||
718 | |||
719 | return 0; |
||
720 | } |
||
721 | |||
722 | /* The SMBus locks itself, but ISA access must be locked explicitely! |
||
723 | We don't want to lock the whole ISA bus, so we lock each client |
||
724 | separately. |
||
725 | We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks, |
||
726 | would slow down the LM78 access and should not be necessary. |
||
727 | There are some ugly typecasts here, but the good new is - they should |
||
728 | nowhere else be necessary! */ |
||
729 | static int lm78_read_value(struct i2c_client *client, u8 reg) |
||
730 | { |
||
731 | int res; |
||
732 | if (i2c_is_isa_client(client)) { |
||
733 | struct lm78_data *data = i2c_get_clientdata(client); |
||
734 | down(&data->lock); |
||
735 | outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET); |
||
736 | res = inb_p(client->addr + LM78_DATA_REG_OFFSET); |
||
737 | up(&data->lock); |
||
738 | return res; |
||
739 | } else |
||
740 | return i2c_smbus_read_byte_data(client, reg); |
||
741 | } |
||
742 | |||
743 | /* The SMBus locks itself, but ISA access muse be locked explicitely! |
||
744 | We don't want to lock the whole ISA bus, so we lock each client |
||
745 | separately. |
||
746 | We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks, |
||
747 | would slow down the LM78 access and should not be necessary. |
||
748 | There are some ugly typecasts here, but the good new is - they should |
||
749 | nowhere else be necessary! */ |
||
750 | static int lm78_write_value(struct i2c_client *client, u8 reg, u8 value) |
||
751 | { |
||
752 | if (i2c_is_isa_client(client)) { |
||
753 | struct lm78_data *data = i2c_get_clientdata(client); |
||
754 | down(&data->lock); |
||
755 | outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET); |
||
756 | outb_p(value, client->addr + LM78_DATA_REG_OFFSET); |
||
757 | up(&data->lock); |
||
758 | return 0; |
||
759 | } else |
||
760 | return i2c_smbus_write_byte_data(client, reg, value); |
||
761 | } |
||
762 | |||
763 | /* Called when we have found a new LM78. It should set limits, etc. */ |
||
764 | static void lm78_init_client(struct i2c_client *client) |
||
765 | { |
||
766 | struct lm78_data *data = i2c_get_clientdata(client); |
||
767 | int vid; |
||
768 | |||
769 | /* Reset all except Watchdog values and last conversion values |
||
770 | This sets fan-divs to 2, among others */ |
||
771 | lm78_write_value(client, LM78_REG_CONFIG, 0x80); |
||
772 | |||
773 | vid = lm78_read_value(client, LM78_REG_VID_FANDIV) & 0x0f; |
||
774 | if (data->type == lm79) |
||
775 | vid |= |
||
776 | (lm78_read_value(client, LM78_REG_CHIPID) & 0x01) << 4; |
||
777 | else |
||
778 | vid |= 0x10; |
||
779 | vid = VID_FROM_REG(vid); |
||
780 | |||
781 | lm78_write_value(client, LM78_REG_IN_MIN(0), |
||
782 | IN_TO_REG(LM78_INIT_IN_MIN_0(vid))); |
||
783 | lm78_write_value(client, LM78_REG_IN_MAX(0), |
||
784 | IN_TO_REG(LM78_INIT_IN_MAX_0(vid))); |
||
785 | lm78_write_value(client, LM78_REG_IN_MIN(1), |
||
786 | IN_TO_REG(LM78_INIT_IN_MIN_1(vid))); |
||
787 | lm78_write_value(client, LM78_REG_IN_MAX(1), |
||
788 | IN_TO_REG(LM78_INIT_IN_MAX_1(vid))); |
||
789 | lm78_write_value(client, LM78_REG_IN_MIN(2), |
||
790 | IN_TO_REG(LM78_INIT_IN_MIN_2)); |
||
791 | lm78_write_value(client, LM78_REG_IN_MAX(2), |
||
792 | IN_TO_REG(LM78_INIT_IN_MAX_2)); |
||
793 | lm78_write_value(client, LM78_REG_IN_MIN(3), |
||
794 | IN_TO_REG(LM78_INIT_IN_MIN_3)); |
||
795 | lm78_write_value(client, LM78_REG_IN_MAX(3), |
||
796 | IN_TO_REG(LM78_INIT_IN_MAX_3)); |
||
797 | lm78_write_value(client, LM78_REG_IN_MIN(4), |
||
798 | IN_TO_REG(LM78_INIT_IN_MIN_4)); |
||
799 | lm78_write_value(client, LM78_REG_IN_MAX(4), |
||
800 | IN_TO_REG(LM78_INIT_IN_MAX_4)); |
||
801 | lm78_write_value(client, LM78_REG_IN_MIN(5), |
||
802 | IN_TO_REG(LM78_INIT_IN_MIN_5)); |
||
803 | lm78_write_value(client, LM78_REG_IN_MAX(5), |
||
804 | IN_TO_REG(LM78_INIT_IN_MAX_5)); |
||
805 | lm78_write_value(client, LM78_REG_IN_MIN(6), |
||
806 | IN_TO_REG(LM78_INIT_IN_MIN_6)); |
||
807 | lm78_write_value(client, LM78_REG_IN_MAX(6), |
||
808 | IN_TO_REG(LM78_INIT_IN_MAX_6)); |
||
809 | lm78_write_value(client, LM78_REG_FAN_MIN(0), |
||
810 | FAN_TO_REG(LM78_INIT_FAN_MIN_1, 2)); |
||
811 | lm78_write_value(client, LM78_REG_FAN_MIN(1), |
||
812 | FAN_TO_REG(LM78_INIT_FAN_MIN_2, 2)); |
||
813 | lm78_write_value(client, LM78_REG_FAN_MIN(2), |
||
814 | FAN_TO_REG(LM78_INIT_FAN_MIN_3, 2)); |
||
815 | lm78_write_value(client, LM78_REG_TEMP_OVER, |
||
816 | TEMP_TO_REG(LM78_INIT_TEMP_OVER)); |
||
817 | lm78_write_value(client, LM78_REG_TEMP_HYST, |
||
818 | TEMP_TO_REG(LM78_INIT_TEMP_HYST)); |
||
819 | |||
820 | /* Start monitoring */ |
||
821 | lm78_write_value(client, LM78_REG_CONFIG, |
||
822 | (lm78_read_value(client, LM78_REG_CONFIG) & 0xf7) |
||
823 | | 0x01); |
||
824 | |||
825 | } |
||
826 | |||
827 | static void lm78_update_client(struct i2c_client *client) |
||
828 | { |
||
829 | struct lm78_data *data = i2c_get_clientdata(client); |
||
830 | int i; |
||
831 | |||
832 | down(&data->update_lock); |
||
833 | |||
834 | if ((jiffies - data->last_updated > HZ + HZ / 2) || |
||
835 | (jiffies < data->last_updated) || !data->valid) { |
||
836 | |||
837 | dev_dbg(&client->dev, "Starting lm78 update\n"); |
||
838 | |||
839 | for (i = 0; i <= 6; i++) { |
||
840 | data->in[i] = |
||
841 | lm78_read_value(client, LM78_REG_IN(i)); |
||
842 | data->in_min[i] = |
||
843 | lm78_read_value(client, LM78_REG_IN_MIN(i)); |
||
844 | data->in_max[i] = |
||
845 | lm78_read_value(client, LM78_REG_IN_MAX(i)); |
||
846 | } |
||
847 | for (i = 0; i < 3; i++) { |
||
848 | data->fan[i] = |
||
849 | lm78_read_value(client, LM78_REG_FAN(i)); |
||
850 | data->fan_min[i] = |
||
851 | lm78_read_value(client, LM78_REG_FAN_MIN(i)); |
||
852 | } |
||
853 | data->temp = lm78_read_value(client, LM78_REG_TEMP); |
||
854 | data->temp_over = |
||
855 | lm78_read_value(client, LM78_REG_TEMP_OVER); |
||
856 | data->temp_hyst = |
||
857 | lm78_read_value(client, LM78_REG_TEMP_HYST); |
||
858 | i = lm78_read_value(client, LM78_REG_VID_FANDIV); |
||
859 | data->vid = i & 0x0f; |
||
860 | if (data->type == lm79) |
||
861 | data->vid |= |
||
862 | (lm78_read_value(client, LM78_REG_CHIPID) & |
||
863 | 0x01) << 4; |
||
864 | else |
||
865 | data->vid |= 0x10; |
||
866 | data->fan_div[0] = (i >> 4) & 0x03; |
||
867 | data->fan_div[1] = i >> 6; |
||
868 | data->alarms = lm78_read_value(client, LM78_REG_ALARM1) + |
||
869 | (lm78_read_value(client, LM78_REG_ALARM2) << 8); |
||
870 | data->last_updated = jiffies; |
||
871 | data->valid = 1; |
||
872 | |||
873 | data->fan_div[2] = 1; |
||
874 | } |
||
875 | |||
876 | up(&data->update_lock); |
||
877 | } |
||
878 | |||
879 | static int __init sm_lm78_init(void) |
||
880 | { |
||
881 | return i2c_add_driver(&lm78_driver); |
||
882 | } |
||
883 | |||
884 | static void __exit sm_lm78_exit(void) |
||
885 | { |
||
886 | i2c_del_driver(&lm78_driver); |
||
887 | } |
||
888 | |||
889 | |||
890 | |||
891 | MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>"); |
||
892 | MODULE_DESCRIPTION("LM78, LM78-J and LM79 driver"); |
||
893 | MODULE_LICENSE("GPL"); |
||
894 | |||
895 | module_init(sm_lm78_init); |
||
896 | module_exit(sm_lm78_exit); |