1 |
/* |
/* |
2 |
* Cisco C7200 (Predator) Simulation Platform. |
* Cisco router simulation platform. |
3 |
* Copyright (C) 2005,2006 Christophe Fillot. All rights reserved. |
* Copyright (C) 2005,2006 Christophe Fillot. All rights reserved. |
4 |
* |
* |
5 |
* Serial Interfaces (Mueslix). |
* Serial Interfaces (Mueslix). |
36 |
#include <errno.h> |
#include <errno.h> |
37 |
#include <assert.h> |
#include <assert.h> |
38 |
|
|
39 |
#include "mips64.h" |
#include "cpu.h" |
40 |
|
#include "vm.h" |
41 |
#include "dynamips.h" |
#include "dynamips.h" |
42 |
#include "memory.h" |
#include "memory.h" |
43 |
#include "device.h" |
#include "device.h" |
96 |
#define MUESLIX_XYMEM_LEN 0x100 |
#define MUESLIX_XYMEM_LEN 0x100 |
97 |
|
|
98 |
/* Maximum packet size */ |
/* Maximum packet size */ |
99 |
#define MUESLIX_MAX_PKT_SIZE 2048 |
#define MUESLIX_MAX_PKT_SIZE 18000 |
100 |
|
|
101 |
/* Send up to 16 packets in a TX ring scan pass */ |
/* Send up to 16 packets in a TX ring scan pass */ |
102 |
#define MUESLIX_TXRING_PASS_COUNT 16 |
#define MUESLIX_TXRING_PASS_COUNT 16 |
109 |
#define MUESLIX_RXDESC_IGNORED 0x08000000 /* Ignored */ |
#define MUESLIX_RXDESC_IGNORED 0x08000000 /* Ignored */ |
110 |
#define MUESLIX_RXDESC_ABORT 0x04000000 /* Abort */ |
#define MUESLIX_RXDESC_ABORT 0x04000000 /* Abort */ |
111 |
#define MUESLIX_RXDESC_CRC 0x02000000 /* CRC error */ |
#define MUESLIX_RXDESC_CRC 0x02000000 /* CRC error */ |
112 |
#define MUESLIX_RXDESC_LEN_MASK 0xfff |
#define MUESLIX_RXDESC_LEN_MASK 0xffff |
113 |
|
|
114 |
/* TX descriptors */ |
/* TX descriptors */ |
115 |
#define MUESLIX_TXDESC_OWN 0x80000000 /* Ownership */ |
#define MUESLIX_TXDESC_OWN 0x80000000 /* Ownership */ |
121 |
#define MUESLIX_TXDESC_PAD 0x00c00000 /* Sort of padding info ? */ |
#define MUESLIX_TXDESC_PAD 0x00c00000 /* Sort of padding info ? */ |
122 |
#define MUESLIX_TXDESC_PAD_SHIFT 22 |
#define MUESLIX_TXDESC_PAD_SHIFT 22 |
123 |
|
|
124 |
#define MUESLIX_TXDESC_LEN_MASK 0xfff |
#define MUESLIX_TXDESC_LEN_MASK 0xffff |
125 |
|
|
126 |
/* RX Descriptor */ |
/* RX Descriptor */ |
127 |
struct rx_desc { |
struct rx_desc { |
141 |
/* Channel ID */ |
/* Channel ID */ |
142 |
u_int id; |
u_int id; |
143 |
|
|
|
/* RX/TX status */ |
|
|
u_int rx_tx_status; |
|
|
|
|
144 |
/* Channel status (0=disabled) */ |
/* Channel status (0=disabled) */ |
145 |
u_int status; |
u_int status; |
146 |
|
|
147 |
|
/* CRC control register */ |
148 |
|
u_int crc_ctrl_reg; |
149 |
|
|
150 |
|
/* CRC size */ |
151 |
|
u_int crc_size; |
152 |
|
|
153 |
/* NetIO descriptor */ |
/* NetIO descriptor */ |
154 |
netio_desc_t *nio; |
netio_desc_t *nio; |
155 |
|
|
169 |
/* Mueslix Data */ |
/* Mueslix Data */ |
170 |
struct mueslix_data { |
struct mueslix_data { |
171 |
char *name; |
char *name; |
172 |
|
|
173 |
|
/* Lock */ |
174 |
|
pthread_mutex_t lock; |
175 |
|
|
176 |
|
/* IRQ status and mask */ |
177 |
|
m_uint32_t irq_status,irq_mask; |
178 |
|
u_int irq_clearing_count; |
179 |
|
|
180 |
/* TPU options */ |
/* TPU options */ |
181 |
m_uint32_t tpu_options; |
m_uint32_t tpu_options; |
182 |
|
|
214 |
MUESLIX_CHANNEL2_OFFSET, MUESLIX_CHANNEL3_OFFSET, |
MUESLIX_CHANNEL2_OFFSET, MUESLIX_CHANNEL3_OFFSET, |
215 |
}; |
}; |
216 |
|
|
217 |
|
/* Lock/Unlock primitives */ |
218 |
|
#define MUESLIX_LOCK(d) pthread_mutex_lock(&(d)->lock) |
219 |
|
#define MUESLIX_UNLOCK(d) pthread_mutex_unlock(&(d)->lock) |
220 |
|
|
221 |
/* Log a Mueslix message */ |
/* Log a Mueslix message */ |
222 |
#define MUESLIX_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg) |
#define MUESLIX_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg) |
223 |
|
|
228 |
return((d->channel_enable_mask >> (id << 1)) & 0x03); |
return((d->channel_enable_mask >> (id << 1)) & 0x03); |
229 |
} |
} |
230 |
|
|
231 |
|
/* Update IRQ status */ |
232 |
|
static inline void dev_mueslix_update_irq_status(struct mueslix_data *d) |
233 |
|
{ |
234 |
|
if (d->irq_status & d->irq_mask) |
235 |
|
pci_dev_trigger_irq(d->vm,d->pci_dev); |
236 |
|
else { |
237 |
|
if (++d->irq_clearing_count == 3) { |
238 |
|
pci_dev_clear_irq(d->vm,d->pci_dev); |
239 |
|
d->irq_clearing_count = 0; |
240 |
|
} |
241 |
|
} |
242 |
|
} |
243 |
|
|
244 |
/* |
/* |
245 |
* Access to channel registers. |
* Access to channel registers. |
246 |
*/ |
*/ |
247 |
void dev_mueslix_chan_access(cpu_mips_t *cpu,struct mueslix_channel *channel, |
void dev_mueslix_chan_access(cpu_gen_t *cpu,struct mueslix_channel *channel, |
248 |
m_uint32_t offset,u_int op_size,u_int op_type, |
m_uint32_t offset,u_int op_size,u_int op_type, |
249 |
m_uint64_t *data) |
m_uint64_t *data) |
250 |
{ |
{ |
251 |
switch(offset) { |
switch(offset) { |
252 |
|
case 0x00: /* CRC control register ? */ |
253 |
|
if (op_type == MTS_READ) { |
254 |
|
*data = channel->crc_ctrl_reg; |
255 |
|
} else { |
256 |
|
channel->crc_ctrl_reg = *data; |
257 |
|
|
258 |
|
switch(channel->crc_ctrl_reg) { |
259 |
|
case 0x08: |
260 |
|
case 0x0a: |
261 |
|
channel->crc_size = channel->crc_ctrl_reg - 0x06; |
262 |
|
break; |
263 |
|
|
264 |
|
default: |
265 |
|
MUESLIX_LOG(channel->parent,"channel %u: unknown value " |
266 |
|
"for CRC ctrl reg 0x%4.4x\n", |
267 |
|
channel->id,channel->crc_ctrl_reg); |
268 |
|
|
269 |
|
channel->crc_size = 2; |
270 |
|
} |
271 |
|
MUESLIX_LOG(channel->parent, |
272 |
|
"channel %u: CRC size set to 0x%4.4x\n", |
273 |
|
channel->id,channel->crc_size); |
274 |
|
} |
275 |
|
break; |
276 |
|
|
277 |
case 0x60: /* signals ? */ |
case 0x60: /* signals ? */ |
278 |
if ((op_type == MTS_READ) && (channel->nio != NULL)) |
if ((op_type == MTS_READ) && (channel->nio != NULL)) |
279 |
*data = 0xFFFFFFFF; |
*data = 0xFFFFFFFF; |
371 |
/* |
/* |
372 |
* dev_mueslix_access() |
* dev_mueslix_access() |
373 |
*/ |
*/ |
374 |
void *dev_mueslix_access(cpu_mips_t *cpu,struct vdevice *dev,m_uint32_t offset, |
void *dev_mueslix_access(cpu_gen_t *cpu,struct vdevice *dev,m_uint32_t offset, |
375 |
u_int op_size,u_int op_type,m_uint64_t *data) |
u_int op_size,u_int op_type,m_uint64_t *data) |
376 |
{ |
{ |
377 |
struct mueslix_data *d = dev->priv_data; |
struct mueslix_data *d = dev->priv_data; |
|
struct mueslix_channel *channel; |
|
|
m_uint32_t irq_status; |
|
378 |
int i; |
int i; |
379 |
|
|
380 |
#if DEBUG_ACCESS >= 2 |
#if DEBUG_ACCESS >= 2 |
381 |
if (op_type == MTS_READ) { |
if (op_type == MTS_READ) { |
382 |
cpu_log(cpu,d->name,"read access to offset=0x%x, pc=0x%llx, size=%u\n", |
cpu_log(cpu,d->name,"read access to offset=0x%x, pc=0x%llx, size=%u\n", |
383 |
offset,cpu->pc,op_size); |
offset,cpu_get_pc(cpu),op_size); |
384 |
} else { |
} else { |
385 |
cpu_log(cpu,d->name,"write access to offset=0x%x, pc=0x%llx, " |
cpu_log(cpu,d->name,"write access to offset=0x%x, pc=0x%llx, " |
386 |
"val=0x%llx, size=%u\n",offset,cpu->pc,*data,op_size); |
"val=0x%llx, size=%u\n",offset,cpu_get_pc(cpu),*data,op_size); |
387 |
} |
} |
388 |
#endif |
#endif |
389 |
|
|
411 |
if ((offset >= channel_offset[i]) && |
if ((offset >= channel_offset[i]) && |
412 |
(offset < (channel_offset[i] + MUESLIX_CHANNEL_LEN))) |
(offset < (channel_offset[i] + MUESLIX_CHANNEL_LEN))) |
413 |
{ |
{ |
414 |
|
MUESLIX_LOCK(d); |
415 |
dev_mueslix_chan_access(cpu,&d->channel[i], |
dev_mueslix_chan_access(cpu,&d->channel[i], |
416 |
offset - channel_offset[i], |
offset - channel_offset[i], |
417 |
op_size,op_type,data); |
op_size,op_type,data); |
418 |
|
MUESLIX_UNLOCK(d); |
419 |
return NULL; |
return NULL; |
420 |
} |
} |
421 |
|
|
422 |
|
MUESLIX_LOCK(d); |
423 |
|
|
424 |
/* Generic case */ |
/* Generic case */ |
425 |
switch(offset) { |
switch(offset) { |
426 |
/* this reg is accessed when an interrupt occurs */ |
/* this reg is accessed when an interrupt occurs */ |
427 |
case 0x0: |
case 0x0: |
428 |
if (op_type == MTS_READ) { |
if (op_type == MTS_READ) { |
429 |
irq_status = 0; |
*data = d->irq_status; |
|
|
|
|
for(i=0;i<MUESLIX_NR_CHANNELS;i++) { |
|
|
channel = &d->channel[i]; |
|
|
|
|
|
if ((dev_mueslix_is_rx_tx_enabled(d,i) & MUESLIX_TX_ENABLE) && |
|
|
(channel->rx_tx_status & MUESLIX_CHANNEL_STATUS_RX)) |
|
|
irq_status |= MUESLIX_RX_IRQ << i; |
|
|
|
|
|
if ((dev_mueslix_is_rx_tx_enabled(d,i) & MUESLIX_TX_ENABLE) && |
|
|
(channel->rx_tx_status & MUESLIX_CHANNEL_STATUS_TX)) |
|
|
irq_status |= MUESLIX_TX_IRQ << i; |
|
|
} |
|
|
|
|
|
/* |
|
|
* Hack: we re-trigger an interrupt here. This was necessary |
|
|
* because the Mueslix driver was not working properly with |
|
|
* a C3620 platform. |
|
|
*/ |
|
|
if (irq_status) |
|
|
pci_dev_trigger_irq(d->vm,d->pci_dev); |
|
|
|
|
|
*data = irq_status; |
|
430 |
} else { |
} else { |
431 |
for(i=0;i<MUESLIX_NR_CHANNELS;i++) { |
d->irq_status &= ~(*data); |
432 |
channel = &d->channel[i]; |
dev_mueslix_update_irq_status(d); |
|
channel->rx_tx_status = 0; |
|
|
} |
|
433 |
} |
} |
434 |
break; |
break; |
435 |
|
|
436 |
/* maybe interrupt mask */ |
/* Maybe interrupt mask */ |
437 |
case 0x10: |
case 0x10: |
438 |
if (op_type == MTS_READ) |
if (op_type == MTS_READ) { |
439 |
*data = 0x2FF; |
*data = d->irq_mask; |
440 |
|
} else { |
441 |
|
d->irq_mask = *data; |
442 |
|
dev_mueslix_update_irq_status(d); |
443 |
|
} |
444 |
break; |
break; |
445 |
|
|
446 |
case 0x14: |
case 0x14: |
450 |
#if DEBUG_ACCESS |
#if DEBUG_ACCESS |
451 |
cpu_log(cpu,d->name, |
cpu_log(cpu,d->name, |
452 |
"channel_enable_mask = 0x%5.5llx at pc=0x%llx\n", |
"channel_enable_mask = 0x%5.5llx at pc=0x%llx\n", |
453 |
*data,cpu->pc); |
*data,cpu_get_pc(cpu)); |
454 |
#endif |
#endif |
455 |
d->channel_enable_mask = *data; |
d->channel_enable_mask = *data; |
456 |
} |
} |
483 |
#if DEBUG_ACCESS |
#if DEBUG_ACCESS |
484 |
if (op_type == MTS_WRITE) { |
if (op_type == MTS_WRITE) { |
485 |
cpu_log(cpu,d->name,"cmd_reg = 0x%5.5llx at pc=0x%llx\n", |
cpu_log(cpu,d->name,"cmd_reg = 0x%5.5llx at pc=0x%llx\n", |
486 |
*data,cpu->pc); |
*data,cpu_get_pc(cpu)); |
487 |
} |
} |
488 |
#endif |
#endif |
489 |
switch(d->chip_mode) { |
switch(d->chip_mode) { |
510 |
if (op_type == MTS_READ) { |
if (op_type == MTS_READ) { |
511 |
cpu_log(cpu,d->name, |
cpu_log(cpu,d->name, |
512 |
"read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", |
"read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", |
513 |
offset,cpu->pc,op_size); |
offset,cpu_get_pc(cpu),op_size); |
514 |
} else { |
} else { |
515 |
cpu_log(cpu,d->name, |
cpu_log(cpu,d->name, |
516 |
"write to unknown addr 0x%x, value=0x%llx, " |
"write to unknown addr 0x%x, value=0x%llx, " |
517 |
"pc=0x%llx (size=%u)\n",offset,*data,cpu->pc,op_size); |
"pc=0x%llx (size=%u)\n", |
518 |
|
offset,*data,cpu_get_pc(cpu),op_size); |
519 |
} |
} |
520 |
#endif |
#endif |
521 |
} |
} |
522 |
|
|
523 |
|
MUESLIX_UNLOCK(d); |
524 |
return NULL; |
return NULL; |
525 |
} |
} |
526 |
|
|
644 |
/* We have finished if the complete packet has been stored */ |
/* We have finished if the complete packet has been stored */ |
645 |
if (tot_len == 0) { |
if (tot_len == 0) { |
646 |
rxdc->rdes[0] = MUESLIX_RXDESC_LS; |
rxdc->rdes[0] = MUESLIX_RXDESC_LS; |
647 |
rxdc->rdes[0] |= cp_len; |
rxdc->rdes[0] |= cp_len + channel->crc_size + 1; |
648 |
|
|
649 |
if (i != 0) |
if (i != 0) |
650 |
physmem_copy_u32_to_vm(d->vm,channel->rx_current,rxdc->rdes[0]); |
physmem_copy_u32_to_vm(d->vm,channel->rx_current,rxdc->rdes[0]); |
689 |
/* Indicate that we have a frame ready (XXX something to do ?) */ |
/* Indicate that we have a frame ready (XXX something to do ?) */ |
690 |
|
|
691 |
/* Generate IRQ on CPU */ |
/* Generate IRQ on CPU */ |
692 |
channel->rx_tx_status |= MUESLIX_CHANNEL_STATUS_RX; |
d->irq_status |= MUESLIX_RX_IRQ << channel->id; |
693 |
pci_dev_trigger_irq(d->vm,d->pci_dev); |
dev_mueslix_update_irq_status(d); |
694 |
} |
} |
695 |
|
|
696 |
/* Handle the Mueslix RX ring of the specified channel */ |
/* Handle the Mueslix RX ring of the specified channel */ |
697 |
static int dev_mueslix_handle_rxring(netio_desc_t *nio, |
static int dev_mueslix_handle_rxring(netio_desc_t *nio, |
698 |
u_char *pkt,ssize_t pkt_len, |
u_char *pkt,ssize_t pkt_len, |
699 |
struct mueslix_channel *channel) |
struct mueslix_channel *channel) |
700 |
{ |
{ |
|
#if DEBUG_RECEIVE |
|
701 |
struct mueslix_data *d = channel->parent; |
struct mueslix_data *d = channel->parent; |
702 |
|
|
703 |
|
#if DEBUG_RECEIVE |
704 |
MUESLIX_LOG(d,"channel %u: receiving a packet of %d bytes\n", |
MUESLIX_LOG(d,"channel %u: receiving a packet of %d bytes\n", |
705 |
channel->id,pkt_len); |
channel->id,pkt_len); |
706 |
mem_dump(log_file,pkt,pkt_len); |
mem_dump(log_file,pkt,pkt_len); |
707 |
#endif |
#endif |
708 |
|
|
709 |
dev_mueslix_receive_pkt(channel,pkt,pkt_len); |
MUESLIX_LOCK(d); |
710 |
|
if (dev_mueslix_is_rx_tx_enabled(d,channel->id) & MUESLIX_RX_ENABLE) |
711 |
|
dev_mueslix_receive_pkt(channel,pkt,pkt_len); |
712 |
|
MUESLIX_UNLOCK(d); |
713 |
return(TRUE); |
return(TRUE); |
714 |
} |
} |
715 |
|
|
806 |
/* Be sure that we have length not null */ |
/* Be sure that we have length not null */ |
807 |
if (clen != 0) { |
if (clen != 0) { |
808 |
//printf("pkt_ptr = %p, ptxd->tdes[1] = 0x%x, clen = %d\n", |
//printf("pkt_ptr = %p, ptxd->tdes[1] = 0x%x, clen = %d\n", |
809 |
//pkt_ptr, ptxd->tdes[1], clen); |
// pkt_ptr, ptxd->tdes[1], clen); |
810 |
physmem_copy_from_vm(d->vm,pkt_ptr,ptxd->tdes[1],clen); |
physmem_copy_from_vm(d->vm,pkt_ptr,ptxd->tdes[1],clen); |
811 |
} |
} |
812 |
|
|
837 |
|
|
838 |
pad = ptxd->tdes[0] & MUESLIX_TXDESC_PAD; |
pad = ptxd->tdes[0] & MUESLIX_TXDESC_PAD; |
839 |
pad >>= MUESLIX_TXDESC_PAD_SHIFT; |
pad >>= MUESLIX_TXDESC_PAD_SHIFT; |
840 |
tot_len += (pad - 1) & 0x03; |
tot_len -= (4 - pad) & 0x03; |
841 |
|
|
842 |
/* send it on wire */ |
/* send it on wire */ |
843 |
netio_send(channel->nio,pkt,tot_len); |
netio_send(channel->nio,pkt,tot_len); |
844 |
} |
} |
847 |
physmem_copy_u32_to_vm(d->vm,tx_start,0); |
physmem_copy_u32_to_vm(d->vm,tx_start,0); |
848 |
|
|
849 |
/* Interrupt on completion ? */ |
/* Interrupt on completion ? */ |
850 |
channel->rx_tx_status |= MUESLIX_CHANNEL_STATUS_TX; |
d->irq_status |= MUESLIX_TX_IRQ << channel->id; |
851 |
pci_dev_trigger_irq(d->vm,d->pci_dev); |
dev_mueslix_update_irq_status(d); |
852 |
return(TRUE); |
return(TRUE); |
853 |
} |
} |
854 |
|
|
855 |
/* Handle the TX ring of a specific channel */ |
/* Handle the TX ring of a specific channel */ |
856 |
static int dev_mueslix_handle_txring(struct mueslix_channel *channel) |
static int dev_mueslix_handle_txring(struct mueslix_channel *channel) |
857 |
{ |
{ |
858 |
int i; |
struct mueslix_data *d = channel->parent; |
859 |
|
int res,i; |
860 |
|
|
861 |
for(i=0;i<MUESLIX_TXRING_PASS_COUNT;i++) |
if (!dev_mueslix_is_rx_tx_enabled(d,channel->id) & MUESLIX_TX_ENABLE) |
862 |
if (!dev_mueslix_handle_txring_single(channel)) |
return(FALSE); |
863 |
|
|
864 |
|
for(i=0;i<MUESLIX_TXRING_PASS_COUNT;i++) { |
865 |
|
MUESLIX_LOCK(d); |
866 |
|
res = dev_mueslix_handle_txring_single(channel); |
867 |
|
MUESLIX_UNLOCK(d); |
868 |
|
|
869 |
|
if (!res) |
870 |
break; |
break; |
871 |
|
} |
872 |
|
|
873 |
return(TRUE); |
return(TRUE); |
874 |
} |
} |
875 |
|
|
876 |
/* pci_mueslix_read() */ |
/* pci_mueslix_read() */ |
877 |
static m_uint32_t pci_mueslix_read(cpu_mips_t *cpu,struct pci_device *dev, |
static m_uint32_t pci_mueslix_read(cpu_gen_t *cpu,struct pci_device *dev, |
878 |
int reg) |
int reg) |
879 |
{ |
{ |
880 |
struct mueslix_data *d = dev->priv_data; |
struct mueslix_data *d = dev->priv_data; |
890 |
} |
} |
891 |
|
|
892 |
/* pci_mueslix_write() */ |
/* pci_mueslix_write() */ |
893 |
static void pci_mueslix_write(cpu_mips_t *cpu,struct pci_device *dev, |
static void pci_mueslix_write(cpu_gen_t *cpu,struct pci_device *dev, |
894 |
int reg,m_uint32_t value) |
int reg,m_uint32_t value) |
895 |
{ |
{ |
896 |
struct mueslix_data *d = dev->priv_data; |
struct mueslix_data *d = dev->priv_data; |
920 |
} |
} |
921 |
|
|
922 |
memset(d,0,sizeof(*d)); |
memset(d,0,sizeof(*d)); |
923 |
|
pthread_mutex_init(&d->lock,NULL); |
924 |
d->chip_mode = chip_mode; |
d->chip_mode = chip_mode; |
925 |
|
|
926 |
for(i=0;i<MUESLIX_NR_CHANNELS;i++) |
for(i=0;i<MUESLIX_NR_CHANNELS;i++) { |
927 |
d->channel[i].id = i; |
d->channel[i].id = i; |
928 |
|
d->channel[i].parent = d; |
929 |
|
} |
930 |
|
|
931 |
/* Add as PCI device */ |
/* Add as PCI device */ |
932 |
pci_dev = pci_dev_add(pci_bus,name, |
pci_dev = pci_dev_add(pci_bus,name, |
989 |
|
|
990 |
/* define the new NIO */ |
/* define the new NIO */ |
991 |
channel->nio = nio; |
channel->nio = nio; |
|
channel->parent = d; |
|
992 |
channel->tx_tid = ptask_add((ptask_callback)dev_mueslix_handle_txring, |
channel->tx_tid = ptask_add((ptask_callback)dev_mueslix_handle_txring, |
993 |
channel,NULL); |
channel,NULL); |
994 |
netio_rxl_add(nio,(netio_rx_handler_t)dev_mueslix_handle_rxring, |
netio_rxl_add(nio,(netio_rx_handler_t)dev_mueslix_handle_rxring, |