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