]>
Commit | Line | Data |
---|---|---|
6ec1b753 SG |
1 | /* |
2 | * Simulate an I2C eeprom | |
3 | * | |
4 | * Copyright (c) 2014 Google, Inc | |
5 | * | |
6 | * SPDX-License-Identifier: GPL-2.0+ | |
7 | */ | |
8 | ||
9 | #include <common.h> | |
10 | #include <dm.h> | |
38068820 | 11 | #include <errno.h> |
6ec1b753 SG |
12 | #include <i2c.h> |
13 | #include <malloc.h> | |
14 | #include <asm/test.h> | |
15 | ||
16 | #ifdef DEBUG | |
17 | #define debug_buffer print_buffer | |
18 | #else | |
19 | #define debug_buffer(x, ...) | |
20 | #endif | |
21 | ||
22 | DECLARE_GLOBAL_DATA_PTR; | |
23 | ||
24 | struct sandbox_i2c_flash_plat_data { | |
25 | enum sandbox_i2c_eeprom_test_mode test_mode; | |
26 | const char *filename; | |
27 | int offset_len; /* Length of an offset in bytes */ | |
28 | int size; /* Size of data buffer */ | |
29 | }; | |
30 | ||
31 | struct sandbox_i2c_flash { | |
32 | uint8_t *data; | |
33 | }; | |
34 | ||
35 | void sandbox_i2c_eeprom_set_test_mode(struct udevice *dev, | |
36 | enum sandbox_i2c_eeprom_test_mode mode) | |
37 | { | |
38 | struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev); | |
39 | ||
40 | plat->test_mode = mode; | |
41 | } | |
42 | ||
43 | void sandbox_i2c_eeprom_set_offset_len(struct udevice *dev, int offset_len) | |
44 | { | |
45 | struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev); | |
46 | ||
47 | plat->offset_len = offset_len; | |
48 | } | |
49 | ||
50 | static int sandbox_i2c_eeprom_xfer(struct udevice *emul, struct i2c_msg *msg, | |
51 | int nmsgs) | |
52 | { | |
53 | struct sandbox_i2c_flash *priv = dev_get_priv(emul); | |
54 | uint offset = 0; | |
55 | ||
56 | debug("\n%s\n", __func__); | |
57 | debug_buffer(0, priv->data, 1, 16, 0); | |
58 | for (; nmsgs > 0; nmsgs--, msg++) { | |
59 | struct sandbox_i2c_flash_plat_data *plat = | |
60 | dev_get_platdata(emul); | |
61 | int len; | |
62 | u8 *ptr; | |
63 | ||
64 | if (!plat->size) | |
65 | return -ENODEV; | |
66 | if (msg->addr + msg->len > plat->size) { | |
67 | debug("%s: Address %x, len %x is outside range 0..%x\n", | |
68 | __func__, msg->addr, msg->len, plat->size); | |
69 | return -EINVAL; | |
70 | } | |
71 | len = msg->len; | |
72 | debug(" %s: msg->len=%d", | |
73 | msg->flags & I2C_M_RD ? "read" : "write", | |
74 | msg->len); | |
75 | if (msg->flags & I2C_M_RD) { | |
76 | if (plat->test_mode == SIE_TEST_MODE_SINGLE_BYTE) | |
77 | len = 1; | |
78 | debug(", offset %x, len %x: ", offset, len); | |
79 | memcpy(msg->buf, priv->data + offset, len); | |
80 | memset(msg->buf + len, '\xff', msg->len - len); | |
81 | debug_buffer(0, msg->buf, 1, msg->len, 0); | |
82 | } else if (len >= plat->offset_len) { | |
83 | int i; | |
84 | ||
85 | ptr = msg->buf; | |
86 | for (i = 0; i < plat->offset_len; i++, len--) | |
87 | offset = (offset << 8) | *ptr++; | |
88 | debug(", set offset %x: ", offset); | |
89 | debug_buffer(0, msg->buf, 1, msg->len, 0); | |
90 | if (plat->test_mode == SIE_TEST_MODE_SINGLE_BYTE) | |
91 | len = min(len, 1); | |
92 | ||
93 | /* For testing, map offsets into our limited buffer */ | |
94 | for (i = 24; i > 0; i -= 8) { | |
95 | if (offset > (1 << i)) { | |
96 | offset = (offset >> i) | | |
97 | (offset & ((1 << i) - 1)); | |
98 | offset += i; | |
99 | } | |
100 | } | |
101 | memcpy(priv->data + offset, ptr, len); | |
102 | } | |
103 | } | |
104 | debug_buffer(0, priv->data, 1, 16, 0); | |
105 | ||
106 | return 0; | |
107 | } | |
108 | ||
109 | struct dm_i2c_ops sandbox_i2c_emul_ops = { | |
110 | .xfer = sandbox_i2c_eeprom_xfer, | |
111 | }; | |
112 | ||
113 | static int sandbox_i2c_eeprom_ofdata_to_platdata(struct udevice *dev) | |
114 | { | |
115 | struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev); | |
116 | ||
2dd57f5e SG |
117 | plat->size = dev_read_u32_default(dev, "sandbox,size", 32); |
118 | plat->filename = dev_read_string(dev, "sandbox,filename"); | |
6ec1b753 SG |
119 | if (!plat->filename) { |
120 | debug("%s: No filename for device '%s'\n", __func__, | |
121 | dev->name); | |
122 | return -EINVAL; | |
123 | } | |
124 | plat->test_mode = SIE_TEST_MODE_NONE; | |
125 | plat->offset_len = 1; | |
126 | ||
127 | return 0; | |
128 | } | |
129 | ||
130 | static int sandbox_i2c_eeprom_probe(struct udevice *dev) | |
131 | { | |
132 | struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev); | |
133 | struct sandbox_i2c_flash *priv = dev_get_priv(dev); | |
134 | ||
135 | priv->data = calloc(1, plat->size); | |
136 | if (!priv->data) | |
137 | return -ENOMEM; | |
138 | ||
139 | return 0; | |
140 | } | |
141 | ||
142 | static int sandbox_i2c_eeprom_remove(struct udevice *dev) | |
143 | { | |
144 | struct sandbox_i2c_flash *priv = dev_get_priv(dev); | |
145 | ||
146 | free(priv->data); | |
147 | ||
148 | return 0; | |
149 | } | |
150 | ||
151 | static const struct udevice_id sandbox_i2c_ids[] = { | |
152 | { .compatible = "sandbox,i2c-eeprom" }, | |
153 | { } | |
154 | }; | |
155 | ||
156 | U_BOOT_DRIVER(sandbox_i2c_emul) = { | |
157 | .name = "sandbox_i2c_eeprom_emul", | |
158 | .id = UCLASS_I2C_EMUL, | |
159 | .of_match = sandbox_i2c_ids, | |
160 | .ofdata_to_platdata = sandbox_i2c_eeprom_ofdata_to_platdata, | |
161 | .probe = sandbox_i2c_eeprom_probe, | |
162 | .remove = sandbox_i2c_eeprom_remove, | |
163 | .priv_auto_alloc_size = sizeof(struct sandbox_i2c_flash), | |
164 | .platdata_auto_alloc_size = sizeof(struct sandbox_i2c_flash_plat_data), | |
165 | .ops = &sandbox_i2c_emul_ops, | |
166 | }; |