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