cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

exynos4210_i2c.c (10064B)


      1/*
      2 *  Exynos4210 I2C Bus Serial Interface Emulation
      3 *
      4 *  Copyright (C) 2012 Samsung Electronics Co Ltd.
      5 *    Maksim Kozlov, <m.kozlov@samsung.com>
      6 *    Igor Mitsyanko, <i.mitsyanko@samsung.com>
      7 *
      8 *  This program is free software; you can redistribute it and/or modify it
      9 *  under the terms of the GNU General Public License as published by the
     10 *  Free Software Foundation; either version 2 of the License, or
     11 *  (at your option) any later version.
     12 *
     13 *  This program is distributed in the hope that it will be useful, but WITHOUT
     14 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     15 *  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
     16 *  for more details.
     17 *
     18 *  You should have received a copy of the GNU General Public License along
     19 *  with this program; if not, see <http://www.gnu.org/licenses/>.
     20 *
     21 */
     22
     23#include "qemu/osdep.h"
     24#include "qemu/module.h"
     25#include "qemu/timer.h"
     26#include "hw/sysbus.h"
     27#include "migration/vmstate.h"
     28#include "hw/i2c/i2c.h"
     29#include "hw/irq.h"
     30#include "qom/object.h"
     31
     32#ifndef EXYNOS4_I2C_DEBUG
     33#define EXYNOS4_I2C_DEBUG                 0
     34#endif
     35
     36#define TYPE_EXYNOS4_I2C                  "exynos4210.i2c"
     37OBJECT_DECLARE_SIMPLE_TYPE(Exynos4210I2CState, EXYNOS4_I2C)
     38
     39/* Exynos4210 I2C memory map */
     40#define EXYNOS4_I2C_MEM_SIZE              0x14
     41#define I2CCON_ADDR                       0x00  /* control register */
     42#define I2CSTAT_ADDR                      0x04  /* control/status register */
     43#define I2CADD_ADDR                       0x08  /* address register */
     44#define I2CDS_ADDR                        0x0c  /* data shift register */
     45#define I2CLC_ADDR                        0x10  /* line control register */
     46
     47#define I2CCON_ACK_GEN                    (1 << 7)
     48#define I2CCON_INTRS_EN                   (1 << 5)
     49#define I2CCON_INT_PEND                   (1 << 4)
     50
     51#define EXYNOS4_I2C_MODE(reg)             (((reg) >> 6) & 3)
     52#define I2C_IN_MASTER_MODE(reg)           (((reg) >> 6) & 2)
     53#define I2CMODE_MASTER_Rx                 0x2
     54#define I2CMODE_MASTER_Tx                 0x3
     55#define I2CSTAT_LAST_BIT                  (1 << 0)
     56#define I2CSTAT_OUTPUT_EN                 (1 << 4)
     57#define I2CSTAT_START_BUSY                (1 << 5)
     58
     59
     60#if EXYNOS4_I2C_DEBUG
     61#define DPRINT(fmt, args...)              \
     62    do { fprintf(stderr, "QEMU I2C: "fmt, ## args); } while (0)
     63
     64static const char *exynos4_i2c_get_regname(unsigned offset)
     65{
     66    switch (offset) {
     67    case I2CCON_ADDR:
     68        return "I2CCON";
     69    case I2CSTAT_ADDR:
     70        return "I2CSTAT";
     71    case I2CADD_ADDR:
     72        return "I2CADD";
     73    case I2CDS_ADDR:
     74        return "I2CDS";
     75    case I2CLC_ADDR:
     76        return "I2CLC";
     77    default:
     78        return "[?]";
     79    }
     80}
     81
     82#else
     83#define DPRINT(fmt, args...)              do { } while (0)
     84#endif
     85
     86struct Exynos4210I2CState {
     87    SysBusDevice parent_obj;
     88
     89    MemoryRegion iomem;
     90    I2CBus *bus;
     91    qemu_irq irq;
     92
     93    uint8_t i2ccon;
     94    uint8_t i2cstat;
     95    uint8_t i2cadd;
     96    uint8_t i2cds;
     97    uint8_t i2clc;
     98    bool scl_free;
     99};
    100
    101static inline void exynos4210_i2c_raise_interrupt(Exynos4210I2CState *s)
    102{
    103    if (s->i2ccon & I2CCON_INTRS_EN) {
    104        s->i2ccon |= I2CCON_INT_PEND;
    105        qemu_irq_raise(s->irq);
    106    }
    107}
    108
    109static void exynos4210_i2c_data_receive(void *opaque)
    110{
    111    Exynos4210I2CState *s = (Exynos4210I2CState *)opaque;
    112
    113    s->i2cstat &= ~I2CSTAT_LAST_BIT;
    114    s->scl_free = false;
    115    s->i2cds = i2c_recv(s->bus);
    116    exynos4210_i2c_raise_interrupt(s);
    117}
    118
    119static void exynos4210_i2c_data_send(void *opaque)
    120{
    121    Exynos4210I2CState *s = (Exynos4210I2CState *)opaque;
    122
    123    s->i2cstat &= ~I2CSTAT_LAST_BIT;
    124    s->scl_free = false;
    125    if (i2c_send(s->bus, s->i2cds) < 0 && (s->i2ccon & I2CCON_ACK_GEN)) {
    126        s->i2cstat |= I2CSTAT_LAST_BIT;
    127    }
    128    exynos4210_i2c_raise_interrupt(s);
    129}
    130
    131static uint64_t exynos4210_i2c_read(void *opaque, hwaddr offset,
    132                                 unsigned size)
    133{
    134    Exynos4210I2CState *s = (Exynos4210I2CState *)opaque;
    135    uint8_t value;
    136
    137    switch (offset) {
    138    case I2CCON_ADDR:
    139        value = s->i2ccon;
    140        break;
    141    case I2CSTAT_ADDR:
    142        value = s->i2cstat;
    143        break;
    144    case I2CADD_ADDR:
    145        value = s->i2cadd;
    146        break;
    147    case I2CDS_ADDR:
    148        value = s->i2cds;
    149        s->scl_free = true;
    150        if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Rx &&
    151               (s->i2cstat & I2CSTAT_START_BUSY) &&
    152               !(s->i2ccon & I2CCON_INT_PEND)) {
    153            exynos4210_i2c_data_receive(s);
    154        }
    155        break;
    156    case I2CLC_ADDR:
    157        value = s->i2clc;
    158        break;
    159    default:
    160        value = 0;
    161        DPRINT("ERROR: Bad read offset 0x%x\n", (unsigned int)offset);
    162        break;
    163    }
    164
    165    DPRINT("read %s [0x%02x] -> 0x%02x\n", exynos4_i2c_get_regname(offset),
    166            (unsigned int)offset, value);
    167    return value;
    168}
    169
    170static void exynos4210_i2c_write(void *opaque, hwaddr offset,
    171                              uint64_t value, unsigned size)
    172{
    173    Exynos4210I2CState *s = (Exynos4210I2CState *)opaque;
    174    uint8_t v = value & 0xff;
    175
    176    DPRINT("write %s [0x%02x] <- 0x%02x\n", exynos4_i2c_get_regname(offset),
    177            (unsigned int)offset, v);
    178
    179    switch (offset) {
    180    case I2CCON_ADDR:
    181        s->i2ccon = (v & ~I2CCON_INT_PEND) | (s->i2ccon & I2CCON_INT_PEND);
    182        if ((s->i2ccon & I2CCON_INT_PEND) && !(v & I2CCON_INT_PEND)) {
    183            s->i2ccon &= ~I2CCON_INT_PEND;
    184            qemu_irq_lower(s->irq);
    185            if (!(s->i2ccon & I2CCON_INTRS_EN)) {
    186                s->i2cstat &= ~I2CSTAT_START_BUSY;
    187            }
    188
    189            if (s->i2cstat & I2CSTAT_START_BUSY) {
    190                if (s->scl_free) {
    191                    if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Tx) {
    192                        exynos4210_i2c_data_send(s);
    193                    } else if (EXYNOS4_I2C_MODE(s->i2cstat) ==
    194                            I2CMODE_MASTER_Rx) {
    195                        exynos4210_i2c_data_receive(s);
    196                    }
    197                } else {
    198                    s->i2ccon |= I2CCON_INT_PEND;
    199                    qemu_irq_raise(s->irq);
    200                }
    201            }
    202        }
    203        break;
    204    case I2CSTAT_ADDR:
    205        s->i2cstat =
    206                (s->i2cstat & I2CSTAT_START_BUSY) | (v & ~I2CSTAT_START_BUSY);
    207
    208        if (!(s->i2cstat & I2CSTAT_OUTPUT_EN)) {
    209            s->i2cstat &= ~I2CSTAT_START_BUSY;
    210            s->scl_free = true;
    211            qemu_irq_lower(s->irq);
    212            break;
    213        }
    214
    215        /* Nothing to do if in i2c slave mode */
    216        if (!I2C_IN_MASTER_MODE(s->i2cstat)) {
    217            break;
    218        }
    219
    220        if (v & I2CSTAT_START_BUSY) {
    221            s->i2cstat &= ~I2CSTAT_LAST_BIT;
    222            s->i2cstat |= I2CSTAT_START_BUSY;    /* Line is busy */
    223            s->scl_free = false;
    224
    225            /* Generate start bit and send slave address */
    226            if (i2c_start_transfer(s->bus, s->i2cds >> 1, s->i2cds & 0x1) &&
    227                    (s->i2ccon & I2CCON_ACK_GEN)) {
    228                s->i2cstat |= I2CSTAT_LAST_BIT;
    229            } else if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Rx) {
    230                exynos4210_i2c_data_receive(s);
    231            }
    232            exynos4210_i2c_raise_interrupt(s);
    233        } else {
    234            i2c_end_transfer(s->bus);
    235            if (!(s->i2ccon & I2CCON_INT_PEND)) {
    236                s->i2cstat &= ~I2CSTAT_START_BUSY;
    237            }
    238            s->scl_free = true;
    239        }
    240        break;
    241    case I2CADD_ADDR:
    242        if ((s->i2cstat & I2CSTAT_OUTPUT_EN) == 0) {
    243            s->i2cadd = v;
    244        }
    245        break;
    246    case I2CDS_ADDR:
    247        if (s->i2cstat & I2CSTAT_OUTPUT_EN) {
    248            s->i2cds = v;
    249            s->scl_free = true;
    250            if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Tx &&
    251                    (s->i2cstat & I2CSTAT_START_BUSY) &&
    252                    !(s->i2ccon & I2CCON_INT_PEND)) {
    253                exynos4210_i2c_data_send(s);
    254            }
    255        }
    256        break;
    257    case I2CLC_ADDR:
    258        s->i2clc = v;
    259        break;
    260    default:
    261        DPRINT("ERROR: Bad write offset 0x%x\n", (unsigned int)offset);
    262        break;
    263    }
    264}
    265
    266static const MemoryRegionOps exynos4210_i2c_ops = {
    267    .read = exynos4210_i2c_read,
    268    .write = exynos4210_i2c_write,
    269    .endianness = DEVICE_NATIVE_ENDIAN,
    270};
    271
    272static const VMStateDescription exynos4210_i2c_vmstate = {
    273    .name = "exynos4210.i2c",
    274    .version_id = 1,
    275    .minimum_version_id = 1,
    276    .fields = (VMStateField[]) {
    277        VMSTATE_UINT8(i2ccon, Exynos4210I2CState),
    278        VMSTATE_UINT8(i2cstat, Exynos4210I2CState),
    279        VMSTATE_UINT8(i2cds, Exynos4210I2CState),
    280        VMSTATE_UINT8(i2cadd, Exynos4210I2CState),
    281        VMSTATE_UINT8(i2clc, Exynos4210I2CState),
    282        VMSTATE_BOOL(scl_free, Exynos4210I2CState),
    283        VMSTATE_END_OF_LIST()
    284    }
    285};
    286
    287static void exynos4210_i2c_reset(DeviceState *d)
    288{
    289    Exynos4210I2CState *s = EXYNOS4_I2C(d);
    290
    291    s->i2ccon  = 0x00;
    292    s->i2cstat = 0x00;
    293    s->i2cds   = 0xFF;
    294    s->i2clc   = 0x00;
    295    s->i2cadd  = 0xFF;
    296    s->scl_free = true;
    297}
    298
    299static void exynos4210_i2c_init(Object *obj)
    300{
    301    DeviceState *dev = DEVICE(obj);
    302    Exynos4210I2CState *s = EXYNOS4_I2C(obj);
    303    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    304
    305    memory_region_init_io(&s->iomem, obj, &exynos4210_i2c_ops, s,
    306                          TYPE_EXYNOS4_I2C, EXYNOS4_I2C_MEM_SIZE);
    307    sysbus_init_mmio(sbd, &s->iomem);
    308    sysbus_init_irq(sbd, &s->irq);
    309    s->bus = i2c_init_bus(dev, "i2c");
    310}
    311
    312static void exynos4210_i2c_class_init(ObjectClass *klass, void *data)
    313{
    314    DeviceClass *dc = DEVICE_CLASS(klass);
    315
    316    dc->vmsd = &exynos4210_i2c_vmstate;
    317    dc->reset = exynos4210_i2c_reset;
    318}
    319
    320static const TypeInfo exynos4210_i2c_type_info = {
    321    .name = TYPE_EXYNOS4_I2C,
    322    .parent = TYPE_SYS_BUS_DEVICE,
    323    .instance_size = sizeof(Exynos4210I2CState),
    324    .instance_init = exynos4210_i2c_init,
    325    .class_init = exynos4210_i2c_class_init,
    326};
    327
    328static void exynos4210_i2c_register_types(void)
    329{
    330    type_register_static(&exynos4210_i2c_type_info);
    331}
    332
    333type_init(exynos4210_i2c_register_types)