Extract musicpal.c I2C bitbanging code and make it gpio aware
Signed-off-by: Benoit Canet <benoit.canet@gmail.com> Signed-off-by: Andrzej Zaborowski <balrogg@gmail.com>
This commit is contained in:
		
							parent
							
								
									343ec8e485
								
							
						
					
					
						commit
						3ead03bd0a
					
				| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
		Loading…
	
		Reference in New Issue