/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * NMC93C46/NMC93C56 Serial EEPROM. */ #include #include #include #include #include "nmc93cX6.h" #define DEBUG_EEPROM 0 /* Internal states */ enum { EEPROM_STATE_INACTIVE = 0, EEPROM_STATE_WAIT_CMD, EEPROM_STATE_DATAOUT, }; /* Get command length for the specified group */ static u_int nmc94cX6_get_cmd_len(struct nmc93cX6_group *g) { switch(g->eeprom_type) { case EEPROM_TYPE_NMC93C46: return(NMC93C46_CMD_BITLEN); case EEPROM_TYPE_NMC93C56: return(NMC93C56_CMD_BITLEN); default: return(0); } } /* Extract EEPROM data address */ static u_int nmc94cX6_get_addr(struct nmc93cX6_group *g,u_int cmd) { switch(g->eeprom_type) { case EEPROM_TYPE_NMC93C46: return((cmd >> 3) & 0x3f); case EEPROM_TYPE_NMC93C56: return(m_reverse_u8((cmd >> 3) & 0xff)); default: return(0); } } /* Check chip select */ static void nmc93cX6_check_cs(struct nmc93cX6_group *g,u_int old,u_int new) { int i,res; for(i=0;inr_eeprom;i++) { g->state[i].dataout_val = 1; if (g->debug) { printf("EEPROM %s(%d): check_cs: check_bit(old,new,select_bit) " "[%8.8x, %8.8x, %d (mask = %8.8x)] = %d\n", g->description, i, old, new, g->def[i]->select_bit, 1 << g->def[i]->select_bit, check_bit(old,new,g->def[i]->select_bit)); } if ((res = check_bit(old,new,g->def[i]->select_bit)) != 0) { g->state[i].cmd_len = 0; /* no bit for command sent now */ g->state[i].cmd_val = 0; if (res == 2) g->state[i].state = EEPROM_STATE_WAIT_CMD; else g->state[i].state = EEPROM_STATE_INACTIVE; } } } /* Check clock set for a specific group */ static void nmc93cX6_check_clk_group(struct nmc93cX6_group *g,int group_id, u_int old,u_int new) { struct cisco_eeprom *eeprom; u_int cmd,op,addr,pos; u_int clk_bit, din_bit; u_int cmd_len; clk_bit = g->def[group_id]->clock_bit; din_bit = g->def[group_id]->din_bit; if (g->debug) { printf("EEPROM %s(%d): check_clk: check_bit(old,new,select_bit) " "[%8.8x, %8.8x, %d (mask = %8.8x)] = %d\n", g->description, group_id, old,new, clk_bit, 1 << clk_bit, check_bit(old,new,clk_bit)); } /* CLK bit set ? */ if (check_bit(old,new,clk_bit) != 2) return; switch(g->state[group_id].state) { case EEPROM_STATE_WAIT_CMD: /* Read DATAIN bit */ if (new & (1 << din_bit)) g->state[group_id].cmd_val |= (1 << g->state[group_id].cmd_len); g->state[group_id].cmd_len++; cmd_len = nmc94cX6_get_cmd_len(g); /* Command is complete ? */ if ((g->state[group_id].cmd_len == cmd_len) && (g->state[group_id].cmd_val & 1)) { #if DEBUG_EEPROM printf("nmc93cX6: %s(%d): command = %x\n", g->description,group_id,g->state[group_id].cmd_val); #endif g->state[group_id].cmd_len = 0; /* we have the command! extract the opcode */ cmd = g->state[group_id].cmd_val; op = cmd & 0x7; switch(op) { case NMC93CX6_CMD_READ: g->state[group_id].state = EEPROM_STATE_DATAOUT; g->state[group_id].dataout_pos = 0; break; #if DEBUG_EEPROM default: printf("nmc93cX6: unhandled opcode %d\n",op); #endif } } break; case EEPROM_STATE_DATAOUT: /* * user want to read data. we read 16-bits. * extract address (6 bits) from command. */ cmd = g->state[group_id].cmd_val; addr = nmc94cX6_get_addr(g,cmd); #if DEBUG_EEPROM if (g->state[group_id].dataout_pos == 0) printf("nmc93cX6: %s(%d): read addr=%x (%d), val = %4.4x\n", g->description,group_id,addr,addr, g->state[group_id].cmd_val); #endif pos = g->state[group_id].dataout_pos++; eeprom = g->eeprom[group_id]; if (eeprom && eeprom->data && (addr < eeprom->len)) { g->state[group_id].dataout_val = eeprom->data[addr] & (1 << pos); } else { /* access out of bounds */ g->state[group_id].dataout_val = (1 << pos); } if (g->state[group_id].dataout_pos == NMC93CX6_CMD_DATALEN) { g->state[group_id].state = EEPROM_STATE_INACTIVE; g->state[group_id].dataout_pos = 0; } break; #if DEBUG_EEPROM default: printf("nmc93cX6: unhandled state %d\n",g->state[group_id].state); #endif } } /* Check clock set for all group */ void nmc93cX6_check_clk(struct nmc93cX6_group *g,u_int old,u_int new) { int i; for(i=0;inr_eeprom;i++) nmc93cX6_check_clk_group(g,i,old,new); } /* Handle write */ void nmc93cX6_write(struct nmc93cX6_group *g,u_int data) { u_int new = data, old = g->eeprom_reg; nmc93cX6_check_cs(g,old,new); nmc93cX6_check_clk(g,old,new); g->eeprom_reg = new; } /* Handle read */ u_int nmc93cX6_read(struct nmc93cX6_group *g) { u_int res; int i; res = g->eeprom_reg; for(i=0;inr_eeprom;i++) { if (!(g->eeprom_reg & (1 << g->def[i]->select_bit))) continue; if (g->state[i].dataout_val) res |= 1 << g->def[i]->dout_bit; else res &= ~(1 << g->def[i]->dout_bit); } return(res); }