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