aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/eeprom/Kconfig11
-rw-r--r--drivers/misc/eeprom/Makefile1
-rw-r--r--drivers/misc/eeprom/eeprom.c257
3 files changed, 269 insertions, 0 deletions
diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig
index 0b21778b7d13..3d31b6644245 100644
--- a/drivers/misc/eeprom/Kconfig
+++ b/drivers/misc/eeprom/Kconfig
@@ -26,4 +26,15 @@ config AT24
26 This driver can also be built as a module. If so, the module 26 This driver can also be built as a module. If so, the module
27 will be called at24. 27 will be called at24.
28 28
29config SENSORS_EEPROM
30 tristate "Old I2C EEPROM reader"
31 depends on I2C && EXPERIMENTAL
32 help
33 If you say yes here you get read-only access to the EEPROM data
34 available on modern memory DIMMs and Sony Vaio laptops via I2C. Such
35 EEPROMs could theoretically be available on other devices as well.
36
37 This driver can also be built as a module. If so, the module
38 will be called eeprom.
39
29endmenu 40endmenu
diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile
index 72cd478eb53f..a3dad28f2724 100644
--- a/drivers/misc/eeprom/Makefile
+++ b/drivers/misc/eeprom/Makefile
@@ -1 +1,2 @@
1obj-$(CONFIG_AT24) += at24.o 1obj-$(CONFIG_AT24) += at24.o
2obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o
diff --git a/drivers/misc/eeprom/eeprom.c b/drivers/misc/eeprom/eeprom.c
new file mode 100644
index 000000000000..2c27193aeaa0
--- /dev/null
+++ b/drivers/misc/eeprom/eeprom.c
@@ -0,0 +1,257 @@
1/*
2 Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and
3 Philip Edelbrock <phil@netroedge.com>
4 Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
5 Copyright (C) 2003 IBM Corp.
6 Copyright (C) 2004 Jean Delvare <khali@linux-fr.org>
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21*/
22
23#include <linux/kernel.h>
24#include <linux/init.h>
25#include <linux/module.h>
26#include <linux/slab.h>
27#include <linux/jiffies.h>
28#include <linux/i2c.h>
29#include <linux/mutex.h>
30
31/* Addresses to scan */
32static const unsigned short normal_i2c[] = { 0x50, 0x51, 0x52, 0x53, 0x54,
33 0x55, 0x56, 0x57, I2C_CLIENT_END };
34
35/* Insmod parameters */
36I2C_CLIENT_INSMOD_1(eeprom);
37
38
39/* Size of EEPROM in bytes */
40#define EEPROM_SIZE 256
41
42/* possible types of eeprom devices */
43enum eeprom_nature {
44 UNKNOWN,
45 VAIO,
46};
47
48/* Each client has this additional data */
49struct eeprom_data {
50 struct mutex update_lock;
51 u8 valid; /* bitfield, bit!=0 if slice is valid */
52 unsigned long last_updated[8]; /* In jiffies, 8 slices */
53 u8 data[EEPROM_SIZE]; /* Register values */
54 enum eeprom_nature nature;
55};
56
57
58static void eeprom_update_client(struct i2c_client *client, u8 slice)
59{
60 struct eeprom_data *data = i2c_get_clientdata(client);
61 int i;
62
63 mutex_lock(&data->update_lock);
64
65 if (!(data->valid & (1 << slice)) ||
66 time_after(jiffies, data->last_updated[slice] + 300 * HZ)) {
67 dev_dbg(&client->dev, "Starting eeprom update, slice %u\n", slice);
68
69 if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
70 for (i = slice << 5; i < (slice + 1) << 5; i += 32)
71 if (i2c_smbus_read_i2c_block_data(client, i,
72 32, data->data + i)
73 != 32)
74 goto exit;
75 } else {
76 for (i = slice << 5; i < (slice + 1) << 5; i += 2) {
77 int word = i2c_smbus_read_word_data(client, i);
78 if (word < 0)
79 goto exit;
80 data->data[i] = word & 0xff;
81 data->data[i + 1] = word >> 8;
82 }
83 }
84 data->last_updated[slice] = jiffies;
85 data->valid |= (1 << slice);
86 }
87exit:
88 mutex_unlock(&data->update_lock);
89}
90
91static ssize_t eeprom_read(struct kobject *kobj, struct bin_attribute *bin_attr,
92 char *buf, loff_t off, size_t count)
93{
94 struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj));
95 struct eeprom_data *data = i2c_get_clientdata(client);
96 u8 slice;
97
98 if (off > EEPROM_SIZE)
99 return 0;
100 if (off + count > EEPROM_SIZE)
101 count = EEPROM_SIZE - off;
102
103 /* Only refresh slices which contain requested bytes */
104 for (slice = off >> 5; slice <= (off + count - 1) >> 5; slice++)
105 eeprom_update_client(client, slice);
106
107 /* Hide Vaio private settings to regular users:
108 - BIOS passwords: bytes 0x00 to 0x0f
109 - UUID: bytes 0x10 to 0x1f
110 - Serial number: 0xc0 to 0xdf */
111 if (data->nature == VAIO && !capable(CAP_SYS_ADMIN)) {
112 int i;
113
114 for (i = 0; i < count; i++) {
115 if ((off + i <= 0x1f) ||
116 (off + i >= 0xc0 && off + i <= 0xdf))
117 buf[i] = 0;
118 else
119 buf[i] = data->data[off + i];
120 }
121 } else {
122 memcpy(buf, &data->data[off], count);
123 }
124
125 return count;
126}
127
128static struct bin_attribute eeprom_attr = {
129 .attr = {
130 .name = "eeprom",
131 .mode = S_IRUGO,
132 },
133 .size = EEPROM_SIZE,
134 .read = eeprom_read,
135};
136
137/* Return 0 if detection is successful, -ENODEV otherwise */
138static int eeprom_detect(struct i2c_client *client, int kind,
139 struct i2c_board_info *info)
140{
141 struct i2c_adapter *adapter = client->adapter;
142
143 /* EDID EEPROMs are often 24C00 EEPROMs, which answer to all
144 addresses 0x50-0x57, but we only care about 0x50. So decline
145 attaching to addresses >= 0x51 on DDC buses */
146 if (!(adapter->class & I2C_CLASS_SPD) && client->addr >= 0x51)
147 return -ENODEV;
148
149 /* There are four ways we can read the EEPROM data:
150 (1) I2C block reads (faster, but unsupported by most adapters)
151 (2) Word reads (128% overhead)
152 (3) Consecutive byte reads (88% overhead, unsafe)
153 (4) Regular byte data reads (265% overhead)
154 The third and fourth methods are not implemented by this driver
155 because all known adapters support one of the first two. */
156 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)
157 && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK))
158 return -ENODEV;
159
160 strlcpy(info->type, "eeprom", I2C_NAME_SIZE);
161
162 return 0;
163}
164
165static int eeprom_probe(struct i2c_client *client,
166 const struct i2c_device_id *id)
167{
168 struct i2c_adapter *adapter = client->adapter;
169 struct eeprom_data *data;
170 int err;
171
172 if (!(data = kzalloc(sizeof(struct eeprom_data), GFP_KERNEL))) {
173 err = -ENOMEM;
174 goto exit;
175 }
176
177 memset(data->data, 0xff, EEPROM_SIZE);
178 i2c_set_clientdata(client, data);
179 mutex_init(&data->update_lock);
180 data->nature = UNKNOWN;
181
182 /* Detect the Vaio nature of EEPROMs.
183 We use the "PCG-" or "VGN-" prefix as the signature. */
184 if (client->addr == 0x57
185 && i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
186 char name[4];
187
188 name[0] = i2c_smbus_read_byte_data(client, 0x80);
189 name[1] = i2c_smbus_read_byte_data(client, 0x81);
190 name[2] = i2c_smbus_read_byte_data(client, 0x82);
191 name[3] = i2c_smbus_read_byte_data(client, 0x83);
192
193 if (!memcmp(name, "PCG-", 4) || !memcmp(name, "VGN-", 4)) {
194 dev_info(&client->dev, "Vaio EEPROM detected, "
195 "enabling privacy protection\n");
196 data->nature = VAIO;
197 }
198 }
199
200 /* create the sysfs eeprom file */
201 err = sysfs_create_bin_file(&client->dev.kobj, &eeprom_attr);
202 if (err)
203 goto exit_kfree;
204
205 return 0;
206
207exit_kfree:
208 kfree(data);
209exit:
210 return err;
211}
212
213static int eeprom_remove(struct i2c_client *client)
214{
215 sysfs_remove_bin_file(&client->dev.kobj, &eeprom_attr);
216 kfree(i2c_get_clientdata(client));
217
218 return 0;
219}
220
221static const struct i2c_device_id eeprom_id[] = {
222 { "eeprom", 0 },
223 { }
224};
225
226static struct i2c_driver eeprom_driver = {
227 .driver = {
228 .name = "eeprom",
229 },
230 .probe = eeprom_probe,
231 .remove = eeprom_remove,
232 .id_table = eeprom_id,
233
234 .class = I2C_CLASS_DDC | I2C_CLASS_SPD,
235 .detect = eeprom_detect,
236 .address_data = &addr_data,
237};
238
239static int __init eeprom_init(void)
240{
241 return i2c_add_driver(&eeprom_driver);
242}
243
244static void __exit eeprom_exit(void)
245{
246 i2c_del_driver(&eeprom_driver);
247}
248
249
250MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and "
251 "Philip Edelbrock <phil@netroedge.com> and "
252 "Greg Kroah-Hartman <greg@kroah.com>");
253MODULE_DESCRIPTION("I2C EEPROM driver");
254MODULE_LICENSE("GPL");
255
256module_init(eeprom_init);
257module_exit(eeprom_exit);