11cf4323eSThomas Huth /*
21cf4323eSThomas Huth * QTest I2C driver
31cf4323eSThomas Huth *
41cf4323eSThomas Huth * Copyright (c) 2012 Andreas Färber
51cf4323eSThomas Huth *
61cf4323eSThomas Huth * This work is licensed under the terms of the GNU GPL, version 2 or later.
71cf4323eSThomas Huth * See the COPYING file in the top-level directory.
81cf4323eSThomas Huth */
91cf4323eSThomas Huth #include "qemu/osdep.h"
10a2ce7dbdSPaolo Bonzini #include "i2c.h"
111cf4323eSThomas Huth
121cf4323eSThomas Huth
131cf4323eSThomas Huth #include "qemu/bswap.h"
14*907b5105SMarc-André Lureau #include "../libqtest.h"
151cf4323eSThomas Huth
161cf4323eSThomas Huth enum OMAPI2CRegisters {
171cf4323eSThomas Huth OMAP_I2C_REV = 0x00,
181cf4323eSThomas Huth OMAP_I2C_STAT = 0x08,
191cf4323eSThomas Huth OMAP_I2C_CNT = 0x18,
201cf4323eSThomas Huth OMAP_I2C_DATA = 0x1c,
211cf4323eSThomas Huth OMAP_I2C_CON = 0x24,
221cf4323eSThomas Huth OMAP_I2C_SA = 0x2c,
231cf4323eSThomas Huth };
241cf4323eSThomas Huth
251cf4323eSThomas Huth enum OMAPI2CSTATBits {
261cf4323eSThomas Huth OMAP_I2C_STAT_NACK = 1 << 1,
271cf4323eSThomas Huth OMAP_I2C_STAT_ARDY = 1 << 2,
281cf4323eSThomas Huth OMAP_I2C_STAT_RRDY = 1 << 3,
291cf4323eSThomas Huth OMAP_I2C_STAT_XRDY = 1 << 4,
301cf4323eSThomas Huth OMAP_I2C_STAT_ROVR = 1 << 11,
311cf4323eSThomas Huth OMAP_I2C_STAT_SBD = 1 << 15,
321cf4323eSThomas Huth };
331cf4323eSThomas Huth
341cf4323eSThomas Huth enum OMAPI2CCONBits {
351cf4323eSThomas Huth OMAP_I2C_CON_STT = 1 << 0,
361cf4323eSThomas Huth OMAP_I2C_CON_STP = 1 << 1,
371cf4323eSThomas Huth OMAP_I2C_CON_TRX = 1 << 9,
381cf4323eSThomas Huth OMAP_I2C_CON_MST = 1 << 10,
391cf4323eSThomas Huth OMAP_I2C_CON_BE = 1 << 14,
401cf4323eSThomas Huth OMAP_I2C_CON_I2C_EN = 1 << 15,
411cf4323eSThomas Huth };
421cf4323eSThomas Huth
431cf4323eSThomas Huth
omap_i2c_set_slave_addr(OMAPI2C * s,uint8_t addr)441cf4323eSThomas Huth static void omap_i2c_set_slave_addr(OMAPI2C *s, uint8_t addr)
451cf4323eSThomas Huth {
461cf4323eSThomas Huth uint16_t data = addr;
471cf4323eSThomas Huth
481cf4323eSThomas Huth qtest_writew(s->parent.qts, s->addr + OMAP_I2C_SA, data);
491cf4323eSThomas Huth data = qtest_readw(s->parent.qts, s->addr + OMAP_I2C_SA);
501cf4323eSThomas Huth g_assert_cmphex(data, ==, addr);
511cf4323eSThomas Huth }
521cf4323eSThomas Huth
omap_i2c_send(I2CAdapter * i2c,uint8_t addr,const uint8_t * buf,uint16_t len)531cf4323eSThomas Huth static void omap_i2c_send(I2CAdapter *i2c, uint8_t addr,
541cf4323eSThomas Huth const uint8_t *buf, uint16_t len)
551cf4323eSThomas Huth {
561cf4323eSThomas Huth OMAPI2C *s = container_of(i2c, OMAPI2C, parent);
571cf4323eSThomas Huth uint16_t data;
581cf4323eSThomas Huth
591cf4323eSThomas Huth omap_i2c_set_slave_addr(s, addr);
601cf4323eSThomas Huth
611cf4323eSThomas Huth data = len;
621cf4323eSThomas Huth qtest_writew(i2c->qts, s->addr + OMAP_I2C_CNT, data);
631cf4323eSThomas Huth
641cf4323eSThomas Huth data = OMAP_I2C_CON_I2C_EN |
651cf4323eSThomas Huth OMAP_I2C_CON_TRX |
661cf4323eSThomas Huth OMAP_I2C_CON_MST |
671cf4323eSThomas Huth OMAP_I2C_CON_STT |
681cf4323eSThomas Huth OMAP_I2C_CON_STP;
691cf4323eSThomas Huth qtest_writew(i2c->qts, s->addr + OMAP_I2C_CON, data);
701cf4323eSThomas Huth data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CON);
711cf4323eSThomas Huth g_assert((data & OMAP_I2C_CON_STP) != 0);
721cf4323eSThomas Huth
731cf4323eSThomas Huth data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT);
741cf4323eSThomas Huth g_assert((data & OMAP_I2C_STAT_NACK) == 0);
751cf4323eSThomas Huth
761cf4323eSThomas Huth while (len > 1) {
771cf4323eSThomas Huth data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT);
781cf4323eSThomas Huth g_assert((data & OMAP_I2C_STAT_XRDY) != 0);
791cf4323eSThomas Huth
801cf4323eSThomas Huth data = buf[0] | ((uint16_t)buf[1] << 8);
811cf4323eSThomas Huth qtest_writew(i2c->qts, s->addr + OMAP_I2C_DATA, data);
821cf4323eSThomas Huth buf = (uint8_t *)buf + 2;
831cf4323eSThomas Huth len -= 2;
841cf4323eSThomas Huth }
851cf4323eSThomas Huth if (len == 1) {
861cf4323eSThomas Huth data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT);
871cf4323eSThomas Huth g_assert((data & OMAP_I2C_STAT_XRDY) != 0);
881cf4323eSThomas Huth
891cf4323eSThomas Huth data = buf[0];
901cf4323eSThomas Huth qtest_writew(i2c->qts, s->addr + OMAP_I2C_DATA, data);
911cf4323eSThomas Huth }
921cf4323eSThomas Huth
931cf4323eSThomas Huth data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CON);
941cf4323eSThomas Huth g_assert((data & OMAP_I2C_CON_STP) == 0);
951cf4323eSThomas Huth }
961cf4323eSThomas Huth
omap_i2c_recv(I2CAdapter * i2c,uint8_t addr,uint8_t * buf,uint16_t len)971cf4323eSThomas Huth static void omap_i2c_recv(I2CAdapter *i2c, uint8_t addr,
981cf4323eSThomas Huth uint8_t *buf, uint16_t len)
991cf4323eSThomas Huth {
1001cf4323eSThomas Huth OMAPI2C *s = container_of(i2c, OMAPI2C, parent);
1011cf4323eSThomas Huth uint16_t data, stat;
1021cf4323eSThomas Huth uint16_t orig_len = len;
1031cf4323eSThomas Huth
1041cf4323eSThomas Huth omap_i2c_set_slave_addr(s, addr);
1051cf4323eSThomas Huth
1061cf4323eSThomas Huth data = len;
1071cf4323eSThomas Huth qtest_writew(i2c->qts, s->addr + OMAP_I2C_CNT, data);
1081cf4323eSThomas Huth
1091cf4323eSThomas Huth data = OMAP_I2C_CON_I2C_EN |
1101cf4323eSThomas Huth OMAP_I2C_CON_MST |
1111cf4323eSThomas Huth OMAP_I2C_CON_STT |
1121cf4323eSThomas Huth OMAP_I2C_CON_STP;
1131cf4323eSThomas Huth qtest_writew(i2c->qts, s->addr + OMAP_I2C_CON, data);
1141cf4323eSThomas Huth
1151cf4323eSThomas Huth data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT);
1161cf4323eSThomas Huth g_assert((data & OMAP_I2C_STAT_NACK) == 0);
1171cf4323eSThomas Huth
1181cf4323eSThomas Huth while (len > 0) {
1191cf4323eSThomas Huth data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CON);
1201cf4323eSThomas Huth if (len <= 4) {
1211cf4323eSThomas Huth g_assert((data & OMAP_I2C_CON_STP) == 0);
1221cf4323eSThomas Huth
1231cf4323eSThomas Huth data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CNT);
1241cf4323eSThomas Huth g_assert_cmpuint(data, ==, orig_len);
1251cf4323eSThomas Huth } else {
1261cf4323eSThomas Huth g_assert((data & OMAP_I2C_CON_STP) != 0);
1271cf4323eSThomas Huth
1281cf4323eSThomas Huth data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CNT);
1291cf4323eSThomas Huth g_assert_cmpuint(data, ==, len - 4);
1301cf4323eSThomas Huth }
1311cf4323eSThomas Huth
1321cf4323eSThomas Huth data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT);
1331cf4323eSThomas Huth g_assert((data & OMAP_I2C_STAT_RRDY) != 0);
1341cf4323eSThomas Huth g_assert((data & OMAP_I2C_STAT_ROVR) == 0);
1351cf4323eSThomas Huth
1361cf4323eSThomas Huth data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_DATA);
1371cf4323eSThomas Huth
1381cf4323eSThomas Huth stat = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT);
1391cf4323eSThomas Huth
1401cf4323eSThomas Huth if (unlikely(len == 1)) {
1411cf4323eSThomas Huth g_assert((stat & OMAP_I2C_STAT_SBD) != 0);
1421cf4323eSThomas Huth
1431cf4323eSThomas Huth buf[0] = data & 0xff;
1441cf4323eSThomas Huth buf++;
1451cf4323eSThomas Huth len--;
1461cf4323eSThomas Huth } else {
1471cf4323eSThomas Huth buf[0] = data & 0xff;
1481cf4323eSThomas Huth buf[1] = data >> 8;
1491cf4323eSThomas Huth buf += 2;
1501cf4323eSThomas Huth len -= 2;
1511cf4323eSThomas Huth }
1521cf4323eSThomas Huth }
1531cf4323eSThomas Huth
1541cf4323eSThomas Huth data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CON);
1551cf4323eSThomas Huth g_assert((data & OMAP_I2C_CON_STP) == 0);
1561cf4323eSThomas Huth }
1571cf4323eSThomas Huth
omap_i2c_get_driver(void * obj,const char * interface)1581cf4323eSThomas Huth static void *omap_i2c_get_driver(void *obj, const char *interface)
1591cf4323eSThomas Huth {
1601cf4323eSThomas Huth OMAPI2C *s = obj;
1611cf4323eSThomas Huth if (!g_strcmp0(interface, "i2c-bus")) {
1621cf4323eSThomas Huth return &s->parent;
1631cf4323eSThomas Huth }
1641cf4323eSThomas Huth fprintf(stderr, "%s not present in omap_i2c\n", interface);
1651cf4323eSThomas Huth g_assert_not_reached();
1661cf4323eSThomas Huth }
1671cf4323eSThomas Huth
omap_i2c_start_hw(QOSGraphObject * object)1681cf4323eSThomas Huth static void omap_i2c_start_hw(QOSGraphObject *object)
1691cf4323eSThomas Huth {
1701cf4323eSThomas Huth OMAPI2C *s = (OMAPI2C *) object;
1711cf4323eSThomas Huth uint16_t data;
1721cf4323eSThomas Huth
1731cf4323eSThomas Huth /* verify the mmio address by looking for a known signature */
1741cf4323eSThomas Huth data = qtest_readw(s->parent.qts, s->addr + OMAP_I2C_REV);
1751cf4323eSThomas Huth g_assert_cmphex(data, ==, 0x34);
1761cf4323eSThomas Huth }
1771cf4323eSThomas Huth
omap_i2c_init(OMAPI2C * s,QTestState * qts,uint64_t addr)1781cf4323eSThomas Huth void omap_i2c_init(OMAPI2C *s, QTestState *qts, uint64_t addr)
1791cf4323eSThomas Huth {
1801cf4323eSThomas Huth s->addr = addr;
1811cf4323eSThomas Huth
1821cf4323eSThomas Huth s->obj.get_driver = omap_i2c_get_driver;
1831cf4323eSThomas Huth s->obj.start_hw = omap_i2c_start_hw;
1841cf4323eSThomas Huth
1851cf4323eSThomas Huth s->parent.send = omap_i2c_send;
1861cf4323eSThomas Huth s->parent.recv = omap_i2c_recv;
1871cf4323eSThomas Huth s->parent.qts = qts;
1881cf4323eSThomas Huth }
1891cf4323eSThomas Huth
omap_i2c_register_nodes(void)1901cf4323eSThomas Huth static void omap_i2c_register_nodes(void)
1911cf4323eSThomas Huth {
1921cf4323eSThomas Huth qos_node_create_driver("omap_i2c", NULL);
1931cf4323eSThomas Huth qos_node_produces("omap_i2c", "i2c-bus");
1941cf4323eSThomas Huth }
1951cf4323eSThomas Huth
1961cf4323eSThomas Huth libqos_init(omap_i2c_register_nodes);
197