1 /*
2  * Cisco router simulation platform.
3  * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr)
4  *
5  * NMC93C46/NMC93C56 Serial EEPROM.
6  */
7 
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12 
13 #include "nmc93cX6.h"
14 
15 #define DEBUG_EEPROM  0
16 
17 /* Internal states */
18 enum {
19    EEPROM_STATE_INACTIVE = 0,
20    EEPROM_STATE_WAIT_CMD,
21    EEPROM_STATE_DATAOUT,
22 };
23 
24 /* Get command length for the specified group */
nmc94cX6_get_cmd_len(struct nmc93cX6_group * g)25 static u_int nmc94cX6_get_cmd_len(struct nmc93cX6_group *g)
26 {
27    switch(g->eeprom_type) {
28       case EEPROM_TYPE_NMC93C46:
29          return(NMC93C46_CMD_BITLEN);
30       case EEPROM_TYPE_NMC93C56:
31          return(NMC93C56_CMD_BITLEN);
32       default:
33          return(0);
34    }
35 }
36 
37 /* Extract EEPROM data address */
nmc94cX6_get_addr(struct nmc93cX6_group * g,u_int cmd)38 static u_int nmc94cX6_get_addr(struct nmc93cX6_group *g,u_int cmd)
39 {
40    switch(g->eeprom_type) {
41       case EEPROM_TYPE_NMC93C46:
42          return((cmd >> 3) & 0x3f);
43       case EEPROM_TYPE_NMC93C56:
44          return(m_reverse_u8((cmd >> 3) & 0xff));
45       default:
46          return(0);
47    }
48 }
49 
50 /* Check chip select */
nmc93cX6_check_cs(struct nmc93cX6_group * g,u_int old,u_int new)51 static void nmc93cX6_check_cs(struct nmc93cX6_group *g,u_int old,u_int new)
52 {
53    int i,res;
54 
55    for(i=0;i<g->nr_eeprom;i++)
56    {
57       if (g->dout_status == EEPROM_DOUT_HIGH)
58          g->state[i].dataout_val = 1;
59 
60       if (g->debug)
61       {
62          printf("EEPROM %s(%d): check_cs:  check_bit(old,new,select_bit) "
63                 "[%8.8x, %8.8x, %d (mask = %8.8x)] = %d\n",
64                 g->description, i,
65                 old, new, g->def[i]->select_bit, 1 << g->def[i]->select_bit,
66                 check_bit(old,new,g->def[i]->select_bit));
67       }
68 
69       if ((res = check_bit(old,new,g->def[i]->select_bit)) != 0) {
70          g->state[i].cmd_len = 0;     /* no bit for command sent now */
71          g->state[i].cmd_val = 0;
72          //g->state[i].dataout_val = 1;
73 
74          if (res == 2)
75             g->state[i].state = EEPROM_STATE_WAIT_CMD;
76          else
77             g->state[i].state = EEPROM_STATE_INACTIVE;
78       }
79    }
80 }
81 
82 /* Check clock set for a specific group */
nmc93cX6_check_clk_group(struct nmc93cX6_group * g,int group_id,u_int old,u_int new)83 static void nmc93cX6_check_clk_group(struct nmc93cX6_group *g,int group_id,
84                                      u_int old,u_int new)
85 {
86    struct cisco_eeprom *eeprom;
87    u_int cmd,op,addr,pos;
88    u_int clk_bit, din_bit;
89    u_int cmd_len;
90 
91    clk_bit = g->def[group_id]->clock_bit;
92    din_bit = g->def[group_id]->din_bit;
93 
94    if (g->debug)
95    {
96       printf("EEPROM %s(%d): check_clk: check_bit(old,new,select_bit) "
97              "[%8.8x, %8.8x, %d (mask = %8.8x)] = %d\n",
98              g->description, group_id,
99              old, new, clk_bit, 1 << clk_bit, check_bit(old,new,clk_bit));
100    }
101 
102    /* CLK bit set ? */
103    if (check_bit(old,new,clk_bit) != 2)
104       return;
105 
106    switch(g->state[group_id].state)
107    {
108       case EEPROM_STATE_WAIT_CMD:
109          /* The first bit must be set to "1" */
110          if ((g->state[group_id].cmd_len == 0) && !(new & (1 << din_bit)))
111             break;
112 
113          /* Read DATAIN bit */
114          if (new & (1 << din_bit))
115             g->state[group_id].cmd_val |= (1 << g->state[group_id].cmd_len);
116 
117          g->state[group_id].cmd_len++;
118 
119          cmd_len = nmc94cX6_get_cmd_len(g);
120 
121          /* Command is complete ? */
122          if (g->state[group_id].cmd_len == cmd_len)
123          {
124 #if DEBUG_EEPROM
125             printf("nmc93cX6: %s(%d): command = %x\n",
126                    g->description,group_id,g->state[group_id].cmd_val);
127 #endif
128             g->state[group_id].cmd_len = 0;
129 
130             /* we have the command! extract the opcode */
131             cmd = g->state[group_id].cmd_val;
132             op = cmd & 0x7;
133 
134             switch(op) {
135                case NMC93CX6_CMD_READ:
136                   g->state[group_id].state = EEPROM_STATE_DATAOUT;
137                   g->state[group_id].dataout_pos = 0;
138                   break;
139 #if DEBUG_EEPROM
140                default:
141                   printf("nmc93cX6: unhandled opcode %d\n",op);
142 #endif
143             }
144          }
145 
146          break;
147 
148       case EEPROM_STATE_DATAOUT:
149          /*
150           * user want to read data. we read 16-bits.
151           * extract address (6/9 bits) from command.
152           */
153 
154          cmd = g->state[group_id].cmd_val;
155          addr = nmc94cX6_get_addr(g,cmd);
156 
157 #if DEBUG_EEPROM
158          if (g->state[group_id].dataout_pos == 0) {
159             printf("nmc93cX6: %s(%d): "
160                    "read addr=%x (%d), val=%4.4x [eeprom=%p]\n",
161                    g->description,group_id,addr,addr,
162                    g->state[group_id].cmd_val,
163                    g->eeprom[group_id]);
164          }
165 #endif
166 
167          pos = g->state[group_id].dataout_pos++;
168 
169          if (g->reverse_data)
170             pos = 15 - pos;
171 
172          eeprom = g->eeprom[group_id];
173 
174          if (eeprom && eeprom->data && (addr < eeprom->len)) {
175             g->state[group_id].dataout_val = eeprom->data[addr] & (1 << pos);
176          } else {
177             /* access out of bounds */
178             g->state[group_id].dataout_val = (1 << pos);
179          }
180 
181          if (g->state[group_id].dataout_pos == NMC93CX6_CMD_DATALEN) {
182             g->state[group_id].state = EEPROM_STATE_INACTIVE;
183             g->state[group_id].dataout_pos = 0;
184          }
185          break;
186 
187 #if DEBUG_EEPROM
188       default:
189          printf("nmc93cX6: unhandled state %d\n",g->state[group_id].state);
190 #endif
191    }
192 }
193 
194 /* Check clock set for all group */
nmc93cX6_check_clk(struct nmc93cX6_group * g,u_int old,u_int new)195 void nmc93cX6_check_clk(struct nmc93cX6_group *g,u_int old,u_int new)
196 {
197    int i;
198 
199    for(i=0;i<g->nr_eeprom;i++)
200       nmc93cX6_check_clk_group(g,i,old,new);
201 }
202 
203 /* Handle write */
nmc93cX6_write(struct nmc93cX6_group * g,u_int data)204 void nmc93cX6_write(struct nmc93cX6_group *g,u_int data)
205 {
206    u_int new = data, old = g->eeprom_reg;
207 
208    nmc93cX6_check_cs(g,old,new);
209    nmc93cX6_check_clk(g,old,new);
210    g->eeprom_reg = new;
211 }
212 
213 /* Returns the TRUE if the EEPROM is active */
nmc93cX6_is_active(struct nmc93cX6_group * g,u_int group_id)214 u_int nmc93cX6_is_active(struct nmc93cX6_group *g,u_int group_id)
215 {
216    return(g->eeprom_reg & (1 << g->def[group_id]->select_bit));
217 }
218 
219 /* Returns the DOUT bit value */
nmc93cX6_get_dout(struct nmc93cX6_group * g,u_int group_id)220 u_int nmc93cX6_get_dout(struct nmc93cX6_group *g,u_int group_id)
221 {
222    if (g->state[group_id].dataout_val)
223       return(1 << g->def[group_id]->dout_bit);
224    else
225       return(0);
226 }
227 
228 /* Handle read */
nmc93cX6_read(struct nmc93cX6_group * g)229 u_int nmc93cX6_read(struct nmc93cX6_group *g)
230 {
231    u_int res;
232    int i;
233 
234    res = g->eeprom_reg;
235 
236    for(i=0;i<g->nr_eeprom;i++) {
237       if (!(g->eeprom_reg & (1 << g->def[i]->select_bit)))
238          continue;
239 
240       if (g->state[i].dataout_val)
241          res |= 1 << g->def[i]->dout_bit;
242       else
243          res &= ~(1 << g->def[i]->dout_bit);
244    }
245 
246    return(res);
247 }
248 
249