qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

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"
     37 OBJECT_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 
     64 static 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 
     86 struct 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 
    101 static 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 
    109 static 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 
    119 static 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 
    131 static 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 
    170 static 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 
    266 static const MemoryRegionOps exynos4210_i2c_ops = {
    267     .read = exynos4210_i2c_read,
    268     .write = exynos4210_i2c_write,
    269     .endianness = DEVICE_NATIVE_ENDIAN,
    270 };
    271 
    272 static 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 
    287 static 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 
    299 static 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 
    312 static 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 
    320 static 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 
    328 static void exynos4210_i2c_register_types(void)
    329 {
    330     type_register_static(&exynos4210_i2c_type_info);
    331 }
    332 
    333 type_init(exynos4210_i2c_register_types)