Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
420 | giacomo | 1 | /* |
2 | eeprom.c - Part of lm_sensors, Linux kernel modules for hardware |
||
3 | monitoring |
||
4 | Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and |
||
5 | Philip Edelbrock <phil@netroedge.com> |
||
6 | Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com> |
||
7 | Copyright (C) 2003 IBM Corp. |
||
8 | |||
9 | This program is free software; you can redistribute it and/or modify |
||
10 | it under the terms of the GNU General Public License as published by |
||
11 | the Free Software Foundation; either version 2 of the License, or |
||
12 | (at your option) any later version. |
||
13 | |||
14 | This program is distributed in the hope that it will be useful, |
||
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
17 | GNU General Public License for more details. |
||
18 | |||
19 | You should have received a copy of the GNU General Public License |
||
20 | along with this program; if not, write to the Free Software |
||
21 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
||
22 | */ |
||
23 | |||
24 | /* #define DEBUG */ |
||
25 | |||
26 | #include <linux/kernel.h> |
||
27 | #include <linux/init.h> |
||
28 | #include <linux/module.h> |
||
29 | #include <linux/slab.h> |
||
30 | #include <linux/i2c.h> |
||
31 | #include <linux/i2c-sensor.h> |
||
32 | |||
33 | /* Addresses to scan */ |
||
34 | static unsigned short normal_i2c[] = { I2C_CLIENT_END }; |
||
35 | static unsigned short normal_i2c_range[] = { 0x50, 0x57, I2C_CLIENT_END }; |
||
36 | static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; |
||
37 | static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END }; |
||
38 | |||
39 | /* Insmod parameters */ |
||
40 | SENSORS_INSMOD_1(eeprom); |
||
41 | |||
42 | static int checksum = 0; |
||
43 | MODULE_PARM(checksum, "i"); |
||
44 | MODULE_PARM_DESC(checksum, "Only accept eeproms whose checksum is correct"); |
||
45 | |||
46 | |||
47 | /* EEPROM registers */ |
||
48 | #define EEPROM_REG_CHECKSUM 0x3f |
||
49 | |||
50 | /* Size of EEPROM in bytes */ |
||
51 | #define EEPROM_SIZE 256 |
||
52 | |||
53 | /* possible types of eeprom devices */ |
||
54 | enum eeprom_nature { |
||
55 | UNKNOWN, |
||
56 | VAIO, |
||
57 | }; |
||
58 | |||
59 | /* Each client has this additional data */ |
||
60 | struct eeprom_data { |
||
61 | struct semaphore update_lock; |
||
62 | char valid; /* !=0 if following fields are valid */ |
||
63 | unsigned long last_updated; /* In jiffies */ |
||
64 | u8 data[EEPROM_SIZE]; /* Register values */ |
||
65 | }; |
||
66 | |||
67 | |||
68 | static int eeprom_attach_adapter(struct i2c_adapter *adapter); |
||
69 | static int eeprom_detect(struct i2c_adapter *adapter, int address, int kind); |
||
70 | static int eeprom_detach_client(struct i2c_client *client); |
||
71 | |||
72 | /* This is the driver that will be inserted */ |
||
73 | static struct i2c_driver eeprom_driver = { |
||
74 | .owner = THIS_MODULE, |
||
75 | .name = "eeprom", |
||
76 | .id = I2C_DRIVERID_EEPROM, |
||
77 | .flags = I2C_DF_NOTIFY, |
||
78 | .attach_adapter = eeprom_attach_adapter, |
||
79 | .detach_client = eeprom_detach_client, |
||
80 | }; |
||
81 | |||
82 | static int eeprom_id = 0; |
||
83 | |||
84 | static void eeprom_update_client(struct i2c_client *client) |
||
85 | { |
||
86 | struct eeprom_data *data = i2c_get_clientdata(client); |
||
87 | int i, j; |
||
88 | |||
89 | down(&data->update_lock); |
||
90 | |||
91 | if ((jiffies - data->last_updated > 300 * HZ) | |
||
92 | (jiffies < data->last_updated) || !data->valid) { |
||
93 | dev_dbg(&client->dev, "Starting eeprom update\n"); |
||
94 | |||
95 | if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { |
||
96 | for (i=0; i < EEPROM_SIZE; i += I2C_SMBUS_I2C_BLOCK_MAX) |
||
97 | if (i2c_smbus_read_i2c_block_data(client, i, data->data + i) != I2C_SMBUS_I2C_BLOCK_MAX) |
||
98 | goto exit; |
||
99 | } else { |
||
100 | if (i2c_smbus_write_byte(client, 0)) { |
||
101 | dev_dbg(&client->dev, "eeprom read start has failed!\n"); |
||
102 | goto exit; |
||
103 | } |
||
104 | for (i = 0; i < EEPROM_SIZE; i++) { |
||
105 | j = i2c_smbus_read_byte(client); |
||
106 | if (j < 0) |
||
107 | goto exit; |
||
108 | data->data[i] = (u8) j; |
||
109 | } |
||
110 | } |
||
111 | data->last_updated = jiffies; |
||
112 | data->valid = 1; |
||
113 | } |
||
114 | exit: |
||
115 | up(&data->update_lock); |
||
116 | } |
||
117 | |||
118 | static ssize_t eeprom_read(struct kobject *kobj, char *buf, loff_t off, size_t count) |
||
119 | { |
||
120 | struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj)); |
||
121 | struct eeprom_data *data = i2c_get_clientdata(client); |
||
122 | |||
123 | eeprom_update_client(client); |
||
124 | |||
125 | if (off > EEPROM_SIZE) |
||
126 | return 0; |
||
127 | if (off + count > EEPROM_SIZE) |
||
128 | count = EEPROM_SIZE - off; |
||
129 | |||
130 | memcpy(buf, &data->data[off], count); |
||
131 | return count; |
||
132 | } |
||
133 | |||
134 | static struct bin_attribute eeprom_attr = { |
||
135 | .attr = { |
||
136 | .name = "eeprom", |
||
137 | .mode = S_IRUGO, |
||
138 | }, |
||
139 | .size = EEPROM_SIZE, |
||
140 | .read = eeprom_read, |
||
141 | }; |
||
142 | |||
143 | static int eeprom_attach_adapter(struct i2c_adapter *adapter) |
||
144 | { |
||
145 | return i2c_detect(adapter, &addr_data, eeprom_detect); |
||
146 | } |
||
147 | |||
148 | /* This function is called by i2c_detect */ |
||
149 | int eeprom_detect(struct i2c_adapter *adapter, int address, int kind) |
||
150 | { |
||
151 | int i, cs; |
||
152 | struct i2c_client *new_client; |
||
153 | struct eeprom_data *data; |
||
154 | enum eeprom_nature nature = UNKNOWN; |
||
155 | int err = 0; |
||
156 | |||
157 | /* Make sure we aren't probing the ISA bus!! This is just a safety check |
||
158 | at this moment; i2c_detect really won't call us. */ |
||
159 | #ifdef DEBUG |
||
160 | if (i2c_is_isa_adapter(adapter)) { |
||
161 | dev_dbg(&adapter->dev, " eeprom_detect called for an ISA bus adapter?!?\n"); |
||
162 | return 0; |
||
163 | } |
||
164 | #endif |
||
165 | |||
166 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) |
||
167 | goto exit; |
||
168 | |||
169 | /* OK. For now, we presume we have a valid client. We now create the |
||
170 | client structure, even though we cannot fill it completely yet. |
||
171 | But it allows us to access eeprom_{read,write}_value. */ |
||
172 | if (!(new_client = kmalloc(sizeof(struct i2c_client) + |
||
173 | sizeof(struct eeprom_data), |
||
174 | GFP_KERNEL))) { |
||
175 | err = -ENOMEM; |
||
176 | goto exit; |
||
177 | } |
||
178 | memset(new_client, 0x00, sizeof(struct i2c_client) + |
||
179 | sizeof(struct eeprom_data)); |
||
180 | |||
181 | data = (struct eeprom_data *) (new_client + 1); |
||
182 | memset(data, 0xff, EEPROM_SIZE); |
||
183 | i2c_set_clientdata(new_client, data); |
||
184 | new_client->addr = address; |
||
185 | new_client->adapter = adapter; |
||
186 | new_client->driver = &eeprom_driver; |
||
187 | new_client->flags = 0; |
||
188 | |||
189 | /* Now, we do the remaining detection. It is not there, unless you force |
||
190 | the checksum to work out. */ |
||
191 | if (checksum) { |
||
192 | /* prevent 24RF08 corruption */ |
||
193 | i2c_smbus_write_quick(new_client, 0); |
||
194 | cs = 0; |
||
195 | for (i = 0; i <= 0x3e; i++) |
||
196 | cs += i2c_smbus_read_byte_data(new_client, i); |
||
197 | cs &= 0xff; |
||
198 | if (i2c_smbus_read_byte_data (new_client, EEPROM_REG_CHECKSUM) != cs) |
||
199 | goto exit_kfree; |
||
200 | } |
||
201 | |||
202 | /* Detect the Vaio nature of EEPROMs. |
||
203 | We use the "PCG-" prefix as the signature. */ |
||
204 | if (address == 0x57) { |
||
205 | if (i2c_smbus_read_byte_data(new_client, 0x80) == 'P' && |
||
206 | i2c_smbus_read_byte_data(new_client, 0x81) == 'C' && |
||
207 | i2c_smbus_read_byte_data(new_client, 0x82) == 'G' && |
||
208 | i2c_smbus_read_byte_data(new_client, 0x83) == '-') |
||
209 | nature = VAIO; |
||
210 | } |
||
211 | |||
212 | /* If this is a VIAO, then we only allow root to read from this file, |
||
213 | as BIOS passwords can be present here in plaintext */ |
||
214 | switch (nature) { |
||
215 | case VAIO: |
||
216 | eeprom_attr.attr.mode = S_IRUSR; |
||
217 | break; |
||
218 | default: |
||
219 | eeprom_attr.attr.mode = S_IRUGO; |
||
220 | } |
||
221 | |||
222 | /* Fill in the remaining client fields */ |
||
223 | strncpy(new_client->name, "eeprom", I2C_NAME_SIZE); |
||
224 | new_client->id = eeprom_id++; |
||
225 | data->valid = 0; |
||
226 | init_MUTEX(&data->update_lock); |
||
227 | |||
228 | /* Tell the I2C layer a new client has arrived */ |
||
229 | if ((err = i2c_attach_client(new_client))) |
||
230 | goto exit_kfree; |
||
231 | |||
232 | /* create the sysfs eeprom file */ |
||
233 | sysfs_create_bin_file(&new_client->dev.kobj, &eeprom_attr); |
||
234 | |||
235 | return 0; |
||
236 | |||
237 | exit_kfree: |
||
238 | kfree(new_client); |
||
239 | exit: |
||
240 | return err; |
||
241 | } |
||
242 | |||
243 | static int eeprom_detach_client(struct i2c_client *client) |
||
244 | { |
||
245 | int err; |
||
246 | |||
247 | err = i2c_detach_client(client); |
||
248 | if (err) { |
||
249 | dev_err(&client->dev, "Client deregistration failed, client not detached.\n"); |
||
250 | return err; |
||
251 | } |
||
252 | |||
253 | kfree(client); |
||
254 | |||
255 | return 0; |
||
256 | } |
||
257 | |||
258 | static int __init eeprom_init(void) |
||
259 | { |
||
260 | return i2c_add_driver(&eeprom_driver); |
||
261 | } |
||
262 | |||
263 | static void __exit eeprom_exit(void) |
||
264 | { |
||
265 | i2c_del_driver(&eeprom_driver); |
||
266 | } |
||
267 | |||
268 | |||
269 | MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and " |
||
270 | "Philip Edelbrock <phil@netroedge.com> and " |
||
271 | "Greg Kroah-Hartman <greg@kroah.com>"); |
||
272 | MODULE_DESCRIPTION("I2C EEPROM driver"); |
||
273 | MODULE_LICENSE("GPL"); |
||
274 | |||
275 | module_init(eeprom_init); |
||
276 | module_exit(eeprom_exit); |