From: Toni Wilen Date: Wed, 27 Aug 2014 17:59:01 +0000 (+0300) Subject: Generic I2C EEPROM emulation. X-Git-Tag: 3000~82 X-Git-Url: https://git.unchartedbackwaters.co.uk/w/?a=commitdiff_plain;h=4762afb002aa806ee836a810b1631f57a63ed722;p=francis%2Fwinuae.git Generic I2C EEPROM emulation. --- diff --git a/flashrom.cpp b/flashrom.cpp index 1ad43411..3139219d 100644 --- a/flashrom.cpp +++ b/flashrom.cpp @@ -15,8 +15,277 @@ #include "memory.h" #include "newcpu.h" #include "debug.h" +#include "gui.h" #define FLASH_LOG 0 +#define EEPROM_LOG 0 + +/* EEPROM */ + +#define NVRAM_PAGE_SIZE 16 + +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; + +typedef enum eeprom_state { + I2C_DEVICEADDR, + I2C_WORDADDR, + I2C_DATA +} eeprom_state; + +struct bitbang_i2c_interface { + bitbang_i2c_state state; + int last_data; + int last_clock; + int device_out; + uint8_t buffer; + int current_addr; + + eeprom_state estate; + int eeprom_addr; + int size; + int write_offset; + uae_u8 *memory; + struct zfile *zf; +}; + +static void nvram_write (struct bitbang_i2c_interface *i2c, int offset, int len) +{ + if (i2c->zf) { + zfile_fseek(i2c->zf, offset, SEEK_SET); + zfile_fwrite(i2c->memory + offset, len, 1, i2c->zf); + } +} + +static void bitbang_i2c_enter_stop(bitbang_i2c_interface *i2c) +{ +#if EEPROM_LOG + write_log(_T("STOP\n")); +#endif + if (i2c->write_offset >= 0) + nvram_write(i2c, i2c->write_offset, 16); + i2c->write_offset = -1; + i2c->current_addr = -1; + i2c->state = STOPPED; + i2c->estate = I2C_DEVICEADDR; +} + +/* 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 eeprom_i2c_set(void *fdv, int line, int level) +{ + struct bitbang_i2c_interface *i2c = (bitbang_i2c_interface*)fdv; + int data; + + if (line == BITBANG_I2C_SDA) { + if (level < 0) + level = i2c->last_data; + 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) { +#if EEPROM_LOG + write_log(_T("START\n")); +#endif + /* START condition. */ + i2c->state = SENDING_BIT7; + i2c->current_addr = -1; + } else { + /* STOP condition. */ + bitbang_i2c_enter_stop(i2c); + } + return bitbang_i2c_ret(i2c, 1); + } else { + if (level < 0) + level = i2c->last_clock; + } + + 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); + + // Writing to EEPROM + case SENDING_BIT7: + case SENDING_BIT6: + case SENDING_BIT5: + case SENDING_BIT4: + case SENDING_BIT3: + case SENDING_BIT2: + case SENDING_BIT1: + case SENDING_BIT0: + i2c->buffer = (i2c->buffer << 1) | data; + /* will end up in WAITING_FOR_ACK */ + i2c->state = (bitbang_i2c_state)((int)i2c->state + 1); + return bitbang_i2c_ret(i2c, 1); + + case WAITING_FOR_ACK: + if (i2c->estate == I2C_DEVICEADDR) { + i2c->current_addr = i2c->buffer; +#if EEPROM_LOG + write_log(_T("Device address 0x%02x\n"), i2c->current_addr); +#endif + if ((i2c->current_addr & 0xf0) != 0xa0) { + write_log (_T("WARNING: I2C_DEVICEADDR: device address != 0xA0\n")); + i2c->state = STOPPED; + return bitbang_i2c_ret(i2c, 0); + } + if (i2c->current_addr & 1) { + i2c->estate = I2C_DATA; + } else { + i2c->estate = I2C_WORDADDR; + i2c->eeprom_addr = ((i2c->buffer >> 1) & 3) << 8; + } + } else if (i2c->estate == I2C_WORDADDR) { + i2c->estate = I2C_DATA; + i2c->eeprom_addr &= 0x300; + i2c->eeprom_addr |= i2c->buffer; +#if EEPROM_LOG + write_log(_T("EEPROM address %04x\n"), i2c->eeprom_addr); +#endif + } else if (!(i2c->current_addr & 1)) { +#if EEPROM_LOG + write_log(_T("Sent %04x 0x%02x\n"), i2c->eeprom_addr, i2c->buffer); +#endif + if (i2c->write_offset < 0) + i2c->write_offset = i2c->eeprom_addr; + i2c->memory[i2c->eeprom_addr] = i2c->buffer; + i2c->eeprom_addr = (i2c->eeprom_addr & ~(NVRAM_PAGE_SIZE - 1)) | (i2c->eeprom_addr + 1) & (NVRAM_PAGE_SIZE - 1); + gui_flicker_led (LED_MD, 0, 2); + } + if (i2c->current_addr & 1) { + i2c->state = RECEIVING_BIT7; + } else { + i2c->state = SENDING_BIT7; + } + return bitbang_i2c_ret(i2c, 0); + + // Reading from EEPROM + case RECEIVING_BIT7: + i2c->buffer = i2c->memory[i2c->eeprom_addr]; + //i2c->buffer = i2c_recv(i2c->bus); +#if EEPROM_LOG + write_log(_T("RX byte %04X 0x%02x\n"), i2c->eeprom_addr, i2c->buffer); +#endif + i2c->eeprom_addr++; + i2c->eeprom_addr &= i2c->size - 1; + gui_flicker_led (LED_MD, 0, 1); + /* Fall through... */ + case RECEIVING_BIT6: + case RECEIVING_BIT5: + case RECEIVING_BIT4: + case RECEIVING_BIT3: + case RECEIVING_BIT2: + case RECEIVING_BIT1: + case RECEIVING_BIT0: + data = i2c->buffer >> 7; + /* will end up in SENDING_ACK */ + i2c->state = (bitbang_i2c_state)((int)i2c->state + 1); + i2c->buffer <<= 1; + return bitbang_i2c_ret(i2c, data); + + case SENDING_ACK: + i2c->state = RECEIVING_BIT7; + if (data != 0) { +#if EEPROM_LOG > 1 + write_log(_T("NACKED\n")); +#endif + i2c->state = SENT_NACK; + //i2c_nack(i2c->bus); + } else { + ; +#if EEPROM_LOG > 1 + write_log(_T("ACKED\n")); +#endif + } + return bitbang_i2c_ret(i2c, 1); + } + abort(); +} + +void eeprom_reset(void *fdv) +{ + struct bitbang_i2c_interface *i2c = (bitbang_i2c_interface*)fdv; + if (!i2c) + return; + i2c->last_data = 1; + i2c->last_clock = 1; + i2c->device_out = 1; + i2c->eeprom_addr = 0; + i2c->write_offset = -1; + i2c->estate = I2C_DEVICEADDR; +} + +void *eeprom_new(uae_u8 *memory, int size, struct zfile *zf) +{ + bitbang_i2c_interface *s; + + s = xcalloc(bitbang_i2c_interface, 1); + + eeprom_reset(s); + + s->memory = memory; + s->size = size; + s->zf = zf; + + return s; +} + +void eeprom_free(void *fdv) +{ + struct bitbang_i2c_interface *i2c = (bitbang_i2c_interface*)fdv; + xfree(i2c); +} + + +/* FLASH */ struct flashrom_data { diff --git a/include/flashrom.h b/include/flashrom.h index 127f18f9..89717c90 100644 --- a/include/flashrom.h +++ b/include/flashrom.h @@ -1,8 +1,23 @@ +/* FLASH */ + void *flash_new(uae_u8 *rom, int flashsize, int allocsize, uae_u8 devicecode, struct zfile *zf); void flash_free(void *fdv); bool flash_write(void *fdv, uaecptr addr, uae_u8 v); uae_u32 flash_read(void *fdv, uaecptr addr); bool flash_active(void *fdv, uaecptr addr); -int flash_size(void *fdv); \ No newline at end of file +int flash_size(void *fdv); + +/* EPROM */ + +#define BITBANG_I2C_SDA 0 +#define BITBANG_I2C_SCL 1 + +void *eeprom_new(uae_u8 *rom, int size, struct zfile *zf); +void eeprom_free(void *i2c); +void eeprom_reset(void *i2c); +int eeprom_i2c_set(void *i2c, int line, int level); + + +