/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot. All rights reserved. * * Cirrus Logic PD6729 PCI-to-PCMCIA host adapter. * * TODO: finish the code! (especially extended registers) */ #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "pci_dev.h" #include "pci_io.h" #define DEBUG_ACCESS 0 /* Cirrus Logic PD6729 PCI vendor/product codes */ #define CLPD6729_PCI_VENDOR_ID 0x1013 #define CLPD6729_PCI_PRODUCT_ID 0x1100 #define CLPD6729_REG_CHIP_REV 0x00 /* Chip Revision */ #define CLPD6729_REG_INT_STATUS 0x01 /* Interface Status */ #define CLPD6729_REG_POWER_CTRL 0x02 /* Power Control */ #define CLPD6729_REG_INTGEN_CTRL 0x03 /* Interrupt & General Control */ #define CLPD6729_REG_CARD_STATUS 0x04 /* Card Status Change */ #define CLPD6729_REG_FIFO_CTRL 0x17 /* FIFO Control */ #define CLPD6729_REG_EXT_INDEX 0x2E /* Extended Index */ /* CLPD6729 private data */ struct clpd6729_data { vm_obj_t vm_obj; struct vdevice dev; struct pci_device *pci_dev; struct pci_io_device *pci_io_dev; /* VM objects present in slots (typically, PCMCIA disks...) */ vm_obj_t *slot_obj[2]; /* Base registers */ m_uint8_t base_index; m_uint8_t base_regs[256]; }; /* Handle access to a base register */ static void clpd6729_base_reg_access(cpu_gen_t *cpu,struct clpd6729_data *d, u_int op_type,m_uint64_t *data) { u_int slot_id,reg; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,"CLPD6729","reading reg 0x%2.2x at pc=0x%llx\n", d->base_index,cpu_get_pc(cpu)); } else { cpu_log(cpu,"CLPD6729","writing reg 0x%2.2x, data=0x%llx at pc=0x%llx\n", d->base_index,*data,cpu_get_pc(cpu)); } #endif if (op_type == MTS_READ) *data = 0; /* Reserved registers */ if (d->base_index >= 0x80) return; /* * Socket A regs: 0x00 to 0x3f * Socket B regs: 0x40 to 0x7f */ if (d->base_index >= 0x40) { slot_id = 1; reg = d->base_index - 0x40; } else { slot_id = 0; reg = d->base_index; } switch(reg) { case CLPD6729_REG_CHIP_REV: if (op_type == MTS_READ) *data = 0x48; break; case CLPD6729_REG_INT_STATUS: if (op_type == MTS_READ) { if (d->slot_obj[slot_id]) *data = 0xEF; else *data = 0x80; } break; case CLPD6729_REG_INTGEN_CTRL: if (op_type == MTS_READ) *data = 0x40; break; case CLPD6729_REG_EXT_INDEX: if (op_type == MTS_WRITE) { cpu_log(cpu,"CLPD6729","ext reg index 0x%2.2llx at pc=0x%llx\n", *data,cpu_get_pc(cpu)); } break; case CLPD6729_REG_FIFO_CTRL: if (op_type == MTS_READ) *data = 0x80; /* FIFO is empty */ break; default: if (op_type == MTS_READ) *data = d->base_regs[d->base_index]; else d->base_regs[d->base_index] = (m_uint8_t)(*data); } } /* * dev_clpd6729_io_access() */ static void *dev_clpd6729_io_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size, u_int op_type,m_uint64_t *data) { struct clpd6729_data *d = dev->priv_data; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,dev->name,"reading at offset 0x%x, pc=0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,dev->name,"writing at offset 0x%x, pc=0x%llx, data=0x%llx\n", offset,cpu_get_pc(cpu),*data); } #endif switch(offset) { case 0: /* Data register */ clpd6729_base_reg_access(cpu,d,op_type,data); break; case 1: /* Index register */ if (op_type == MTS_READ) *data = d->base_index; else d->base_index = *data; break; } return NULL; } /* Shutdown a CLPD6729 device */ void dev_clpd6729_shutdown(vm_instance_t *vm,struct clpd6729_data *d) { if (d != NULL) { /* Remove the PCI device */ pci_dev_remove(d->pci_dev); /* Remove the PCI I/O device */ pci_io_remove(d->pci_io_dev); /* Free the structure itself */ free(d); } } /* * dev_clpd6729_init() */ int dev_clpd6729_init(vm_instance_t *vm, struct pci_bus *pci_bus,int pci_device, struct pci_io_data *pci_io_data, m_uint32_t io_start,m_uint32_t io_end) { struct clpd6729_data *d; /* Allocate the private data structure */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"CLPD6729: unable to create device.\n"); return(-1); } memset(d,0,sizeof(*d)); vm_object_init(&d->vm_obj); d->vm_obj.name = "clpd6729"; d->vm_obj.data = d; d->vm_obj.shutdown = (vm_shutdown_t)dev_clpd6729_shutdown; dev_init(&d->dev); d->dev.name = "clpd6729"; d->dev.priv_data = d; d->pci_io_dev = pci_io_add(pci_io_data,io_start,io_end,&d->dev, dev_clpd6729_io_access); d->pci_dev = pci_dev_add(pci_bus,"clpd6729", CLPD6729_PCI_VENDOR_ID,CLPD6729_PCI_PRODUCT_ID, pci_device,0,-1,&d->dev,NULL,NULL,NULL); if (!d->pci_io_dev || !d->pci_dev) { fprintf(stderr,"CLPD6729: unable to create PCI devices.\n"); dev_clpd6729_shutdown(vm,d); return(-1); } vm_object_add(vm,&d->vm_obj); #if 1 /* PCMCIA disk test */ if (vm->pcmcia_disk_size[0]) d->slot_obj[0] = dev_pcmcia_disk_init(vm,"disk0",0x40000000ULL,0x200000, vm->pcmcia_disk_size[0],0); if (vm->pcmcia_disk_size[1]) d->slot_obj[1] = dev_pcmcia_disk_init(vm,"disk1",0x44000000ULL,0x200000, vm->pcmcia_disk_size[1],0); #endif #if 0 /* PCMCIA disk test */ if (vm->pcmcia_disk_size[0]) d->slot_obj[0] = dev_pcmcia_disk_init(vm,"disk0",0xd8000000ULL,0x200000, vm->pcmcia_disk_size[0],0); if (vm->pcmcia_disk_size[1]) d->slot_obj[1] = dev_pcmcia_disk_init(vm,"disk1",0xdc000000ULL,0x200000, vm->pcmcia_disk_size[1],0); #endif return(0); }