diff i2c.c @ 1414:d94855080529

Move I2C EEPROM and NOR Flash functions out of romdb.c into new files
author Michael Pavone <pavone@retrodev.com>
date Fri, 23 Jun 2017 21:48:38 -0700
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/i2c.c	Fri Jun 23 21:48:38 2017 -0700
@@ -0,0 +1,250 @@
+#include "genesis.h"
+#include "util.h"
+
+enum {
+	I2C_IDLE,
+	I2C_START,
+	I2C_DEVICE_ACK,
+	I2C_ADDRESS_HI,
+	I2C_ADDRESS_HI_ACK,
+	I2C_ADDRESS,
+	I2C_ADDRESS_ACK,
+	I2C_READ,
+	I2C_READ_ACK,
+	I2C_WRITE,
+	I2C_WRITE_ACK
+};
+
+char * i2c_states[] = {
+	"idle",
+	"start",
+	"device ack",
+	"address hi",
+	"address hi ack",
+	"address",
+	"address ack",
+	"read",
+	"read_ack",
+	"write",
+	"write_ack"
+};
+
+void eeprom_init(eeprom_state *state, uint8_t *buffer, uint32_t size)
+{
+	state->slave_sda = 1;
+	state->host_sda = state->scl = 0;
+	state->buffer = buffer;
+	state->size = size;
+	state->state = I2C_IDLE;
+}
+
+void set_host_sda(eeprom_state *state, uint8_t val)
+{
+	if (state->scl) {
+		if (val & ~state->host_sda) {
+			//low to high, stop condition
+			state->state = I2C_IDLE;
+			state->slave_sda = 1;
+		} else if (~val & state->host_sda) {
+			//high to low, start condition
+			state->state = I2C_START;
+			state->slave_sda = 1;
+			state->counter = 8;
+		}
+	}
+	state->host_sda = val;
+}
+
+void set_scl(eeprom_state *state, uint8_t val)
+{
+	if (val & ~state->scl) {
+		//low to high transition
+		switch (state->state)
+		{
+		case I2C_START:
+		case I2C_ADDRESS_HI:
+		case I2C_ADDRESS:
+		case I2C_WRITE:
+			state->latch = state->host_sda | state->latch << 1;
+			state->counter--;
+			if (!state->counter) {
+				switch (state->state & 0x7F)
+				{
+				case I2C_START:
+					state->state = I2C_DEVICE_ACK;
+					break;
+				case I2C_ADDRESS_HI:
+					state->address = state->latch << 8;
+					state->state = I2C_ADDRESS_HI_ACK;
+					break;
+				case I2C_ADDRESS:
+					state->address |= state->latch;
+					state->state = I2C_ADDRESS_ACK;
+					break;
+				case I2C_WRITE:
+					state->buffer[state->address] = state->latch;
+					state->state = I2C_WRITE_ACK;
+					break;
+				}
+			}
+			break;
+		case I2C_DEVICE_ACK:
+			if (state->latch & 1) {
+				state->state = I2C_READ;
+				state->counter = 8;
+				if (state->size < 256) {
+					state->address = state->latch >> 1;
+				}
+				state->latch = state->buffer[state->address];
+			} else {
+				if (state->size < 256) {
+					state->address = state->latch >> 1;
+					state->state = I2C_WRITE;
+				} else if (state->size < 4096) {
+					state->address = (state->latch & 0xE) << 7;
+					state->state = I2C_ADDRESS;
+				} else {
+					state->state = I2C_ADDRESS_HI;
+				}
+				state->counter = 8;
+			}
+			break;
+		case I2C_ADDRESS_HI_ACK:
+			state->state = I2C_ADDRESS;
+			state->counter = 8;
+			break;
+		case I2C_ADDRESS_ACK:
+			state->state = I2C_WRITE;
+			state->address &= state->size-1;
+			state->counter = 8;
+			break;
+		case I2C_READ:
+			state->counter--;
+			if (!state->counter) {
+				state->state = I2C_READ_ACK;
+			}
+			break;
+		case I2C_READ_ACK:
+			state->state = I2C_READ;
+			state->counter = 8;
+			state->address++;
+			//TODO: page mask
+			state->address &= state->size-1;
+			state->latch = state->buffer[state->address];
+			break;
+		case I2C_WRITE_ACK:
+			state->state = I2C_WRITE;
+			state->counter = 8;
+			state->address++;
+			//TODO: page mask
+			state->address &= state->size-1;
+			break;
+		}
+	} else if (~val & state->scl) {
+		//high to low transition
+		switch (state->state & 0x7F)
+		{
+		case I2C_DEVICE_ACK:
+		case I2C_ADDRESS_HI_ACK:
+		case I2C_ADDRESS_ACK:
+		case I2C_READ_ACK:
+		case I2C_WRITE_ACK:
+			state->slave_sda = 0;
+			break;
+		case I2C_READ:
+			state->slave_sda = state->latch >> 7;
+			state->latch = state->latch << 1;
+			break;
+		default:
+			state->slave_sda = 1;
+			break;
+		}
+	}
+	state->scl = val;
+}
+
+uint8_t get_sda(eeprom_state *state)
+{
+	return state->host_sda & state->slave_sda;
+}
+
+eeprom_map *find_eeprom_map(uint32_t address, genesis_context *gen)
+{
+	for (int i = 0; i < gen->num_eeprom; i++)
+	{
+		if (address >= gen->eeprom_map[i].start && address <= gen->eeprom_map[i].end) {
+			return  gen->eeprom_map + i;
+		}
+	}
+	return NULL;
+}
+
+void * write_eeprom_i2c_w(uint32_t address, void * context, uint16_t value)
+{
+	genesis_context *gen = ((m68k_context *)context)->system;
+	eeprom_map *map = find_eeprom_map(address, gen);
+	if (!map) {
+		fatal_error("Could not find EEPROM map for address %X\n", address);
+	}
+	if (map->scl_mask) {
+		set_scl(&gen->eeprom, (value & map->scl_mask) != 0);
+	}
+	if (map->sda_write_mask) {
+		set_host_sda(&gen->eeprom, (value & map->sda_write_mask) != 0);
+	}
+	return context;
+}
+
+void * write_eeprom_i2c_b(uint32_t address, void * context, uint8_t value)
+{
+	genesis_context *gen = ((m68k_context *)context)->system;
+	eeprom_map *map = find_eeprom_map(address, gen);
+	if (!map) {
+		fatal_error("Could not find EEPROM map for address %X\n", address);
+	}
+
+	uint16_t expanded, mask;
+	if (address & 1) {
+		expanded = value;
+		mask = 0xFF;
+	} else {
+		expanded = value << 8;
+		mask = 0xFF00;
+	}
+	if (map->scl_mask & mask) {
+		set_scl(&gen->eeprom, (expanded & map->scl_mask) != 0);
+	}
+	if (map->sda_write_mask & mask) {
+		set_host_sda(&gen->eeprom, (expanded & map->sda_write_mask) != 0);
+	}
+	return context;
+}
+
+uint16_t read_eeprom_i2c_w(uint32_t address, void * context)
+{
+	genesis_context *gen = ((m68k_context *)context)->system;
+	eeprom_map *map = find_eeprom_map(address, gen);
+	if (!map) {
+		fatal_error("Could not find EEPROM map for address %X\n", address);
+	}
+	uint16_t ret = 0;
+	if (map->sda_read_bit < 16) {
+		ret = get_sda(&gen->eeprom) << map->sda_read_bit;
+	}
+	return ret;	
+}
+
+uint8_t read_eeprom_i2c_b(uint32_t address, void * context)
+{
+	genesis_context *gen = ((m68k_context *)context)->system;
+	eeprom_map *map = find_eeprom_map(address, gen);
+	if (!map) {
+		fatal_error("Could not find EEPROM map for address %X\n", address);
+	}
+	uint8_t bit = address & 1 ? map->sda_read_bit : map->sda_read_bit - 8;
+	uint8_t ret = 0;
+	if (bit < 8) {
+		ret = get_sda(&gen->eeprom) << bit;
+	}
+	return ret;
+}