]> git.unchartedbackwaters.co.uk Git - francis/winuae.git/commitdiff
Generic I2C EEPROM emulation.
authorToni Wilen <twilen@winuae.net>
Wed, 27 Aug 2014 17:59:01 +0000 (20:59 +0300)
committerToni Wilen <twilen@winuae.net>
Wed, 27 Aug 2014 17:59:01 +0000 (20:59 +0300)
flashrom.cpp
include/flashrom.h

index 1ad43411bfddbddc7bfff726df21516981444944..3139219dc5894864cc9eb39e3bbbfba0d311a246 100644 (file)
 #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
 {
index 127f18f9990d849d0e1986f13d558aaaf935be2e..89717c90db28e97642ba96b99f03b8420e9ac74e 100644 (file)
@@ -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);
+
+
+