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 | ||||
|  | ||||
							
								
								
									
										179
									
								
								hw/bitbang_i2c.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								hw/bitbang_i2c.c
									
									
									
									
									
										Normal file
									
								
							| @ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Andrzej Zaborowski
						Andrzej Zaborowski