1 |
/* |
2 |
* Cisco router simulation platform. |
3 |
* Copyright (c) 2007 Christophe Fillot (cf@utc.fr) |
4 |
* |
5 |
* C6k-Sup1a Midplane FPGA. |
6 |
*/ |
7 |
|
8 |
#include <stdio.h> |
9 |
#include <stdlib.h> |
10 |
#include <string.h> |
11 |
|
12 |
#include "cpu.h" |
13 |
#include "vm.h" |
14 |
#include "dynamips.h" |
15 |
#include "memory.h" |
16 |
#include "device.h" |
17 |
#include "nmc93cX6.h" |
18 |
#include "dev_c6sup1.h" |
19 |
|
20 |
#define DEBUG_UNKNOWN 1 |
21 |
#define DEBUG_ACCESS 1 |
22 |
#define DEBUG_NET_IRQ 1 |
23 |
|
24 |
/* |
25 |
* Function 0xX000: |
26 |
* bit 0: 0:present, 1:absent. |
27 |
* bit 1: power ok (?) |
28 |
*/ |
29 |
#define SLOT_NOT_PRESENT 0x01 |
30 |
#define SLOT_POWER_OK 0x02 |
31 |
|
32 |
/* |
33 |
* Function 0xX200: requires bit 3 to be set to avoid error about power |
34 |
* convertor failure. |
35 |
*/ |
36 |
#define SLOT_POWER_CONVERTOR 0x08 |
37 |
|
38 |
/* Midplane FPGA private data */ |
39 |
struct c6sup1_mpfpga_data { |
40 |
vm_obj_t vm_obj; |
41 |
struct vdevice dev; |
42 |
|
43 |
c6sup1_t *router; |
44 |
m_uint32_t irq_status; |
45 |
m_uint32_t intr_enable; |
46 |
|
47 |
/* Slot/function selector */ |
48 |
u_int slot_sel; |
49 |
|
50 |
/* Slot status (up/down) */ |
51 |
u_int slot_status[C6SUP1_MAX_SLOTS]; |
52 |
}; |
53 |
|
54 |
/* === Definitions for "Backplane" EEPROM (Chassis Clock, VTT, ...) ======= */ |
55 |
#define EEPROM_BP_DOUT 0 /* reg 0x3c */ |
56 |
#define EEPROM_BP_DIN 0 /* reg 0x20 */ |
57 |
#define EEPROM_BP_CLK 1 |
58 |
|
59 |
/* Chip select (CS) bits */ |
60 |
#define EEPROM_BP_CS_CHASSIS 3 /* Chassis (6509,...) */ |
61 |
#define EEPROM_BP_CS_CHASSIS2 4 /* Chassis redundant EEPROM ? */ |
62 |
#define EEPROM_BP_CS_PS1 5 /* Power Supply #1 */ |
63 |
#define EEPROM_BP_CS_PS2 6 /* Power Supply #2 */ |
64 |
#define EEPROM_BP_CS_CLK1 7 /* Clock card #1 */ |
65 |
#define EEPROM_BP_CS_CLK2 8 /* Clock card #2 */ |
66 |
#define EEPROM_BP_CS_VTT1 9 /* VTT #1 */ |
67 |
#define EEPROM_BP_CS_VTT2 10 /* VTT #2 */ |
68 |
#define EEPROM_BP_CS_VTT3 11 /* VTT #3 */ |
69 |
|
70 |
static const struct nmc93cX6_eeprom_def eeprom_bp_def_chassis = { |
71 |
EEPROM_BP_CLK, EEPROM_BP_CS_CHASSIS, EEPROM_BP_DIN, EEPROM_BP_DOUT, |
72 |
}; |
73 |
|
74 |
static const struct nmc93cX6_eeprom_def eeprom_bp_def_chassis2 = { |
75 |
EEPROM_BP_CLK, EEPROM_BP_CS_CHASSIS2, EEPROM_BP_DIN, EEPROM_BP_DOUT, |
76 |
}; |
77 |
|
78 |
static const struct nmc93cX6_eeprom_def eeprom_bp_def_ps1 = { |
79 |
EEPROM_BP_CLK, EEPROM_BP_CS_PS1, EEPROM_BP_DIN, EEPROM_BP_DOUT, |
80 |
}; |
81 |
|
82 |
static const struct nmc93cX6_eeprom_def eeprom_bp_def_ps2 = { |
83 |
EEPROM_BP_CLK, EEPROM_BP_CS_PS2, EEPROM_BP_DIN, EEPROM_BP_DOUT, |
84 |
}; |
85 |
|
86 |
static const struct nmc93cX6_eeprom_def eeprom_bp_def_clk1 = { |
87 |
EEPROM_BP_CLK, EEPROM_BP_CS_CLK1, EEPROM_BP_DIN, EEPROM_BP_DOUT, |
88 |
}; |
89 |
|
90 |
static const struct nmc93cX6_eeprom_def eeprom_bp_def_clk2 = { |
91 |
EEPROM_BP_CLK, EEPROM_BP_CS_CLK2, EEPROM_BP_DIN, EEPROM_BP_DOUT, |
92 |
}; |
93 |
|
94 |
static const struct nmc93cX6_eeprom_def eeprom_bp_def_vtt1 = { |
95 |
EEPROM_BP_CLK, EEPROM_BP_CS_VTT1, EEPROM_BP_DIN, EEPROM_BP_DOUT, |
96 |
}; |
97 |
|
98 |
static const struct nmc93cX6_eeprom_def eeprom_bp_def_vtt2 = { |
99 |
EEPROM_BP_CLK, EEPROM_BP_CS_VTT2, EEPROM_BP_DIN, EEPROM_BP_DOUT, |
100 |
}; |
101 |
|
102 |
static const struct nmc93cX6_eeprom_def eeprom_bp_def_vtt3 = { |
103 |
EEPROM_BP_CLK, EEPROM_BP_CS_VTT3, EEPROM_BP_DIN, EEPROM_BP_DOUT, |
104 |
}; |
105 |
|
106 |
/* Backplane EEPROMs */ |
107 |
static const struct nmc93cX6_group eeprom_bp_group = { |
108 |
EEPROM_TYPE_NMC93C56, 9, 0, |
109 |
EEPROM_DORD_REVERSED, |
110 |
EEPROM_DOUT_KEEP, |
111 |
EEPROM_DEBUG_DISABLED, |
112 |
"Backplane EEPROMs", |
113 |
{ |
114 |
&eeprom_bp_def_chassis, |
115 |
&eeprom_bp_def_chassis2, |
116 |
&eeprom_bp_def_ps1, |
117 |
&eeprom_bp_def_ps2, |
118 |
&eeprom_bp_def_clk1, |
119 |
&eeprom_bp_def_clk2, |
120 |
&eeprom_bp_def_vtt1, |
121 |
&eeprom_bp_def_vtt2, |
122 |
&eeprom_bp_def_vtt3, |
123 |
}, |
124 |
}; |
125 |
|
126 |
/* === Definitions for "Supervisor" EEPROMs (Sup1A,PFC/EARL) ============== */ |
127 |
#define EEPROM_SUP_DOUT 0 /* XXX */ |
128 |
#define EEPROM_SUP_DIN 2 |
129 |
#define EEPROM_SUP_CLK 1 |
130 |
#define EEPROM_SUP_CS 3 |
131 |
|
132 |
#define EEPROM_EARL_DOUT 2 /* XXX */ |
133 |
#define EEPROM_EARL_DIN 9 |
134 |
#define EEPROM_EARL_CLK 10 |
135 |
#define EEPROM_EARL_CS 8 |
136 |
|
137 |
static const struct nmc93cX6_eeprom_def eeprom_sup_def = { |
138 |
EEPROM_SUP_CLK, EEPROM_SUP_CS, EEPROM_SUP_DIN, EEPROM_SUP_DOUT, |
139 |
}; |
140 |
|
141 |
static const struct nmc93cX6_eeprom_def eeprom_earl_def = { |
142 |
EEPROM_EARL_CLK, EEPROM_EARL_CS, EEPROM_EARL_DIN, EEPROM_EARL_DOUT, |
143 |
}; |
144 |
|
145 |
/* Supervisor EEPROMs */ |
146 |
static const struct nmc93cX6_group eeprom_sup_group = { |
147 |
EEPROM_TYPE_NMC93C56, 2, 0, |
148 |
EEPROM_DORD_REVERSED, |
149 |
EEPROM_DOUT_KEEP, |
150 |
EEPROM_DEBUG_DISABLED, |
151 |
"Supervisor EEPROMs", |
152 |
{ &eeprom_sup_def, &eeprom_earl_def }, |
153 |
}; |
154 |
|
155 |
/* === Definitions for "Slot" EEPROM ====================================== */ |
156 |
#define EEPROM_SLOT_DOUT 0 /* reg 0x4c */ |
157 |
#define EEPROM_SLOT_DIN 0 /* reg 0x48 */ |
158 |
#define EEPROM_SLOT_CLK 1 |
159 |
#define EEPROM_SLOT_CS 3 |
160 |
|
161 |
static const struct nmc93cX6_eeprom_def eeprom_slot_def = { |
162 |
EEPROM_SLOT_CLK, EEPROM_SLOT_CS, EEPROM_SLOT_DIN, EEPROM_SLOT_DOUT, |
163 |
}; |
164 |
|
165 |
static const struct nmc93cX6_group eeprom_slot_group = { |
166 |
EEPROM_TYPE_NMC93C56, 1, 0, |
167 |
EEPROM_DORD_REVERSED, |
168 |
EEPROM_DOUT_KEEP, |
169 |
EEPROM_DEBUG_DISABLED, |
170 |
"Slot EEPROMs", |
171 |
{ &eeprom_slot_def }, |
172 |
}; |
173 |
|
174 |
/* ------------------------------------------------------------------------ */ |
175 |
|
176 |
/* Update network interrupt status */ |
177 |
static inline |
178 |
void dev_c6sup1_mpfpga_net_update_irq(struct c6sup1_mpfpga_data *d) |
179 |
{ |
180 |
if (d->irq_status) { |
181 |
vm_set_irq(d->router->vm,C6SUP1_NETIO_IRQ); |
182 |
} else { |
183 |
vm_clear_irq(d->router->vm,C6SUP1_NETIO_IRQ); |
184 |
} |
185 |
} |
186 |
|
187 |
/* Trigger a Network IRQ for the specified slot/port */ |
188 |
void dev_c6sup1_mpfpga_net_set_irq(struct c6sup1_mpfpga_data *d, |
189 |
u_int slot,u_int port) |
190 |
{ |
191 |
#if DEBUG_NET_IRQ |
192 |
vm_log(d->router->vm,"MP_FPGA","setting NetIRQ for slot %u port %u\n", |
193 |
slot,port); |
194 |
#endif |
195 |
d->irq_status |= 1 << slot; |
196 |
dev_c6sup1_mpfpga_net_update_irq(d); |
197 |
} |
198 |
|
199 |
/* Clear a Network IRQ for the specified slot/port */ |
200 |
void dev_c6sup1_mpfpga_net_clear_irq(struct c6sup1_mpfpga_data *d, |
201 |
u_int slot,u_int port) |
202 |
{ |
203 |
#if DEBUG_NET_IRQ |
204 |
vm_log(d->router->vm,"MP_FPGA","clearing NetIRQ for slot %u port %u\n", |
205 |
slot,port); |
206 |
#endif |
207 |
d->irq_status &= ~(1 << slot); |
208 |
dev_c6sup1_mpfpga_net_update_irq(d); |
209 |
} |
210 |
|
211 |
/* |
212 |
* dev_c6sup1_access() |
213 |
*/ |
214 |
void *dev_c6sup1_mpfpga_access(cpu_gen_t *cpu,struct vdevice *dev, |
215 |
m_uint32_t offset,u_int op_size,u_int op_type, |
216 |
m_uint64_t *data) |
217 |
{ |
218 |
struct c6sup1_mpfpga_data *d = dev->priv_data; |
219 |
struct nmc93cX6_group *grp; |
220 |
u_int i,slot,func; |
221 |
|
222 |
if (op_type == MTS_READ) |
223 |
*data = 0xFFFFFFFF; |
224 |
|
225 |
#if DEBUG_ACCESS |
226 |
if (op_type == MTS_READ) { |
227 |
cpu_log(cpu,"MP_FPGA", |
228 |
"reading reg 0x%x at pc=0x%llx, ra=0x%llx (size=%u)\n", |
229 |
offset,cpu_get_pc(cpu),CPU_MIPS64(cpu)->gpr[MIPS_GPR_RA], |
230 |
op_size); |
231 |
} else { |
232 |
cpu_log(cpu,"MP_FPGA", |
233 |
"writing reg 0x%x at pc=0x%llx, ra=0x%llx " |
234 |
"data=0x%llx (size=%u)\n", |
235 |
offset,cpu_get_pc(cpu),CPU_MIPS64(cpu)->gpr[MIPS_GPR_RA], |
236 |
*data,op_size); |
237 |
} |
238 |
#endif |
239 |
|
240 |
switch(offset) { |
241 |
case 0x0c: |
242 |
case 0x14: |
243 |
case 0x1c: |
244 |
if (op_type == MTS_READ) |
245 |
*data = 0; |
246 |
break; |
247 |
|
248 |
case 0x18: |
249 |
if (op_type == MTS_READ) |
250 |
*data = 0x8000; |
251 |
break; |
252 |
|
253 |
/* 0x3E80 is written regularly here (watchdog ?) */ |
254 |
case 0x20004: |
255 |
break; |
256 |
|
257 |
/* Backplane EEPROMs */ |
258 |
case 0x000020: |
259 |
if (op_type == MTS_WRITE) { |
260 |
//m_log("EEPROM","write access(BP): data=0x%4.4llx\n",*data); |
261 |
nmc93cX6_write(&d->router->bp_eeprom_group,(u_int)(*data)); |
262 |
} |
263 |
break; |
264 |
|
265 |
/* Supervisor EEPROMs */ |
266 |
case 0x000024: |
267 |
if (op_type == MTS_WRITE) { |
268 |
//m_log("EEPROM","write access(SUP): data=0x%4.4llx\n",*data); |
269 |
nmc93cX6_write(&d->router->sup_eeprom_group,(u_int)(*data)); |
270 |
} |
271 |
break; |
272 |
|
273 |
/* Backplane/Supervisor EEPROMs read access */ |
274 |
case 0x00003C: |
275 |
if (op_type == MTS_READ) { |
276 |
*data = 0x0000; |
277 |
|
278 |
/* Backplane EEPROMs */ |
279 |
grp = &d->router->bp_eeprom_group; |
280 |
|
281 |
for(i=0;i<grp->nr_eeprom;i++) { |
282 |
if (nmc93cX6_is_active(grp,i)) |
283 |
*data |= nmc93cX6_get_dout(grp,i); |
284 |
} |
285 |
|
286 |
/* Supervisor EEPROMs */ |
287 |
grp = &d->router->sup_eeprom_group; |
288 |
|
289 |
for(i=0;i<grp->nr_eeprom;i++) { |
290 |
if (nmc93cX6_is_active(grp,i)) |
291 |
if (nmc93cX6_get_dout(grp,i)) |
292 |
*data |= 0xFFFF; //nmc93cX6_get_dout(grp,i); |
293 |
} |
294 |
} |
295 |
break; |
296 |
|
297 |
/* Slot selection */ |
298 |
case 0x000044: |
299 |
if (op_type == MTS_WRITE) { |
300 |
d->slot_sel = *data; |
301 |
slot = (d->slot_sel & 0xF000) >> 12; |
302 |
func = (d->slot_sel & 0x0F00) >> 8; |
303 |
|
304 |
if (slot <= C6SUP1_MAX_SLOTS) { |
305 |
grp = &d->router->slot_eeprom_group; |
306 |
grp->eeprom[0] = &d->router->slot_eeprom[slot-1]; |
307 |
|
308 |
/* mark the slot as powered on */ |
309 |
if (func == 0x02) { |
310 |
//printf("Marking slot %u as powered ON\n",slot); |
311 |
d->slot_status[slot-1] = TRUE; |
312 |
} |
313 |
} |
314 |
} |
315 |
break; |
316 |
|
317 |
/* Slot EEPROM write */ |
318 |
case 0x000048: |
319 |
if (op_type == MTS_WRITE) |
320 |
nmc93cX6_write(&d->router->slot_eeprom_group,(u_int)(*data)); |
321 |
break; |
322 |
|
323 |
/* Slot EEPROM read */ |
324 |
case 0x00004c: |
325 |
if (op_type == MTS_READ) { |
326 |
grp = &d->router->slot_eeprom_group; |
327 |
slot = (d->slot_sel & 0xF000) >> 12; |
328 |
func = (d->slot_sel & 0x0F00) >> 8; |
329 |
*data = 0; |
330 |
|
331 |
switch(func) { |
332 |
/* Presence + power ? */ |
333 |
case 0x00: |
334 |
*data = SLOT_NOT_PRESENT; |
335 |
|
336 |
if (grp->eeprom[0] && grp->eeprom[0]->data) { |
337 |
*data = 0; |
338 |
|
339 |
/* The SUP slot is always powered */ |
340 |
if (d->slot_status[slot-1] || |
341 |
(slot == d->router->sup_slot)) |
342 |
*data |= SLOT_POWER_OK; |
343 |
} |
344 |
break; |
345 |
|
346 |
case 0x01: |
347 |
*data = 0x0001; |
348 |
|
349 |
if (grp->eeprom[0] && grp->eeprom[0]->data) { |
350 |
*data = 0x0000; |
351 |
} |
352 |
break; |
353 |
|
354 |
/* Power-related */ |
355 |
case 0x02: |
356 |
*data = SLOT_POWER_CONVERTOR; |
357 |
break; |
358 |
|
359 |
/* EEPROM reading */ |
360 |
case 0x05: |
361 |
if (nmc93cX6_is_active(grp,0)) |
362 |
*data |= nmc93cX6_get_dout(grp,0); |
363 |
break; |
364 |
|
365 |
default: |
366 |
cpu_log(cpu,"MP_FPGA","slot control: unknown func 0x%2.2x\n", |
367 |
func); |
368 |
} |
369 |
} |
370 |
break; |
371 |
|
372 |
/* Slot Identification */ |
373 |
case 0x000004: |
374 |
if (op_type == MTS_READ) |
375 |
*data = (d->router->sup_slot << 8) | 0x80; |
376 |
break; |
377 |
|
378 |
/* Unknown: EARL interrupt ? */ |
379 |
/* 00:00:27: %CPU_MONITOR-3-PEER_EXCEPTION: |
380 |
CPU_MONITOR peer has failed due to exception , resetting [0/1] */ |
381 |
case 0x000050: |
382 |
if (op_type == MTS_READ) |
383 |
*data = 0; //0xFFFF; |
384 |
break; |
385 |
|
386 |
case 0x000074: |
387 |
if (op_type == MTS_READ) |
388 |
*data = 0x0000; //0x3FFF; |
389 |
break; |
390 |
|
391 |
#if DEBUG_UNKNOWN |
392 |
default: |
393 |
if (op_type == MTS_READ) { |
394 |
cpu_log(cpu,"MP_FPGA", |
395 |
"read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", |
396 |
offset,cpu_get_pc(cpu),op_size); |
397 |
} else { |
398 |
cpu_log(cpu,"MP_FPGA", |
399 |
"write to unknown addr 0x%x, value=0x%llx, pc=0x%llx " |
400 |
"(op_size=%u)\n",offset,*data,cpu_get_pc(cpu),op_size); |
401 |
} |
402 |
#endif |
403 |
} |
404 |
|
405 |
return NULL; |
406 |
} |
407 |
|
408 |
/* Shutdown the MP FPGA device */ |
409 |
static void |
410 |
dev_c6sup1_mpfpga_shutdown(vm_instance_t *vm,struct c6sup1_mpfpga_data *d) |
411 |
{ |
412 |
if (d != NULL) { |
413 |
/* Remove the device */ |
414 |
dev_remove(vm,&d->dev); |
415 |
|
416 |
/* Free the structure itself */ |
417 |
free(d); |
418 |
} |
419 |
} |
420 |
|
421 |
/* Initialize EEPROM groups */ |
422 |
void c6sup1_init_eeprom_groups(c6sup1_t *router) |
423 |
{ |
424 |
struct nmc93cX6_group *grp; |
425 |
|
426 |
router->bp_eeprom_group = eeprom_bp_group; |
427 |
router->sup_eeprom_group = eeprom_sup_group; |
428 |
router->slot_eeprom_group = eeprom_slot_group; |
429 |
|
430 |
/* XXX */ |
431 |
grp = &router->bp_eeprom_group; |
432 |
grp->eeprom[0] = cisco_eeprom_find_c6k("C6K-CHASSIS-6509"); |
433 |
grp->eeprom[2] = cisco_eeprom_find_c6k("C6K-POWER-1000W"); |
434 |
grp->eeprom[3] = cisco_eeprom_find_c6k("C6K-POWER-1000W"); |
435 |
grp->eeprom[6] = cisco_eeprom_find_c6k("C6K-VTT"); |
436 |
grp->eeprom[7] = cisco_eeprom_find_c6k("C6K-VTT"); |
437 |
grp->eeprom[8] = cisco_eeprom_find_c6k("C6K-VTT"); |
438 |
|
439 |
grp = &router->sup_eeprom_group; |
440 |
grp->eeprom[0] = cisco_eeprom_find_c6k("C6K-SUP-SUP1A-2GE"); |
441 |
grp->eeprom[1] = cisco_eeprom_find_c6k("C6K-EARL-PFC1"); |
442 |
|
443 |
cisco_eeprom_copy(&router->slot_eeprom[0], |
444 |
cisco_eeprom_find_c6k("C6K-SUP-SUP1A-2GE")); |
445 |
|
446 |
cisco_eeprom_copy(&router->slot_eeprom[8], |
447 |
cisco_eeprom_find_c6k("C6K-LC-WS-X6248")); |
448 |
} |
449 |
|
450 |
/* |
451 |
* dev_c6sup1_mpfpga_init() |
452 |
*/ |
453 |
int dev_c6sup1_mpfpga_init(c6sup1_t *router,m_uint64_t paddr,m_uint32_t len) |
454 |
{ |
455 |
struct c6sup1_mpfpga_data *d; |
456 |
|
457 |
/* Allocate private data structure */ |
458 |
if (!(d = malloc(sizeof(*d)))) { |
459 |
fprintf(stderr,"MP_FPGA: out of memory\n"); |
460 |
return(-1); |
461 |
} |
462 |
|
463 |
memset(d,0,sizeof(*d)); |
464 |
d->router = router; |
465 |
|
466 |
vm_object_init(&d->vm_obj); |
467 |
d->vm_obj.name = "mp_fpga"; |
468 |
d->vm_obj.data = d; |
469 |
d->vm_obj.shutdown = (vm_shutdown_t)dev_c6sup1_mpfpga_shutdown; |
470 |
|
471 |
/* Set device properties */ |
472 |
dev_init(&d->dev); |
473 |
d->dev.name = "mp_fpga"; |
474 |
d->dev.phys_addr = paddr; |
475 |
d->dev.phys_len = len; |
476 |
d->dev.handler = dev_c6sup1_mpfpga_access; |
477 |
d->dev.priv_data = d; |
478 |
|
479 |
/* Map this device to the VM */ |
480 |
vm_bind_device(router->vm,&d->dev); |
481 |
vm_object_add(router->vm,&d->vm_obj); |
482 |
return(0); |
483 |
} |