From 53ed424e09f555598f7af286787a76d9c397e812 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 5 Feb 2013 13:07:03 +0100 Subject: hw: move I2C controllers to hw/i2c/, configure via default-configs/ Signed-off-by: Paolo Bonzini --- default-configs/arm-softmmu.mak | 1 + hw/arm/Makefile.objs | 5 +- hw/bitbang_i2c.c | 245 -------------------- hw/exynos4210_i2c.c | 334 --------------------------- hw/i2c/Makefile.objs | 3 + hw/i2c/bitbang_i2c.c | 245 ++++++++++++++++++++ hw/i2c/exynos4210_i2c.c | 334 +++++++++++++++++++++++++++ hw/i2c/omap_i2c.c | 492 ++++++++++++++++++++++++++++++++++++++++ hw/omap_i2c.c | 492 ---------------------------------------- 9 files changed, 1077 insertions(+), 1074 deletions(-) delete mode 100644 hw/bitbang_i2c.c delete mode 100644 hw/exynos4210_i2c.c create mode 100644 hw/i2c/bitbang_i2c.c create mode 100644 hw/i2c/exynos4210_i2c.c create mode 100644 hw/i2c/omap_i2c.c delete mode 100644 hw/omap_i2c.c diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index a7dd44a432..24b0d41114 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -50,6 +50,7 @@ CONFIG_CADENCE=y CONFIG_XGMAC=y CONFIG_EXYNOS4=y CONFIG_PXA2XX=y +CONFIG_BITBANG_I2C=y CONFIG_FRAMEBUFFER=y CONFIG_MARVELL_88W8618=y CONFIG_OMAP=y diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 3efefe59cf..45bde58952 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -6,20 +6,19 @@ obj-y += realview_gic.o arm_sysctl.o arm11mpcore.o a9mpcore.o obj-y += exynos4210_gic.o exynos4210_combiner.o obj-y += exynos4210_uart.o exynos4210_pwm.o obj-y += exynos4210_pmu.o exynos4210_mct.o -obj-y += exynos4210_rtc.o exynos4210_i2c.o +obj-y += exynos4210_rtc.o obj-y += arm_mptimer.o a15mpcore.o obj-y += armv7m_nvic.o obj-y += pxa2xx_timer.o pxa2xx_dma.o obj-y += pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o obj-y += zaurus.o -obj-y += omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o \ +obj-y += omap_dma.o omap_clk.o omap_mmc.o \ omap_gpio.o omap_intc.o omap_uart.o obj-y += soc_dma.o omap_gptimer.o omap_synctimer.o \ omap_gpmc.o omap_sdrc.o omap_spi.o omap_tap.o omap_l4.o obj-y += tsc210x.o obj-y += cbus.o tusb6010.o obj-y += mst_fpga.o -obj-y += bitbang_i2c.o obj-y += strongarm.o obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o obj-$(CONFIG_KVM) += kvm/arm_gic.o diff --git a/hw/bitbang_i2c.c b/hw/bitbang_i2c.c deleted file mode 100644 index b8e6d3a103..0000000000 --- a/hw/bitbang_i2c.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Bit-Bang i2c emulation extracted from - * Marvell MV88W8618 / Freecom MusicPal emulation. - * - * Copyright (c) 2008 Jan Kiszka - * - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ -#include "hw/hw.h" -#include "hw/bitbang_i2c.h" -#include "hw/sysbus.h" - -//#define DEBUG_BITBANG_I2C - -#ifdef DEBUG_BITBANG_I2C -#define DPRINTF(fmt, ...) \ -do { printf("bitbang_i2c: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#endif - -typedef enum bitbang_i2c_state { - STOPPED = 0, - SENDING_BIT7, - SENDING_BIT6, - SENDING_BIT5, - SENDING_BIT4, - SENDING_BIT3, - SENDING_BIT2, - SENDING_BIT1, - SENDING_BIT0, - WAITING_FOR_ACK, - RECEIVING_BIT7, - RECEIVING_BIT6, - RECEIVING_BIT5, - RECEIVING_BIT4, - RECEIVING_BIT3, - RECEIVING_BIT2, - RECEIVING_BIT1, - RECEIVING_BIT0, - SENDING_ACK, - SENT_NACK -} bitbang_i2c_state; - -struct bitbang_i2c_interface { - i2c_bus *bus; - bitbang_i2c_state state; - int last_data; - int last_clock; - int device_out; - uint8_t buffer; - int current_addr; -}; - -static void bitbang_i2c_enter_stop(bitbang_i2c_interface *i2c) -{ - DPRINTF("STOP\n"); - if (i2c->current_addr >= 0) - i2c_end_transfer(i2c->bus); - i2c->current_addr = -1; - i2c->state = STOPPED; -} - -/* Set device data pin. */ -static int bitbang_i2c_ret(bitbang_i2c_interface *i2c, int level) -{ - i2c->device_out = level; - //DPRINTF("%d %d %d\n", i2c->last_clock, i2c->last_data, i2c->device_out); - return level & i2c->last_data; -} - -/* Leave device data pin unodified. */ -static int bitbang_i2c_nop(bitbang_i2c_interface *i2c) -{ - return bitbang_i2c_ret(i2c, i2c->device_out); -} - -/* Returns data line level. */ -int bitbang_i2c_set(bitbang_i2c_interface *i2c, int line, int level) -{ - int data; - - if (level != 0 && level != 1) { - abort(); - } - - if (line == BITBANG_I2C_SDA) { - if (level == i2c->last_data) { - return bitbang_i2c_nop(i2c); - } - i2c->last_data = level; - if (i2c->last_clock == 0) { - return bitbang_i2c_nop(i2c); - } - if (level == 0) { - DPRINTF("START\n"); - /* START condition. */ - i2c->state = SENDING_BIT7; - i2c->current_addr = -1; - } else { - /* STOP condition. */ - bitbang_i2c_enter_stop(i2c); - } - return bitbang_i2c_ret(i2c, 1); - } - - data = i2c->last_data; - if (i2c->last_clock == level) { - return bitbang_i2c_nop(i2c); - } - i2c->last_clock = level; - if (level == 0) { - /* State is set/read at the start of the clock pulse. - release the data line at the end. */ - return bitbang_i2c_ret(i2c, 1); - } - switch (i2c->state) { - case STOPPED: - case SENT_NACK: - return bitbang_i2c_ret(i2c, 1); - - case SENDING_BIT7 ... SENDING_BIT0: - i2c->buffer = (i2c->buffer << 1) | data; - /* will end up in WAITING_FOR_ACK */ - i2c->state++; - return bitbang_i2c_ret(i2c, 1); - - case WAITING_FOR_ACK: - if (i2c->current_addr < 0) { - i2c->current_addr = i2c->buffer; - DPRINTF("Address 0x%02x\n", i2c->current_addr); - i2c_start_transfer(i2c->bus, i2c->current_addr >> 1, - i2c->current_addr & 1); - } else { - DPRINTF("Sent 0x%02x\n", i2c->buffer); - i2c_send(i2c->bus, i2c->buffer); - } - if (i2c->current_addr & 1) { - i2c->state = RECEIVING_BIT7; - } else { - i2c->state = SENDING_BIT7; - } - return bitbang_i2c_ret(i2c, 0); - - case RECEIVING_BIT7: - i2c->buffer = i2c_recv(i2c->bus); - DPRINTF("RX byte 0x%02x\n", i2c->buffer); - /* Fall through... */ - case RECEIVING_BIT6 ... RECEIVING_BIT0: - data = i2c->buffer >> 7; - /* will end up in SENDING_ACK */ - i2c->state++; - i2c->buffer <<= 1; - return bitbang_i2c_ret(i2c, data); - - case SENDING_ACK: - i2c->state = RECEIVING_BIT7; - if (data != 0) { - DPRINTF("NACKED\n"); - i2c->state = SENT_NACK; - i2c_nack(i2c->bus); - } else { - DPRINTF("ACKED\n"); - } - return bitbang_i2c_ret(i2c, 1); - } - abort(); -} - -bitbang_i2c_interface *bitbang_i2c_init(i2c_bus *bus) -{ - bitbang_i2c_interface *s; - - s = g_malloc0(sizeof(bitbang_i2c_interface)); - - s->bus = bus; - s->last_data = 1; - s->last_clock = 1; - s->device_out = 1; - - return s; -} - -/* GPIO interface. */ -typedef struct { - SysBusDevice busdev; - MemoryRegion dummy_iomem; - bitbang_i2c_interface *bitbang; - int last_level; - qemu_irq out; -} GPIOI2CState; - -static void bitbang_i2c_gpio_set(void *opaque, int irq, int level) -{ - GPIOI2CState *s = opaque; - - level = bitbang_i2c_set(s->bitbang, irq, level); - if (level != s->last_level) { - s->last_level = level; - qemu_set_irq(s->out, level); - } -} - -static int gpio_i2c_init(SysBusDevice *dev) -{ - GPIOI2CState *s = FROM_SYSBUS(GPIOI2CState, dev); - i2c_bus *bus; - - memory_region_init(&s->dummy_iomem, "gpio_i2c", 0); - sysbus_init_mmio(dev, &s->dummy_iomem); - - bus = i2c_init_bus(&dev->qdev, "i2c"); - s->bitbang = bitbang_i2c_init(bus); - - qdev_init_gpio_in(&dev->qdev, bitbang_i2c_gpio_set, 2); - qdev_init_gpio_out(&dev->qdev, &s->out, 1); - - return 0; -} - -static void gpio_i2c_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = gpio_i2c_init; - dc->desc = "Virtual GPIO to I2C bridge"; -} - -static const TypeInfo gpio_i2c_info = { - .name = "gpio_i2c", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(GPIOI2CState), - .class_init = gpio_i2c_class_init, -}; - -static void bitbang_i2c_register_types(void) -{ - type_register_static(&gpio_i2c_info); -} - -type_init(bitbang_i2c_register_types) diff --git a/hw/exynos4210_i2c.c b/hw/exynos4210_i2c.c deleted file mode 100644 index 196f88907d..0000000000 --- a/hw/exynos4210_i2c.c +++ /dev/null @@ -1,334 +0,0 @@ -/* - * 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 "hw/sysbus.h" -#include "hw/i2c/i2c.h" - -#ifndef EXYNOS4_I2C_DEBUG -#define EXYNOS4_I2C_DEBUG 0 -#endif - -#define TYPE_EXYNOS4_I2C "exynos4210.i2c" -#define EXYNOS4_I2C(obj) \ - OBJECT_CHECK(Exynos4210I2CState, (obj), TYPE_EXYNOS4_I2C) - -/* 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_ACK_GEN (1 << 7) -#define I2CCON_INTRS_EN (1 << 5) -#define I2CCON_INT_PEND (1 << 4) - -#define EXYNOS4_I2C_MODE(reg) (((reg) >> 6) & 3) -#define I2C_IN_MASTER_MODE(reg) (((reg) >> 6) & 2) -#define I2CMODE_MASTER_Rx 0x2 -#define I2CMODE_MASTER_Tx 0x3 -#define I2CSTAT_LAST_BIT (1 << 0) -#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_get_regname(unsigned offset) -{ - switch (offset) { - case I2CCON_ADDR: - return "I2CCON"; - case I2CSTAT_ADDR: - return "I2CSTAT"; - case I2CADD_ADDR: - return "I2CADD"; - case I2CDS_ADDR: - return "I2CDS"; - case I2CLC_ADDR: - return "I2CLC"; - default: - return "[?]"; - } -} - -#else -#define DPRINT(fmt, args...) do { } while (0) -#endif - -typedef struct Exynos4210I2CState { - SysBusDevice busdev; - MemoryRegion iomem; - i2c_bus *bus; - qemu_irq irq; - - uint8_t i2ccon; - uint8_t i2cstat; - uint8_t i2cadd; - uint8_t i2cds; - uint8_t i2clc; - bool scl_free; -} Exynos4210I2CState; - -static inline void exynos4210_i2c_raise_interrupt(Exynos4210I2CState *s) -{ - if (s->i2ccon & I2CCON_INTRS_EN) { - s->i2ccon |= I2CCON_INT_PEND; - qemu_irq_raise(s->irq); - } -} - -static void exynos4210_i2c_data_receive(void *opaque) -{ - Exynos4210I2CState *s = (Exynos4210I2CState *)opaque; - int ret; - - s->i2cstat &= ~I2CSTAT_LAST_BIT; - s->scl_free = false; - ret = i2c_recv(s->bus); - if (ret < 0 && (s->i2ccon & I2CCON_ACK_GEN)) { - s->i2cstat |= I2CSTAT_LAST_BIT; /* Data is not acknowledged */ - } else { - s->i2cds = ret; - } - exynos4210_i2c_raise_interrupt(s); -} - -static void exynos4210_i2c_data_send(void *opaque) -{ - Exynos4210I2CState *s = (Exynos4210I2CState *)opaque; - - s->i2cstat &= ~I2CSTAT_LAST_BIT; - s->scl_free = false; - if (i2c_send(s->bus, s->i2cds) < 0 && (s->i2ccon & I2CCON_ACK_GEN)) { - s->i2cstat |= I2CSTAT_LAST_BIT; - } - exynos4210_i2c_raise_interrupt(s); -} - -static uint64_t exynos4210_i2c_read(void *opaque, hwaddr 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; - s->scl_free = true; - if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Rx && - (s->i2cstat & I2CSTAT_START_BUSY) && - !(s->i2ccon & I2CCON_INT_PEND)) { - exynos4210_i2c_data_receive(s); - } - 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_get_regname(offset), - (unsigned int)offset, value); - return value; -} - -static void exynos4210_i2c_write(void *opaque, hwaddr 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_get_regname(offset), - (unsigned int)offset, v); - - switch (offset) { - case I2CCON_ADDR: - s->i2ccon = (v & ~I2CCON_INT_PEND) | (s->i2ccon & I2CCON_INT_PEND); - if ((s->i2ccon & I2CCON_INT_PEND) && !(v & I2CCON_INT_PEND)) { - s->i2ccon &= ~I2CCON_INT_PEND; - qemu_irq_lower(s->irq); - if (!(s->i2ccon & I2CCON_INTRS_EN)) { - s->i2cstat &= ~I2CSTAT_START_BUSY; - } - - if (s->i2cstat & I2CSTAT_START_BUSY) { - if (s->scl_free) { - if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Tx) { - exynos4210_i2c_data_send(s); - } else if (EXYNOS4_I2C_MODE(s->i2cstat) == - I2CMODE_MASTER_Rx) { - exynos4210_i2c_data_receive(s); - } - } else { - s->i2ccon |= I2CCON_INT_PEND; - qemu_irq_raise(s->irq); - } - } - } - 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; - s->scl_free = true; - qemu_irq_lower(s->irq); - 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_LAST_BIT; - s->i2cstat |= I2CSTAT_START_BUSY; /* Line is busy */ - s->scl_free = false; - - /* Generate start bit and send slave address */ - if (i2c_start_transfer(s->bus, s->i2cds >> 1, s->i2cds & 0x1) && - (s->i2ccon & I2CCON_ACK_GEN)) { - s->i2cstat |= I2CSTAT_LAST_BIT; - } else if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Rx) { - exynos4210_i2c_data_receive(s); - } - exynos4210_i2c_raise_interrupt(s); - } else { - i2c_end_transfer(s->bus); - if (!(s->i2ccon & I2CCON_INT_PEND)) { - s->i2cstat &= ~I2CSTAT_START_BUSY; - } - s->scl_free = true; - } - break; - case I2CADD_ADDR: - if ((s->i2cstat & I2CSTAT_OUTPUT_EN) == 0) { - s->i2cadd = v; - } - break; - case I2CDS_ADDR: - if (s->i2cstat & I2CSTAT_OUTPUT_EN) { - s->i2cds = v; - s->scl_free = true; - if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Tx && - (s->i2cstat & I2CSTAT_START_BUSY) && - !(s->i2ccon & I2CCON_INT_PEND)) { - exynos4210_i2c_data_send(s); - } - } - 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_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(i2cadd, Exynos4210I2CState), - VMSTATE_UINT8(i2clc, Exynos4210I2CState), - VMSTATE_BOOL(scl_free, Exynos4210I2CState), - VMSTATE_END_OF_LIST() - } -}; - -static void exynos4210_i2c_reset(DeviceState *d) -{ - Exynos4210I2CState *s = EXYNOS4_I2C(d); - - s->i2ccon = 0x00; - s->i2cstat = 0x00; - s->i2cds = 0xFF; - s->i2clc = 0x00; - s->i2cadd = 0xFF; - s->scl_free = true; -} - -static int exynos4210_i2c_realize(SysBusDevice *dev) -{ - Exynos4210I2CState *s = EXYNOS4_I2C(dev); - - 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"); - return 0; -} - -static void exynos4210_i2c_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *sbdc = SYS_BUS_DEVICE_CLASS(klass); - - dc->vmsd = &exynos4210_i2c_vmstate; - dc->reset = exynos4210_i2c_reset; - sbdc->init = exynos4210_i2c_realize; -} - -static const TypeInfo exynos4210_i2c_type_info = { - .name = TYPE_EXYNOS4_I2C, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Exynos4210I2CState), - .class_init = exynos4210_i2c_class_init, -}; - -static void exynos4210_i2c_register_types(void) -{ - type_register_static(&exynos4210_i2c_type_info); -} - -type_init(exynos4210_i2c_register_types) diff --git a/hw/i2c/Makefile.objs b/hw/i2c/Makefile.objs index f6bd8fa6ed..648278e7c4 100644 --- a/hw/i2c/Makefile.objs +++ b/hw/i2c/Makefile.objs @@ -2,3 +2,6 @@ common-obj-y += core.o smbus.o smbus_eeprom.o common-obj-$(CONFIG_VERSATILE_I2C) += versatile_i2c.o common-obj-$(CONFIG_ACPI) += smbus_ich9.o common-obj-$(CONFIG_APM) += pm_smbus.o +common-obj-$(CONFIG_BITBANG_I2C) += bitbang_i2c.o +common-obj-$(CONFIG_EXYNOS4) += exynos4210_i2c.o +obj-$(CONFIG_OMAP) += omap_i2c.o diff --git a/hw/i2c/bitbang_i2c.c b/hw/i2c/bitbang_i2c.c new file mode 100644 index 0000000000..b8e6d3a103 --- /dev/null +++ b/hw/i2c/bitbang_i2c.c @@ -0,0 +1,245 @@ +/* + * Bit-Bang i2c emulation extracted from + * Marvell MV88W8618 / Freecom MusicPal emulation. + * + * Copyright (c) 2008 Jan Kiszka + * + * This code is licensed under the GNU GPL v2. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ +#include "hw/hw.h" +#include "hw/bitbang_i2c.h" +#include "hw/sysbus.h" + +//#define DEBUG_BITBANG_I2C + +#ifdef DEBUG_BITBANG_I2C +#define DPRINTF(fmt, ...) \ +do { printf("bitbang_i2c: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) do {} while(0) +#endif + +typedef enum bitbang_i2c_state { + STOPPED = 0, + SENDING_BIT7, + SENDING_BIT6, + SENDING_BIT5, + SENDING_BIT4, + SENDING_BIT3, + SENDING_BIT2, + SENDING_BIT1, + SENDING_BIT0, + WAITING_FOR_ACK, + RECEIVING_BIT7, + RECEIVING_BIT6, + RECEIVING_BIT5, + RECEIVING_BIT4, + RECEIVING_BIT3, + RECEIVING_BIT2, + RECEIVING_BIT1, + RECEIVING_BIT0, + SENDING_ACK, + SENT_NACK +} bitbang_i2c_state; + +struct bitbang_i2c_interface { + i2c_bus *bus; + bitbang_i2c_state state; + int last_data; + int last_clock; + int device_out; + uint8_t buffer; + int current_addr; +}; + +static void bitbang_i2c_enter_stop(bitbang_i2c_interface *i2c) +{ + DPRINTF("STOP\n"); + if (i2c->current_addr >= 0) + i2c_end_transfer(i2c->bus); + i2c->current_addr = -1; + i2c->state = STOPPED; +} + +/* Set device data pin. */ +static int bitbang_i2c_ret(bitbang_i2c_interface *i2c, int level) +{ + i2c->device_out = level; + //DPRINTF("%d %d %d\n", i2c->last_clock, i2c->last_data, i2c->device_out); + return level & i2c->last_data; +} + +/* Leave device data pin unodified. */ +static int bitbang_i2c_nop(bitbang_i2c_interface *i2c) +{ + return bitbang_i2c_ret(i2c, i2c->device_out); +} + +/* Returns data line level. */ +int bitbang_i2c_set(bitbang_i2c_interface *i2c, int line, int level) +{ + int data; + + if (level != 0 && level != 1) { + abort(); + } + + if (line == BITBANG_I2C_SDA) { + if (level == i2c->last_data) { + return bitbang_i2c_nop(i2c); + } + i2c->last_data = level; + if (i2c->last_clock == 0) { + return bitbang_i2c_nop(i2c); + } + if (level == 0) { + DPRINTF("START\n"); + /* START condition. */ + i2c->state = SENDING_BIT7; + i2c->current_addr = -1; + } else { + /* STOP condition. */ + bitbang_i2c_enter_stop(i2c); + } + return bitbang_i2c_ret(i2c, 1); + } + + data = i2c->last_data; + if (i2c->last_clock == level) { + return bitbang_i2c_nop(i2c); + } + i2c->last_clock = level; + if (level == 0) { + /* State is set/read at the start of the clock pulse. + release the data line at the end. */ + return bitbang_i2c_ret(i2c, 1); + } + switch (i2c->state) { + case STOPPED: + case SENT_NACK: + return bitbang_i2c_ret(i2c, 1); + + case SENDING_BIT7 ... SENDING_BIT0: + i2c->buffer = (i2c->buffer << 1) | data; + /* will end up in WAITING_FOR_ACK */ + i2c->state++; + return bitbang_i2c_ret(i2c, 1); + + case WAITING_FOR_ACK: + if (i2c->current_addr < 0) { + i2c->current_addr = i2c->buffer; + DPRINTF("Address 0x%02x\n", i2c->current_addr); + i2c_start_transfer(i2c->bus, i2c->current_addr >> 1, + i2c->current_addr & 1); + } else { + DPRINTF("Sent 0x%02x\n", i2c->buffer); + i2c_send(i2c->bus, i2c->buffer); + } + if (i2c->current_addr & 1) { + i2c->state = RECEIVING_BIT7; + } else { + i2c->state = SENDING_BIT7; + } + return bitbang_i2c_ret(i2c, 0); + + case RECEIVING_BIT7: + i2c->buffer = i2c_recv(i2c->bus); + DPRINTF("RX byte 0x%02x\n", i2c->buffer); + /* Fall through... */ + case RECEIVING_BIT6 ... RECEIVING_BIT0: + data = i2c->buffer >> 7; + /* will end up in SENDING_ACK */ + i2c->state++; + i2c->buffer <<= 1; + return bitbang_i2c_ret(i2c, data); + + case SENDING_ACK: + i2c->state = RECEIVING_BIT7; + if (data != 0) { + DPRINTF("NACKED\n"); + i2c->state = SENT_NACK; + i2c_nack(i2c->bus); + } else { + DPRINTF("ACKED\n"); + } + return bitbang_i2c_ret(i2c, 1); + } + abort(); +} + +bitbang_i2c_interface *bitbang_i2c_init(i2c_bus *bus) +{ + bitbang_i2c_interface *s; + + s = g_malloc0(sizeof(bitbang_i2c_interface)); + + s->bus = bus; + s->last_data = 1; + s->last_clock = 1; + s->device_out = 1; + + return s; +} + +/* GPIO interface. */ +typedef struct { + SysBusDevice busdev; + MemoryRegion dummy_iomem; + bitbang_i2c_interface *bitbang; + int last_level; + qemu_irq out; +} GPIOI2CState; + +static void bitbang_i2c_gpio_set(void *opaque, int irq, int level) +{ + GPIOI2CState *s = opaque; + + level = bitbang_i2c_set(s->bitbang, irq, level); + if (level != s->last_level) { + s->last_level = level; + qemu_set_irq(s->out, level); + } +} + +static int gpio_i2c_init(SysBusDevice *dev) +{ + GPIOI2CState *s = FROM_SYSBUS(GPIOI2CState, dev); + i2c_bus *bus; + + memory_region_init(&s->dummy_iomem, "gpio_i2c", 0); + sysbus_init_mmio(dev, &s->dummy_iomem); + + bus = i2c_init_bus(&dev->qdev, "i2c"); + s->bitbang = bitbang_i2c_init(bus); + + qdev_init_gpio_in(&dev->qdev, bitbang_i2c_gpio_set, 2); + qdev_init_gpio_out(&dev->qdev, &s->out, 1); + + return 0; +} + +static void gpio_i2c_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = gpio_i2c_init; + dc->desc = "Virtual GPIO to I2C bridge"; +} + +static const TypeInfo gpio_i2c_info = { + .name = "gpio_i2c", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(GPIOI2CState), + .class_init = gpio_i2c_class_init, +}; + +static void bitbang_i2c_register_types(void) +{ + type_register_static(&gpio_i2c_info); +} + +type_init(bitbang_i2c_register_types) diff --git a/hw/i2c/exynos4210_i2c.c b/hw/i2c/exynos4210_i2c.c new file mode 100644 index 0000000000..196f88907d --- /dev/null +++ b/hw/i2c/exynos4210_i2c.c @@ -0,0 +1,334 @@ +/* + * 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 "hw/sysbus.h" +#include "hw/i2c/i2c.h" + +#ifndef EXYNOS4_I2C_DEBUG +#define EXYNOS4_I2C_DEBUG 0 +#endif + +#define TYPE_EXYNOS4_I2C "exynos4210.i2c" +#define EXYNOS4_I2C(obj) \ + OBJECT_CHECK(Exynos4210I2CState, (obj), TYPE_EXYNOS4_I2C) + +/* 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_ACK_GEN (1 << 7) +#define I2CCON_INTRS_EN (1 << 5) +#define I2CCON_INT_PEND (1 << 4) + +#define EXYNOS4_I2C_MODE(reg) (((reg) >> 6) & 3) +#define I2C_IN_MASTER_MODE(reg) (((reg) >> 6) & 2) +#define I2CMODE_MASTER_Rx 0x2 +#define I2CMODE_MASTER_Tx 0x3 +#define I2CSTAT_LAST_BIT (1 << 0) +#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_get_regname(unsigned offset) +{ + switch (offset) { + case I2CCON_ADDR: + return "I2CCON"; + case I2CSTAT_ADDR: + return "I2CSTAT"; + case I2CADD_ADDR: + return "I2CADD"; + case I2CDS_ADDR: + return "I2CDS"; + case I2CLC_ADDR: + return "I2CLC"; + default: + return "[?]"; + } +} + +#else +#define DPRINT(fmt, args...) do { } while (0) +#endif + +typedef struct Exynos4210I2CState { + SysBusDevice busdev; + MemoryRegion iomem; + i2c_bus *bus; + qemu_irq irq; + + uint8_t i2ccon; + uint8_t i2cstat; + uint8_t i2cadd; + uint8_t i2cds; + uint8_t i2clc; + bool scl_free; +} Exynos4210I2CState; + +static inline void exynos4210_i2c_raise_interrupt(Exynos4210I2CState *s) +{ + if (s->i2ccon & I2CCON_INTRS_EN) { + s->i2ccon |= I2CCON_INT_PEND; + qemu_irq_raise(s->irq); + } +} + +static void exynos4210_i2c_data_receive(void *opaque) +{ + Exynos4210I2CState *s = (Exynos4210I2CState *)opaque; + int ret; + + s->i2cstat &= ~I2CSTAT_LAST_BIT; + s->scl_free = false; + ret = i2c_recv(s->bus); + if (ret < 0 && (s->i2ccon & I2CCON_ACK_GEN)) { + s->i2cstat |= I2CSTAT_LAST_BIT; /* Data is not acknowledged */ + } else { + s->i2cds = ret; + } + exynos4210_i2c_raise_interrupt(s); +} + +static void exynos4210_i2c_data_send(void *opaque) +{ + Exynos4210I2CState *s = (Exynos4210I2CState *)opaque; + + s->i2cstat &= ~I2CSTAT_LAST_BIT; + s->scl_free = false; + if (i2c_send(s->bus, s->i2cds) < 0 && (s->i2ccon & I2CCON_ACK_GEN)) { + s->i2cstat |= I2CSTAT_LAST_BIT; + } + exynos4210_i2c_raise_interrupt(s); +} + +static uint64_t exynos4210_i2c_read(void *opaque, hwaddr 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; + s->scl_free = true; + if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Rx && + (s->i2cstat & I2CSTAT_START_BUSY) && + !(s->i2ccon & I2CCON_INT_PEND)) { + exynos4210_i2c_data_receive(s); + } + 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_get_regname(offset), + (unsigned int)offset, value); + return value; +} + +static void exynos4210_i2c_write(void *opaque, hwaddr 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_get_regname(offset), + (unsigned int)offset, v); + + switch (offset) { + case I2CCON_ADDR: + s->i2ccon = (v & ~I2CCON_INT_PEND) | (s->i2ccon & I2CCON_INT_PEND); + if ((s->i2ccon & I2CCON_INT_PEND) && !(v & I2CCON_INT_PEND)) { + s->i2ccon &= ~I2CCON_INT_PEND; + qemu_irq_lower(s->irq); + if (!(s->i2ccon & I2CCON_INTRS_EN)) { + s->i2cstat &= ~I2CSTAT_START_BUSY; + } + + if (s->i2cstat & I2CSTAT_START_BUSY) { + if (s->scl_free) { + if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Tx) { + exynos4210_i2c_data_send(s); + } else if (EXYNOS4_I2C_MODE(s->i2cstat) == + I2CMODE_MASTER_Rx) { + exynos4210_i2c_data_receive(s); + } + } else { + s->i2ccon |= I2CCON_INT_PEND; + qemu_irq_raise(s->irq); + } + } + } + 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; + s->scl_free = true; + qemu_irq_lower(s->irq); + 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_LAST_BIT; + s->i2cstat |= I2CSTAT_START_BUSY; /* Line is busy */ + s->scl_free = false; + + /* Generate start bit and send slave address */ + if (i2c_start_transfer(s->bus, s->i2cds >> 1, s->i2cds & 0x1) && + (s->i2ccon & I2CCON_ACK_GEN)) { + s->i2cstat |= I2CSTAT_LAST_BIT; + } else if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Rx) { + exynos4210_i2c_data_receive(s); + } + exynos4210_i2c_raise_interrupt(s); + } else { + i2c_end_transfer(s->bus); + if (!(s->i2ccon & I2CCON_INT_PEND)) { + s->i2cstat &= ~I2CSTAT_START_BUSY; + } + s->scl_free = true; + } + break; + case I2CADD_ADDR: + if ((s->i2cstat & I2CSTAT_OUTPUT_EN) == 0) { + s->i2cadd = v; + } + break; + case I2CDS_ADDR: + if (s->i2cstat & I2CSTAT_OUTPUT_EN) { + s->i2cds = v; + s->scl_free = true; + if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Tx && + (s->i2cstat & I2CSTAT_START_BUSY) && + !(s->i2ccon & I2CCON_INT_PEND)) { + exynos4210_i2c_data_send(s); + } + } + 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_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(i2cadd, Exynos4210I2CState), + VMSTATE_UINT8(i2clc, Exynos4210I2CState), + VMSTATE_BOOL(scl_free, Exynos4210I2CState), + VMSTATE_END_OF_LIST() + } +}; + +static void exynos4210_i2c_reset(DeviceState *d) +{ + Exynos4210I2CState *s = EXYNOS4_I2C(d); + + s->i2ccon = 0x00; + s->i2cstat = 0x00; + s->i2cds = 0xFF; + s->i2clc = 0x00; + s->i2cadd = 0xFF; + s->scl_free = true; +} + +static int exynos4210_i2c_realize(SysBusDevice *dev) +{ + Exynos4210I2CState *s = EXYNOS4_I2C(dev); + + 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"); + return 0; +} + +static void exynos4210_i2c_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *sbdc = SYS_BUS_DEVICE_CLASS(klass); + + dc->vmsd = &exynos4210_i2c_vmstate; + dc->reset = exynos4210_i2c_reset; + sbdc->init = exynos4210_i2c_realize; +} + +static const TypeInfo exynos4210_i2c_type_info = { + .name = TYPE_EXYNOS4_I2C, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Exynos4210I2CState), + .class_init = exynos4210_i2c_class_init, +}; + +static void exynos4210_i2c_register_types(void) +{ + type_register_static(&exynos4210_i2c_type_info); +} + +type_init(exynos4210_i2c_register_types) diff --git a/hw/i2c/omap_i2c.c b/hw/i2c/omap_i2c.c new file mode 100644 index 0000000000..efb2254aea --- /dev/null +++ b/hw/i2c/omap_i2c.c @@ -0,0 +1,492 @@ +/* + * TI OMAP on-chip I2C controller. Only "new I2C" mode supported. + * + * Copyright (C) 2007 Andrzej Zaborowski + * + * 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 "hw/hw.h" +#include "hw/i2c/i2c.h" +#include "hw/arm/omap.h" +#include "hw/sysbus.h" + + +typedef struct OMAPI2CState { + SysBusDevice busdev; + MemoryRegion iomem; + qemu_irq irq; + qemu_irq drq[2]; + i2c_bus *bus; + + uint8_t revision; + void *iclk; + void *fclk; + + uint8_t mask; + uint16_t stat; + uint16_t dma; + uint16_t count; + int count_cur; + uint32_t fifo; + int rxlen; + int txlen; + uint16_t control; + uint16_t addr[2]; + uint8_t divider; + uint8_t times[2]; + uint16_t test; +} OMAPI2CState; + +#define OMAP2_INTR_REV 0x34 +#define OMAP2_GC_REV 0x34 + +static void omap_i2c_interrupts_update(OMAPI2CState *s) +{ + qemu_set_irq(s->irq, s->stat & s->mask); + if ((s->dma >> 15) & 1) /* RDMA_EN */ + qemu_set_irq(s->drq[0], (s->stat >> 3) & 1); /* RRDY */ + if ((s->dma >> 7) & 1) /* XDMA_EN */ + qemu_set_irq(s->drq[1], (s->stat >> 4) & 1); /* XRDY */ +} + +static void omap_i2c_fifo_run(OMAPI2CState *s) +{ + int ack = 1; + + if (!i2c_bus_busy(s->bus)) + return; + + if ((s->control >> 2) & 1) { /* RM */ + if ((s->control >> 1) & 1) { /* STP */ + i2c_end_transfer(s->bus); + s->control &= ~(1 << 1); /* STP */ + s->count_cur = s->count; + s->txlen = 0; + } else if ((s->control >> 9) & 1) { /* TRX */ + while (ack && s->txlen) + ack = (i2c_send(s->bus, + (s->fifo >> ((-- s->txlen) << 3)) & + 0xff) >= 0); + s->stat |= 1 << 4; /* XRDY */ + } else { + while (s->rxlen < 4) + s->fifo |= i2c_recv(s->bus) << ((s->rxlen ++) << 3); + s->stat |= 1 << 3; /* RRDY */ + } + } else { + if ((s->control >> 9) & 1) { /* TRX */ + while (ack && s->count_cur && s->txlen) { + ack = (i2c_send(s->bus, + (s->fifo >> ((-- s->txlen) << 3)) & + 0xff) >= 0); + s->count_cur --; + } + if (ack && s->count_cur) + s->stat |= 1 << 4; /* XRDY */ + else + s->stat &= ~(1 << 4); /* XRDY */ + if (!s->count_cur) { + s->stat |= 1 << 2; /* ARDY */ + s->control &= ~(1 << 10); /* MST */ + } + } else { + while (s->count_cur && s->rxlen < 4) { + s->fifo |= i2c_recv(s->bus) << ((s->rxlen ++) << 3); + s->count_cur --; + } + if (s->rxlen) + s->stat |= 1 << 3; /* RRDY */ + else + s->stat &= ~(1 << 3); /* RRDY */ + } + if (!s->count_cur) { + if ((s->control >> 1) & 1) { /* STP */ + i2c_end_transfer(s->bus); + s->control &= ~(1 << 1); /* STP */ + s->count_cur = s->count; + s->txlen = 0; + } else { + s->stat |= 1 << 2; /* ARDY */ + s->control &= ~(1 << 10); /* MST */ + } + } + } + + s->stat |= (!ack) << 1; /* NACK */ + if (!ack) + s->control &= ~(1 << 1); /* STP */ +} + +static void omap_i2c_reset(DeviceState *dev) +{ + OMAPI2CState *s = FROM_SYSBUS(OMAPI2CState, + SYS_BUS_DEVICE(dev)); + s->mask = 0; + s->stat = 0; + s->dma = 0; + s->count = 0; + s->count_cur = 0; + s->fifo = 0; + s->rxlen = 0; + s->txlen = 0; + s->control = 0; + s->addr[0] = 0; + s->addr[1] = 0; + s->divider = 0; + s->times[0] = 0; + s->times[1] = 0; + s->test = 0; +} + +static uint32_t omap_i2c_read(void *opaque, hwaddr addr) +{ + OMAPI2CState *s = opaque; + int offset = addr & OMAP_MPUI_REG_MASK; + uint16_t ret; + + switch (offset) { + case 0x00: /* I2C_REV */ + return s->revision; /* REV */ + + case 0x04: /* I2C_IE */ + return s->mask; + + case 0x08: /* I2C_STAT */ + return s->stat | (i2c_bus_busy(s->bus) << 12); + + case 0x0c: /* I2C_IV */ + if (s->revision >= OMAP2_INTR_REV) + break; + ret = ffs(s->stat & s->mask); + if (ret) + s->stat ^= 1 << (ret - 1); + omap_i2c_interrupts_update(s); + return ret; + + case 0x10: /* I2C_SYSS */ + return (s->control >> 15) & 1; /* I2C_EN */ + + case 0x14: /* I2C_BUF */ + return s->dma; + + case 0x18: /* I2C_CNT */ + return s->count_cur; /* DCOUNT */ + + case 0x1c: /* I2C_DATA */ + ret = 0; + if (s->control & (1 << 14)) { /* BE */ + ret |= ((s->fifo >> 0) & 0xff) << 8; + ret |= ((s->fifo >> 8) & 0xff) << 0; + } else { + ret |= ((s->fifo >> 8) & 0xff) << 8; + ret |= ((s->fifo >> 0) & 0xff) << 0; + } + if (s->rxlen == 1) { + s->stat |= 1 << 15; /* SBD */ + s->rxlen = 0; + } else if (s->rxlen > 1) { + if (s->rxlen > 2) + s->fifo >>= 16; + s->rxlen -= 2; + } else { + /* XXX: remote access (qualifier) error - what's that? */ + } + if (!s->rxlen) { + s->stat &= ~(1 << 3); /* RRDY */ + if (((s->control >> 10) & 1) && /* MST */ + ((~s->control >> 9) & 1)) { /* TRX */ + s->stat |= 1 << 2; /* ARDY */ + s->control &= ~(1 << 10); /* MST */ + } + } + s->stat &= ~(1 << 11); /* ROVR */ + omap_i2c_fifo_run(s); + omap_i2c_interrupts_update(s); + return ret; + + case 0x20: /* I2C_SYSC */ + return 0; + + case 0x24: /* I2C_CON */ + return s->control; + + case 0x28: /* I2C_OA */ + return s->addr[0]; + + case 0x2c: /* I2C_SA */ + return s->addr[1]; + + case 0x30: /* I2C_PSC */ + return s->divider; + + case 0x34: /* I2C_SCLL */ + return s->times[0]; + + case 0x38: /* I2C_SCLH */ + return s->times[1]; + + case 0x3c: /* I2C_SYSTEST */ + if (s->test & (1 << 15)) { /* ST_EN */ + s->test ^= 0xa; + return s->test; + } else + return s->test & ~0x300f; + } + + OMAP_BAD_REG(addr); + return 0; +} + +static void omap_i2c_write(void *opaque, hwaddr addr, + uint32_t value) +{ + OMAPI2CState *s = opaque; + int offset = addr & OMAP_MPUI_REG_MASK; + int nack; + + switch (offset) { + case 0x00: /* I2C_REV */ + case 0x0c: /* I2C_IV */ + case 0x10: /* I2C_SYSS */ + OMAP_RO_REG(addr); + return; + + case 0x04: /* I2C_IE */ + s->mask = value & (s->revision < OMAP2_GC_REV ? 0x1f : 0x3f); + break; + + case 0x08: /* I2C_STAT */ + if (s->revision < OMAP2_INTR_REV) { + OMAP_RO_REG(addr); + return; + } + + /* RRDY and XRDY are reset by hardware. (in all versions???) */ + s->stat &= ~(value & 0x27); + omap_i2c_interrupts_update(s); + break; + + case 0x14: /* I2C_BUF */ + s->dma = value & 0x8080; + if (value & (1 << 15)) /* RDMA_EN */ + s->mask &= ~(1 << 3); /* RRDY_IE */ + if (value & (1 << 7)) /* XDMA_EN */ + s->mask &= ~(1 << 4); /* XRDY_IE */ + break; + + case 0x18: /* I2C_CNT */ + s->count = value; /* DCOUNT */ + break; + + case 0x1c: /* I2C_DATA */ + if (s->txlen > 2) { + /* XXX: remote access (qualifier) error - what's that? */ + break; + } + s->fifo <<= 16; + s->txlen += 2; + if (s->control & (1 << 14)) { /* BE */ + s->fifo |= ((value >> 8) & 0xff) << 8; + s->fifo |= ((value >> 0) & 0xff) << 0; + } else { + s->fifo |= ((value >> 0) & 0xff) << 8; + s->fifo |= ((value >> 8) & 0xff) << 0; + } + s->stat &= ~(1 << 10); /* XUDF */ + if (s->txlen > 2) + s->stat &= ~(1 << 4); /* XRDY */ + omap_i2c_fifo_run(s); + omap_i2c_interrupts_update(s); + break; + + case 0x20: /* I2C_SYSC */ + if (s->revision < OMAP2_INTR_REV) { + OMAP_BAD_REG(addr); + return; + } + + if (value & 2) + omap_i2c_reset(&s->busdev.qdev); + break; + + case 0x24: /* I2C_CON */ + s->control = value & 0xcf87; + if (~value & (1 << 15)) { /* I2C_EN */ + if (s->revision < OMAP2_INTR_REV) + omap_i2c_reset(&s->busdev.qdev); + break; + } + if ((value & (1 << 15)) && !(value & (1 << 10))) { /* MST */ + fprintf(stderr, "%s: I^2C slave mode not supported\n", + __FUNCTION__); + break; + } + if ((value & (1 << 15)) && value & (1 << 8)) { /* XA */ + fprintf(stderr, "%s: 10-bit addressing mode not supported\n", + __FUNCTION__); + break; + } + if ((value & (1 << 15)) && value & (1 << 0)) { /* STT */ + nack = !!i2c_start_transfer(s->bus, s->addr[1], /* SA */ + (~value >> 9) & 1); /* TRX */ + s->stat |= nack << 1; /* NACK */ + s->control &= ~(1 << 0); /* STT */ + s->fifo = 0; + if (nack) + s->control &= ~(1 << 1); /* STP */ + else { + s->count_cur = s->count; + omap_i2c_fifo_run(s); + } + omap_i2c_interrupts_update(s); + } + break; + + case 0x28: /* I2C_OA */ + s->addr[0] = value & 0x3ff; + break; + + case 0x2c: /* I2C_SA */ + s->addr[1] = value & 0x3ff; + break; + + case 0x30: /* I2C_PSC */ + s->divider = value; + break; + + case 0x34: /* I2C_SCLL */ + s->times[0] = value; + break; + + case 0x38: /* I2C_SCLH */ + s->times[1] = value; + break; + + case 0x3c: /* I2C_SYSTEST */ + s->test = value & 0xf80f; + if (value & (1 << 11)) /* SBB */ + if (s->revision >= OMAP2_INTR_REV) { + s->stat |= 0x3f; + omap_i2c_interrupts_update(s); + } + if (value & (1 << 15)) /* ST_EN */ + fprintf(stderr, "%s: System Test not supported\n", __FUNCTION__); + break; + + default: + OMAP_BAD_REG(addr); + return; + } +} + +static void omap_i2c_writeb(void *opaque, hwaddr addr, + uint32_t value) +{ + OMAPI2CState *s = opaque; + int offset = addr & OMAP_MPUI_REG_MASK; + + switch (offset) { + case 0x1c: /* I2C_DATA */ + if (s->txlen > 2) { + /* XXX: remote access (qualifier) error - what's that? */ + break; + } + s->fifo <<= 8; + s->txlen += 1; + s->fifo |= value & 0xff; + s->stat &= ~(1 << 10); /* XUDF */ + if (s->txlen > 2) + s->stat &= ~(1 << 4); /* XRDY */ + omap_i2c_fifo_run(s); + omap_i2c_interrupts_update(s); + break; + + default: + OMAP_BAD_REG(addr); + return; + } +} + +static const MemoryRegionOps omap_i2c_ops = { + .old_mmio = { + .read = { + omap_badwidth_read16, + omap_i2c_read, + omap_badwidth_read16, + }, + .write = { + omap_i2c_writeb, /* Only the last fifo write can be 8 bit. */ + omap_i2c_write, + omap_badwidth_write16, + }, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int omap_i2c_init(SysBusDevice *dev) +{ + OMAPI2CState *s = FROM_SYSBUS(OMAPI2CState, dev); + + if (!s->fclk) { + hw_error("omap_i2c: fclk not connected\n"); + } + if (s->revision >= OMAP2_INTR_REV && !s->iclk) { + /* Note that OMAP1 doesn't have a separate interface clock */ + hw_error("omap_i2c: iclk not connected\n"); + } + sysbus_init_irq(dev, &s->irq); + sysbus_init_irq(dev, &s->drq[0]); + sysbus_init_irq(dev, &s->drq[1]); + memory_region_init_io(&s->iomem, &omap_i2c_ops, s, "omap.i2c", + (s->revision < OMAP2_INTR_REV) ? 0x800 : 0x1000); + sysbus_init_mmio(dev, &s->iomem); + s->bus = i2c_init_bus(&dev->qdev, NULL); + return 0; +} + +static Property omap_i2c_properties[] = { + DEFINE_PROP_UINT8("revision", OMAPI2CState, revision, 0), + DEFINE_PROP_PTR("iclk", OMAPI2CState, iclk), + DEFINE_PROP_PTR("fclk", OMAPI2CState, fclk), + DEFINE_PROP_END_OF_LIST(), +}; + +static void omap_i2c_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + k->init = omap_i2c_init; + dc->props = omap_i2c_properties; + dc->reset = omap_i2c_reset; +} + +static const TypeInfo omap_i2c_info = { + .name = "omap_i2c", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(OMAPI2CState), + .class_init = omap_i2c_class_init, +}; + +static void omap_i2c_register_types(void) +{ + type_register_static(&omap_i2c_info); +} + +i2c_bus *omap_i2c_bus(DeviceState *omap_i2c) +{ + OMAPI2CState *s = FROM_SYSBUS(OMAPI2CState, SYS_BUS_DEVICE(omap_i2c)); + return s->bus; +} + +type_init(omap_i2c_register_types) diff --git a/hw/omap_i2c.c b/hw/omap_i2c.c deleted file mode 100644 index efb2254aea..0000000000 --- a/hw/omap_i2c.c +++ /dev/null @@ -1,492 +0,0 @@ -/* - * TI OMAP on-chip I2C controller. Only "new I2C" mode supported. - * - * Copyright (C) 2007 Andrzej Zaborowski - * - * 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 "hw/hw.h" -#include "hw/i2c/i2c.h" -#include "hw/arm/omap.h" -#include "hw/sysbus.h" - - -typedef struct OMAPI2CState { - SysBusDevice busdev; - MemoryRegion iomem; - qemu_irq irq; - qemu_irq drq[2]; - i2c_bus *bus; - - uint8_t revision; - void *iclk; - void *fclk; - - uint8_t mask; - uint16_t stat; - uint16_t dma; - uint16_t count; - int count_cur; - uint32_t fifo; - int rxlen; - int txlen; - uint16_t control; - uint16_t addr[2]; - uint8_t divider; - uint8_t times[2]; - uint16_t test; -} OMAPI2CState; - -#define OMAP2_INTR_REV 0x34 -#define OMAP2_GC_REV 0x34 - -static void omap_i2c_interrupts_update(OMAPI2CState *s) -{ - qemu_set_irq(s->irq, s->stat & s->mask); - if ((s->dma >> 15) & 1) /* RDMA_EN */ - qemu_set_irq(s->drq[0], (s->stat >> 3) & 1); /* RRDY */ - if ((s->dma >> 7) & 1) /* XDMA_EN */ - qemu_set_irq(s->drq[1], (s->stat >> 4) & 1); /* XRDY */ -} - -static void omap_i2c_fifo_run(OMAPI2CState *s) -{ - int ack = 1; - - if (!i2c_bus_busy(s->bus)) - return; - - if ((s->control >> 2) & 1) { /* RM */ - if ((s->control >> 1) & 1) { /* STP */ - i2c_end_transfer(s->bus); - s->control &= ~(1 << 1); /* STP */ - s->count_cur = s->count; - s->txlen = 0; - } else if ((s->control >> 9) & 1) { /* TRX */ - while (ack && s->txlen) - ack = (i2c_send(s->bus, - (s->fifo >> ((-- s->txlen) << 3)) & - 0xff) >= 0); - s->stat |= 1 << 4; /* XRDY */ - } else { - while (s->rxlen < 4) - s->fifo |= i2c_recv(s->bus) << ((s->rxlen ++) << 3); - s->stat |= 1 << 3; /* RRDY */ - } - } else { - if ((s->control >> 9) & 1) { /* TRX */ - while (ack && s->count_cur && s->txlen) { - ack = (i2c_send(s->bus, - (s->fifo >> ((-- s->txlen) << 3)) & - 0xff) >= 0); - s->count_cur --; - } - if (ack && s->count_cur) - s->stat |= 1 << 4; /* XRDY */ - else - s->stat &= ~(1 << 4); /* XRDY */ - if (!s->count_cur) { - s->stat |= 1 << 2; /* ARDY */ - s->control &= ~(1 << 10); /* MST */ - } - } else { - while (s->count_cur && s->rxlen < 4) { - s->fifo |= i2c_recv(s->bus) << ((s->rxlen ++) << 3); - s->count_cur --; - } - if (s->rxlen) - s->stat |= 1 << 3; /* RRDY */ - else - s->stat &= ~(1 << 3); /* RRDY */ - } - if (!s->count_cur) { - if ((s->control >> 1) & 1) { /* STP */ - i2c_end_transfer(s->bus); - s->control &= ~(1 << 1); /* STP */ - s->count_cur = s->count; - s->txlen = 0; - } else { - s->stat |= 1 << 2; /* ARDY */ - s->control &= ~(1 << 10); /* MST */ - } - } - } - - s->stat |= (!ack) << 1; /* NACK */ - if (!ack) - s->control &= ~(1 << 1); /* STP */ -} - -static void omap_i2c_reset(DeviceState *dev) -{ - OMAPI2CState *s = FROM_SYSBUS(OMAPI2CState, - SYS_BUS_DEVICE(dev)); - s->mask = 0; - s->stat = 0; - s->dma = 0; - s->count = 0; - s->count_cur = 0; - s->fifo = 0; - s->rxlen = 0; - s->txlen = 0; - s->control = 0; - s->addr[0] = 0; - s->addr[1] = 0; - s->divider = 0; - s->times[0] = 0; - s->times[1] = 0; - s->test = 0; -} - -static uint32_t omap_i2c_read(void *opaque, hwaddr addr) -{ - OMAPI2CState *s = opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - uint16_t ret; - - switch (offset) { - case 0x00: /* I2C_REV */ - return s->revision; /* REV */ - - case 0x04: /* I2C_IE */ - return s->mask; - - case 0x08: /* I2C_STAT */ - return s->stat | (i2c_bus_busy(s->bus) << 12); - - case 0x0c: /* I2C_IV */ - if (s->revision >= OMAP2_INTR_REV) - break; - ret = ffs(s->stat & s->mask); - if (ret) - s->stat ^= 1 << (ret - 1); - omap_i2c_interrupts_update(s); - return ret; - - case 0x10: /* I2C_SYSS */ - return (s->control >> 15) & 1; /* I2C_EN */ - - case 0x14: /* I2C_BUF */ - return s->dma; - - case 0x18: /* I2C_CNT */ - return s->count_cur; /* DCOUNT */ - - case 0x1c: /* I2C_DATA */ - ret = 0; - if (s->control & (1 << 14)) { /* BE */ - ret |= ((s->fifo >> 0) & 0xff) << 8; - ret |= ((s->fifo >> 8) & 0xff) << 0; - } else { - ret |= ((s->fifo >> 8) & 0xff) << 8; - ret |= ((s->fifo >> 0) & 0xff) << 0; - } - if (s->rxlen == 1) { - s->stat |= 1 << 15; /* SBD */ - s->rxlen = 0; - } else if (s->rxlen > 1) { - if (s->rxlen > 2) - s->fifo >>= 16; - s->rxlen -= 2; - } else { - /* XXX: remote access (qualifier) error - what's that? */ - } - if (!s->rxlen) { - s->stat &= ~(1 << 3); /* RRDY */ - if (((s->control >> 10) & 1) && /* MST */ - ((~s->control >> 9) & 1)) { /* TRX */ - s->stat |= 1 << 2; /* ARDY */ - s->control &= ~(1 << 10); /* MST */ - } - } - s->stat &= ~(1 << 11); /* ROVR */ - omap_i2c_fifo_run(s); - omap_i2c_interrupts_update(s); - return ret; - - case 0x20: /* I2C_SYSC */ - return 0; - - case 0x24: /* I2C_CON */ - return s->control; - - case 0x28: /* I2C_OA */ - return s->addr[0]; - - case 0x2c: /* I2C_SA */ - return s->addr[1]; - - case 0x30: /* I2C_PSC */ - return s->divider; - - case 0x34: /* I2C_SCLL */ - return s->times[0]; - - case 0x38: /* I2C_SCLH */ - return s->times[1]; - - case 0x3c: /* I2C_SYSTEST */ - if (s->test & (1 << 15)) { /* ST_EN */ - s->test ^= 0xa; - return s->test; - } else - return s->test & ~0x300f; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_i2c_write(void *opaque, hwaddr addr, - uint32_t value) -{ - OMAPI2CState *s = opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - int nack; - - switch (offset) { - case 0x00: /* I2C_REV */ - case 0x0c: /* I2C_IV */ - case 0x10: /* I2C_SYSS */ - OMAP_RO_REG(addr); - return; - - case 0x04: /* I2C_IE */ - s->mask = value & (s->revision < OMAP2_GC_REV ? 0x1f : 0x3f); - break; - - case 0x08: /* I2C_STAT */ - if (s->revision < OMAP2_INTR_REV) { - OMAP_RO_REG(addr); - return; - } - - /* RRDY and XRDY are reset by hardware. (in all versions???) */ - s->stat &= ~(value & 0x27); - omap_i2c_interrupts_update(s); - break; - - case 0x14: /* I2C_BUF */ - s->dma = value & 0x8080; - if (value & (1 << 15)) /* RDMA_EN */ - s->mask &= ~(1 << 3); /* RRDY_IE */ - if (value & (1 << 7)) /* XDMA_EN */ - s->mask &= ~(1 << 4); /* XRDY_IE */ - break; - - case 0x18: /* I2C_CNT */ - s->count = value; /* DCOUNT */ - break; - - case 0x1c: /* I2C_DATA */ - if (s->txlen > 2) { - /* XXX: remote access (qualifier) error - what's that? */ - break; - } - s->fifo <<= 16; - s->txlen += 2; - if (s->control & (1 << 14)) { /* BE */ - s->fifo |= ((value >> 8) & 0xff) << 8; - s->fifo |= ((value >> 0) & 0xff) << 0; - } else { - s->fifo |= ((value >> 0) & 0xff) << 8; - s->fifo |= ((value >> 8) & 0xff) << 0; - } - s->stat &= ~(1 << 10); /* XUDF */ - if (s->txlen > 2) - s->stat &= ~(1 << 4); /* XRDY */ - omap_i2c_fifo_run(s); - omap_i2c_interrupts_update(s); - break; - - case 0x20: /* I2C_SYSC */ - if (s->revision < OMAP2_INTR_REV) { - OMAP_BAD_REG(addr); - return; - } - - if (value & 2) - omap_i2c_reset(&s->busdev.qdev); - break; - - case 0x24: /* I2C_CON */ - s->control = value & 0xcf87; - if (~value & (1 << 15)) { /* I2C_EN */ - if (s->revision < OMAP2_INTR_REV) - omap_i2c_reset(&s->busdev.qdev); - break; - } - if ((value & (1 << 15)) && !(value & (1 << 10))) { /* MST */ - fprintf(stderr, "%s: I^2C slave mode not supported\n", - __FUNCTION__); - break; - } - if ((value & (1 << 15)) && value & (1 << 8)) { /* XA */ - fprintf(stderr, "%s: 10-bit addressing mode not supported\n", - __FUNCTION__); - break; - } - if ((value & (1 << 15)) && value & (1 << 0)) { /* STT */ - nack = !!i2c_start_transfer(s->bus, s->addr[1], /* SA */ - (~value >> 9) & 1); /* TRX */ - s->stat |= nack << 1; /* NACK */ - s->control &= ~(1 << 0); /* STT */ - s->fifo = 0; - if (nack) - s->control &= ~(1 << 1); /* STP */ - else { - s->count_cur = s->count; - omap_i2c_fifo_run(s); - } - omap_i2c_interrupts_update(s); - } - break; - - case 0x28: /* I2C_OA */ - s->addr[0] = value & 0x3ff; - break; - - case 0x2c: /* I2C_SA */ - s->addr[1] = value & 0x3ff; - break; - - case 0x30: /* I2C_PSC */ - s->divider = value; - break; - - case 0x34: /* I2C_SCLL */ - s->times[0] = value; - break; - - case 0x38: /* I2C_SCLH */ - s->times[1] = value; - break; - - case 0x3c: /* I2C_SYSTEST */ - s->test = value & 0xf80f; - if (value & (1 << 11)) /* SBB */ - if (s->revision >= OMAP2_INTR_REV) { - s->stat |= 0x3f; - omap_i2c_interrupts_update(s); - } - if (value & (1 << 15)) /* ST_EN */ - fprintf(stderr, "%s: System Test not supported\n", __FUNCTION__); - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static void omap_i2c_writeb(void *opaque, hwaddr addr, - uint32_t value) -{ - OMAPI2CState *s = opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - - switch (offset) { - case 0x1c: /* I2C_DATA */ - if (s->txlen > 2) { - /* XXX: remote access (qualifier) error - what's that? */ - break; - } - s->fifo <<= 8; - s->txlen += 1; - s->fifo |= value & 0xff; - s->stat &= ~(1 << 10); /* XUDF */ - if (s->txlen > 2) - s->stat &= ~(1 << 4); /* XRDY */ - omap_i2c_fifo_run(s); - omap_i2c_interrupts_update(s); - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap_i2c_ops = { - .old_mmio = { - .read = { - omap_badwidth_read16, - omap_i2c_read, - omap_badwidth_read16, - }, - .write = { - omap_i2c_writeb, /* Only the last fifo write can be 8 bit. */ - omap_i2c_write, - omap_badwidth_write16, - }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int omap_i2c_init(SysBusDevice *dev) -{ - OMAPI2CState *s = FROM_SYSBUS(OMAPI2CState, dev); - - if (!s->fclk) { - hw_error("omap_i2c: fclk not connected\n"); - } - if (s->revision >= OMAP2_INTR_REV && !s->iclk) { - /* Note that OMAP1 doesn't have a separate interface clock */ - hw_error("omap_i2c: iclk not connected\n"); - } - sysbus_init_irq(dev, &s->irq); - sysbus_init_irq(dev, &s->drq[0]); - sysbus_init_irq(dev, &s->drq[1]); - memory_region_init_io(&s->iomem, &omap_i2c_ops, s, "omap.i2c", - (s->revision < OMAP2_INTR_REV) ? 0x800 : 0x1000); - sysbus_init_mmio(dev, &s->iomem); - s->bus = i2c_init_bus(&dev->qdev, NULL); - return 0; -} - -static Property omap_i2c_properties[] = { - DEFINE_PROP_UINT8("revision", OMAPI2CState, revision, 0), - DEFINE_PROP_PTR("iclk", OMAPI2CState, iclk), - DEFINE_PROP_PTR("fclk", OMAPI2CState, fclk), - DEFINE_PROP_END_OF_LIST(), -}; - -static void omap_i2c_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = omap_i2c_init; - dc->props = omap_i2c_properties; - dc->reset = omap_i2c_reset; -} - -static const TypeInfo omap_i2c_info = { - .name = "omap_i2c", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(OMAPI2CState), - .class_init = omap_i2c_class_init, -}; - -static void omap_i2c_register_types(void) -{ - type_register_static(&omap_i2c_info); -} - -i2c_bus *omap_i2c_bus(DeviceState *omap_i2c) -{ - OMAPI2CState *s = FROM_SYSBUS(OMAPI2CState, SYS_BUS_DEVICE(omap_i2c)); - return s->bus; -} - -type_init(omap_i2c_register_types) -- cgit v1.2.3-55-g7522