diff options
Diffstat (limited to 'drivers/i2c/i2c-slave-eeprom.c')
-rw-r--r-- | drivers/i2c/i2c-slave-eeprom.c | 42 |
1 files changed, 31 insertions, 11 deletions
diff --git a/drivers/i2c/i2c-slave-eeprom.c b/drivers/i2c/i2c-slave-eeprom.c index be65d3842878..92ff9991bae8 100644 --- a/drivers/i2c/i2c-slave-eeprom.c +++ b/drivers/i2c/i2c-slave-eeprom.c | |||
@@ -11,6 +11,13 @@ | |||
11 | * pointer, yet implementation is deferred until the need actually arises. | 11 | * pointer, yet implementation is deferred until the need actually arises. |
12 | */ | 12 | */ |
13 | 13 | ||
14 | /* | ||
15 | * FIXME: What to do if only 8 bits of a 16 bit address are sent? | ||
16 | * The ST-M24C64 sends only 0xff then. Needs verification with other | ||
17 | * EEPROMs, though. We currently use the 8 bit as a valid address. | ||
18 | */ | ||
19 | |||
20 | #include <linux/bitfield.h> | ||
14 | #include <linux/i2c.h> | 21 | #include <linux/i2c.h> |
15 | #include <linux/init.h> | 22 | #include <linux/init.h> |
16 | #include <linux/module.h> | 23 | #include <linux/module.h> |
@@ -21,12 +28,18 @@ | |||
21 | 28 | ||
22 | struct eeprom_data { | 29 | struct eeprom_data { |
23 | struct bin_attribute bin; | 30 | struct bin_attribute bin; |
24 | bool first_write; | ||
25 | spinlock_t buffer_lock; | 31 | spinlock_t buffer_lock; |
26 | u8 buffer_idx; | 32 | u16 buffer_idx; |
33 | u16 address_mask; | ||
34 | u8 num_address_bytes; | ||
35 | u8 idx_write_cnt; | ||
27 | u8 buffer[]; | 36 | u8 buffer[]; |
28 | }; | 37 | }; |
29 | 38 | ||
39 | #define I2C_SLAVE_BYTELEN GENMASK(15, 0) | ||
40 | #define I2C_SLAVE_FLAG_ADDR16 BIT(16) | ||
41 | #define I2C_SLAVE_DEVICE_MAGIC(_len, _flags) ((_flags) | (_len)) | ||
42 | |||
30 | static int i2c_slave_eeprom_slave_cb(struct i2c_client *client, | 43 | static int i2c_slave_eeprom_slave_cb(struct i2c_client *client, |
31 | enum i2c_slave_event event, u8 *val) | 44 | enum i2c_slave_event event, u8 *val) |
32 | { | 45 | { |
@@ -34,12 +47,14 @@ static int i2c_slave_eeprom_slave_cb(struct i2c_client *client, | |||
34 | 47 | ||
35 | switch (event) { | 48 | switch (event) { |
36 | case I2C_SLAVE_WRITE_RECEIVED: | 49 | case I2C_SLAVE_WRITE_RECEIVED: |
37 | if (eeprom->first_write) { | 50 | if (eeprom->idx_write_cnt < eeprom->num_address_bytes) { |
38 | eeprom->buffer_idx = *val; | 51 | if (eeprom->idx_write_cnt == 0) |
39 | eeprom->first_write = false; | 52 | eeprom->buffer_idx = 0; |
53 | eeprom->buffer_idx = *val | (eeprom->buffer_idx << 8); | ||
54 | eeprom->idx_write_cnt++; | ||
40 | } else { | 55 | } else { |
41 | spin_lock(&eeprom->buffer_lock); | 56 | spin_lock(&eeprom->buffer_lock); |
42 | eeprom->buffer[eeprom->buffer_idx++] = *val; | 57 | eeprom->buffer[eeprom->buffer_idx++ & eeprom->address_mask] = *val; |
43 | spin_unlock(&eeprom->buffer_lock); | 58 | spin_unlock(&eeprom->buffer_lock); |
44 | } | 59 | } |
45 | break; | 60 | break; |
@@ -50,7 +65,7 @@ static int i2c_slave_eeprom_slave_cb(struct i2c_client *client, | |||
50 | /* fallthrough */ | 65 | /* fallthrough */ |
51 | case I2C_SLAVE_READ_REQUESTED: | 66 | case I2C_SLAVE_READ_REQUESTED: |
52 | spin_lock(&eeprom->buffer_lock); | 67 | spin_lock(&eeprom->buffer_lock); |
53 | *val = eeprom->buffer[eeprom->buffer_idx]; | 68 | *val = eeprom->buffer[eeprom->buffer_idx & eeprom->address_mask]; |
54 | spin_unlock(&eeprom->buffer_lock); | 69 | spin_unlock(&eeprom->buffer_lock); |
55 | /* | 70 | /* |
56 | * Do not increment buffer_idx here, because we don't know if | 71 | * Do not increment buffer_idx here, because we don't know if |
@@ -61,7 +76,7 @@ static int i2c_slave_eeprom_slave_cb(struct i2c_client *client, | |||
61 | 76 | ||
62 | case I2C_SLAVE_STOP: | 77 | case I2C_SLAVE_STOP: |
63 | case I2C_SLAVE_WRITE_REQUESTED: | 78 | case I2C_SLAVE_WRITE_REQUESTED: |
64 | eeprom->first_write = true; | 79 | eeprom->idx_write_cnt = 0; |
65 | break; | 80 | break; |
66 | 81 | ||
67 | default: | 82 | default: |
@@ -105,13 +120,16 @@ static int i2c_slave_eeprom_probe(struct i2c_client *client, const struct i2c_de | |||
105 | { | 120 | { |
106 | struct eeprom_data *eeprom; | 121 | struct eeprom_data *eeprom; |
107 | int ret; | 122 | int ret; |
108 | unsigned size = id->driver_data; | 123 | unsigned int size = FIELD_GET(I2C_SLAVE_BYTELEN, id->driver_data); |
124 | unsigned int flag_addr16 = FIELD_GET(I2C_SLAVE_FLAG_ADDR16, id->driver_data); | ||
109 | 125 | ||
110 | eeprom = devm_kzalloc(&client->dev, sizeof(struct eeprom_data) + size, GFP_KERNEL); | 126 | eeprom = devm_kzalloc(&client->dev, sizeof(struct eeprom_data) + size, GFP_KERNEL); |
111 | if (!eeprom) | 127 | if (!eeprom) |
112 | return -ENOMEM; | 128 | return -ENOMEM; |
113 | 129 | ||
114 | eeprom->first_write = true; | 130 | eeprom->idx_write_cnt = 0; |
131 | eeprom->num_address_bytes = flag_addr16 ? 2 : 1; | ||
132 | eeprom->address_mask = size - 1; | ||
115 | spin_lock_init(&eeprom->buffer_lock); | 133 | spin_lock_init(&eeprom->buffer_lock); |
116 | i2c_set_clientdata(client, eeprom); | 134 | i2c_set_clientdata(client, eeprom); |
117 | 135 | ||
@@ -146,7 +164,9 @@ static int i2c_slave_eeprom_remove(struct i2c_client *client) | |||
146 | } | 164 | } |
147 | 165 | ||
148 | static const struct i2c_device_id i2c_slave_eeprom_id[] = { | 166 | static const struct i2c_device_id i2c_slave_eeprom_id[] = { |
149 | { "slave-24c02", 2048 / 8 }, | 167 | { "slave-24c02", I2C_SLAVE_DEVICE_MAGIC(2048 / 8, 0) }, |
168 | { "slave-24c32", I2C_SLAVE_DEVICE_MAGIC(32768 / 8, I2C_SLAVE_FLAG_ADDR16) }, | ||
169 | { "slave-24c64", I2C_SLAVE_DEVICE_MAGIC(65536 / 8, I2C_SLAVE_FLAG_ADDR16) }, | ||
150 | { } | 170 | { } |
151 | }; | 171 | }; |
152 | MODULE_DEVICE_TABLE(i2c, i2c_slave_eeprom_id); | 172 | MODULE_DEVICE_TABLE(i2c, i2c_slave_eeprom_id); |