main i2c.c
  1#include "genesis.h"
  2#include "util.h"
  3
  4enum {
  5	I2C_IDLE,
  6	I2C_START,
  7	I2C_DEVICE_ACK,
  8	I2C_ADDRESS_HI,
  9	I2C_ADDRESS_HI_ACK,
 10	I2C_ADDRESS,
 11	I2C_ADDRESS_ACK,
 12	I2C_READ,
 13	I2C_READ_ACK,
 14	I2C_WRITE,
 15	I2C_WRITE_ACK
 16};
 17
 18char * i2c_states[] = {
 19	"idle",
 20	"start",
 21	"device ack",
 22	"address hi",
 23	"address hi ack",
 24	"address",
 25	"address ack",
 26	"read",
 27	"read_ack",
 28	"write",
 29	"write_ack"
 30};
 31
 32void eeprom_init(eeprom_state *state, uint8_t *buffer, uint32_t size)
 33{
 34	state->slave_sda = 1;
 35	state->host_sda = state->scl = 0;
 36	state->buffer = buffer;
 37	state->size = size;
 38	state->state = I2C_IDLE;
 39}
 40
 41void set_host_sda(eeprom_state *state, uint8_t val)
 42{
 43	if (state->scl) {
 44		if (val & ~state->host_sda) {
 45			//low to high, stop condition
 46			state->state = I2C_IDLE;
 47			state->slave_sda = 1;
 48		} else if (~val & state->host_sda) {
 49			//high to low, start condition
 50			state->state = I2C_START;
 51			state->slave_sda = 1;
 52			state->counter = 8;
 53		}
 54	}
 55	state->host_sda = val;
 56}
 57
 58void set_scl(eeprom_state *state, uint8_t val)
 59{
 60	if (val & ~state->scl) {
 61		//low to high transition
 62		switch (state->state)
 63		{
 64		case I2C_START:
 65		case I2C_ADDRESS_HI:
 66		case I2C_ADDRESS:
 67		case I2C_WRITE:
 68			state->latch = state->host_sda | state->latch << 1;
 69			state->counter--;
 70			if (!state->counter) {
 71				switch (state->state & 0x7F)
 72				{
 73				case I2C_START:
 74					state->state = I2C_DEVICE_ACK;
 75					break;
 76				case I2C_ADDRESS_HI:
 77					state->address = state->latch << 8;
 78					state->state = I2C_ADDRESS_HI_ACK;
 79					break;
 80				case I2C_ADDRESS:
 81					state->address |= state->latch;
 82					state->state = I2C_ADDRESS_ACK;
 83					break;
 84				case I2C_WRITE:
 85					state->buffer[state->address] = state->latch;
 86					state->state = I2C_WRITE_ACK;
 87					break;
 88				}
 89			}
 90			break;
 91		case I2C_DEVICE_ACK:
 92			if (state->latch & 1) {
 93				state->state = I2C_READ;
 94				state->counter = 8;
 95				if (state->size < 256) {
 96					state->address = state->latch >> 1;
 97				}
 98				state->latch = state->buffer[state->address];
 99			} else {
100				if (state->size < 256) {
101					state->address = state->latch >> 1;
102					state->state = I2C_WRITE;
103				} else if (state->size < 4096) {
104					state->address = (state->latch & 0xE) << 7;
105					state->state = I2C_ADDRESS;
106				} else {
107					state->state = I2C_ADDRESS_HI;
108				}
109				state->counter = 8;
110			}
111			break;
112		case I2C_ADDRESS_HI_ACK:
113			state->state = I2C_ADDRESS;
114			state->counter = 8;
115			break;
116		case I2C_ADDRESS_ACK:
117			state->state = I2C_WRITE;
118			state->address &= state->size-1;
119			state->counter = 8;
120			break;
121		case I2C_READ:
122			state->counter--;
123			if (!state->counter) {
124				state->state = I2C_READ_ACK;
125			}
126			break;
127		case I2C_READ_ACK:
128			state->state = I2C_READ;
129			state->counter = 8;
130			state->address++;
131			//TODO: page mask
132			state->address &= state->size-1;
133			state->latch = state->buffer[state->address];
134			break;
135		case I2C_WRITE_ACK:
136			state->state = I2C_WRITE;
137			state->counter = 8;
138			state->address++;
139			//TODO: page mask
140			state->address &= state->size-1;
141			break;
142		}
143	} else if (~val & state->scl) {
144		//high to low transition
145		switch (state->state & 0x7F)
146		{
147		case I2C_DEVICE_ACK:
148		case I2C_ADDRESS_HI_ACK:
149		case I2C_ADDRESS_ACK:
150		case I2C_READ_ACK:
151		case I2C_WRITE_ACK:
152			state->slave_sda = 0;
153			break;
154		case I2C_READ:
155			state->slave_sda = state->latch >> 7;
156			state->latch = state->latch << 1;
157			break;
158		default:
159			state->slave_sda = 1;
160			break;
161		}
162	}
163	state->scl = val;
164}
165
166uint8_t get_sda(eeprom_state *state)
167{
168	return state->host_sda & state->slave_sda;
169}
170
171eeprom_map *find_eeprom_map(uint32_t address, genesis_context *gen)
172{
173	for (int i = 0; i < gen->num_eeprom; i++)
174	{
175		if (address >= gen->eeprom_map[i].start && address <= gen->eeprom_map[i].end) {
176			return  gen->eeprom_map + i;
177		}
178	}
179	return NULL;
180}
181
182void * write_eeprom_i2c_w(uint32_t address, void * context, uint16_t value)
183{
184	genesis_context *gen = ((m68k_context *)context)->system;
185	eeprom_map *map = find_eeprom_map(address, gen);
186	if (!map) {
187		fatal_error("Could not find EEPROM map for address %X\n", address);
188	}
189	if (map->scl_mask) {
190		set_scl(&gen->eeprom, (value & map->scl_mask) != 0);
191	}
192	if (map->sda_write_mask) {
193		set_host_sda(&gen->eeprom, (value & map->sda_write_mask) != 0);
194	}
195	return context;
196}
197
198void * write_eeprom_i2c_b(uint32_t address, void * context, uint8_t value)
199{
200	genesis_context *gen = ((m68k_context *)context)->system;
201	eeprom_map *map = find_eeprom_map(address, gen);
202	if (!map) {
203		fatal_error("Could not find EEPROM map for address %X\n", address);
204	}
205
206	uint16_t expanded, mask;
207	if (address & 1) {
208		expanded = value;
209		mask = 0xFF;
210	} else {
211		expanded = value << 8;
212		mask = 0xFF00;
213	}
214	if (map->scl_mask & mask) {
215		set_scl(&gen->eeprom, (expanded & map->scl_mask) != 0);
216	}
217	if (map->sda_write_mask & mask) {
218		set_host_sda(&gen->eeprom, (expanded & map->sda_write_mask) != 0);
219	}
220	return context;
221}
222
223uint16_t read_eeprom_i2c_w(uint32_t address, void * context)
224{
225	genesis_context *gen = ((m68k_context *)context)->system;
226	eeprom_map *map = find_eeprom_map(address, gen);
227	if (!map) {
228		fatal_error("Could not find EEPROM map for address %X\n", address);
229	}
230	uint16_t ret = 0;
231	if (map->sda_read_bit < 16) {
232		ret = get_sda(&gen->eeprom) << map->sda_read_bit;
233	}
234	return ret;	
235}
236
237uint8_t read_eeprom_i2c_b(uint32_t address, void * context)
238{
239	genesis_context *gen = ((m68k_context *)context)->system;
240	eeprom_map *map = find_eeprom_map(address, gen);
241	if (!map) {
242		fatal_error("Could not find EEPROM map for address %X\n", address);
243	}
244	uint8_t bit = address & 1 ? map->sda_read_bit : map->sda_read_bit - 8;
245	uint8_t ret = 0;
246	if (bit < 8) {
247		ret = get_sda(&gen->eeprom) << bit;
248	}
249	return ret;
250}