1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#include "libqos/i2c.h"
21
22#include <glib.h>
23#include <string.h>
24
25#include "qemu/osdep.h"
26#include "libqtest.h"
27
28#include "hw/i2c/imx_i2c.h"
29
30enum IMXI2CDirection {
31 IMX_I2C_READ,
32 IMX_I2C_WRITE,
33};
34
35typedef struct IMXI2C {
36 I2CAdapter parent;
37
38 uint64_t addr;
39} IMXI2C;
40
41
42static void imx_i2c_set_slave_addr(IMXI2C *s, uint8_t addr,
43 enum IMXI2CDirection direction)
44{
45 writeb(s->addr + I2DR_ADDR, (addr << 1) |
46 (direction == IMX_I2C_READ ? 1 : 0));
47}
48
49static void imx_i2c_send(I2CAdapter *i2c, uint8_t addr,
50 const uint8_t *buf, uint16_t len)
51{
52 IMXI2C *s = (IMXI2C *)i2c;
53 uint8_t data;
54 uint8_t status;
55 uint16_t size = 0;
56
57 if (!len) {
58 return;
59 }
60
61
62 data = I2CR_IEN |
63 I2CR_IIEN |
64 I2CR_MSTA |
65 I2CR_MTX |
66 I2CR_TXAK;
67
68 writeb(s->addr + I2CR_ADDR, data);
69 status = readb(s->addr + I2SR_ADDR);
70 g_assert((status & I2SR_IBB) != 0);
71
72
73 imx_i2c_set_slave_addr(s, addr, IMX_I2C_WRITE);
74 status = readb(s->addr + I2SR_ADDR);
75 g_assert((status & I2SR_IIF) != 0);
76 g_assert((status & I2SR_RXAK) == 0);
77
78
79 writeb(s->addr + I2SR_ADDR, 0);
80 status = readb(s->addr + I2SR_ADDR);
81 g_assert((status & I2SR_IIF) == 0);
82
83 while (size < len) {
84
85 status = readb(s->addr + I2SR_ADDR);
86 g_assert((status & I2SR_IBB) != 0);
87
88
89 writeb(s->addr + I2DR_ADDR, buf[size]);
90 status = readb(s->addr + I2SR_ADDR);
91 g_assert((status & I2SR_IIF) != 0);
92 g_assert((status & I2SR_RXAK) == 0);
93
94
95 writeb(s->addr + I2SR_ADDR, 0);
96 status = readb(s->addr + I2SR_ADDR);
97 g_assert((status & I2SR_IIF) == 0);
98
99 size++;
100 }
101
102
103 data &= ~(I2CR_MSTA | I2CR_MTX);
104 writeb(s->addr + I2CR_ADDR, data);
105 status = readb(s->addr + I2SR_ADDR);
106 g_assert((status & I2SR_IBB) == 0);
107}
108
109static void imx_i2c_recv(I2CAdapter *i2c, uint8_t addr,
110 uint8_t *buf, uint16_t len)
111{
112 IMXI2C *s = (IMXI2C *)i2c;
113 uint8_t data;
114 uint8_t status;
115 uint16_t size = 0;
116
117 if (!len) {
118 return;
119 }
120
121
122 data = I2CR_IEN |
123 I2CR_IIEN |
124 I2CR_MSTA |
125 I2CR_MTX |
126 I2CR_TXAK;
127
128 writeb(s->addr + I2CR_ADDR, data);
129 status = readb(s->addr + I2SR_ADDR);
130 g_assert((status & I2SR_IBB) != 0);
131
132
133 imx_i2c_set_slave_addr(s, addr, IMX_I2C_READ);
134 status = readb(s->addr + I2SR_ADDR);
135 g_assert((status & I2SR_IIF) != 0);
136 g_assert((status & I2SR_RXAK) == 0);
137
138
139 writeb(s->addr + I2SR_ADDR, 0);
140 status = readb(s->addr + I2SR_ADDR);
141 g_assert((status & I2SR_IIF) == 0);
142
143
144 data &= ~I2CR_MTX;
145
146 if (len != 1) {
147 data &= ~I2CR_TXAK;
148 }
149 writeb(s->addr + I2CR_ADDR, data);
150 status = readb(s->addr + I2SR_ADDR);
151 g_assert((status & I2SR_IBB) != 0);
152
153
154 readb(s->addr + I2DR_ADDR);
155 status = readb(s->addr + I2SR_ADDR);
156 g_assert((status & I2SR_IIF) != 0);
157
158
159 writeb(s->addr + I2SR_ADDR, 0);
160 status = readb(s->addr + I2SR_ADDR);
161 g_assert((status & I2SR_IIF) == 0);
162
163 while (size < len) {
164
165 status = readb(s->addr + I2SR_ADDR);
166 g_assert((status & I2SR_IBB) != 0);
167
168 if (size == (len - 1)) {
169
170 data &= ~(I2CR_MSTA | I2CR_MTX);
171 } else {
172
173 data |= I2CR_TXAK;
174 }
175 writeb(s->addr + I2CR_ADDR, data);
176
177
178 buf[size] = readb(s->addr + I2DR_ADDR);
179
180 if (size != (len - 1)) {
181 status = readb(s->addr + I2SR_ADDR);
182 g_assert((status & I2SR_IIF) != 0);
183
184
185 writeb(s->addr + I2SR_ADDR, 0);
186 }
187
188 status = readb(s->addr + I2SR_ADDR);
189 g_assert((status & I2SR_IIF) == 0);
190
191 size++;
192 }
193
194 status = readb(s->addr + I2SR_ADDR);
195 g_assert((status & I2SR_IBB) == 0);
196}
197
198I2CAdapter *imx_i2c_create(uint64_t addr)
199{
200 IMXI2C *s = g_malloc0(sizeof(*s));
201 I2CAdapter *i2c = (I2CAdapter *)s;
202
203 s->addr = addr;
204
205 i2c->send = imx_i2c_send;
206 i2c->recv = imx_i2c_recv;
207
208 return i2c;
209}
210