]>
Commit | Line | Data |
---|---|---|
7f398627 JCD |
1 | /* |
2 | * QTest i.MX I2C driver | |
3 | * | |
4 | * Copyright (c) 2013 Jean-Christophe Dubois | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
14 | * for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License along | |
17 | * with this program; if not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
681c28a3 | 20 | #include "qemu/osdep.h" |
7f398627 JCD |
21 | #include "libqos/i2c.h" |
22 | ||
7f398627 | 23 | |
7f398627 JCD |
24 | #include "libqtest.h" |
25 | ||
26 | #include "hw/i2c/imx_i2c.h" | |
27 | ||
28 | enum IMXI2CDirection { | |
29 | IMX_I2C_READ, | |
30 | IMX_I2C_WRITE, | |
31 | }; | |
32 | ||
7f398627 JCD |
33 | static void imx_i2c_set_slave_addr(IMXI2C *s, uint8_t addr, |
34 | enum IMXI2CDirection direction) | |
35 | { | |
f1dfd507 EB |
36 | qtest_writeb(s->parent.qts, s->addr + I2DR_ADDR, |
37 | (addr << 1) | (direction == IMX_I2C_READ ? 1 : 0)); | |
7f398627 JCD |
38 | } |
39 | ||
40 | static void imx_i2c_send(I2CAdapter *i2c, uint8_t addr, | |
41 | const uint8_t *buf, uint16_t len) | |
42 | { | |
732c919c | 43 | IMXI2C *s = container_of(i2c, IMXI2C, parent); |
7f398627 JCD |
44 | uint8_t data; |
45 | uint8_t status; | |
46 | uint16_t size = 0; | |
47 | ||
48 | if (!len) { | |
49 | return; | |
50 | } | |
51 | ||
52 | /* set the bus for write */ | |
53 | data = I2CR_IEN | | |
54 | I2CR_IIEN | | |
55 | I2CR_MSTA | | |
56 | I2CR_MTX | | |
57 | I2CR_TXAK; | |
58 | ||
f1dfd507 EB |
59 | qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data); |
60 | status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); | |
7f398627 JCD |
61 | g_assert((status & I2SR_IBB) != 0); |
62 | ||
63 | /* set the slave address */ | |
64 | imx_i2c_set_slave_addr(s, addr, IMX_I2C_WRITE); | |
f1dfd507 | 65 | status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); |
7f398627 JCD |
66 | g_assert((status & I2SR_IIF) != 0); |
67 | g_assert((status & I2SR_RXAK) == 0); | |
68 | ||
69 | /* ack the interrupt */ | |
f1dfd507 EB |
70 | qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0); |
71 | status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); | |
7f398627 JCD |
72 | g_assert((status & I2SR_IIF) == 0); |
73 | ||
74 | while (size < len) { | |
75 | /* check we are still busy */ | |
f1dfd507 | 76 | status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); |
7f398627 JCD |
77 | g_assert((status & I2SR_IBB) != 0); |
78 | ||
79 | /* write the data */ | |
f1dfd507 EB |
80 | qtest_writeb(i2c->qts, s->addr + I2DR_ADDR, buf[size]); |
81 | status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); | |
7f398627 JCD |
82 | g_assert((status & I2SR_IIF) != 0); |
83 | g_assert((status & I2SR_RXAK) == 0); | |
84 | ||
85 | /* ack the interrupt */ | |
f1dfd507 EB |
86 | qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0); |
87 | status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); | |
7f398627 JCD |
88 | g_assert((status & I2SR_IIF) == 0); |
89 | ||
90 | size++; | |
91 | } | |
92 | ||
93 | /* release the bus */ | |
94 | data &= ~(I2CR_MSTA | I2CR_MTX); | |
f1dfd507 EB |
95 | qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data); |
96 | status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); | |
7f398627 JCD |
97 | g_assert((status & I2SR_IBB) == 0); |
98 | } | |
99 | ||
100 | static void imx_i2c_recv(I2CAdapter *i2c, uint8_t addr, | |
101 | uint8_t *buf, uint16_t len) | |
102 | { | |
732c919c | 103 | IMXI2C *s = container_of(i2c, IMXI2C, parent); |
7f398627 JCD |
104 | uint8_t data; |
105 | uint8_t status; | |
106 | uint16_t size = 0; | |
107 | ||
108 | if (!len) { | |
109 | return; | |
110 | } | |
111 | ||
112 | /* set the bus for write */ | |
113 | data = I2CR_IEN | | |
114 | I2CR_IIEN | | |
115 | I2CR_MSTA | | |
116 | I2CR_MTX | | |
117 | I2CR_TXAK; | |
118 | ||
f1dfd507 EB |
119 | qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data); |
120 | status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); | |
7f398627 JCD |
121 | g_assert((status & I2SR_IBB) != 0); |
122 | ||
123 | /* set the slave address */ | |
124 | imx_i2c_set_slave_addr(s, addr, IMX_I2C_READ); | |
f1dfd507 | 125 | status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); |
7f398627 JCD |
126 | g_assert((status & I2SR_IIF) != 0); |
127 | g_assert((status & I2SR_RXAK) == 0); | |
128 | ||
129 | /* ack the interrupt */ | |
f1dfd507 EB |
130 | qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0); |
131 | status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); | |
7f398627 JCD |
132 | g_assert((status & I2SR_IIF) == 0); |
133 | ||
134 | /* set the bus for read */ | |
135 | data &= ~I2CR_MTX; | |
136 | /* if only one byte don't ack */ | |
137 | if (len != 1) { | |
138 | data &= ~I2CR_TXAK; | |
139 | } | |
f1dfd507 EB |
140 | qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data); |
141 | status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); | |
7f398627 JCD |
142 | g_assert((status & I2SR_IBB) != 0); |
143 | ||
144 | /* dummy read */ | |
f1dfd507 EB |
145 | qtest_readb(i2c->qts, s->addr + I2DR_ADDR); |
146 | status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); | |
7f398627 JCD |
147 | g_assert((status & I2SR_IIF) != 0); |
148 | ||
149 | /* ack the interrupt */ | |
f1dfd507 EB |
150 | qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0); |
151 | status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); | |
7f398627 JCD |
152 | g_assert((status & I2SR_IIF) == 0); |
153 | ||
154 | while (size < len) { | |
155 | /* check we are still busy */ | |
f1dfd507 | 156 | status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); |
7f398627 JCD |
157 | g_assert((status & I2SR_IBB) != 0); |
158 | ||
159 | if (size == (len - 1)) { | |
160 | /* stop the read transaction */ | |
161 | data &= ~(I2CR_MSTA | I2CR_MTX); | |
162 | } else { | |
163 | /* ack the data read */ | |
164 | data |= I2CR_TXAK; | |
165 | } | |
f1dfd507 | 166 | qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data); |
7f398627 JCD |
167 | |
168 | /* read the data */ | |
f1dfd507 | 169 | buf[size] = qtest_readb(i2c->qts, s->addr + I2DR_ADDR); |
7f398627 JCD |
170 | |
171 | if (size != (len - 1)) { | |
f1dfd507 | 172 | status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); |
7f398627 JCD |
173 | g_assert((status & I2SR_IIF) != 0); |
174 | ||
175 | /* ack the interrupt */ | |
f1dfd507 | 176 | qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0); |
7f398627 JCD |
177 | } |
178 | ||
f1dfd507 | 179 | status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); |
7f398627 JCD |
180 | g_assert((status & I2SR_IIF) == 0); |
181 | ||
182 | size++; | |
183 | } | |
184 | ||
f1dfd507 | 185 | status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR); |
7f398627 JCD |
186 | g_assert((status & I2SR_IBB) == 0); |
187 | } | |
188 | ||
c0825c63 PB |
189 | static void *imx_i2c_get_driver(void *obj, const char *interface) |
190 | { | |
191 | IMXI2C *s = obj; | |
192 | if (!g_strcmp0(interface, "i2c-bus")) { | |
193 | return &s->parent; | |
194 | } | |
195 | fprintf(stderr, "%s not present in imx-i2c\n", interface); | |
196 | g_assert_not_reached(); | |
197 | } | |
198 | ||
732c919c PB |
199 | void imx_i2c_init(IMXI2C *s, QTestState *qts, uint64_t addr) |
200 | { | |
201 | s->addr = addr; | |
202 | ||
c0825c63 PB |
203 | s->obj.get_driver = imx_i2c_get_driver; |
204 | ||
732c919c PB |
205 | s->parent.send = imx_i2c_send; |
206 | s->parent.recv = imx_i2c_recv; | |
207 | s->parent.qts = qts; | |
208 | } | |
209 | ||
c0825c63 PB |
210 | static void imx_i2c_register_nodes(void) |
211 | { | |
212 | qos_node_create_driver("imx.i2c", NULL); | |
213 | qos_node_produces("imx.i2c", "i2c-bus"); | |
214 | } | |
215 | ||
216 | libqos_init(imx_i2c_register_nodes); |