xref: /qemu/tests/qtest/libqos/i2c-imx.c (revision bbaf7a0d4c9f653edd085c06bc2a343e356d9b6a)
17f398627SJean-Christophe Dubois /*
27f398627SJean-Christophe Dubois  * QTest i.MX I2C driver
37f398627SJean-Christophe Dubois  *
47f398627SJean-Christophe Dubois  * Copyright (c) 2013 Jean-Christophe Dubois
57f398627SJean-Christophe Dubois  *
67f398627SJean-Christophe Dubois  *  This program is free software; you can redistribute it and/or modify it
77f398627SJean-Christophe Dubois  *  under the terms of the GNU General Public License as published by the
87f398627SJean-Christophe Dubois  *  Free Software Foundation; either version 2 of the License, or
97f398627SJean-Christophe Dubois  *  (at your option) any later version.
107f398627SJean-Christophe Dubois  *
117f398627SJean-Christophe Dubois  *  This program is distributed in the hope that it will be useful, but WITHOUT
127f398627SJean-Christophe Dubois  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
137f398627SJean-Christophe Dubois  *  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
147f398627SJean-Christophe Dubois  *  for more details.
157f398627SJean-Christophe Dubois  *
167f398627SJean-Christophe Dubois  *  You should have received a copy of the GNU General Public License along
177f398627SJean-Christophe Dubois  *  with this program; if not, see <http://www.gnu.org/licenses/>.
187f398627SJean-Christophe Dubois  */
197f398627SJean-Christophe Dubois 
20681c28a3SPeter Maydell #include "qemu/osdep.h"
21a2ce7dbdSPaolo Bonzini #include "i2c.h"
227f398627SJean-Christophe Dubois 
237f398627SJean-Christophe Dubois 
24907b5105SMarc-André Lureau #include "../libqtest.h"
257f398627SJean-Christophe Dubois 
267f398627SJean-Christophe Dubois #include "hw/i2c/imx_i2c.h"
277f398627SJean-Christophe Dubois 
287f398627SJean-Christophe Dubois enum IMXI2CDirection {
297f398627SJean-Christophe Dubois     IMX_I2C_READ,
307f398627SJean-Christophe Dubois     IMX_I2C_WRITE,
317f398627SJean-Christophe Dubois };
327f398627SJean-Christophe Dubois 
imx_i2c_set_slave_addr(IMXI2C * s,uint8_t addr,enum IMXI2CDirection direction)337f398627SJean-Christophe Dubois static void imx_i2c_set_slave_addr(IMXI2C *s, uint8_t addr,
347f398627SJean-Christophe Dubois                                    enum IMXI2CDirection direction)
357f398627SJean-Christophe Dubois {
36f1dfd507SEric Blake     qtest_writeb(s->parent.qts, s->addr + I2DR_ADDR,
37f1dfd507SEric Blake                  (addr << 1) | (direction == IMX_I2C_READ ? 1 : 0));
387f398627SJean-Christophe Dubois }
397f398627SJean-Christophe Dubois 
imx_i2c_send(I2CAdapter * i2c,uint8_t addr,const uint8_t * buf,uint16_t len)407f398627SJean-Christophe Dubois static void imx_i2c_send(I2CAdapter *i2c, uint8_t addr,
417f398627SJean-Christophe Dubois                          const uint8_t *buf, uint16_t len)
427f398627SJean-Christophe Dubois {
43732c919cSPaolo Bonzini     IMXI2C *s = container_of(i2c, IMXI2C, parent);
447f398627SJean-Christophe Dubois     uint8_t data;
457f398627SJean-Christophe Dubois     uint8_t status;
467f398627SJean-Christophe Dubois     uint16_t size = 0;
477f398627SJean-Christophe Dubois 
487f398627SJean-Christophe Dubois     if (!len) {
497f398627SJean-Christophe Dubois         return;
507f398627SJean-Christophe Dubois     }
517f398627SJean-Christophe Dubois 
527f398627SJean-Christophe Dubois     /* set the bus for write */
537f398627SJean-Christophe Dubois     data = I2CR_IEN |
547f398627SJean-Christophe Dubois            I2CR_IIEN |
557f398627SJean-Christophe Dubois            I2CR_MSTA |
567f398627SJean-Christophe Dubois            I2CR_MTX |
577f398627SJean-Christophe Dubois            I2CR_TXAK;
587f398627SJean-Christophe Dubois 
59f1dfd507SEric Blake     qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data);
60f1dfd507SEric Blake     status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
617f398627SJean-Christophe Dubois     g_assert((status & I2SR_IBB) != 0);
627f398627SJean-Christophe Dubois 
637f398627SJean-Christophe Dubois     /* set the slave address */
647f398627SJean-Christophe Dubois     imx_i2c_set_slave_addr(s, addr, IMX_I2C_WRITE);
65f1dfd507SEric Blake     status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
667f398627SJean-Christophe Dubois     g_assert((status & I2SR_IIF) != 0);
677f398627SJean-Christophe Dubois     g_assert((status & I2SR_RXAK) == 0);
687f398627SJean-Christophe Dubois 
697f398627SJean-Christophe Dubois     /* ack the interrupt */
70f1dfd507SEric Blake     qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0);
71f1dfd507SEric Blake     status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
727f398627SJean-Christophe Dubois     g_assert((status & I2SR_IIF) == 0);
737f398627SJean-Christophe Dubois 
747f398627SJean-Christophe Dubois     while (size < len) {
757f398627SJean-Christophe Dubois         /* check we are still busy */
76f1dfd507SEric Blake         status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
777f398627SJean-Christophe Dubois         g_assert((status & I2SR_IBB) != 0);
787f398627SJean-Christophe Dubois 
797f398627SJean-Christophe Dubois         /* write the data */
80f1dfd507SEric Blake         qtest_writeb(i2c->qts, s->addr + I2DR_ADDR, buf[size]);
81f1dfd507SEric Blake         status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
827f398627SJean-Christophe Dubois         g_assert((status & I2SR_IIF) != 0);
837f398627SJean-Christophe Dubois         g_assert((status & I2SR_RXAK) == 0);
847f398627SJean-Christophe Dubois 
857f398627SJean-Christophe Dubois         /* ack the interrupt */
86f1dfd507SEric Blake         qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0);
87f1dfd507SEric Blake         status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
887f398627SJean-Christophe Dubois         g_assert((status & I2SR_IIF) == 0);
897f398627SJean-Christophe Dubois 
907f398627SJean-Christophe Dubois         size++;
917f398627SJean-Christophe Dubois     }
927f398627SJean-Christophe Dubois 
937f398627SJean-Christophe Dubois     /* release the bus */
947f398627SJean-Christophe Dubois     data &= ~(I2CR_MSTA | I2CR_MTX);
95f1dfd507SEric Blake     qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data);
96f1dfd507SEric Blake     status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
977f398627SJean-Christophe Dubois     g_assert((status & I2SR_IBB) == 0);
987f398627SJean-Christophe Dubois }
997f398627SJean-Christophe Dubois 
imx_i2c_recv(I2CAdapter * i2c,uint8_t addr,uint8_t * buf,uint16_t len)1007f398627SJean-Christophe Dubois static void imx_i2c_recv(I2CAdapter *i2c, uint8_t addr,
1017f398627SJean-Christophe Dubois                          uint8_t *buf, uint16_t len)
1027f398627SJean-Christophe Dubois {
103732c919cSPaolo Bonzini     IMXI2C *s = container_of(i2c, IMXI2C, parent);
1047f398627SJean-Christophe Dubois     uint8_t data;
1057f398627SJean-Christophe Dubois     uint8_t status;
1067f398627SJean-Christophe Dubois     uint16_t size = 0;
1077f398627SJean-Christophe Dubois 
1087f398627SJean-Christophe Dubois     if (!len) {
1097f398627SJean-Christophe Dubois         return;
1107f398627SJean-Christophe Dubois     }
1117f398627SJean-Christophe Dubois 
1127f398627SJean-Christophe Dubois     /* set the bus for write */
1137f398627SJean-Christophe Dubois     data = I2CR_IEN |
1147f398627SJean-Christophe Dubois            I2CR_IIEN |
1157f398627SJean-Christophe Dubois            I2CR_MSTA |
1167f398627SJean-Christophe Dubois            I2CR_MTX |
1177f398627SJean-Christophe Dubois            I2CR_TXAK;
1187f398627SJean-Christophe Dubois 
119f1dfd507SEric Blake     qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data);
120f1dfd507SEric Blake     status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
1217f398627SJean-Christophe Dubois     g_assert((status & I2SR_IBB) != 0);
1227f398627SJean-Christophe Dubois 
1237f398627SJean-Christophe Dubois     /* set the slave address */
1247f398627SJean-Christophe Dubois     imx_i2c_set_slave_addr(s, addr, IMX_I2C_READ);
125f1dfd507SEric Blake     status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
1267f398627SJean-Christophe Dubois     g_assert((status & I2SR_IIF) != 0);
1277f398627SJean-Christophe Dubois     g_assert((status & I2SR_RXAK) == 0);
1287f398627SJean-Christophe Dubois 
1297f398627SJean-Christophe Dubois     /* ack the interrupt */
130f1dfd507SEric Blake     qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0);
131f1dfd507SEric Blake     status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
1327f398627SJean-Christophe Dubois     g_assert((status & I2SR_IIF) == 0);
1337f398627SJean-Christophe Dubois 
1347f398627SJean-Christophe Dubois     /* set the bus for read */
1357f398627SJean-Christophe Dubois     data &= ~I2CR_MTX;
1367f398627SJean-Christophe Dubois     /* if only one byte don't ack */
1377f398627SJean-Christophe Dubois     if (len != 1) {
1387f398627SJean-Christophe Dubois         data &= ~I2CR_TXAK;
1397f398627SJean-Christophe Dubois     }
140f1dfd507SEric Blake     qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data);
141f1dfd507SEric Blake     status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
1427f398627SJean-Christophe Dubois     g_assert((status & I2SR_IBB) != 0);
1437f398627SJean-Christophe Dubois 
1447f398627SJean-Christophe Dubois     /* dummy read */
145f1dfd507SEric Blake     qtest_readb(i2c->qts, s->addr + I2DR_ADDR);
146f1dfd507SEric Blake     status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
1477f398627SJean-Christophe Dubois     g_assert((status & I2SR_IIF) != 0);
1487f398627SJean-Christophe Dubois 
1497f398627SJean-Christophe Dubois     /* ack the interrupt */
150f1dfd507SEric Blake     qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0);
151f1dfd507SEric Blake     status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
1527f398627SJean-Christophe Dubois     g_assert((status & I2SR_IIF) == 0);
1537f398627SJean-Christophe Dubois 
1547f398627SJean-Christophe Dubois     while (size < len) {
1557f398627SJean-Christophe Dubois         /* check we are still busy */
156f1dfd507SEric Blake         status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
1577f398627SJean-Christophe Dubois         g_assert((status & I2SR_IBB) != 0);
1587f398627SJean-Christophe Dubois 
1597f398627SJean-Christophe Dubois         if (size == (len - 1)) {
1607f398627SJean-Christophe Dubois             /* stop the read transaction */
1617f398627SJean-Christophe Dubois             data &= ~(I2CR_MSTA | I2CR_MTX);
1627f398627SJean-Christophe Dubois         } else {
1637f398627SJean-Christophe Dubois             /* ack the data read */
1647f398627SJean-Christophe Dubois             data |= I2CR_TXAK;
1657f398627SJean-Christophe Dubois         }
166f1dfd507SEric Blake         qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data);
1677f398627SJean-Christophe Dubois 
1687f398627SJean-Christophe Dubois         /* read the data */
169f1dfd507SEric Blake         buf[size] = qtest_readb(i2c->qts, s->addr + I2DR_ADDR);
1707f398627SJean-Christophe Dubois 
1717f398627SJean-Christophe Dubois         if (size != (len - 1)) {
172f1dfd507SEric Blake             status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
1737f398627SJean-Christophe Dubois             g_assert((status & I2SR_IIF) != 0);
1747f398627SJean-Christophe Dubois 
1757f398627SJean-Christophe Dubois             /* ack the interrupt */
176f1dfd507SEric Blake             qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0);
1777f398627SJean-Christophe Dubois         }
1787f398627SJean-Christophe Dubois 
179f1dfd507SEric Blake         status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
1807f398627SJean-Christophe Dubois         g_assert((status & I2SR_IIF) == 0);
1817f398627SJean-Christophe Dubois 
1827f398627SJean-Christophe Dubois         size++;
1837f398627SJean-Christophe Dubois     }
1847f398627SJean-Christophe Dubois 
185f1dfd507SEric Blake     status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
1867f398627SJean-Christophe Dubois     g_assert((status & I2SR_IBB) == 0);
1877f398627SJean-Christophe Dubois }
1887f398627SJean-Christophe Dubois 
imx_i2c_get_driver(void * obj,const char * interface)189c0825c63SPaolo Bonzini static void *imx_i2c_get_driver(void *obj, const char *interface)
190c0825c63SPaolo Bonzini {
191c0825c63SPaolo Bonzini     IMXI2C *s = obj;
192c0825c63SPaolo Bonzini     if (!g_strcmp0(interface, "i2c-bus")) {
193c0825c63SPaolo Bonzini         return &s->parent;
194c0825c63SPaolo Bonzini     }
195c0825c63SPaolo Bonzini     fprintf(stderr, "%s not present in imx-i2c\n", interface);
196c0825c63SPaolo Bonzini     g_assert_not_reached();
197c0825c63SPaolo Bonzini }
198c0825c63SPaolo Bonzini 
imx_i2c_init(IMXI2C * s,QTestState * qts,uint64_t addr)199732c919cSPaolo Bonzini void imx_i2c_init(IMXI2C *s, QTestState *qts, uint64_t addr)
200732c919cSPaolo Bonzini {
201732c919cSPaolo Bonzini     s->addr = addr;
202732c919cSPaolo Bonzini 
203c0825c63SPaolo Bonzini     s->obj.get_driver = imx_i2c_get_driver;
204c0825c63SPaolo Bonzini 
205732c919cSPaolo Bonzini     s->parent.send = imx_i2c_send;
206732c919cSPaolo Bonzini     s->parent.recv = imx_i2c_recv;
207732c919cSPaolo Bonzini     s->parent.qts = qts;
208732c919cSPaolo Bonzini }
209732c919cSPaolo Bonzini 
imx_i2c_register_nodes(void)210c0825c63SPaolo Bonzini static void imx_i2c_register_nodes(void)
211c0825c63SPaolo Bonzini {
212*bbaf7a0dSBernhard Beschow     qos_node_create_driver(TYPE_IMX_I2C, NULL);
213*bbaf7a0dSBernhard Beschow     qos_node_produces(TYPE_IMX_I2C, "i2c-bus");
214c0825c63SPaolo Bonzini }
215c0825c63SPaolo Bonzini 
216c0825c63SPaolo Bonzini libqos_init(imx_i2c_register_nodes);
217