1 /*
2 * Cisco router simulation platform.
3 * Copyright (c) 2007 Christophe Fillot (cf@utc.fr)
4 *
5 * Cisco C6k-SUP1 I/O FPGA:
6 * - Simulates a NMC93C56 Serial EEPROM.
7 * - Simulates a DALLAS DS1620 for Temperature Sensors.
8 * - Simulates console and AUX ports (SCN2681).
9 *
10 * This is very similar to c7200 platform.
11 */
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <unistd.h>
17 #include <sys/types.h>
18
19 #include <termios.h>
20 #include <fcntl.h>
21 #include <pthread.h>
22
23 #include "ptask.h"
24 #include "cpu.h"
25 #include "vm.h"
26 #include "dynamips.h"
27 #include "memory.h"
28 #include "device.h"
29 #include "dev_vtty.h"
30 #include "nmc93cX6.h"
31 #include "dev_ds1620.h"
32 #include "dev_c6sup1.h"
33
34 /* Debugging flags */
35 #define DEBUG_UNKNOWN 1
36 #define DEBUG_ACCESS 0
37 #define DEBUG_LED 0
38 #define DEBUG_IO_CTL 0
39 #define DEBUG_ENVM 0
40
41 /* DUART RX/TX status (SRA/SRB) */
42 #define DUART_RX_READY 0x01
43 #define DUART_TX_READY 0x04
44
45 /* DUART RX/TX Interrupt Status/Mask */
46 #define DUART_TXRDYA 0x01
47 #define DUART_RXRDYA 0x02
48 #define DUART_TXRDYB 0x10
49 #define DUART_RXRDYB 0x20
50
51 /* Pack the NVRAM */
52 #define NVRAM_PACKED 0x04
53
54 /* Temperature: 22�C as default value */
55 #define C6SUP1_DEFAULT_TEMP 22
56 #define DS1620_CHIP(d,id) (&(d)->router->ds1620_sensors[(id)])
57
58 /* 2 temperature sensors in a MSFC1: chassis inlet and oulet */
59 #define C6SUP1_TEMP_SENSORS 2
60 #define C6SUP1_DEFAULT_TEMP 22 /* default temperature: 22�C */
61
62 /* IO FPGA structure */
63 struct iofpga_data {
64 vm_obj_t vm_obj;
65 struct vdevice dev;
66 c6sup1_t *router;
67
68 /* Lock test */
69 pthread_mutex_t lock;
70
71 /* Periodic task to trigger dummy DUART IRQ */
72 ptask_id_t duart_irq_tid;
73
74 /* DUART & Console Management */
75 u_int duart_isr,duart_imr,duart_irq_seq;
76
77 /* IO control register */
78 u_int io_ctrl_reg;
79
80 /* Temperature Control */
81 u_int temp_cfg_reg[C6SUP1_TEMP_SENSORS];
82 u_int temp_deg_reg[C6SUP1_TEMP_SENSORS];
83 u_int temp_clk_low;
84
85 u_int temp_cmd;
86 u_int temp_cmd_pos;
87
88 u_int temp_data;
89 u_int temp_data_pos;
90
91 /* Voltages */
92 u_int mux;
93 };
94
95 #define IOFPGA_LOCK(d) pthread_mutex_lock(&(d)->lock)
96 #define IOFPGA_UNLOCK(d) pthread_mutex_unlock(&(d)->lock)
97
98 /* Console port input */
tty_con_input(vtty_t * vtty)99 static void tty_con_input(vtty_t *vtty)
100 {
101 struct iofpga_data *d = vtty->priv_data;
102
103 IOFPGA_LOCK(d);
104 if (d->duart_imr & DUART_RXRDYA) {
105 d->duart_isr |= DUART_RXRDYA;
106 vm_set_irq(d->router->vm,C6SUP1_DUART_IRQ);
107 }
108 IOFPGA_UNLOCK(d);
109 }
110
111 /* AUX port input */
tty_aux_input(vtty_t * vtty)112 static void tty_aux_input(vtty_t *vtty)
113 {
114 struct iofpga_data *d = vtty->priv_data;
115
116 IOFPGA_LOCK(d);
117 if (d->duart_imr & DUART_RXRDYB) {
118 d->duart_isr |= DUART_RXRDYB;
119 vm_set_irq(d->router->vm,C6SUP1_DUART_IRQ);
120 }
121 IOFPGA_UNLOCK(d);
122 }
123
124 /* IRQ trickery for Console and AUX ports */
tty_trigger_dummy_irq(struct iofpga_data * d,void * arg)125 static int tty_trigger_dummy_irq(struct iofpga_data *d,void *arg)
126 {
127 u_int mask;
128
129 IOFPGA_LOCK(d);
130 d->duart_irq_seq++;
131
132 if (d->duart_irq_seq == 2) {
133 mask = DUART_TXRDYA|DUART_TXRDYB;
134 if (d->duart_imr & mask) {
135 d->duart_isr |= DUART_TXRDYA|DUART_TXRDYB;
136 vm_set_irq(d->router->vm,C6SUP1_DUART_IRQ);
137 }
138
139 d->duart_irq_seq = 0;
140 }
141
142 IOFPGA_UNLOCK(d);
143 return(0);
144 }
145
146 /*
147 * dev_c6sup1_iofpga_access()
148 */
dev_c6sup1_iofpga_access(cpu_gen_t * cpu,struct vdevice * dev,m_uint32_t offset,u_int op_size,u_int op_type,m_uint64_t * data)149 void *dev_c6sup1_iofpga_access(cpu_gen_t *cpu,struct vdevice *dev,
150 m_uint32_t offset,u_int op_size,u_int op_type,
151 m_uint64_t *data)
152 {
153 struct iofpga_data *d = dev->priv_data;
154 vm_instance_t *vm = d->router->vm;
155 u_char odata;
156 int i;
157
158 if (op_type == MTS_READ)
159 *data = 0x0;
160
161 #if DEBUG_ACCESS
162 if (op_type == MTS_READ) {
163 cpu_log(cpu,"IO_FPGA","reading reg 0x%x at pc=0x%llx\n",
164 offset,cpu_get_pc(cpu));
165 } else {
166 cpu_log(cpu,"IO_FPGA","writing reg 0x%x at pc=0x%llx, data=0x%llx\n",
167 offset,cpu_get_pc(cpu),*data);
168 }
169 #endif
170
171 IOFPGA_LOCK(d);
172
173 switch(offset) {
174 /* I/O control register */
175 case 0x204:
176 if (op_type == MTS_WRITE) {
177 #if DEBUG_IO_CTL
178 vm_log(vm,"IO_FPGA","setting value 0x%llx in io_ctrl_reg\n",*data);
179 #endif
180 d->io_ctrl_reg = *data;
181 } else {
182 *data = d->io_ctrl_reg;
183 *data |= NVRAM_PACKED; /* Packed NVRAM */
184 }
185 break;
186
187 /* Watchdog */
188 case 0x234:
189 break;
190
191 /*
192 * FPGA release/presence ? Flash SIMM size:
193 * 0x0001: 2048K Flash (2 banks)
194 * 0x0504: 8192K Flash (2 banks)
195 * 0x0704: 16384K Flash (2 banks)
196 * 0x0904: 32768K Flash (2 banks)
197 * 0x0B04: 65536K Flash (2 banks)
198 * 0x2001: 1024K Flash (1 bank)
199 * 0x2504: 4096K Flash (1 bank)
200 * 0x2704: 8192K Flash (1 bank)
201 * 0x2904: 16384K Flash (1 bank)
202 * 0x2B04: 32768K Flash (1 bank)
203 *
204 * Number of Flash SIMM banks + size.
205 * Touching some lower bits causes problems with environmental monitor.
206 *
207 * It is displayed by command "sh bootflash: chips"
208 */
209 case 0x23c:
210 if (op_type == MTS_READ)
211 *data = 0x2704;
212 break;
213
214 /* LEDs */
215 case 0x244:
216 #if DEBUG_LED
217 vm_log(vm,"IO_FPGA","LED register is now 0x%x (0x%x)\n",
218 *data,(~*data) & 0x0F);
219 #endif
220 break;
221
222 /* ==== DUART SCN2681 (console/aux) ==== */
223 case 0x404: /* Mode Register A (MRA) */
224 break;
225
226 case 0x40c: /* Status Register A (SRA) */
227 if (op_type == MTS_READ) {
228 odata = 0;
229
230 if (vtty_is_char_avail(vm->vtty_con))
231 odata |= DUART_RX_READY;
232
233 odata |= DUART_TX_READY;
234
235 vm_clear_irq(vm,C6SUP1_DUART_IRQ);
236 *data = odata;
237 }
238 break;
239
240 case 0x414: /* Command Register A (CRA) */
241 /* Disable TX = High */
242 if ((op_type == MTS_WRITE) && (*data & 0x8)) {
243 vm->vtty_con->managed_flush = TRUE;
244 vtty_flush(vm->vtty_con);
245 }
246 break;
247
248 case 0x41c: /* RX/TX Holding Register A (RHRA/THRA) */
249 if (op_type == MTS_WRITE) {
250 vtty_put_char(vm->vtty_con,(char)*data);
251 d->duart_isr &= ~DUART_TXRDYA;
252 } else {
253 *data = vtty_get_char(vm->vtty_con);
254 d->duart_isr &= ~DUART_RXRDYA;
255 }
256 break;
257
258 case 0x424: /* WRITE: Aux Control Register (ACR) */
259 break;
260
261 case 0x42c: /* Interrupt Status/Mask Register (ISR/IMR) */
262 if (op_type == MTS_WRITE) {
263 d->duart_imr = *data;
264 } else
265 *data = d->duart_isr;
266 break;
267
268 case 0x434: /* Counter/Timer Upper Value (CTU) */
269 case 0x43c: /* Counter/Timer Lower Value (CTL) */
270 case 0x444: /* Mode Register B (MRB) */
271 break;
272
273 case 0x44c: /* Status Register B (SRB) */
274 if (op_type == MTS_READ) {
275 odata = 0;
276
277 if (vtty_is_char_avail(vm->vtty_aux))
278 odata |= DUART_RX_READY;
279
280 odata |= DUART_TX_READY;
281
282 //vm_clear_irq(vm,C6SUP1_DUART_IRQ);
283 *data = odata;
284 }
285 break;
286
287 case 0x454: /* Command Register B (CRB) */
288 /* Disable TX = High */
289 if ((op_type == MTS_WRITE) && (*data & 0x8)) {
290 vm->vtty_aux->managed_flush = TRUE;
291 vtty_flush(vm->vtty_aux);
292 }
293 break;
294
295 case 0x45c: /* RX/TX Holding Register B (RHRB/THRB) */
296 if (op_type == MTS_WRITE) {
297 vtty_put_char(vm->vtty_aux,(char)*data);
298 d->duart_isr &= ~DUART_TXRDYA;
299 } else {
300 *data = vtty_get_char(vm->vtty_aux);
301 d->duart_isr &= ~DUART_RXRDYB;
302 }
303 break;
304
305 case 0x46c: /* WRITE: Output Port Configuration Register (OPCR) */
306 case 0x474: /* READ: Start Counter Command; */
307 /* WRITE: Set Output Port Bits Command */
308 case 0x47c: /* WRITE: Reset Output Port Bits Command */
309 break;
310
311 /* ==== DS 1620 (temp sensors) ==== */
312 case 0x20c: /* Temperature Control */
313 if (op_type == MTS_WRITE) {
314 for(i=0;i<C6SUP1_TEMP_SENSORS;i++) {
315 ds1620_set_rst_bit(DS1620_CHIP(d,i),(*data >> i) & 0x01);
316 ds1620_set_clk_bit(DS1620_CHIP(d,i),(*data >> 4) & 0x01);
317 }
318 }
319 break;
320
321 case 0x214: /* Temperature data write */
322 if (op_type == MTS_WRITE) {
323 d->mux = *data;
324
325 for(i=0;i<C6SUP1_TEMP_SENSORS;i++)
326 ds1620_write_data_bit(DS1620_CHIP(d,i),*data & 0x01);
327 }
328 break;
329
330 case 0x22c: /* Temperature data read */
331 if (op_type == MTS_READ) {
332 *data = 0;
333
334 for(i=0;i<C6SUP1_TEMP_SENSORS;i++)
335 *data |= ds1620_read_data_bit(DS1620_CHIP(d,i)) << i;
336 }
337 break;
338
339 #if DEBUG_UNKNOWN
340 default:
341 if (op_type == MTS_READ) {
342 cpu_log(cpu,"IO_FPGA","read from addr 0x%x, pc=0x%llx (size=%u)\n",
343 offset,cpu_get_pc(cpu),op_size);
344 } else {
345 cpu_log(cpu,"IO_FPGA","write to addr 0x%x, value=0x%llx, "
346 "pc=0x%llx (size=%u)\n",
347 offset,*data,cpu_get_pc(cpu),op_size);
348 }
349 #endif
350 }
351
352 IOFPGA_UNLOCK(d);
353 return NULL;
354 }
355
356 /* Shutdown the IO FPGA device */
dev_c6sup1_iofpga_shutdown(vm_instance_t * vm,struct iofpga_data * d)357 void dev_c6sup1_iofpga_shutdown(vm_instance_t *vm,struct iofpga_data *d)
358 {
359 if (d != NULL) {
360 IOFPGA_LOCK(d);
361 vm->vtty_con->read_notifier = NULL;
362 vm->vtty_aux->read_notifier = NULL;
363 IOFPGA_UNLOCK(d);
364
365 /* Remove the dummy IRQ periodic task */
366 ptask_remove(d->duart_irq_tid);
367
368 /* Remove the device */
369 dev_remove(vm,&d->dev);
370
371 /* Free the structure itself */
372 free(d);
373 }
374 }
375
376 /*
377 * dev_c6sup1_iofpga_init()
378 */
dev_c6sup1_iofpga_init(c6sup1_t * router,m_uint64_t paddr,m_uint32_t len)379 int dev_c6sup1_iofpga_init(c6sup1_t *router,m_uint64_t paddr,m_uint32_t len)
380 {
381 vm_instance_t *vm = router->vm;
382 struct iofpga_data *d;
383 u_int i;
384
385 /* Allocate private data structure */
386 if (!(d = malloc(sizeof(*d)))) {
387 fprintf(stderr,"IO_FPGA: out of memory\n");
388 return(-1);
389 }
390
391 memset(d,0,sizeof(*d));
392
393 pthread_mutex_init(&d->lock,NULL);
394 d->router = router;
395
396 for(i=0;i<C6SUP1_TEMP_SENSORS;i++)
397 ds1620_init(DS1620_CHIP(d,i),C6SUP1_DEFAULT_TEMP);
398
399 vm_object_init(&d->vm_obj);
400 d->vm_obj.name = "io_fpga";
401 d->vm_obj.data = d;
402 d->vm_obj.shutdown = (vm_shutdown_t)dev_c6sup1_iofpga_shutdown;
403
404 /* Set device properties */
405 dev_init(&d->dev);
406 d->dev.name = "io_fpga";
407 d->dev.phys_addr = paddr;
408 d->dev.phys_len = len;
409 d->dev.handler = dev_c6sup1_iofpga_access;
410 d->dev.priv_data = d;
411
412 /* Set console and AUX port notifying functions */
413 vm->vtty_con->priv_data = d;
414 vm->vtty_aux->priv_data = d;
415 vm->vtty_con->read_notifier = tty_con_input;
416 vm->vtty_aux->read_notifier = tty_aux_input;
417
418 /* Trigger periodically a dummy IRQ to flush buffers */
419 d->duart_irq_tid = ptask_add((ptask_callback)tty_trigger_dummy_irq,
420 d,NULL);
421
422 /* Map this device to the VM */
423 vm_bind_device(vm,&d->dev);
424 vm_object_add(vm,&d->vm_obj);
425 return(0);
426 }
427