1 /* 2 * QTest I2C driver 3 * 4 * Copyright (c) 2012 Andreas Färber 5 * 6 * This work is licensed under the terms of the GNU GPL, version 2 or later. 7 * See the COPYING file in the top-level directory. 8 */ 9 #include "qemu/osdep.h" 10 #include "libqos/i2c.h" 11 12 13 #include "qemu/bswap.h" 14 #include "libqtest.h" 15 16 enum OMAPI2CRegisters { 17 OMAP_I2C_REV = 0x00, 18 OMAP_I2C_STAT = 0x08, 19 OMAP_I2C_CNT = 0x18, 20 OMAP_I2C_DATA = 0x1c, 21 OMAP_I2C_CON = 0x24, 22 OMAP_I2C_SA = 0x2c, 23 }; 24 25 enum OMAPI2CSTATBits { 26 OMAP_I2C_STAT_NACK = 1 << 1, 27 OMAP_I2C_STAT_ARDY = 1 << 2, 28 OMAP_I2C_STAT_RRDY = 1 << 3, 29 OMAP_I2C_STAT_XRDY = 1 << 4, 30 OMAP_I2C_STAT_ROVR = 1 << 11, 31 OMAP_I2C_STAT_SBD = 1 << 15, 32 }; 33 34 enum OMAPI2CCONBits { 35 OMAP_I2C_CON_STT = 1 << 0, 36 OMAP_I2C_CON_STP = 1 << 1, 37 OMAP_I2C_CON_TRX = 1 << 9, 38 OMAP_I2C_CON_MST = 1 << 10, 39 OMAP_I2C_CON_BE = 1 << 14, 40 OMAP_I2C_CON_I2C_EN = 1 << 15, 41 }; 42 43 44 static void omap_i2c_set_slave_addr(OMAPI2C *s, uint8_t addr) 45 { 46 uint16_t data = addr; 47 48 qtest_writew(s->parent.qts, s->addr + OMAP_I2C_SA, data); 49 data = qtest_readw(s->parent.qts, s->addr + OMAP_I2C_SA); 50 g_assert_cmphex(data, ==, addr); 51 } 52 53 static void omap_i2c_send(I2CAdapter *i2c, uint8_t addr, 54 const uint8_t *buf, uint16_t len) 55 { 56 OMAPI2C *s = container_of(i2c, OMAPI2C, parent); 57 uint16_t data; 58 59 omap_i2c_set_slave_addr(s, addr); 60 61 data = len; 62 qtest_writew(i2c->qts, s->addr + OMAP_I2C_CNT, data); 63 64 data = OMAP_I2C_CON_I2C_EN | 65 OMAP_I2C_CON_TRX | 66 OMAP_I2C_CON_MST | 67 OMAP_I2C_CON_STT | 68 OMAP_I2C_CON_STP; 69 qtest_writew(i2c->qts, s->addr + OMAP_I2C_CON, data); 70 data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CON); 71 g_assert((data & OMAP_I2C_CON_STP) != 0); 72 73 data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT); 74 g_assert((data & OMAP_I2C_STAT_NACK) == 0); 75 76 while (len > 1) { 77 data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT); 78 g_assert((data & OMAP_I2C_STAT_XRDY) != 0); 79 80 data = buf[0] | ((uint16_t)buf[1] << 8); 81 qtest_writew(i2c->qts, s->addr + OMAP_I2C_DATA, data); 82 buf = (uint8_t *)buf + 2; 83 len -= 2; 84 } 85 if (len == 1) { 86 data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT); 87 g_assert((data & OMAP_I2C_STAT_XRDY) != 0); 88 89 data = buf[0]; 90 qtest_writew(i2c->qts, s->addr + OMAP_I2C_DATA, data); 91 } 92 93 data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CON); 94 g_assert((data & OMAP_I2C_CON_STP) == 0); 95 } 96 97 static void omap_i2c_recv(I2CAdapter *i2c, uint8_t addr, 98 uint8_t *buf, uint16_t len) 99 { 100 OMAPI2C *s = container_of(i2c, OMAPI2C, parent); 101 uint16_t data, stat; 102 uint16_t orig_len = len; 103 104 omap_i2c_set_slave_addr(s, addr); 105 106 data = len; 107 qtest_writew(i2c->qts, s->addr + OMAP_I2C_CNT, data); 108 109 data = OMAP_I2C_CON_I2C_EN | 110 OMAP_I2C_CON_MST | 111 OMAP_I2C_CON_STT | 112 OMAP_I2C_CON_STP; 113 qtest_writew(i2c->qts, s->addr + OMAP_I2C_CON, data); 114 115 data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT); 116 g_assert((data & OMAP_I2C_STAT_NACK) == 0); 117 118 while (len > 0) { 119 data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CON); 120 if (len <= 4) { 121 g_assert((data & OMAP_I2C_CON_STP) == 0); 122 123 data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CNT); 124 g_assert_cmpuint(data, ==, orig_len); 125 } else { 126 g_assert((data & OMAP_I2C_CON_STP) != 0); 127 128 data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CNT); 129 g_assert_cmpuint(data, ==, len - 4); 130 } 131 132 data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT); 133 g_assert((data & OMAP_I2C_STAT_RRDY) != 0); 134 g_assert((data & OMAP_I2C_STAT_ROVR) == 0); 135 136 data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_DATA); 137 138 stat = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT); 139 140 if (unlikely(len == 1)) { 141 g_assert((stat & OMAP_I2C_STAT_SBD) != 0); 142 143 buf[0] = data & 0xff; 144 buf++; 145 len--; 146 } else { 147 buf[0] = data & 0xff; 148 buf[1] = data >> 8; 149 buf += 2; 150 len -= 2; 151 } 152 } 153 154 data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CON); 155 g_assert((data & OMAP_I2C_CON_STP) == 0); 156 } 157 158 static void *omap_i2c_get_driver(void *obj, const char *interface) 159 { 160 OMAPI2C *s = obj; 161 if (!g_strcmp0(interface, "i2c-bus")) { 162 return &s->parent; 163 } 164 fprintf(stderr, "%s not present in omap_i2c\n", interface); 165 g_assert_not_reached(); 166 } 167 168 static void omap_i2c_start_hw(QOSGraphObject *object) 169 { 170 OMAPI2C *s = (OMAPI2C *) object; 171 uint16_t data; 172 173 /* verify the mmio address by looking for a known signature */ 174 data = qtest_readw(s->parent.qts, s->addr + OMAP_I2C_REV); 175 g_assert_cmphex(data, ==, 0x34); 176 } 177 178 void omap_i2c_init(OMAPI2C *s, QTestState *qts, uint64_t addr) 179 { 180 s->addr = addr; 181 182 s->obj.get_driver = omap_i2c_get_driver; 183 s->obj.start_hw = omap_i2c_start_hw; 184 185 s->parent.send = omap_i2c_send; 186 s->parent.recv = omap_i2c_recv; 187 s->parent.qts = qts; 188 } 189 190 static void omap_i2c_register_nodes(void) 191 { 192 qos_node_create_driver("omap_i2c", NULL); 193 qos_node_produces("omap_i2c", "i2c-bus"); 194 } 195 196 libqos_init(omap_i2c_register_nodes); 197