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 20*681c28a3SPeter Maydell #include "qemu/osdep.h" 217f398627SJean-Christophe Dubois #include "libqos/i2c.h" 227f398627SJean-Christophe Dubois 237f398627SJean-Christophe Dubois #include <glib.h> 247f398627SJean-Christophe Dubois 257f398627SJean-Christophe Dubois #include "libqtest.h" 267f398627SJean-Christophe Dubois 277f398627SJean-Christophe Dubois #include "hw/i2c/imx_i2c.h" 287f398627SJean-Christophe Dubois 297f398627SJean-Christophe Dubois enum IMXI2CDirection { 307f398627SJean-Christophe Dubois IMX_I2C_READ, 317f398627SJean-Christophe Dubois IMX_I2C_WRITE, 327f398627SJean-Christophe Dubois }; 337f398627SJean-Christophe Dubois 347f398627SJean-Christophe Dubois typedef struct IMXI2C { 357f398627SJean-Christophe Dubois I2CAdapter parent; 367f398627SJean-Christophe Dubois 377f398627SJean-Christophe Dubois uint64_t addr; 387f398627SJean-Christophe Dubois } IMXI2C; 397f398627SJean-Christophe Dubois 407f398627SJean-Christophe Dubois 417f398627SJean-Christophe Dubois static void imx_i2c_set_slave_addr(IMXI2C *s, uint8_t addr, 427f398627SJean-Christophe Dubois enum IMXI2CDirection direction) 437f398627SJean-Christophe Dubois { 447f398627SJean-Christophe Dubois writeb(s->addr + I2DR_ADDR, (addr << 1) | 457f398627SJean-Christophe Dubois (direction == IMX_I2C_READ ? 1 : 0)); 467f398627SJean-Christophe Dubois } 477f398627SJean-Christophe Dubois 487f398627SJean-Christophe Dubois static void imx_i2c_send(I2CAdapter *i2c, uint8_t addr, 497f398627SJean-Christophe Dubois const uint8_t *buf, uint16_t len) 507f398627SJean-Christophe Dubois { 517f398627SJean-Christophe Dubois IMXI2C *s = (IMXI2C *)i2c; 527f398627SJean-Christophe Dubois uint8_t data; 537f398627SJean-Christophe Dubois uint8_t status; 547f398627SJean-Christophe Dubois uint16_t size = 0; 557f398627SJean-Christophe Dubois 567f398627SJean-Christophe Dubois if (!len) { 577f398627SJean-Christophe Dubois return; 587f398627SJean-Christophe Dubois } 597f398627SJean-Christophe Dubois 607f398627SJean-Christophe Dubois /* set the bus for write */ 617f398627SJean-Christophe Dubois data = I2CR_IEN | 627f398627SJean-Christophe Dubois I2CR_IIEN | 637f398627SJean-Christophe Dubois I2CR_MSTA | 647f398627SJean-Christophe Dubois I2CR_MTX | 657f398627SJean-Christophe Dubois I2CR_TXAK; 667f398627SJean-Christophe Dubois 677f398627SJean-Christophe Dubois writeb(s->addr + I2CR_ADDR, data); 687f398627SJean-Christophe Dubois status = readb(s->addr + I2SR_ADDR); 697f398627SJean-Christophe Dubois g_assert((status & I2SR_IBB) != 0); 707f398627SJean-Christophe Dubois 717f398627SJean-Christophe Dubois /* set the slave address */ 727f398627SJean-Christophe Dubois imx_i2c_set_slave_addr(s, addr, IMX_I2C_WRITE); 737f398627SJean-Christophe Dubois status = readb(s->addr + I2SR_ADDR); 747f398627SJean-Christophe Dubois g_assert((status & I2SR_IIF) != 0); 757f398627SJean-Christophe Dubois g_assert((status & I2SR_RXAK) == 0); 767f398627SJean-Christophe Dubois 777f398627SJean-Christophe Dubois /* ack the interrupt */ 787f398627SJean-Christophe Dubois writeb(s->addr + I2SR_ADDR, 0); 797f398627SJean-Christophe Dubois status = readb(s->addr + I2SR_ADDR); 807f398627SJean-Christophe Dubois g_assert((status & I2SR_IIF) == 0); 817f398627SJean-Christophe Dubois 827f398627SJean-Christophe Dubois while (size < len) { 837f398627SJean-Christophe Dubois /* check we are still busy */ 847f398627SJean-Christophe Dubois status = readb(s->addr + I2SR_ADDR); 857f398627SJean-Christophe Dubois g_assert((status & I2SR_IBB) != 0); 867f398627SJean-Christophe Dubois 877f398627SJean-Christophe Dubois /* write the data */ 887f398627SJean-Christophe Dubois writeb(s->addr + I2DR_ADDR, buf[size]); 897f398627SJean-Christophe Dubois status = readb(s->addr + I2SR_ADDR); 907f398627SJean-Christophe Dubois g_assert((status & I2SR_IIF) != 0); 917f398627SJean-Christophe Dubois g_assert((status & I2SR_RXAK) == 0); 927f398627SJean-Christophe Dubois 937f398627SJean-Christophe Dubois /* ack the interrupt */ 947f398627SJean-Christophe Dubois writeb(s->addr + I2SR_ADDR, 0); 957f398627SJean-Christophe Dubois status = readb(s->addr + I2SR_ADDR); 967f398627SJean-Christophe Dubois g_assert((status & I2SR_IIF) == 0); 977f398627SJean-Christophe Dubois 987f398627SJean-Christophe Dubois size++; 997f398627SJean-Christophe Dubois } 1007f398627SJean-Christophe Dubois 1017f398627SJean-Christophe Dubois /* release the bus */ 1027f398627SJean-Christophe Dubois data &= ~(I2CR_MSTA | I2CR_MTX); 1037f398627SJean-Christophe Dubois writeb(s->addr + I2CR_ADDR, data); 1047f398627SJean-Christophe Dubois status = readb(s->addr + I2SR_ADDR); 1057f398627SJean-Christophe Dubois g_assert((status & I2SR_IBB) == 0); 1067f398627SJean-Christophe Dubois } 1077f398627SJean-Christophe Dubois 1087f398627SJean-Christophe Dubois static void imx_i2c_recv(I2CAdapter *i2c, uint8_t addr, 1097f398627SJean-Christophe Dubois uint8_t *buf, uint16_t len) 1107f398627SJean-Christophe Dubois { 1117f398627SJean-Christophe Dubois IMXI2C *s = (IMXI2C *)i2c; 1127f398627SJean-Christophe Dubois uint8_t data; 1137f398627SJean-Christophe Dubois uint8_t status; 1147f398627SJean-Christophe Dubois uint16_t size = 0; 1157f398627SJean-Christophe Dubois 1167f398627SJean-Christophe Dubois if (!len) { 1177f398627SJean-Christophe Dubois return; 1187f398627SJean-Christophe Dubois } 1197f398627SJean-Christophe Dubois 1207f398627SJean-Christophe Dubois /* set the bus for write */ 1217f398627SJean-Christophe Dubois data = I2CR_IEN | 1227f398627SJean-Christophe Dubois I2CR_IIEN | 1237f398627SJean-Christophe Dubois I2CR_MSTA | 1247f398627SJean-Christophe Dubois I2CR_MTX | 1257f398627SJean-Christophe Dubois I2CR_TXAK; 1267f398627SJean-Christophe Dubois 1277f398627SJean-Christophe Dubois writeb(s->addr + I2CR_ADDR, data); 1287f398627SJean-Christophe Dubois status = readb(s->addr + I2SR_ADDR); 1297f398627SJean-Christophe Dubois g_assert((status & I2SR_IBB) != 0); 1307f398627SJean-Christophe Dubois 1317f398627SJean-Christophe Dubois /* set the slave address */ 1327f398627SJean-Christophe Dubois imx_i2c_set_slave_addr(s, addr, IMX_I2C_READ); 1337f398627SJean-Christophe Dubois status = readb(s->addr + I2SR_ADDR); 1347f398627SJean-Christophe Dubois g_assert((status & I2SR_IIF) != 0); 1357f398627SJean-Christophe Dubois g_assert((status & I2SR_RXAK) == 0); 1367f398627SJean-Christophe Dubois 1377f398627SJean-Christophe Dubois /* ack the interrupt */ 1387f398627SJean-Christophe Dubois writeb(s->addr + I2SR_ADDR, 0); 1397f398627SJean-Christophe Dubois status = readb(s->addr + I2SR_ADDR); 1407f398627SJean-Christophe Dubois g_assert((status & I2SR_IIF) == 0); 1417f398627SJean-Christophe Dubois 1427f398627SJean-Christophe Dubois /* set the bus for read */ 1437f398627SJean-Christophe Dubois data &= ~I2CR_MTX; 1447f398627SJean-Christophe Dubois /* if only one byte don't ack */ 1457f398627SJean-Christophe Dubois if (len != 1) { 1467f398627SJean-Christophe Dubois data &= ~I2CR_TXAK; 1477f398627SJean-Christophe Dubois } 1487f398627SJean-Christophe Dubois writeb(s->addr + I2CR_ADDR, data); 1497f398627SJean-Christophe Dubois status = readb(s->addr + I2SR_ADDR); 1507f398627SJean-Christophe Dubois g_assert((status & I2SR_IBB) != 0); 1517f398627SJean-Christophe Dubois 1527f398627SJean-Christophe Dubois /* dummy read */ 1537f398627SJean-Christophe Dubois readb(s->addr + I2DR_ADDR); 1547f398627SJean-Christophe Dubois status = readb(s->addr + I2SR_ADDR); 1557f398627SJean-Christophe Dubois g_assert((status & I2SR_IIF) != 0); 1567f398627SJean-Christophe Dubois 1577f398627SJean-Christophe Dubois /* ack the interrupt */ 1587f398627SJean-Christophe Dubois writeb(s->addr + I2SR_ADDR, 0); 1597f398627SJean-Christophe Dubois status = readb(s->addr + I2SR_ADDR); 1607f398627SJean-Christophe Dubois g_assert((status & I2SR_IIF) == 0); 1617f398627SJean-Christophe Dubois 1627f398627SJean-Christophe Dubois while (size < len) { 1637f398627SJean-Christophe Dubois /* check we are still busy */ 1647f398627SJean-Christophe Dubois status = readb(s->addr + I2SR_ADDR); 1657f398627SJean-Christophe Dubois g_assert((status & I2SR_IBB) != 0); 1667f398627SJean-Christophe Dubois 1677f398627SJean-Christophe Dubois if (size == (len - 1)) { 1687f398627SJean-Christophe Dubois /* stop the read transaction */ 1697f398627SJean-Christophe Dubois data &= ~(I2CR_MSTA | I2CR_MTX); 1707f398627SJean-Christophe Dubois } else { 1717f398627SJean-Christophe Dubois /* ack the data read */ 1727f398627SJean-Christophe Dubois data |= I2CR_TXAK; 1737f398627SJean-Christophe Dubois } 1747f398627SJean-Christophe Dubois writeb(s->addr + I2CR_ADDR, data); 1757f398627SJean-Christophe Dubois 1767f398627SJean-Christophe Dubois /* read the data */ 1777f398627SJean-Christophe Dubois buf[size] = readb(s->addr + I2DR_ADDR); 1787f398627SJean-Christophe Dubois 1797f398627SJean-Christophe Dubois if (size != (len - 1)) { 1807f398627SJean-Christophe Dubois status = readb(s->addr + I2SR_ADDR); 1817f398627SJean-Christophe Dubois g_assert((status & I2SR_IIF) != 0); 1827f398627SJean-Christophe Dubois 1837f398627SJean-Christophe Dubois /* ack the interrupt */ 1847f398627SJean-Christophe Dubois writeb(s->addr + I2SR_ADDR, 0); 1857f398627SJean-Christophe Dubois } 1867f398627SJean-Christophe Dubois 1877f398627SJean-Christophe Dubois status = readb(s->addr + I2SR_ADDR); 1887f398627SJean-Christophe Dubois g_assert((status & I2SR_IIF) == 0); 1897f398627SJean-Christophe Dubois 1907f398627SJean-Christophe Dubois size++; 1917f398627SJean-Christophe Dubois } 1927f398627SJean-Christophe Dubois 1937f398627SJean-Christophe Dubois status = readb(s->addr + I2SR_ADDR); 1947f398627SJean-Christophe Dubois g_assert((status & I2SR_IBB) == 0); 1957f398627SJean-Christophe Dubois } 1967f398627SJean-Christophe Dubois 1977f398627SJean-Christophe Dubois I2CAdapter *imx_i2c_create(uint64_t addr) 1987f398627SJean-Christophe Dubois { 1997f398627SJean-Christophe Dubois IMXI2C *s = g_malloc0(sizeof(*s)); 2007f398627SJean-Christophe Dubois I2CAdapter *i2c = (I2CAdapter *)s; 2017f398627SJean-Christophe Dubois 2027f398627SJean-Christophe Dubois s->addr = addr; 2037f398627SJean-Christophe Dubois 2047f398627SJean-Christophe Dubois i2c->send = imx_i2c_send; 2057f398627SJean-Christophe Dubois i2c->recv = imx_i2c_recv; 2067f398627SJean-Christophe Dubois 2077f398627SJean-Christophe Dubois return i2c; 2087f398627SJean-Christophe Dubois } 209