root/lm-sensors/trunk/kernel/chips/pca9540.c @ 2837

Revision 2837, 6.5 KB (checked in by khali, 8 years ago)

Do not return negative values to i2c_detect on non-fatal errors.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2 * pca9540.c - Part of lm_sensors, Linux kernel modules for hardware
3 *             monitoring
4 * Copyright (c) 2004  Jean Delvare <khali@linux-fr.org>
5 *
6 * Based on pcf8574.c from the same project by Frodo Looijaard,
7 * Philip Edelbrock, Dan Eaton and Aurelien Jarno.
8 *
9 * The PCA9540 is a 2-channel I2C multiplexer made by Philips
10 * Semiconductors. It is controlled via the I2C bus itself.
11 * The SCL/SDA upstream pair fans out to two SCL/SDA downstream
12 * pairs, or channels. Only one SCL/SDA channel is selected at a
13 * time, determined by the contents of the programmable control
14 * register.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 */
30
31#include <linux/module.h>
32#include <linux/slab.h>
33#include <linux/i2c.h>
34#include <linux/i2c-proc.h>
35#include <linux/init.h>
36#include "version.h"
37
38MODULE_LICENSE("GPL");
39
40/* Addresses to scan */
41static unsigned short normal_i2c[] = { 0x70, SENSORS_I2C_END };
42static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
43static unsigned int normal_isa[] = { SENSORS_ISA_END };
44static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
45
46/* Insmod parameters */
47SENSORS_INSMOD_1(pca9540);
48
49/* Each client has this additional data */
50struct pca9540_data {
51        struct i2c_client client;
52        int sysctl_id;
53
54        struct semaphore update_lock;
55
56        u8 control;     /* Register value */
57};
58
59static int pca9540_attach_adapter(struct i2c_adapter *adapter);
60static int pca9540_detect(struct i2c_adapter *adapter, int address,
61                          unsigned short flags, int kind);
62static int pca9540_detach_client(struct i2c_client *client);
63static void pca9540_update_client(struct i2c_client *client);
64
65static void pca9540_channel(struct i2c_client *client, int operation,
66                            int ctl_name, int *nrels_mag, long *results);
67
68/* This is the driver that will be inserted */
69static struct i2c_driver pca9540_driver = {
70        .name           = "PCA9540 chip driver",
71        .flags          = I2C_DF_NOTIFY,
72        .attach_adapter = pca9540_attach_adapter,
73        .detach_client  = pca9540_detach_client,
74};
75
76
77/* -- SENSORS SYSCTL START -- */
78
79#define PCA9540_SYSCTL_CHANNEL          1000
80
81/* -- SENSORS SYSCTL END -- */
82
83static ctl_table pca9540_dir_table_template[] = {
84        {PCA9540_SYSCTL_CHANNEL, "channel", NULL, 0, 0644, NULL, &i2c_proc_real,
85         &i2c_sysctl_real, NULL, &pca9540_channel},
86        {0}
87};
88
89static int pca9540_id = 0;
90
91static int pca9540_attach_adapter(struct i2c_adapter *adapter)
92{
93        return i2c_detect(adapter, &addr_data, pca9540_detect);
94}
95
96/* This function is called by i2c_detect. */
97int pca9540_detect(struct i2c_adapter *adapter, int address,
98                   unsigned short flags, int kind)
99{
100        int i;
101        struct i2c_client *new_client;
102        struct pca9540_data *data;
103        int err = 0;
104
105        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
106                goto ERROR0;
107
108        /* OK. For now, we presume we have a valid client. We now create the
109           client structure, even though we cannot fill it completely yet. */
110        if (!(data = kmalloc(sizeof(struct pca9540_data), GFP_KERNEL))) {
111                err = -ENOMEM;
112                goto ERROR0;
113        }
114
115        new_client = &data->client;
116        new_client->addr = address;
117        new_client->data = data;
118        new_client->adapter = adapter;
119        new_client->driver = &pca9540_driver;
120        new_client->flags = 0;
121
122        /* The detection is very weak. */
123        if (kind < 0) {
124                u8 reg = i2c_smbus_read_byte(new_client);
125                if ((reg & 0xfa) != 0x00
126                 || reg != i2c_smbus_read_byte(new_client)
127                 || reg != i2c_smbus_read_byte(new_client)
128                 || reg != i2c_smbus_read_byte(new_client))
129                        goto ERROR1;
130        }
131
132        kind = pca9540;
133
134        /* Fill in the remaining client fields and put it into the global list */
135        strcpy(new_client->name, "PCA9540 chip");
136        new_client->id = pca9540_id++;
137        init_MUTEX(&data->update_lock);
138
139        /* Tell the I2C layer a new client has arrived */
140        if ((err = i2c_attach_client(new_client)))
141                goto ERROR1;
142
143        /* Register a new directory entry with module sensors */
144        if ((i = i2c_register_entry(new_client, "pca9540",
145                                    pca9540_dir_table_template,
146                                    THIS_MODULE)) < 0) {
147                err = i;
148                goto ERROR2;
149        }
150        data->sysctl_id = i;
151
152        /* Initialize the PCA9540 chip: nothing needed */
153
154        return 0;
155
156/* OK, this is not exactly good programming practice, usually. But it is
157   very code-efficient in this case. */
158
159      ERROR2:
160        i2c_detach_client(new_client);
161      ERROR1:
162        kfree(data);
163      ERROR0:
164        return err;
165}
166
167static int pca9540_detach_client(struct i2c_client *client)
168{
169        int err;
170
171        i2c_deregister_entry(((struct pca9540_data *) (client->data))->sysctl_id);
172
173        if ((err = i2c_detach_client(client))) {
174                printk(KERN_ERR "pca9540.o: Client deregistration failed, "
175                       "client not detached.\n");
176                return err;
177        }
178
179        kfree(client->data);
180
181        return 0;
182}
183
184static void pca9540_update_client(struct i2c_client *client)
185{
186        struct pca9540_data *data = client->data;
187
188        down(&data->update_lock);
189
190#ifdef DEBUG
191        printk(KERN_DEBUG "pca9540.o: Starting update.\n");
192#endif
193        data->control = i2c_smbus_read_byte(client) & 0x07; 
194
195        up(&data->update_lock);
196}
197
198
199void pca9540_channel(struct i2c_client *client, int operation,
200                     int ctl_name, int *nrels_mag, long *results)
201{
202        struct pca9540_data *data = client->data;
203
204        if (operation == SENSORS_PROC_REAL_INFO)
205                *nrels_mag = 0;
206        else if (operation == SENSORS_PROC_REAL_READ) {
207                pca9540_update_client(client);
208                switch(data->control) {
209                        case 4: results[0] = 1; break;
210                        case 5: results[0] = 2; break;
211                        default: results[0] = 0;
212                }
213                *nrels_mag = 1;
214        } else if (operation == SENSORS_PROC_REAL_WRITE) {
215                if (*nrels_mag >= 1) {
216                        switch(results[0]) {
217                                case 1: data->control = 4; break;
218                                case 2: data->control = 5; break;
219                                case 0: data->control = 0; break;
220                                default: return; /* invalid value */
221                        }
222                        i2c_smbus_write_byte(client, data->control);
223                }
224        }
225}
226
227
228static int __init pca9540_init(void)
229{
230        printk("pca9540.o version %s (%s)\n", LM_VERSION, LM_DATE);
231        return i2c_add_driver(&pca9540_driver);
232}
233
234static void __exit pca9540_exit(void)
235{
236        i2c_del_driver(&pca9540_driver);
237}
238
239
240MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
241MODULE_DESCRIPTION("PCA9540 driver");
242
243module_init(pca9540_init);
244module_exit(pca9540_exit);
Note: See TracBrowser for help on using the browser.