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
dev_c6sup1_mpfpga_net_update_irq(struct c6sup1_mpfpga_data * d)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 */
dev_c6sup1_mpfpga_net_set_irq(struct c6sup1_mpfpga_data * d,u_int slot,u_int 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 */
dev_c6sup1_mpfpga_net_clear_irq(struct c6sup1_mpfpga_data * d,u_int slot,u_int 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  */
dev_c6sup1_mpfpga_access(cpu_gen_t * cpu,struct vdevice * dev,m_uint32_t offset,u_int op_size,u_int op_type,m_uint64_t * data)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
dev_c6sup1_mpfpga_shutdown(vm_instance_t * vm,struct c6sup1_mpfpga_data * d)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 */
c6sup1_init_eeprom_groups(c6sup1_t * router)422 void c6sup1_init_eeprom_groups(c6sup1_t *router)
423 {
424    struct nmc93cX6_group *grp;
425    struct cisco_eeprom *buf;
426 
427    router->bp_eeprom_group = eeprom_bp_group;
428    router->sup_eeprom_group = eeprom_sup_group;
429    router->slot_eeprom_group = eeprom_slot_group;
430 
431    /* XXX */
432    buf = &router->bp_eeprom[0];
433    grp = &router->bp_eeprom_group;
434    grp->eeprom[0] = buf;  cisco_eeprom_copy(buf++, cisco_eeprom_find_c6k("C6K-CHASSIS-6509"));
435    grp->eeprom[2] = buf;  cisco_eeprom_copy(buf++, cisco_eeprom_find_c6k("C6K-POWER-1000W"));
436    grp->eeprom[3] = buf;  cisco_eeprom_copy(buf++, cisco_eeprom_find_c6k("C6K-POWER-1000W"));
437    grp->eeprom[6] = buf;  cisco_eeprom_copy(buf++, cisco_eeprom_find_c6k("C6K-VTT"));
438    grp->eeprom[7] = buf;  cisco_eeprom_copy(buf++, cisco_eeprom_find_c6k("C6K-VTT"));
439    grp->eeprom[8] = buf;  cisco_eeprom_copy(buf++, cisco_eeprom_find_c6k("C6K-VTT"));
440 
441    buf = &router->sup_eeprom[0];
442    grp = &router->sup_eeprom_group;
443    grp->eeprom[0] = buf;  cisco_eeprom_copy(buf++, cisco_eeprom_find_c6k("C6K-SUP-SUP1A-2GE"));
444    grp->eeprom[1] = buf;  cisco_eeprom_copy(buf++, cisco_eeprom_find_c6k("C6K-EARL-PFC1"));
445 
446    cisco_eeprom_copy(&router->slot_eeprom[0],
447                      cisco_eeprom_find_c6k("C6K-SUP-SUP1A-2GE"));
448 
449    cisco_eeprom_copy(&router->slot_eeprom[8],
450                      cisco_eeprom_find_c6k("C6K-LC-WS-X6248"));
451 }
452 
453 /*
454  * dev_c6sup1_mpfpga_init()
455  */
dev_c6sup1_mpfpga_init(c6sup1_t * router,m_uint64_t paddr,m_uint32_t len)456 int dev_c6sup1_mpfpga_init(c6sup1_t *router,m_uint64_t paddr,m_uint32_t len)
457 {
458    struct c6sup1_mpfpga_data *d;
459 
460    /* Allocate private data structure */
461    if (!(d = malloc(sizeof(*d)))) {
462       fprintf(stderr,"MP_FPGA: out of memory\n");
463       return(-1);
464    }
465 
466    memset(d,0,sizeof(*d));
467    d->router = router;
468 
469    vm_object_init(&d->vm_obj);
470    d->vm_obj.name = "mp_fpga";
471    d->vm_obj.data = d;
472    d->vm_obj.shutdown = (vm_shutdown_t)dev_c6sup1_mpfpga_shutdown;
473 
474    /* Set device properties */
475    dev_init(&d->dev);
476    d->dev.name      = "mp_fpga";
477    d->dev.phys_addr = paddr;
478    d->dev.phys_len  = len;
479    d->dev.handler   = dev_c6sup1_mpfpga_access;
480    d->dev.priv_data = d;
481 
482    /* Map this device to the VM */
483    vm_bind_device(router->vm,&d->dev);
484    vm_object_add(router->vm,&d->vm_obj);
485    return(0);
486 }
487