aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/Kconfig10
-rw-r--r--drivers/i2c/Makefile1
-rw-r--r--drivers/i2c/i2c-slave-eeprom.c170
3 files changed, 181 insertions, 0 deletions
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index b51a402752c4..8c9e619f3026 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -110,6 +110,16 @@ config I2C_STUB
110 110
111 If you don't know what to do here, definitely say N. 111 If you don't know what to do here, definitely say N.
112 112
113config I2C_SLAVE
114 bool "I2C slave support"
115
116if I2C_SLAVE
117
118config I2C_SLAVE_EEPROM
119 tristate "I2C eeprom slave driver"
120
121endif
122
113config I2C_DEBUG_CORE 123config I2C_DEBUG_CORE
114 bool "I2C Core debugging messages" 124 bool "I2C Core debugging messages"
115 help 125 help
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index 1722f50f2473..45095b3d16a9 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
9obj-$(CONFIG_I2C_MUX) += i2c-mux.o 9obj-$(CONFIG_I2C_MUX) += i2c-mux.o
10obj-y += algos/ busses/ muxes/ 10obj-y += algos/ busses/ muxes/
11obj-$(CONFIG_I2C_STUB) += i2c-stub.o 11obj-$(CONFIG_I2C_STUB) += i2c-stub.o
12obj-$(CONFIG_I2C_SLAVE_EEPROM) += i2c-slave-eeprom.o
12 13
13ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG 14ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG
14CFLAGS_i2c-core.o := -Wno-deprecated-declarations 15CFLAGS_i2c-core.o := -Wno-deprecated-declarations
diff --git a/drivers/i2c/i2c-slave-eeprom.c b/drivers/i2c/i2c-slave-eeprom.c
new file mode 100644
index 000000000000..6631400b5f02
--- /dev/null
+++ b/drivers/i2c/i2c-slave-eeprom.c
@@ -0,0 +1,170 @@
1/*
2 * I2C slave mode EEPROM simulator
3 *
4 * Copyright (C) 2014 by Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>
5 * Copyright (C) 2014 by Renesas Electronics Corporation
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; version 2 of the License.
10 *
11 * Because most IP blocks can only detect one I2C slave address anyhow, this
12 * driver does not support simulating EEPROM types which take more than one
13 * address. It is prepared to simulate bigger EEPROMs with an internal 16 bit
14 * pointer, yet implementation is deferred until the need actually arises.
15 */
16
17#include <linux/i2c.h>
18#include <linux/init.h>
19#include <linux/module.h>
20#include <linux/of.h>
21#include <linux/slab.h>
22#include <linux/spinlock.h>
23#include <linux/sysfs.h>
24
25struct eeprom_data {
26 struct bin_attribute bin;
27 bool first_write;
28 spinlock_t buffer_lock;
29 u8 buffer_idx;
30 u8 buffer[];
31};
32
33static int i2c_slave_eeprom_slave_cb(struct i2c_client *client,
34 enum i2c_slave_event event, u8 *val)
35{
36 struct eeprom_data *eeprom = i2c_get_clientdata(client);
37
38 switch (event) {
39 case I2C_SLAVE_REQ_WRITE_END:
40 if (eeprom->first_write) {
41 eeprom->buffer_idx = *val;
42 eeprom->first_write = false;
43 } else {
44 spin_lock(&eeprom->buffer_lock);
45 eeprom->buffer[eeprom->buffer_idx++] = *val;
46 spin_unlock(&eeprom->buffer_lock);
47 }
48 break;
49
50 case I2C_SLAVE_REQ_READ_START:
51 spin_lock(&eeprom->buffer_lock);
52 *val = eeprom->buffer[eeprom->buffer_idx];
53 spin_unlock(&eeprom->buffer_lock);
54 break;
55
56 case I2C_SLAVE_REQ_READ_END:
57 eeprom->buffer_idx++;
58 break;
59
60 case I2C_SLAVE_STOP:
61 eeprom->first_write = true;
62 break;
63
64 default:
65 break;
66 }
67
68 return 0;
69}
70
71static ssize_t i2c_slave_eeprom_bin_read(struct file *filp, struct kobject *kobj,
72 struct bin_attribute *attr, char *buf, loff_t off, size_t count)
73{
74 struct eeprom_data *eeprom;
75 unsigned long flags;
76
77 if (off + count >= attr->size)
78 return -EFBIG;
79
80 eeprom = dev_get_drvdata(container_of(kobj, struct device, kobj));
81
82 spin_lock_irqsave(&eeprom->buffer_lock, flags);
83 memcpy(buf, &eeprom->buffer[off], count);
84 spin_unlock_irqrestore(&eeprom->buffer_lock, flags);
85
86 return count;
87}
88
89static ssize_t i2c_slave_eeprom_bin_write(struct file *filp, struct kobject *kobj,
90 struct bin_attribute *attr, char *buf, loff_t off, size_t count)
91{
92 struct eeprom_data *eeprom;
93 unsigned long flags;
94
95 if (off + count >= attr->size)
96 return -EFBIG;
97
98 eeprom = dev_get_drvdata(container_of(kobj, struct device, kobj));
99
100 spin_lock_irqsave(&eeprom->buffer_lock, flags);
101 memcpy(&eeprom->buffer[off], buf, count);
102 spin_unlock_irqrestore(&eeprom->buffer_lock, flags);
103
104 return count;
105}
106
107static int i2c_slave_eeprom_probe(struct i2c_client *client, const struct i2c_device_id *id)
108{
109 struct eeprom_data *eeprom;
110 int ret;
111 unsigned size = id->driver_data;
112
113 eeprom = devm_kzalloc(&client->dev, sizeof(struct eeprom_data) + size, GFP_KERNEL);
114 if (!eeprom)
115 return -ENOMEM;
116
117 eeprom->first_write = true;
118 spin_lock_init(&eeprom->buffer_lock);
119 i2c_set_clientdata(client, eeprom);
120
121 sysfs_bin_attr_init(&eeprom->bin);
122 eeprom->bin.attr.name = "slave-eeprom";
123 eeprom->bin.attr.mode = S_IRUSR | S_IWUSR;
124 eeprom->bin.read = i2c_slave_eeprom_bin_read;
125 eeprom->bin.write = i2c_slave_eeprom_bin_write;
126 eeprom->bin.size = size;
127
128 ret = sysfs_create_bin_file(&client->dev.kobj, &eeprom->bin);
129 if (ret)
130 return ret;
131
132 ret = i2c_slave_register(client, i2c_slave_eeprom_slave_cb);
133 if (ret) {
134 sysfs_remove_bin_file(&client->dev.kobj, &eeprom->bin);
135 return ret;
136 }
137
138 return 0;
139};
140
141static int i2c_slave_eeprom_remove(struct i2c_client *client)
142{
143 struct eeprom_data *eeprom = i2c_get_clientdata(client);
144
145 i2c_slave_unregister(client);
146 sysfs_remove_bin_file(&client->dev.kobj, &eeprom->bin);
147
148 return 0;
149}
150
151static const struct i2c_device_id i2c_slave_eeprom_id[] = {
152 { "slave-24c02", 2048 / 8 },
153 { }
154};
155MODULE_DEVICE_TABLE(i2c, i2c_slave_eeprom_id);
156
157static struct i2c_driver i2c_slave_eeprom_driver = {
158 .driver = {
159 .name = "i2c-slave-eeprom",
160 .owner = THIS_MODULE,
161 },
162 .probe = i2c_slave_eeprom_probe,
163 .remove = i2c_slave_eeprom_remove,
164 .id_table = i2c_slave_eeprom_id,
165};
166module_i2c_driver(i2c_slave_eeprom_driver);
167
168MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>");
169MODULE_DESCRIPTION("I2C slave mode EEPROM simulator");
170MODULE_LICENSE("GPL v2");