root/lm-sensors/trunk/kernel/chips/w83l785ts.c @ 3152

Revision 3152, 9.8 KB (checked in by khali, 8 years ago)

Using s8 instead of u8 to store temperature register values saves
a few instructions (backport from Linux 2.6).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2 * w83l785ts.c - Part of lm_sensors, Linux kernel modules for hardware
3 *               monitoring
4 * Copyright (C) 2003-2004  Jean Delvare <khali@linux-fr.org>
5 *
6 * Inspired from the lm83 driver. The W83L785TS-S is a sensor chip made
7 * by Winbond. It reports a single external temperature with a 1 deg
8 * resolution and a 3 deg accuracy. Data sheet can be obtained from
9 * Winbond's website at:
10 *   http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83L785TS-S.pdf
11 *
12 * Thanks to James Bolt <james@evilpenguin.com> for benchmarking the read
13 * error handling mechanism.
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 */
29
30#include <linux/module.h>
31#include <linux/slab.h>
32#include <linux/i2c.h>
33#include <linux/i2c-proc.h>
34#include <linux/init.h>
35#include <linux/delay.h>
36#include "version.h"
37
38#ifndef I2C_DRIVERID_W83L785TS
39#define I2C_DRIVERID_W83L785TS  1047
40#endif
41
42/* How many retries on register read error */
43#define MAX_RETRIES     5
44
45/*
46 * Address to scan
47 * Address is fully defined internally and cannot be changed.
48 */
49
50static unsigned short normal_i2c[] = { 0x2e, SENSORS_I2C_END };
51static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
52static unsigned int normal_isa[] = { SENSORS_ISA_END };
53static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
54
55/*
56 * Insmod parameters
57 */
58
59SENSORS_INSMOD_1(w83l785ts);
60
61/*
62 * The W83L785TS-S registers
63 * Manufacturer ID is 0x5CA3 for Winbond.
64 */
65
66#define W83L785TS_REG_MAN_ID1           0x4D
67#define W83L785TS_REG_MAN_ID2           0x4C
68#define W83L785TS_REG_CHIP_ID           0x4E
69#define W83L785TS_REG_CONFIG            0x40
70#define W83L785TS_REG_TYPE              0x52
71#define W83L785TS_REG_TEMP              0x27
72#define W83L785TS_REG_TEMP_OVER         0x53 /* not sure about this one */
73
74/*
75 * Conversions
76 * The W83L785TS-S uses signed 8-bit values.
77 */
78
79#define TEMP_FROM_REG(val)      (val)
80
81/*
82 * Functions declaration
83 */
84
85static int w83l785ts_attach_adapter(struct i2c_adapter *adapter);
86static int w83l785ts_detect(struct i2c_adapter *adapter, int address, unsigned
87        short flags, int kind);
88static int w83l785ts_detach_client(struct i2c_client *client);
89static u8 w83l785ts_read_value(struct i2c_client *client, u8 reg, u8 defval);
90static void w83l785ts_update_client(struct i2c_client *client);
91static void w83l785ts_temp(struct i2c_client *client, int operation, int
92        ctl_name, int *nrels_mag, long *results);
93
94/*
95 * Driver data (common to all clients)
96 */
97 
98static struct i2c_driver w83l785ts_driver = {
99        .name           = "W83L785S-S sensor driver",
100        .id             = I2C_DRIVERID_W83L785TS,
101        .flags          = I2C_DF_NOTIFY,
102        .attach_adapter = w83l785ts_attach_adapter,
103        .detach_client  = w83l785ts_detach_client,
104};
105
106/*
107 * Client data (each client gets its own)
108 */
109
110struct w83l785ts_data {
111        struct i2c_client client;
112        int sysctl_id;
113
114        struct semaphore update_lock;
115        char valid; /* zero until following fields are valid */
116        unsigned long last_updated; /* in jiffies */
117
118        /* registers values */
119        s8 temp, temp_over;
120};
121
122/*
123 * Proc entries
124 * These files are created for each detected W83L785TS-S.
125 */
126
127/* -- SENSORS SYSCTL START -- */
128
129#define W83L785TS_SYSCTL_TEMP   1200
130
131/* -- SENSORS SYSCTL END -- */
132
133
134static ctl_table w83l785ts_dir_table_template[] =
135{
136        {W83L785TS_SYSCTL_TEMP, "temp", NULL, 0, 0444, NULL,
137         &i2c_proc_real, &i2c_sysctl_real, NULL, &w83l785ts_temp},
138        {0}
139};
140
141/*
142 * Real code
143 */
144
145static int w83l785ts_attach_adapter(struct i2c_adapter *adapter)
146{
147        return i2c_detect(adapter, &addr_data, w83l785ts_detect);
148}
149
150/*
151 * The following function does more than just detection. If detection
152 * succeeds, it also registers the new chip.
153 */
154static int w83l785ts_detect(struct i2c_adapter *adapter, int address,
155        unsigned short flags, int kind)
156{
157        struct i2c_client *new_client;
158        struct w83l785ts_data *data;
159        int err = 0;
160        const char *type_name = "";
161        const char *client_name = "";
162
163        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
164#ifdef DEBUG
165                printk(KERN_DEBUG "w83l785ts.o: I2C bus doesn't support "
166                        "byte read mode, skipping.\n");
167#endif
168                return 0;
169        }
170
171        if (!(data = kmalloc(sizeof(struct w83l785ts_data), GFP_KERNEL))) {
172                printk(KERN_ERR "w83l785ts.o: Out of memory in w83l785ts_detect "
173                        "(new_client).\n");
174                return -ENOMEM;
175        }
176
177        /*
178         * The common I2C client data is placed right after the
179         * W83L785TS-specific. The W83L785TS-specific data is pointed to by the
180         * data field from the I2C client data.
181         */
182
183        new_client = &data->client;
184        new_client->addr = address;
185        new_client->data = data;
186        new_client->adapter = adapter;
187        new_client->driver = &w83l785ts_driver;
188        new_client->flags = 0;
189
190        /*
191         * Now we do the remaining detection. A negative kind means that
192         * the driver was loaded with no force parameter (default), so we
193         * must both detect and identify the chip (actually there is only
194         * one possible kind of chip for now, W83L785TS-S). A zero kind means
195         * that the driver was loaded with the force parameter, the detection
196         * step shall be skipped. A positive kind means that the driver
197         * was loaded with the force parameter and a given kind of chip is
198         * requested, so both the detection and the identification steps
199         * are skipped.
200         */
201
202        if (kind < 0) { /* detection */
203                if (((w83l785ts_read_value(new_client, W83L785TS_REG_CONFIG, 0)
204                        & 0x80) != 0x00)
205                 || ((w83l785ts_read_value(new_client, W83L785TS_REG_TYPE, 0)
206                        & 0xFC) != 0x00)) {
207#ifdef DEBUG
208                        printk(KERN_DEBUG "w83l785ts.o: Detection failed at "
209                                "0x%02x.\n", address);
210#endif
211                        goto ERROR1;
212                }
213        }
214
215        if (kind <= 0) { /* identification */
216                u16 man_id;
217                u8 chip_id;
218
219                man_id = (w83l785ts_read_value(new_client, W83L785TS_REG_MAN_ID1, 0) << 8)
220                       +  w83l785ts_read_value(new_client, W83L785TS_REG_MAN_ID2, 0);
221                chip_id = w83l785ts_read_value(new_client, W83L785TS_REG_CHIP_ID, 0);
222                if (man_id == 0x5CA3) { /* Winbond */
223                        if (chip_id == 0x70)
224                                kind = w83l785ts;
225                }
226        }
227
228        if (kind <= 0) { /* identification failed */
229                printk(KERN_INFO "w83l785ts.o: Unsupported chip.\n");
230                goto ERROR1;
231        }
232
233        if (kind == w83l785ts) {
234                type_name = "w83l785ts";
235                client_name = "W83L785TS-S chip";
236        } else {
237                printk(KERN_ERR "w83l785ts.o: Unknown kind %d.\n", kind);
238                goto ERROR1;
239        }
240       
241        /*
242         * OK, we got a valid chip so we can fill in the remaining client
243         * fields.
244         */
245
246        strcpy(new_client->name, client_name);
247        data->valid = 0;
248        init_MUTEX(&data->update_lock);
249
250        /*
251         * Tell the I2C layer a new client has arrived.
252         */
253
254        if ((err = i2c_attach_client(new_client))) {
255#ifdef DEBUG
256                printk(KERN_ERR "w83l785ts.o: Failed attaching client.\n");
257#endif
258                goto ERROR1;
259        }
260
261        /*
262         * Register a new directory entry.
263         */
264
265        if ((err = i2c_register_entry(new_client, type_name,
266            w83l785ts_dir_table_template, THIS_MODULE)) < 0) {
267#ifdef DEBUG
268                printk(KERN_ERR "w83l785ts.o: Failed registering directory "
269                        "entry.\n");
270#endif
271                goto ERROR2;
272        }
273        data->sysctl_id = err;
274
275        /*
276         * Initialize the W83L785TS chip
277         * Nothing yet, assume it is already started.
278         */
279
280        return 0;
281
282        ERROR2:
283        i2c_detach_client(new_client);
284        ERROR1:
285        kfree(data);
286        return err;
287}
288
289static int w83l785ts_detach_client(struct i2c_client *client)
290{
291        int err;
292
293        i2c_deregister_entry(((struct w83l785ts_data *) (client->data))->sysctl_id);
294        if ((err = i2c_detach_client(client))) {
295                printk(KERN_ERR "w83l785ts.o: Client deregistration failed, "
296                        "client not detached.\n");
297                return err;
298        }
299
300        kfree(client->data);
301        return 0;
302}
303
304static u8 w83l785ts_read_value(struct i2c_client *client, u8 reg, u8 defval)
305{
306        int value, i;
307
308        /* Frequent read errors have been reported on Asus boards, so we
309         * retry on read errors. If it still fails (unlikely), return the
310         * default value requested by the caller. */
311        for (i = 1; i <= MAX_RETRIES; i++) {
312                value = i2c_smbus_read_byte_data(client, reg);
313                if (value >= 0)
314                        return value;
315                printk(KERN_WARNING "w83l785ts.o: Read failed, will retry "
316                        "in %d.\n", i);
317                mdelay(i);
318        }
319
320        printk(KERN_ERR "w83l785ts.o: Couldn't read value from register. "
321                "Please report.\n");
322        return defval;
323}
324
325static void w83l785ts_update_client(struct i2c_client *client)
326{
327        struct w83l785ts_data *data = client->data;
328
329        down(&data->update_lock);
330
331        if ((jiffies - data->last_updated > HZ * 2)
332         || (jiffies < data->last_updated)
333         || !data->valid) {
334#ifdef DEBUG
335                printk(KERN_DEBUG "w83l785ts.o: Updating data.\n");
336#endif
337                data->temp = w83l785ts_read_value(client, W83L785TS_REG_TEMP,
338                        data->temp);
339                data->temp_over = w83l785ts_read_value(client,
340                        W83L785TS_REG_TEMP_OVER, data->temp_over);
341                data->last_updated = jiffies;
342                data->valid = 1;
343        }
344
345        up(&data->update_lock);
346}
347
348static void w83l785ts_temp(struct i2c_client *client, int operation,
349        int ctl_name, int *nrels_mag, long *results)
350{
351        struct w83l785ts_data *data = client->data;
352
353        if (operation == SENSORS_PROC_REAL_INFO) {
354                *nrels_mag = 0; /* magnitude */
355        } else if (operation == SENSORS_PROC_REAL_READ) {
356                w83l785ts_update_client(client);
357                results[0] = TEMP_FROM_REG(data->temp_over);
358                results[1] = TEMP_FROM_REG(data->temp);
359                *nrels_mag = 2;
360        }
361}
362
363static int __init sm_w83l785ts_init(void)
364{
365        printk(KERN_INFO "w83l785ts.o version %s (%s)\n", LM_VERSION, LM_DATE);
366        return i2c_add_driver(&w83l785ts_driver);
367}
368
369static void __exit sm_w83l785ts_exit(void)
370{
371        i2c_del_driver(&w83l785ts_driver);
372}
373
374MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
375MODULE_DESCRIPTION("W83L785TS-S sensor driver");
376MODULE_LICENSE("GPL");
377
378module_init(sm_w83l785ts_init);
379module_exit(sm_w83l785ts_exit);
Note: See TracBrowser for help on using the browser.