Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/misc/eeprom/eeprom.c
15109 views
1
/*
2
* Copyright (C) 1998, 1999 Frodo Looijaard <[email protected]> and
3
* Philip Edelbrock <[email protected]>
4
* Copyright (C) 2003 Greg Kroah-Hartman <[email protected]>
5
* Copyright (C) 2003 IBM Corp.
6
* Copyright (C) 2004 Jean Delvare <[email protected]>
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
19
#include <linux/kernel.h>
20
#include <linux/init.h>
21
#include <linux/module.h>
22
#include <linux/slab.h>
23
#include <linux/jiffies.h>
24
#include <linux/i2c.h>
25
#include <linux/mutex.h>
26
27
/* Addresses to scan */
28
static const unsigned short normal_i2c[] = { 0x50, 0x51, 0x52, 0x53, 0x54,
29
0x55, 0x56, 0x57, I2C_CLIENT_END };
30
31
32
/* Size of EEPROM in bytes */
33
#define EEPROM_SIZE 256
34
35
/* possible types of eeprom devices */
36
enum eeprom_nature {
37
UNKNOWN,
38
VAIO,
39
};
40
41
/* Each client has this additional data */
42
struct eeprom_data {
43
struct mutex update_lock;
44
u8 valid; /* bitfield, bit!=0 if slice is valid */
45
unsigned long last_updated[8]; /* In jiffies, 8 slices */
46
u8 data[EEPROM_SIZE]; /* Register values */
47
enum eeprom_nature nature;
48
};
49
50
51
static void eeprom_update_client(struct i2c_client *client, u8 slice)
52
{
53
struct eeprom_data *data = i2c_get_clientdata(client);
54
int i;
55
56
mutex_lock(&data->update_lock);
57
58
if (!(data->valid & (1 << slice)) ||
59
time_after(jiffies, data->last_updated[slice] + 300 * HZ)) {
60
dev_dbg(&client->dev, "Starting eeprom update, slice %u\n", slice);
61
62
if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
63
for (i = slice << 5; i < (slice + 1) << 5; i += 32)
64
if (i2c_smbus_read_i2c_block_data(client, i,
65
32, data->data + i)
66
!= 32)
67
goto exit;
68
} else {
69
for (i = slice << 5; i < (slice + 1) << 5; i += 2) {
70
int word = i2c_smbus_read_word_data(client, i);
71
if (word < 0)
72
goto exit;
73
data->data[i] = word & 0xff;
74
data->data[i + 1] = word >> 8;
75
}
76
}
77
data->last_updated[slice] = jiffies;
78
data->valid |= (1 << slice);
79
}
80
exit:
81
mutex_unlock(&data->update_lock);
82
}
83
84
static ssize_t eeprom_read(struct file *filp, struct kobject *kobj,
85
struct bin_attribute *bin_attr,
86
char *buf, loff_t off, size_t count)
87
{
88
struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj));
89
struct eeprom_data *data = i2c_get_clientdata(client);
90
u8 slice;
91
92
if (off > EEPROM_SIZE)
93
return 0;
94
if (off + count > EEPROM_SIZE)
95
count = EEPROM_SIZE - off;
96
97
/* Only refresh slices which contain requested bytes */
98
for (slice = off >> 5; slice <= (off + count - 1) >> 5; slice++)
99
eeprom_update_client(client, slice);
100
101
/* Hide Vaio private settings to regular users:
102
- BIOS passwords: bytes 0x00 to 0x0f
103
- UUID: bytes 0x10 to 0x1f
104
- Serial number: 0xc0 to 0xdf */
105
if (data->nature == VAIO && !capable(CAP_SYS_ADMIN)) {
106
int i;
107
108
for (i = 0; i < count; i++) {
109
if ((off + i <= 0x1f) ||
110
(off + i >= 0xc0 && off + i <= 0xdf))
111
buf[i] = 0;
112
else
113
buf[i] = data->data[off + i];
114
}
115
} else {
116
memcpy(buf, &data->data[off], count);
117
}
118
119
return count;
120
}
121
122
static struct bin_attribute eeprom_attr = {
123
.attr = {
124
.name = "eeprom",
125
.mode = S_IRUGO,
126
},
127
.size = EEPROM_SIZE,
128
.read = eeprom_read,
129
};
130
131
/* Return 0 if detection is successful, -ENODEV otherwise */
132
static int eeprom_detect(struct i2c_client *client, struct i2c_board_info *info)
133
{
134
struct i2c_adapter *adapter = client->adapter;
135
136
/* EDID EEPROMs are often 24C00 EEPROMs, which answer to all
137
addresses 0x50-0x57, but we only care about 0x50. So decline
138
attaching to addresses >= 0x51 on DDC buses */
139
if (!(adapter->class & I2C_CLASS_SPD) && client->addr >= 0x51)
140
return -ENODEV;
141
142
/* There are four ways we can read the EEPROM data:
143
(1) I2C block reads (faster, but unsupported by most adapters)
144
(2) Word reads (128% overhead)
145
(3) Consecutive byte reads (88% overhead, unsafe)
146
(4) Regular byte data reads (265% overhead)
147
The third and fourth methods are not implemented by this driver
148
because all known adapters support one of the first two. */
149
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)
150
&& !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK))
151
return -ENODEV;
152
153
strlcpy(info->type, "eeprom", I2C_NAME_SIZE);
154
155
return 0;
156
}
157
158
static int eeprom_probe(struct i2c_client *client,
159
const struct i2c_device_id *id)
160
{
161
struct i2c_adapter *adapter = client->adapter;
162
struct eeprom_data *data;
163
int err;
164
165
if (!(data = kzalloc(sizeof(struct eeprom_data), GFP_KERNEL))) {
166
err = -ENOMEM;
167
goto exit;
168
}
169
170
memset(data->data, 0xff, EEPROM_SIZE);
171
i2c_set_clientdata(client, data);
172
mutex_init(&data->update_lock);
173
data->nature = UNKNOWN;
174
175
/* Detect the Vaio nature of EEPROMs.
176
We use the "PCG-" or "VGN-" prefix as the signature. */
177
if (client->addr == 0x57
178
&& i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
179
char name[4];
180
181
name[0] = i2c_smbus_read_byte_data(client, 0x80);
182
name[1] = i2c_smbus_read_byte_data(client, 0x81);
183
name[2] = i2c_smbus_read_byte_data(client, 0x82);
184
name[3] = i2c_smbus_read_byte_data(client, 0x83);
185
186
if (!memcmp(name, "PCG-", 4) || !memcmp(name, "VGN-", 4)) {
187
dev_info(&client->dev, "Vaio EEPROM detected, "
188
"enabling privacy protection\n");
189
data->nature = VAIO;
190
}
191
}
192
193
/* create the sysfs eeprom file */
194
err = sysfs_create_bin_file(&client->dev.kobj, &eeprom_attr);
195
if (err)
196
goto exit_kfree;
197
198
return 0;
199
200
exit_kfree:
201
kfree(data);
202
exit:
203
return err;
204
}
205
206
static int eeprom_remove(struct i2c_client *client)
207
{
208
sysfs_remove_bin_file(&client->dev.kobj, &eeprom_attr);
209
kfree(i2c_get_clientdata(client));
210
211
return 0;
212
}
213
214
static const struct i2c_device_id eeprom_id[] = {
215
{ "eeprom", 0 },
216
{ }
217
};
218
219
static struct i2c_driver eeprom_driver = {
220
.driver = {
221
.name = "eeprom",
222
},
223
.probe = eeprom_probe,
224
.remove = eeprom_remove,
225
.id_table = eeprom_id,
226
227
.class = I2C_CLASS_DDC | I2C_CLASS_SPD,
228
.detect = eeprom_detect,
229
.address_list = normal_i2c,
230
};
231
232
static int __init eeprom_init(void)
233
{
234
return i2c_add_driver(&eeprom_driver);
235
}
236
237
static void __exit eeprom_exit(void)
238
{
239
i2c_del_driver(&eeprom_driver);
240
}
241
242
243
MODULE_AUTHOR("Frodo Looijaard <[email protected]> and "
244
"Philip Edelbrock <[email protected]> and "
245
"Greg Kroah-Hartman <[email protected]>");
246
MODULE_DESCRIPTION("I2C EEPROM driver");
247
MODULE_LICENSE("GPL");
248
249
module_init(eeprom_init);
250
module_exit(eeprom_exit);
251
252