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