diff options
Diffstat (limited to 'trunk/src/emu/machine/pci.c')
-rw-r--r-- | trunk/src/emu/machine/pci.c | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/trunk/src/emu/machine/pci.c b/trunk/src/emu/machine/pci.c new file mode 100644 index 00000000000..df1d1de0da4 --- /dev/null +++ b/trunk/src/emu/machine/pci.c @@ -0,0 +1,335 @@ +/*************************************************************************** + + machine/pci.c + + PCI bus + + The PCI bus is a 32-bit bus introduced by Intel, so it is little endian + + Control word: + bit 31: Enable bit + bits 30-24: Reserved + bits 23-16: PCI bus number + bits 15-11: PCI device number + bits 10- 8: PCI function number + bits 7- 0: Offset address + + Standard PCI registers: + 0x00 2 Vendor ID + 0x02 2 Device ID + 0x04 2 PCI Command + 0x06 2 PCI Status + 0x08 1 Revision ID + 0x09 1 Programming Interface + 0x0A 1 Subclass Code + 0x0B 1 Class Code + + Class Code/Subclass Code/Programming Interface + 0x00XXXX Pre-PCI 2.0 devices + 0x000000 Non-VGA device + 0x000101 VGA device + 0x01XXXX Storage Controller + 0x010000 SCSI + 0x0101XX IDE + 0x0102XX Floppy + 0x0103XX IPI + 0x0104XX RAID + 0x0180XX Other + 0x02XXXX Network Card + 0x020000 Ethernet + 0x020100 Tokenring + 0x020200 FDDI + 0x020300 ATM + 0x028000 Other + 0x03XXXX Display Controller + 0x030000 VGA + 0x030001 8514 Compatible + 0x030100 XGA + 0x038000 Other + 0x04XXXX Multimedia + 0x040000 Video + 0x040100 Audio + 0x048000 Other + 0x05XXXX Memory Controller + 0x050000 RAM + 0x050100 Flash + 0x058000 Other + 0x06XXXX Bridge + 0x060000 Host/PCI + 0x060100 PCI/ISA + 0x060200 PCI/EISA + 0x060300 PCI/Micro Channel + 0x060400 PCI/PCI + 0x060500 PCI/PCMCIA + 0x060600 PCI/NuBus + 0x060700 PCI/CardBus + 0x068000 Other + + Information on PCI vendors can be found at http://www.pcidatabase.com/ + +***************************************************************************/ + +#include "emu.h" +#include "devconv.h" +#include "machine/pci.h" + +#define LOG_PCI 0 + +typedef struct _pci_bus_state pci_bus_state; +struct _pci_bus_state +{ + device_t * busdevice; + const pci_bus_config * config; + device_t * device[32]; + pci_bus_state * siblings[8]; + UINT8 siblings_busnum[8]; + int siblings_count; + offs_t address; + INT8 devicenum; // device number we are addressing + INT8 busnum; // pci bus number we are addressing + pci_bus_state * busnumaddr; // pci bus we are addressing +}; + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + get_safe_token - makes sure that the passed + in device is, in fact, a PCI bus +-------------------------------------------------*/ + +INLINE pci_bus_state *get_safe_token(device_t *device) +{ + assert(device != NULL); + assert(device->type() == PCI_BUS); + + return (pci_bus_state *)downcast<legacy_device_base *>(device)->token(); +} + + + +READ32_DEVICE_HANDLER( pci_32le_r ) +{ + pci_bus_state *pcibus = get_safe_token(device); + UINT32 result = 0xffffffff; + int function, reg; + + offset %= 2; + + switch (offset) + { + case 0: + result = pcibus->address; + break; + + case 1: + if (pcibus->devicenum != -1) + { + pci_read_func read = pcibus->busnumaddr->config->device[pcibus->devicenum].read_callback; + if (read != NULL) + { + function = (pcibus->address >> 8) & 0x07; + reg = (pcibus->address >> 0) & 0xfc; + result = (*read)(pcibus->busnumaddr->busdevice, pcibus->busnumaddr->device[pcibus->devicenum], function, reg, mem_mask); + } + } + break; + } + + if (LOG_PCI) + logerror("pci_32le_r('%s'): offset=%d result=0x%08X\n", device->tag(), offset, result); + + return result; +} + + + +static pci_bus_state *pci_search_bustree(int busnum, int devicenum, pci_bus_state *pcibus) +{ +int a; +pci_bus_state *ret; + + if (pcibus->config->busnum == busnum) + { + return pcibus; + } + for (a = 0; a < pcibus->siblings_count; a++) + { + ret = pci_search_bustree(busnum, devicenum, pcibus->siblings[a]); + if (ret != NULL) + return ret; + } + return NULL; +} + + + +WRITE32_DEVICE_HANDLER( pci_32le_w ) +{ + pci_bus_state *pcibus = get_safe_token(device); + + offset %= 2; + + if (LOG_PCI) + logerror("pci_32le_w('%s'): offset=%d data=0x%08X\n", device->tag(), offset, data); + + switch (offset) + { + case 0: + pcibus->address = data; + + /* lookup current device */ + if (pcibus->address & 0x80000000) + { + int busnum = (pcibus->address >> 16) & 0xff; + int devicenum = (pcibus->address >> 11) & 0x1f; + pcibus->busnumaddr = pci_search_bustree(busnum, devicenum, pcibus); + if (pcibus->busnumaddr != NULL) + { + pcibus->busnum = busnum; + pcibus->devicenum = devicenum; + } + else + pcibus->devicenum = -1; + if (LOG_PCI) + logerror(" bus:%d device:%d\n", busnum, devicenum); + } + break; + + case 1: + if (pcibus->devicenum != -1) + { + pci_write_func write = pcibus->busnumaddr->config->device[pcibus->devicenum].write_callback; + if (write != NULL) + { + int function = (pcibus->address >> 8) & 0x07; + int reg = (pcibus->address >> 0) & 0xfc; + (*write)(pcibus->busnumaddr->busdevice, pcibus->busnumaddr->device[pcibus->devicenum], function, reg, data, mem_mask); + } + if (LOG_PCI) + logerror(" function:%d register:%d\n", (pcibus->address >> 8) & 0x07, (pcibus->address >> 0) & 0xfc); + } + break; + } +} + + + +READ64_DEVICE_HANDLER(pci_64be_r) { return read64be_with_32le_device_handler(pci_32le_r, device, offset, mem_mask); } +WRITE64_DEVICE_HANDLER(pci_64be_w) { write64be_with_32le_device_handler(pci_32le_w, device, offset, data, mem_mask); } + + +int pci_add_sibling( running_machine &machine, char *pcitag, char *sibling ) +{ + device_t *device1 = machine.device(pcitag); + device_t *device2 = machine.device(sibling); + pci_bus_state *pcibus1 = get_safe_token(device1); + pci_bus_state *pcibus2 = get_safe_token(device2); + pci_bus_config *config2; + + if ((device1 == NULL) || (device2 == NULL) || (pcibus1 == NULL) || (pcibus2 == NULL)) + return 0; + if (pcibus1->siblings_count == 8) + return 0; + config2 = (pci_bus_config *)downcast<const legacy_device_base *>(device2)->inline_config(); + pcibus1->siblings[pcibus1->siblings_count] = get_safe_token(device2); + pcibus1->siblings_busnum[pcibus1->siblings_count] = config2->busnum; + pcibus1->siblings_count++; + return 1; +} + + +/*************************************************************************** + DEVICE INTERFACE +***************************************************************************/ + + +static void pci_bus_postload(pci_bus_state *pcibus) +{ + if (pcibus->devicenum != -1) + { + pcibus->busnumaddr = pci_search_bustree(pcibus->busnum, pcibus->devicenum, pcibus); + } +} + + +/*------------------------------------------------- + device start callback +-------------------------------------------------*/ + +static DEVICE_START( pci_bus ) +{ + pci_bus_state *pcibus = get_safe_token(device); + int devicenum; + + /* validate some basic stuff */ + assert(device != NULL); + assert(device->static_config() == NULL); + assert(downcast<const legacy_device_base *>(device)->inline_config() != NULL); + + /* store a pointer back to the device */ + pcibus->config = (const pci_bus_config *)downcast<const legacy_device_base *>(device)->inline_config(); + pcibus->busdevice = device; + pcibus->devicenum = -1; + + /* find all our devices */ + for (devicenum = 0; devicenum < ARRAY_LENGTH(pcibus->device); devicenum++) + if (pcibus->config->device[devicenum].devtag != NULL) + pcibus->device[devicenum] = device->machine().device(pcibus->config->device[devicenum].devtag); + + if (pcibus->config->father != NULL) + pci_add_sibling(device->machine(), (char *)pcibus->config->father, (char *)device->tag()); + + /* register pci states */ + device->save_item(NAME(pcibus->address)); + device->save_item(NAME(pcibus->devicenum)); + device->save_item(NAME(pcibus->busnum)); + + device->machine().save().register_postload(save_prepost_delegate(FUNC(pci_bus_postload), pcibus)); +} + + +/*------------------------------------------------- + device reset callback +-------------------------------------------------*/ + +static DEVICE_RESET( pci_bus ) +{ + pci_bus_state *pcibus = get_safe_token(device); + + /* reset the drive state */ + pcibus->devicenum = -1; + pcibus->address = 0; +} + + +/*------------------------------------------------- + device get info callback +-------------------------------------------------*/ + +DEVICE_GET_INFO( pci_bus ) +{ + switch (state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(pci_bus_state); break; + case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = sizeof(pci_bus_config); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(pci_bus); break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(pci_bus);break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case DEVINFO_STR_NAME: strcpy(info->s, "PCI Bus"); break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "Peripherial Bus"); break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +} + + +DEFINE_LEGACY_DEVICE(PCI_BUS, pci_bus); |