Skip to content

Commit

Permalink
Extract musicpal.c I2C bitbanging code and make it gpio aware
Browse files Browse the repository at this point in the history
Signed-off-by: Benoit Canet <[email protected]>
Signed-off-by: Andrzej Zaborowski <[email protected]>
  • Loading branch information
balrog-kun committed Aug 23, 2009
1 parent 343ec8e commit 3ead03b
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Makefile.target
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ obj-arm-y += omap2.o omap_dss.o soc_dma.o
obj-arm-y += omap_sx1.o palm.o tsc210x.o
obj-arm-y += nseries.o blizzard.o onenand.o vga.o cbus.o tusb6010.o usb-musb.o
obj-arm-y += mst_fpga.o mainstone.o
obj-arm-y += musicpal.o pflash_cfi02.o
obj-arm-y += musicpal.o pflash_cfi02.o bitbang_i2c.o
obj-arm-y += framebuffer.o
obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o
obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o
Expand Down
179 changes: 179 additions & 0 deletions hw/bitbang_i2c.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/*
* Bit-Bang i2c emulation extracted from
* Marvell MV88W8618 / Freecom MusicPal emulation.
*
* Copyright (c) 2008 Jan Kiszka
*
* This code is licenced under the GNU GPL v2.
*/
#include "hw.h"
#include "i2c.h"
#include "sysbus.h"

typedef enum bitbang_i2c_state {
STOPPED = 0,
INITIALIZING,
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
} bitbang_i2c_state;

typedef struct bitbang_i2c_interface {
SysBusDevice busdev;
i2c_bus *bus;
bitbang_i2c_state state;
int last_data;
int last_clock;
uint8_t buffer;
int current_addr;
qemu_irq out;
} bitbang_i2c_interface;

static void bitbang_i2c_enter_stop(bitbang_i2c_interface *i2c)
{
if (i2c->current_addr >= 0)
i2c_end_transfer(i2c->bus);
i2c->current_addr = -1;
i2c->state = STOPPED;
}

static void bitbang_i2c_gpio_set(void *opaque, int irq, int level)
{
bitbang_i2c_interface *i2c = opaque;
int data;
int clock;
int data_goes_up;
int data_goes_down;
int clock_goes_up;
int clock_goes_down;

/* get pins states */
data = i2c->last_data;
clock = i2c->last_clock;

if (irq == 0)
data = level;
if (irq == 1)
clock = level;

/* compute pins changes */
data_goes_up = data == 1 && i2c->last_data == 0;
data_goes_down = data == 0 && i2c->last_data == 1;
clock_goes_up = clock == 1 && i2c->last_clock == 0;
clock_goes_down = clock == 0 && i2c->last_clock == 1;

if (data_goes_up == 0 && data_goes_down == 0 &&
clock_goes_up == 0 && clock_goes_down == 0)
return;

if (!i2c)
return;

if ((RECEIVING_BIT7 > i2c->state && i2c->state > RECEIVING_BIT0)
|| i2c->state == WAITING_FOR_ACK)
qemu_set_irq(i2c->out, 0);

switch (i2c->state) {
case STOPPED:
if (data_goes_down && clock == 1)
i2c->state = INITIALIZING;
break;

case INITIALIZING:
if (clock_goes_down && data == 0)
i2c->state = SENDING_BIT7;
else
bitbang_i2c_enter_stop(i2c);
break;

case SENDING_BIT7 ... SENDING_BIT0:
if (clock_goes_down) {
i2c->buffer = (i2c->buffer << 1) | data;
/* will end up in WAITING_FOR_ACK */
i2c->state++;
} else if (data_goes_up && clock == 1)
bitbang_i2c_enter_stop(i2c);
break;

case WAITING_FOR_ACK:
if (clock_goes_down) {
if (i2c->current_addr < 0) {
i2c->current_addr = i2c->buffer;
i2c_start_transfer(i2c->bus, (i2c->current_addr & 0xfe) / 2,
i2c->buffer & 1);
} else
i2c_send(i2c->bus, i2c->buffer);
if (i2c->current_addr & 1) {
i2c->state = RECEIVING_BIT7;
i2c->buffer = i2c_recv(i2c->bus);
} else
i2c->state = SENDING_BIT7;
} else if (data_goes_up && clock == 1)
bitbang_i2c_enter_stop(i2c);
break;

case RECEIVING_BIT7 ... RECEIVING_BIT0:
qemu_set_irq(i2c->out, i2c->buffer >> 7);
if (clock_goes_down) {
/* will end up in SENDING_ACK */
i2c->state++;
i2c->buffer <<= 1;
} else if (data_goes_up && clock == 1)
bitbang_i2c_enter_stop(i2c);
break;

case SENDING_ACK:
if (clock_goes_down) {
i2c->state = RECEIVING_BIT7;
if (data == 0)
i2c->buffer = i2c_recv(i2c->bus);
else
i2c_nack(i2c->bus);
} else if (data_goes_up && clock == 1)
bitbang_i2c_enter_stop(i2c);
break;
}

i2c->last_data = data;
i2c->last_clock = clock;
}

static void bitbang_i2c_init(SysBusDevice *dev)
{
bitbang_i2c_interface *s = FROM_SYSBUS(bitbang_i2c_interface, dev);
i2c_bus *bus;

sysbus_init_mmio(dev, 0x0, 0);

bus = i2c_init_bus(&dev->qdev, "i2c");
s->bus = bus;

s->last_data = 1;
s->last_clock = 1;

qdev_init_gpio_in(&dev->qdev, bitbang_i2c_gpio_set, 2);
qdev_init_gpio_out(&dev->qdev, &s->out, 1);
}

static void bitbang_i2c_register(void)
{
sysbus_register_dev("bitbang_i2c",
sizeof(bitbang_i2c_interface), bitbang_i2c_init);
}

device_init(bitbang_i2c_register)

0 comments on commit 3ead03b

Please sign in to comment.