From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([208.118.235.92]:50943) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SF57k-0007pu-P3 for qemu-devel@nongnu.org; Tue, 03 Apr 2012 10:54:26 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1SF57d-0007ZG-85 for qemu-devel@nongnu.org; Tue, 03 Apr 2012 10:54:20 -0400 Received: from mailout1.w1.samsung.com ([210.118.77.11]:51124) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SF57c-0007YC-RK for qemu-devel@nongnu.org; Tue, 03 Apr 2012 10:54:13 -0400 Received: from euspt2 (mailout1.w1.samsung.com [210.118.77.11]) by mailout1.w1.samsung.com (iPlanet Messaging Server 5.2 Patch 2 (built Jul 14 2004)) with ESMTP id <0M1W00020S0XRZ@mailout1.w1.samsung.com> for qemu-devel@nongnu.org; Tue, 03 Apr 2012 15:53:21 +0100 (BST) Received: from [106.109.8.162] by spt2.w1.samsung.com (iPlanet Messaging Server 5.2 Patch 2 (built Jul 14 2004)) with ESMTPA id <0M1W00A5US22FS@spt2.w1.samsung.com> for qemu-devel@nongnu.org; Tue, 03 Apr 2012 15:54:03 +0100 (BST) Date: Tue, 03 Apr 2012 18:54:03 +0400 From: Igor Mitsyanko In-reply-to: <4F7B0107.3020202@ispras.ru> Message-id: <4F7B0F0B.1030903@samsung.com> MIME-version: 1.0 Content-type: text/plain; charset=UTF-8; format=flowed Content-transfer-encoding: 7BIT References: <1331796924-30669-1-git-send-email-i.mitsyanko@samsung.com> <1331796924-30669-2-git-send-email-i.mitsyanko@samsung.com> <4F7B0107.3020202@ispras.ru> Subject: Re: [Qemu-devel] [PATCH V2 1/3] exynos4210: add Exynos4210 i2c implementation Reply-To: i.mitsyanko@samsung.com List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Dmitry Zhurikhin Cc: peter.maydell@linaro.org, e.voevodin@samsung.com, qemu-devel@nongnu.org, kyungmin.park@samsung.com, d.solodkiy@samsung.com, m.kozlov@samsung.com, afaerber@suse.de On 04/03/2012 05:54 PM, Dmitry Zhurikhin wrote: > Let me add my two cents here. I tested this patch slightly and didn't > find any problems. > Great, thanks! > On 2012-03-15 11:35, Igor Mitsyanko wrote: >> Create 9 exynos4210 i2c interfaces. >> >> Signed-off-by: Igor Mitsyanko >> --- >> Makefile.target | 1 + >> hw/exynos4210.c | 26 +++ >> hw/exynos4210.h | 3 + >> hw/exynos4210_i2c.c | 469 +++++++++++++++++++++++++++++++++++++++++++++++++++ >> 4 files changed, 499 insertions(+), 0 deletions(-) >> create mode 100644 hw/exynos4210_i2c.c >> >> diff --git a/Makefile.target b/Makefile.target >> index eb25941..5e8a1d4 100644 >> --- a/Makefile.target >> +++ b/Makefile.target >> @@ -357,6 +357,7 @@ obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o >> obj-arm-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o >> obj-arm-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o >> obj-arm-y += exynos4210_pmu.o exynos4210_mct.o exynos4210_fimd.o >> +obj-arm-y += exynos4210_i2c.o >> obj-arm-y += arm_l2x0.o >> obj-arm-y += arm_mptimer.o a15mpcore.o >> obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o >> diff --git a/hw/exynos4210.c b/hw/exynos4210.c >> index f904370..464f157 100644 >> --- a/hw/exynos4210.c >> +++ b/hw/exynos4210.c >> @@ -35,6 +35,13 @@ >> /* MCT */ >> #define EXYNOS4210_MCT_BASE_ADDR 0x10050000 >> >> +/* I2C */ >> +#define EXYNOS4210_I2C_SHIFT 0x00010000 >> +#define EXYNOS4210_I2C_BASE_ADDR 0x13860000 >> +/* Interrupt Group of External Interrupt Combiner for I2C */ >> +#define EXYNOS4210_I2C_INTG 27 >> +#define EXYNOS4210_HDMI_INTG 16 >> + >> /* UART's definitions */ >> #define EXYNOS4210_UART0_BASE_ADDR 0x13800000 >> #define EXYNOS4210_UART1_BASE_ADDR 0x13810000 >> @@ -242,6 +249,25 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, >> s->irq_table[exynos4210_get_irq(35, 3)]); >> sysbus_mmio_map(busdev, 0, EXYNOS4210_MCT_BASE_ADDR); >> >> + /*** I2C ***/ >> + for (n = 0; n< EXYNOS4210_I2C_NUMBER; n++) { >> + uint32_t addr = EXYNOS4210_I2C_BASE_ADDR + EXYNOS4210_I2C_SHIFT * n; >> + qemu_irq i2c_irq; >> + >> + if (n< 8) { >> + i2c_irq = s->irq_table[exynos4210_get_irq(EXYNOS4210_I2C_INTG, n)]; >> + } else { >> + i2c_irq = s->irq_table[exynos4210_get_irq(EXYNOS4210_HDMI_INTG, 1)]; >> + } >> + >> + dev = qdev_create(NULL, "exynos4210.i2c"); >> + qdev_init_nofail(dev); >> + busdev = sysbus_from_qdev(dev); >> + sysbus_connect_irq(busdev, 0, i2c_irq); >> + sysbus_mmio_map(busdev, 0, addr); >> + s->i2c_if[n] = (i2c_bus *)qdev_get_child_bus(dev, "i2c"); >> + } >> + >> /*** UARTs ***/ >> exynos4210_uart_create(EXYNOS4210_UART0_BASE_ADDR, >> EXYNOS4210_UART0_FIFO_SIZE, 0, NULL, >> diff --git a/hw/exynos4210.h b/hw/exynos4210.h >> index c112e03..1205fb5 100644 >> --- a/hw/exynos4210.h >> +++ b/hw/exynos4210.h >> @@ -74,6 +74,8 @@ >> #define EXYNOS4210_EXT_GIC_NIRQ (160-32) >> #define EXYNOS4210_INT_GIC_NIRQ 64 >> >> +#define EXYNOS4210_I2C_NUMBER 9 >> + >> typedef struct Exynos4210Irq { >> qemu_irq int_combiner_irq[EXYNOS4210_MAX_INT_COMBINER_IN_IRQ]; >> qemu_irq ext_combiner_irq[EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ]; >> @@ -95,6 +97,7 @@ typedef struct Exynos4210State { >> MemoryRegion dram1_mem; >> MemoryRegion boot_secondary; >> MemoryRegion bootreg_mem; >> + i2c_bus *i2c_if[EXYNOS4210_I2C_NUMBER]; >> } Exynos4210State; >> >> Exynos4210State *exynos4210_init(MemoryRegion *system_mem, >> diff --git a/hw/exynos4210_i2c.c b/hw/exynos4210_i2c.c >> new file mode 100644 >> index 0000000..42f770d >> --- /dev/null >> +++ b/hw/exynos4210_i2c.c >> @@ -0,0 +1,469 @@ >> +/* >> + * Exynos4210 I2C Bus Serial Interface Emulation >> + * >> + * Copyright (C) 2012 Samsung Electronics Co Ltd. >> + * Maksim Kozlov, >> + * Igor Mitsyanko, >> + * >> + * This program is free software; you can redistribute it and/or modify it >> + * under the terms of the GNU General Public License as published by the >> + * Free Software Foundation; either version 2 of the License, or >> + * (at your option) any later version. >> + * >> + * This program is distributed in the hope that it will be useful, but WITHOUT >> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or >> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License >> + * for more details. >> + * >> + * You should have received a copy of the GNU General Public License along >> + * with this program; if not, see. >> + * >> + */ >> + >> +#include "qemu-timer.h" >> +#include "sysbus.h" >> +#include "i2c.h" >> + >> +#ifndef EXYNOS4_I2C_DEBUG >> +#define EXYNOS4_I2C_DEBUG 0 >> +#endif >> + >> +#define TYPE_EXYNOS4_I2C "exynos4210.i2c" >> +#define TYPE_EXYNOS4_I2C_SLAVE "exynos4210.i2c-slave" >> +#define EXYNOS4_I2C(obj) \ >> + OBJECT_CHECK(Exynos4210I2CState, (obj), TYPE_EXYNOS4_I2C) >> +#define EXYNOS4_I2C_SLAVE(obj) \ >> + OBJECT_CHECK(Exynos4210I2CSlave, (obj), TYPE_EXYNOS4_I2C_SLAVE) >> + >> +/* Exynos4210 I2C memory map */ >> +#define EXYNOS4_I2C_MEM_SIZE 0x14 >> +#define I2CCON_ADDR 0x00 /* control register */ >> +#define I2CSTAT_ADDR 0x04 /* control/status register */ >> +#define I2CADD_ADDR 0x08 /* address register */ >> +#define I2CDS_ADDR 0x0c /* data shift register */ >> +#define I2CLC_ADDR 0x10 /* line control register */ >> + >> +#define I2CCON_INTERRUPTS_EN (1<< 5) >> +#define I2CCON_INTERRUPT_PEND (1<< 4) >> + >> +#define EXYNOS4_I2C_MODE(reg) (((reg)>> 6)& 3) >> +#define I2C_IN_MASTER_MODE(reg) (((reg)>> 6)& 2) >> +#define I2CSTAT_MODE_SLAVE_Rx 0x0 >> +#define I2CSTAT_MODE_SLAVE_Tx 0x1 >> +#define I2CMODE_MASTER_Rx 0x2 >> +#define I2CMODE_MASTER_Tx 0x3 >> +#define I2CSTAT_LAST_BIT (1<< 0) >> +#define I2CSTAT_SLAVE_ADDR_ZERO (1<< 1) >> +#define I2CSTAT_SLAVE_ADDR_MATCH (1<< 2) >> +#define I2CSTAT_ARBITR_STATUS (1<< 3) >> +#define I2CSTAT_OUTPUT_EN (1<< 4) >> +#define I2CSTAT_START_BUSY (1<< 5) >> + >> + >> +#if EXYNOS4_I2C_DEBUG >> +#define DPRINT(fmt, args...) \ >> + do {fprintf(stderr, "QEMU I2C: "fmt, ## args); } while (0) >> + >> +static const char *exynos4_i2c_regs_names[] = { >> + [I2CCON_ADDR>> 2] = "I2CCON", >> + [I2CSTAT_ADDR>> 2] = "I2CSTAT", >> + [I2CADD_ADDR>> 2] = "I2CADD", >> + [I2CDS_ADDR>> 2] = "I2CDS", >> + [I2CLC_ADDR>> 2] = "I2CLC", >> +}; >> + >> +#else >> +#define DPRINT(fmt, args...) do { } while (0) >> +#endif >> + >> +typedef struct Exynos4210I2CSlave Exynos4210I2CSlave; >> + >> +typedef struct Exynos4210I2CState { >> + SysBusDevice busdev; >> + MemoryRegion iomem; >> + i2c_bus *bus; >> + Exynos4210I2CSlave *slave; >> + qemu_irq irq; >> + QEMUTimer *receive_timer; >> + QEMUTimer *send_timer; >> + >> + uint8_t i2ccon; >> + uint8_t i2cstat; >> + uint8_t i2cadd; >> + uint8_t i2cds; >> + uint8_t i2clc; >> +} Exynos4210I2CState; >> + >> +enum { >> + idle, >> + start_bit_received, >> + sending_data, >> + receiving_data >> +}; >> + >> +struct Exynos4210I2CSlave { >> + I2CSlave i2c; >> + Exynos4210I2CState *host; >> + uint32_t status; >> +}; >> + >> + >> +static inline void exynos4210_i2c_set_irq(Exynos4210I2CState *s) >> +{ >> + if (s->i2ccon& I2CCON_INTERRUPTS_EN) { >> + s->i2ccon |= I2CCON_INTERRUPT_PEND; >> + qemu_irq_raise(s->irq); >> + } >> +} >> + >> +static void exynos4210_i2c_data_received(void *opaque) >> +{ >> + Exynos4210I2CState *s = (Exynos4210I2CState *)opaque; >> + int ret; >> + >> + ret = i2c_recv(s->bus); >> + if (ret< 0) { >> + s->i2cstat |= I2CSTAT_LAST_BIT; /* Data is not acknowledged */ >> + } else { >> + s->i2cds = ret; >> + s->i2cstat&= ~I2CSTAT_LAST_BIT; /* Data is acknowledged */ >> + } >> + exynos4210_i2c_set_irq(s); >> +} >> + >> +static void exynos4210_i2c_data_sent(void *opaque) >> +{ >> + Exynos4210I2CState *s = (Exynos4210I2CState *)opaque; >> + >> + if (i2c_send(s->bus, s->i2cds)< 0) { >> + s->i2cstat |= I2CSTAT_LAST_BIT; /* Data is not acknowledged */ >> + } else { >> + s->i2cstat&= ~I2CSTAT_LAST_BIT; /* Data is acknowledged */ >> + } >> + exynos4210_i2c_set_irq(s); >> +} >> + >> +static uint64_t exynos4210_i2c_read(void *opaque, target_phys_addr_t offset, >> + unsigned size) >> +{ >> + Exynos4210I2CState *s = (Exynos4210I2CState *)opaque; >> + uint8_t value; >> + >> + switch (offset) { >> + case I2CCON_ADDR: >> + value = s->i2ccon; >> + break; >> + case I2CSTAT_ADDR: >> + value = s->i2cstat; >> + break; >> + case I2CADD_ADDR: >> + value = s->i2cadd; >> + break; >> + case I2CDS_ADDR: >> + value = s->i2cds; >> + break; >> + case I2CLC_ADDR: >> + value = s->i2clc; >> + break; >> + default: >> + value = 0; >> + DPRINT("ERROR: Bad read offset 0x%x\n", (unsigned int)offset); >> + break; >> + } >> + >> + DPRINT("read %s [0x%02x] -> 0x%02x\n", exynos4_i2c_regs_names[offset>> 2], >> + (unsigned int)offset, value); > Just for security it's better to check offset to not go out of the array > borders. Ok, good point. >> + return value; >> +} >> + >> +static void exynos4210_i2c_write(void *opaque, target_phys_addr_t offset, >> + uint64_t value, unsigned size) >> +{ >> + Exynos4210I2CState *s = (Exynos4210I2CState *)opaque; >> + uint8_t v = value& 0xff; >> + >> + DPRINT("write %s [0x%02x]<- 0x%02x\n", exynos4_i2c_regs_names[offset>> 2], >> + (unsigned int)offset, v); >> + >> + switch (offset) { >> + case I2CCON_ADDR: >> + if ((s->i2ccon& I2CCON_INTERRUPT_PEND)&& >> + !(v& I2CCON_INTERRUPT_PEND)) { >> + s->i2ccon&= ~I2CCON_INTERRUPT_PEND; >> + if (!(s->i2ccon& I2CCON_INTERRUPTS_EN)) { >> + s->i2cstat&= ~I2CSTAT_START_BUSY; >> + } >> + qemu_irq_lower(s->irq); >> + >> + if (!(s->i2cstat& I2CSTAT_OUTPUT_EN) || >> + !(s->i2cstat& I2CSTAT_START_BUSY)) { >> + s->i2cstat&= ~I2CSTAT_START_BUSY; /* Line is not busy */ >> + break; >> + } >> + /* Continue operation if transfer is ongoing */ >> + if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Rx) { >> + qemu_mod_timer(s->receive_timer, qemu_get_clock_ns(vm_clock)+1); >> + } else if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Tx) { >> + qemu_mod_timer(s->send_timer, qemu_get_clock_ns(vm_clock) + 1); > Why do you need to use timers here? Why not just call the corresponding > functions directly? I can guess because when sending new message Linux > driver first enables the interrupt and then writes the stat register. > Am I right? Yes, and I wanted to emulate a small delay between qemu_irq_lower() and qemu_raise_irq(), But I've just tried to replace timer usage with direct functions calling and it still works stably, I think I wil remove timers in next version. >> + } >> + } >> + >> + /* bit 4 can't be written */ >> + s->i2ccon = (v& ~I2CCON_INTERRUPT_PEND) | >> + (s->i2ccon& I2CCON_INTERRUPT_PEND); >> + break; >> + case I2CSTAT_ADDR: >> + s->i2cstat = >> + (s->i2cstat& I2CSTAT_START_BUSY) | (v& ~I2CSTAT_START_BUSY); >> + >> + if (!(s->i2cstat& I2CSTAT_OUTPUT_EN)) { >> + s->i2cstat&= ~I2CSTAT_START_BUSY; >> + qemu_del_timer(s->receive_timer); >> + qemu_del_timer(s->send_timer); >> + break; >> + } >> + >> + /* Nothing to do if in i2c slave mode */ >> + if (!I2C_IN_MASTER_MODE(s->i2cstat)) { >> + break; >> + } >> + >> + if (v& I2CSTAT_START_BUSY) { >> + s->i2cstat |= I2CSTAT_START_BUSY; /* Line is busy */ >> + /* Generate start bit and send slave address */ >> + i2c_start_transfer(s->bus, s->i2cds>> 1, s->i2cds& 0x1); >> + exynos4210_i2c_data_sent(s); >> + } else { >> + i2c_end_transfer(s->bus); >> + /* Linux driver may start next i2c transaction before it >> + * acknowledges an interrupt request from previous transaction. >> + * This may result in timeout waiting for I2C bus idle and >> + * corrupted transfer. Releasing I2C bus only after interrupt is >> + * accepted prevents this corruption. */ >> + if (!(s->i2ccon& I2CCON_INTERRUPT_PEND)) { >> + s->i2cstat&= ~I2CSTAT_START_BUSY; >> + } >> + } >> + break; >> + case I2CADD_ADDR: >> + if ((s->i2cstat& I2CSTAT_OUTPUT_EN) == 0) { >> + s->i2cadd = v; >> + i2c_set_slave_address(&s->slave->i2c, v); >> + } >> + break; >> + case I2CDS_ADDR: >> + if (s->i2cstat& I2CSTAT_OUTPUT_EN) { >> + s->i2cds = v; >> + } >> + break; >> + case I2CLC_ADDR: >> + s->i2clc = v; >> + break; >> + default: >> + DPRINT("ERROR: Bad write offset 0x%x\n", (unsigned int)offset); >> + break; >> + } >> +} >> + >> +static const MemoryRegionOps exynos4210_i2c_ops = { >> + .read = exynos4210_i2c_read, >> + .write = exynos4210_i2c_write, >> + .endianness = DEVICE_NATIVE_ENDIAN, >> +}; >> + >> +static const VMStateDescription exynos4210_i2c_slave_vmstate = { >> + .name = TYPE_EXYNOS4_I2C_SLAVE, >> + .version_id = 1, >> + .minimum_version_id = 1, >> + .fields = (VMStateField[]) { >> + VMSTATE_I2C_SLAVE(i2c, Exynos4210I2CSlave), >> + VMSTATE_UINT32(status, Exynos4210I2CSlave), >> + VMSTATE_END_OF_LIST() >> + } >> +}; >> + >> +static const VMStateDescription exynos4210_i2c_vmstate = { >> + .name = TYPE_EXYNOS4_I2C, >> + .version_id = 1, >> + .minimum_version_id = 1, >> + .fields = (VMStateField[]) { >> + VMSTATE_UINT8(i2ccon, Exynos4210I2CState), >> + VMSTATE_UINT8(i2cstat, Exynos4210I2CState), >> + VMSTATE_UINT8(i2cds, Exynos4210I2CState), >> + VMSTATE_UINT8(i2clc, Exynos4210I2CState), > Should i2cadd also be saved here? Yes >> + VMSTATE_TIMER(receive_timer, Exynos4210I2CState), >> + VMSTATE_TIMER(send_timer, Exynos4210I2CState), >> + VMSTATE_STRUCT_POINTER(slave, Exynos4210I2CState, >> + exynos4210_i2c_slave_vmstate, Exynos4210I2CSlave *), >> + VMSTATE_END_OF_LIST() >> + } >> +}; >> + >> +static void exynos4210_i2c_reset(DeviceState *d) >> +{ >> + Exynos4210I2CState *s = EXYNOS4_I2C(d); >> + >> + s->i2ccon = 0x0f; /* 0x0X */ >> + s->i2cstat = 0x00; >> + s->i2cds = 0xff; /* 0xXX */ >> + s->i2clc = 0x00; >> + s->i2cadd = 0xff; /* 0xXX */ >> + s->slave->status = idle; >> + qemu_del_timer(s->receive_timer); >> + qemu_del_timer(s->send_timer); >> +} >> + >> +static int exynos4210_i2c_init(SysBusDevice *dev) >> +{ >> + Exynos4210I2CState *s = EXYNOS4_I2C(dev); >> + DeviceState *slave; >> + >> + memory_region_init_io(&s->iomem,&exynos4210_i2c_ops, s, TYPE_EXYNOS4_I2C, >> + EXYNOS4_I2C_MEM_SIZE); >> + sysbus_init_mmio(dev,&s->iomem); >> + sysbus_init_irq(dev,&s->irq); >> + s->bus = i2c_init_bus(&dev->qdev, "i2c"); >> + s->receive_timer = >> + qemu_new_timer_ns(vm_clock, exynos4210_i2c_data_received, s); >> + s->send_timer = qemu_new_timer_ns(vm_clock, exynos4210_i2c_data_sent, s); >> + >> + /* i2c slave initialization */ >> + slave = i2c_create_slave(s->bus, TYPE_EXYNOS4_I2C_SLAVE, 0xff); >> + s->slave = EXYNOS4_I2C_SLAVE(slave); >> + s->slave->host = s; >> + >> + return 0; >> +} >> + >> +static void exynos4210_i2c_class_init(ObjectClass *class, void *data) >> +{ >> + DeviceClass *dc = DEVICE_CLASS(class); >> + SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(class); >> + >> + dc->vmsd =&exynos4210_i2c_vmstate; >> + dc->reset = exynos4210_i2c_reset; >> + sbc->init = exynos4210_i2c_init; >> +} >> + >> +static TypeInfo exynos4210_i2c_info = { >> + .name = TYPE_EXYNOS4_I2C, >> + .parent = TYPE_SYS_BUS_DEVICE, >> + .instance_size = sizeof(Exynos4210I2CState), >> + .class_init = exynos4210_i2c_class_init, >> +}; >> + >> +static int exynos4210_i2c_slave_init(I2CSlave *i2c) >> +{ >> + Exynos4210I2CSlave *s = EXYNOS4_I2C_SLAVE(i2c); >> + >> + s->status = idle; >> + return 0; >> +} >> + >> +static void exynos4210_i2c_slave_event(I2CSlave *i2c, enum i2c_event event) >> +{ >> + Exynos4210I2CSlave *s = EXYNOS4_I2C_SLAVE(i2c); >> + Exynos4210I2CState *host = s->host; >> + >> + if (!(host->i2cstat& I2CSTAT_OUTPUT_EN) || >> + I2C_IN_MASTER_MODE(host->i2cstat)) { >> + return; >> + } >> + >> + switch (event) { >> + case I2C_START_RECV: >> + case I2C_START_SEND: >> + host->i2cstat&= ~(I2CSTAT_SLAVE_ADDR_ZERO | I2CSTAT_SLAVE_ADDR_MATCH); >> + s->status = start_bit_received; >> + host->i2cstat |= I2CSTAT_START_BUSY; /* Line is busy */ >> + break; >> + case I2C_FINISH: >> + s->status = idle; >> + host->i2cstat&= ~(I2CSTAT_SLAVE_ADDR_ZERO | I2CSTAT_SLAVE_ADDR_MATCH | >> + I2CSTAT_START_BUSY); >> + break; >> + default: >> + break; >> + } >> +} >> + >> +static int exynos4210_i2c_slave_recv(I2CSlave *i2c) >> +{ >> + Exynos4210I2CSlave *s = EXYNOS4_I2C_SLAVE(i2c); >> + Exynos4210I2CState *host = s->host; >> + >> + if (!(host->i2cstat& I2CSTAT_OUTPUT_EN) || >> + I2C_IN_MASTER_MODE(host->i2cstat)) { >> + return -1; >> + } >> + >> + if ((s->status != sending_data) || (EXYNOS4_I2C_MODE(host->i2cstat) != >> + I2CSTAT_MODE_SLAVE_Tx)) { >> + return -1; >> + } >> + >> + exynos4210_i2c_set_irq(host); >> + return host->i2cds; >> +} >> + >> +static int exynos4210_i2c_slave_send(I2CSlave *i2c, uint8_t data) >> +{ >> + Exynos4210I2CSlave *s = EXYNOS4_I2C_SLAVE(i2c); >> + Exynos4210I2CState *host = s->host; >> + >> + if (!(host->i2cstat& I2CSTAT_OUTPUT_EN) || >> + I2C_IN_MASTER_MODE(host->i2cstat)) { >> + return -1; >> + } >> + >> + if (s->status == start_bit_received) { >> + host->i2cds = data; >> + s->status = idle; >> + if ((data& 0xFE) == 0) { >> + host->i2cstat |= I2CSTAT_SLAVE_ADDR_ZERO; >> + return -1; >> + } >> + if ((host->i2cadd& 0xFE) != (data& 0xFE)) { >> + return -1; >> + } >> + host->i2cstat |= I2CSTAT_SLAVE_ADDR_MATCH; >> + (data& 0x1) ? (s->status = receiving_data) : >> + (s->status = sending_data); >> + exynos4210_i2c_set_irq(host); >> + return 0; >> + } >> + >> + if ((s->status != receiving_data) || (EXYNOS4_I2C_MODE(host->i2cstat) != >> + I2CSTAT_MODE_SLAVE_Rx)) { >> + return -1; >> + } >> + >> + host->i2cds = data; >> + exynos4210_i2c_set_irq(host); >> + return 0; >> +} >> + >> +static void exynos4210_i2cslave_class_init(ObjectClass *klass, void *data) >> +{ >> + I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); >> + >> + k->init = exynos4210_i2c_slave_init; >> + k->event = exynos4210_i2c_slave_event; >> + k->recv = exynos4210_i2c_slave_recv; >> + k->send = exynos4210_i2c_slave_send; >> +} >> + >> +static TypeInfo exynos4210_i2c_slave = { >> + .name = TYPE_EXYNOS4_I2C_SLAVE, >> + .parent = TYPE_I2C_SLAVE, >> + .instance_size = sizeof(Exynos4210I2CSlave), >> + .class_init = exynos4210_i2cslave_class_init, >> +}; >> + >> +static void exynos4210_i2c_register_types(void) >> +{ >> + type_register_static(&exynos4210_i2c_slave); >> + type_register_static(&exynos4210_i2c_info); >> +} >> + >> +type_init(exynos4210_i2c_register_types) > PS: This I2C implementation seems to be rather complex. Did you check > what we wrote for S5PC110 (ah, I also learnt that Samsung renamed it to > Exynos3210) almost three years ago > (https://gitorious.org/samsung-linux-platform/qemu/blobs/s5pc110-stable/hw/s5pc1xx_i2c.c)? > When I compared documentation and kernel driver sources for both devices > I couldn't find any significant difference. Generally speaking, is it > worth redoing everything from scratch instead of updating? mm, it doesn't worth it, something went wrong here I think ... -- Mitsyanko Igor ASWG, Moscow R&D center, Samsung Electronics email: i.mitsyanko@samsung.com