Mercurial > flash_v2
view packages/io/pci/current/src/pci.c @ 1467:8ad4d297cbf8
* src/pci.c (cyg_pci_get_device_info): Added fix for devices that
only implement the bottom 16 bits of an IO BAR. This is allowed by
the standard but is very rare and mostly present in older devices
only. Without this cyg_pci_allocate_io_priv() gets confused.
| author | nickg |
|---|---|
| date | Wed, 14 Jan 2004 15:39:43 +0000 |
| parents | efdacf0f8122 |
| children | 5dd2b854cc32 |
line wrap: on
line source
//============================================================================= // // pci.c // // PCI library // //============================================================================= //####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. // // eCos is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free // Software Foundation; either version 2 or (at your option) any later version. // // eCos is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License along // with eCos; if not, write to the Free Software Foundation, Inc., // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. // // As a special exception, if other files instantiate templates or use macros // or inline functions from this file, or you compile this file and link it // with other works to produce a work based on this file, this file does not // by itself cause the resulting work to be covered by the GNU General Public // License. However the source code for this file must still be made available // in accordance with section (3) of the GNU General Public License. // // This exception does not invalidate any other reasons why a work based on // this file might be covered by the GNU General Public License. // // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. // at http://sources.redhat.com/ecos/ecos-license/ // ------------------------------------------- //####ECOSGPLCOPYRIGHTEND#### //============================================================================= //#####DESCRIPTIONBEGIN#### // // Author(s): jskov, from design by nickg // Contributors: jskov // Date: 1999-08-09 // Purpose: PCI configuration // Description: // PCI bus support library. // Handles simple resource allocation for devices. // Can configure 64bit devices, but drivers may need special // magic to access all of this memory space - this is platform // specific and the driver must know how to handle it on its own. //####DESCRIPTIONEND#### // //============================================================================= #include <pkgconf/hal.h> #include <pkgconf/io_pci.h> #include <cyg/io/pci_hw.h> // CYG_PCI_PRESENT only gets defined for targets that provide PCI HAL support. // See pci_hw.h for details. #ifdef CYG_PCI_PRESENT #include <cyg/io/pci.h> #include <cyg/infra/cyg_ass.h> static cyg_bool cyg_pci_lib_initialized = false; static CYG_PCI_ADDRESS64 cyg_pci_memory_base; static CYG_PCI_ADDRESS32 cyg_pci_io_base; void cyg_pci_init( void ) { if (!cyg_pci_lib_initialized) { cyg_pci_set_memory_base(HAL_PCI_ALLOC_BASE_MEMORY); cyg_pci_set_io_base(HAL_PCI_ALLOC_BASE_IO); // Initialize the PCI bus, preparing it for general access. cyg_pcihw_init(); cyg_pci_lib_initialized = true; } } //--------------------------------------------------------------------------- // Common device configuration access functions void cyg_pci_get_device_info ( cyg_pci_device_id devid, cyg_pci_device *dev_info ) { int i; unsigned char bar_count; cyg_uint8 bus = CYG_PCI_DEV_GET_BUS(devid); cyg_uint8 devfn = CYG_PCI_DEV_GET_DEVFN(devid); cyg_uint8 header_type; dev_info->devid = devid; cyg_pcihw_read_config_uint16(bus, devfn, CYG_PCI_CFG_VENDOR, &dev_info->vendor); cyg_pcihw_read_config_uint16(bus, devfn, CYG_PCI_CFG_DEVICE, &dev_info->device); cyg_pcihw_read_config_uint16(bus, devfn, CYG_PCI_CFG_COMMAND, &dev_info->command); cyg_pcihw_read_config_uint16(bus, devfn, CYG_PCI_CFG_STATUS, &dev_info->status); cyg_pcihw_read_config_uint32(bus, devfn, CYG_PCI_CFG_CLASS_REV, &dev_info->class_rev); cyg_pcihw_read_config_uint8(bus, devfn, CYG_PCI_CFG_CACHE_LINE_SIZE, &dev_info->cache_line_size); cyg_pcihw_read_config_uint8(bus, devfn, CYG_PCI_CFG_LATENCY_TIMER, &dev_info->latency_timer); cyg_pcihw_read_config_uint8(bus, devfn, CYG_PCI_CFG_HEADER_TYPE, &header_type); dev_info->header_type = (cyg_pci_header_type) header_type; cyg_pcihw_read_config_uint8(bus, devfn, CYG_PCI_CFG_BIST, &dev_info->bist); if ((dev_info->header_type & CYG_PCI_CFG_HEADER_TYPE_MASK) == CYG_PCI_HEADER_BRIDGE) dev_info->num_bars = 2; else dev_info->num_bars = 6; // Clear out all address info (the code below stops short) for (i = 0; i < CYG_PCI_MAX_BAR; i++) { dev_info->base_map[i] = 0xffffffff; dev_info->base_address[i] = 0; dev_info->base_size[i] = 0; } for (i = 0; i < dev_info->num_bars; i++) { cyg_pcihw_read_config_uint32(bus, devfn, CYG_PCI_CFG_BAR_BASE + 4*i, &dev_info->base_address[i]); } // If device is disabled, probe BARs for sizes. if ((dev_info->command & CYG_PCI_CFG_COMMAND_ACTIVE) == 0) { bar_count = 0; for (i = 0; i < dev_info->num_bars; i++){ cyg_uint32 size; cyg_pcihw_write_config_uint32(bus, devfn, CYG_PCI_CFG_BAR_BASE + 4*i, 0xffffffff); cyg_pcihw_read_config_uint32(bus, devfn, CYG_PCI_CFG_BAR_BASE + 4*i, &size); dev_info->base_size[i] = size; dev_info->base_map[i] = 0xffffffff; // Increment BAR count only if it has valid entry. BARs may not // be in contiguous locations. if (size != 0) { ++bar_count; } // Check for a 64bit memory region. if (CYG_PCI_CFG_BAR_SPACE_MEM == (size & CYG_PCI_CFG_BAR_SPACE_MASK)) { if (size & CYG_PRI_CFG_BAR_MEM_TYPE_64) { // Clear fields for next BAR - it's the upper 32 bits. i++; dev_info->base_size[i] = 0; dev_info->base_map[i] = 0xffffffff; } } // Fix any IO devices that only implement the bottom 16 // bits of the the BAR. Just fill in these bits so it // looks like a full 32 bit BAR. if ((CYG_PCI_CFG_BAR_SPACE_IO == (size & CYG_PCI_CFG_BAR_SPACE_MASK)) && ((size & 0xFFFF0000) == 0) ) { dev_info->base_size[i] |= 0xFFFF0000; } } dev_info->num_bars = bar_count; } else { // If the device is already configured. Fill in the base_map. CYG_PCI_ADDRESS64 tmp_addr; cyg_uint32 bar; bar_count = 0; for (i = 0; i < dev_info->num_bars; i++){ dev_info->base_size[i] = 0; bar = dev_info->base_address[i]; // Increment BAR count only if it has valid entry. BARs may not // be in contiguous locations. if (bar != 0) { ++bar_count; } if ((bar & CYG_PCI_CFG_BAR_SPACE_MASK) == CYG_PCI_CFG_BAR_SPACE_IO) { dev_info->base_map[i] = (bar & CYG_PRI_CFG_BAR_IO_MASK) + HAL_PCI_PHYSICAL_IO_BASE; bar &= CYG_PRI_CFG_BAR_IO_MASK; if ((bar < 0xFF000000) && (bar >= cyg_pci_io_base)) { // Assume that this device owns 512 'registers' cyg_pci_io_base = bar + 0x200; #ifdef CYGPKG_IO_PCI_DEBUG diag_printf("Update I/O base to %x from bar: %d\n", cyg_pci_io_base, i); #endif } } else { tmp_addr = bar & CYG_PRI_CFG_BAR_MEM_MASK; if ((bar & CYG_PRI_CFG_BAR_MEM_TYPE_MASK) == CYG_PRI_CFG_BAR_MEM_TYPE_64) tmp_addr |= ((CYG_PCI_ADDRESS64)(dev_info->base_address[i+1] & CYG_PRI_CFG_BAR_MEM_MASK)) << 32; if ((tmp_addr < 0xFF000000) && (tmp_addr >= cyg_pci_memory_base)) { // Assume that this device owns 1M cyg_pci_memory_base = tmp_addr + 0x100000; #ifdef CYGPKG_IO_PCI_DEBUG diag_printf("Update memory base to %llx from bar: %d\n", cyg_pci_memory_base, i); #endif } tmp_addr += HAL_PCI_PHYSICAL_MEMORY_BASE; dev_info->base_map[i] = tmp_addr; if ((bar & CYG_PRI_CFG_BAR_MEM_TYPE_MASK) == CYG_PRI_CFG_BAR_MEM_TYPE_64) dev_info->base_map[++i] = tmp_addr >> 32; } } dev_info->num_bars = bar_count; } switch (dev_info->header_type & CYG_PCI_CFG_HEADER_TYPE_MASK) { case CYG_PCI_HEADER_NORMAL: cyg_pcihw_read_config_uint32(bus, devfn, CYG_PCI_CFG_CARDBUS_CIS, &dev_info->header.normal.cardbus_cis); cyg_pcihw_read_config_uint16(bus, devfn, CYG_PCI_CFG_SUB_VENDOR, &dev_info->header.normal.sub_vendor); cyg_pcihw_read_config_uint16(bus, devfn, CYG_PCI_CFG_SUB_ID, &dev_info->header.normal.sub_id); cyg_pcihw_read_config_uint32(bus, devfn, CYG_PCI_CFG_ROM_ADDRESS, &dev_info->header.normal.rom_address); cyg_pcihw_read_config_uint8(bus, devfn, CYG_PCI_CFG_CAP_LIST, &dev_info->header.normal.cap_list); cyg_pcihw_read_config_uint8(bus, devfn, CYG_PCI_CFG_INT_LINE, &dev_info->header.normal.int_line); cyg_pcihw_read_config_uint8(bus, devfn, CYG_PCI_CFG_INT_PIN, &dev_info->header.normal.int_pin); cyg_pcihw_read_config_uint8(bus, devfn, CYG_PCI_CFG_MIN_GNT, &dev_info->header.normal.min_gnt); cyg_pcihw_read_config_uint8(bus, devfn, CYG_PCI_CFG_MAX_LAT, &dev_info->header.normal.max_lat); break; case CYG_PCI_HEADER_BRIDGE: cyg_pcihw_read_config_uint8(bus, devfn, CYG_PCI_CFG_PRI_BUS, &dev_info->header.bridge.pri_bus); cyg_pcihw_read_config_uint8(bus, devfn, CYG_PCI_CFG_SEC_BUS, &dev_info->header.bridge.sec_bus); cyg_pcihw_read_config_uint8(bus, devfn, CYG_PCI_CFG_SUB_BUS, &dev_info->header.bridge.sub_bus); cyg_pcihw_read_config_uint8(bus, devfn, CYG_PCI_CFG_SEC_LATENCY_TIMER, &dev_info->header.bridge.sec_latency_timer); cyg_pcihw_read_config_uint8(bus, devfn, CYG_PCI_CFG_IO_BASE, &dev_info->header.bridge.io_base); cyg_pcihw_read_config_uint8(bus, devfn, CYG_PCI_CFG_IO_LIMIT, &dev_info->header.bridge.io_limit); cyg_pcihw_read_config_uint16(bus, devfn, CYG_PCI_CFG_SEC_STATUS, &dev_info->header.bridge.sec_status); cyg_pcihw_read_config_uint16(bus, devfn, CYG_PCI_CFG_MEM_BASE, &dev_info->header.bridge.mem_base); cyg_pcihw_read_config_uint16(bus, devfn, CYG_PCI_CFG_MEM_LIMIT, &dev_info->header.bridge.mem_limit); cyg_pcihw_read_config_uint16(bus, devfn, CYG_PCI_CFG_PREFETCH_BASE, &dev_info->header.bridge.prefetch_base); cyg_pcihw_read_config_uint16(bus, devfn, CYG_PCI_CFG_PREFETCH_LIMIT, &dev_info->header.bridge.prefetch_limit); cyg_pcihw_read_config_uint32(bus, devfn, CYG_PCI_CFG_PREFETCH_BASE_UPPER32, &dev_info->header.bridge.prefetch_base_upper32); cyg_pcihw_read_config_uint32(bus, devfn, CYG_PCI_CFG_PREFETCH_LIMIT_UPPER32, &dev_info->header.bridge.prefetch_limit_upper32); cyg_pcihw_read_config_uint16(bus, devfn, CYG_PCI_CFG_IO_BASE_UPPER16, &dev_info->header.bridge.io_base_upper16); cyg_pcihw_read_config_uint16(bus, devfn, CYG_PCI_CFG_IO_LIMIT_UPPER16, &dev_info->header.bridge.io_limit_upper16); cyg_pcihw_read_config_uint32(bus, devfn, CYG_PCI_CFG_BRIDGE_ROM_ADDRESS, &dev_info->header.bridge.rom_address); cyg_pcihw_read_config_uint8(bus, devfn, CYG_PCI_CFG_INT_LINE, &dev_info->header.bridge.int_line); cyg_pcihw_read_config_uint8(bus, devfn, CYG_PCI_CFG_INT_PIN, &dev_info->header.bridge.int_pin); cyg_pcihw_read_config_uint16(bus, devfn, CYG_PCI_CFG_BRIDGE_CONTROL, &dev_info->header.bridge.control); break; case CYG_PCI_HEADER_CARDBUS_BRIDGE: CYG_FAIL("PCI device header 'cardbus bridge' support not implemented"); break; default: CYG_FAIL("Unknown PCI device header type"); break; } } void cyg_pci_set_device_info ( cyg_pci_device_id devid, cyg_pci_device *dev_info ) { cyg_uint8 bus = CYG_PCI_DEV_GET_BUS(devid); cyg_uint8 devfn = CYG_PCI_DEV_GET_DEVFN(devid); int i; // Only writable entries are updated. cyg_pcihw_write_config_uint16(bus, devfn, CYG_PCI_CFG_COMMAND, dev_info->command); cyg_pcihw_write_config_uint16(bus, devfn, CYG_PCI_CFG_STATUS, dev_info->status); cyg_pcihw_write_config_uint8(bus, devfn, CYG_PCI_CFG_CACHE_LINE_SIZE, dev_info->cache_line_size); cyg_pcihw_write_config_uint8(bus, devfn, CYG_PCI_CFG_LATENCY_TIMER, dev_info->latency_timer); cyg_pcihw_write_config_uint8(bus, devfn, CYG_PCI_CFG_BIST, dev_info->bist); // Check for all possible BARs because they may be non-contiguous for (i = 0; i < CYG_PCI_MAX_BAR; i++) { if (dev_info->base_address[i]) { cyg_pcihw_write_config_uint32 (bus, devfn, CYG_PCI_CFG_BAR_BASE+4*i, dev_info->base_address[i]); } } switch (dev_info->header_type & CYG_PCI_CFG_HEADER_TYPE_MASK) { case CYG_PCI_HEADER_NORMAL: cyg_pcihw_write_config_uint32(bus, devfn, CYG_PCI_CFG_CARDBUS_CIS, dev_info->header.normal.cardbus_cis); cyg_pcihw_write_config_uint16(bus, devfn, CYG_PCI_CFG_SUB_VENDOR, dev_info->header.normal.sub_vendor); cyg_pcihw_write_config_uint16(bus, devfn, CYG_PCI_CFG_SUB_ID, dev_info->header.normal.sub_id); cyg_pcihw_write_config_uint32(bus, devfn, CYG_PCI_CFG_ROM_ADDRESS, dev_info->header.normal.rom_address); cyg_pcihw_write_config_uint8(bus, devfn, CYG_PCI_CFG_INT_LINE, dev_info->header.normal.int_line); cyg_pcihw_write_config_uint8(bus, devfn, CYG_PCI_CFG_INT_PIN, dev_info->header.normal.int_pin); cyg_pcihw_write_config_uint8(bus, devfn, CYG_PCI_CFG_MIN_GNT, dev_info->header.normal.min_gnt); cyg_pcihw_write_config_uint8(bus, devfn, CYG_PCI_CFG_MAX_LAT, dev_info->header.normal.max_lat); break; case CYG_PCI_HEADER_BRIDGE: cyg_pcihw_write_config_uint8(bus, devfn, CYG_PCI_CFG_PRI_BUS, dev_info->header.bridge.pri_bus); cyg_pcihw_write_config_uint8(bus, devfn, CYG_PCI_CFG_SEC_BUS, dev_info->header.bridge.sec_bus); cyg_pcihw_write_config_uint8(bus, devfn, CYG_PCI_CFG_SUB_BUS, dev_info->header.bridge.sub_bus); cyg_pcihw_write_config_uint8(bus, devfn, CYG_PCI_CFG_SEC_LATENCY_TIMER, dev_info->header.bridge.sec_latency_timer); cyg_pcihw_write_config_uint8(bus, devfn, CYG_PCI_CFG_IO_BASE, dev_info->header.bridge.io_base); cyg_pcihw_write_config_uint8(bus, devfn, CYG_PCI_CFG_IO_LIMIT, dev_info->header.bridge.io_limit); cyg_pcihw_write_config_uint16(bus, devfn, CYG_PCI_CFG_SEC_STATUS, dev_info->header.bridge.sec_status); cyg_pcihw_write_config_uint16(bus, devfn, CYG_PCI_CFG_MEM_BASE, dev_info->header.bridge.mem_base); cyg_pcihw_write_config_uint16(bus, devfn, CYG_PCI_CFG_MEM_LIMIT, dev_info->header.bridge.mem_limit); cyg_pcihw_write_config_uint16(bus, devfn, CYG_PCI_CFG_PREFETCH_BASE, dev_info->header.bridge.prefetch_base); cyg_pcihw_write_config_uint16(bus, devfn, CYG_PCI_CFG_PREFETCH_LIMIT, dev_info->header.bridge.prefetch_limit); cyg_pcihw_write_config_uint32(bus, devfn, CYG_PCI_CFG_PREFETCH_BASE_UPPER32, dev_info->header.bridge.prefetch_base_upper32); cyg_pcihw_write_config_uint32(bus, devfn, CYG_PCI_CFG_PREFETCH_LIMIT_UPPER32, dev_info->header.bridge.prefetch_limit_upper32); cyg_pcihw_write_config_uint16(bus, devfn, CYG_PCI_CFG_IO_BASE_UPPER16, dev_info->header.bridge.io_base_upper16); cyg_pcihw_write_config_uint16(bus, devfn, CYG_PCI_CFG_IO_LIMIT_UPPER16, dev_info->header.bridge.io_limit_upper16); cyg_pcihw_write_config_uint32(bus, devfn, CYG_PCI_CFG_BRIDGE_ROM_ADDRESS, dev_info->header.bridge.rom_address); cyg_pcihw_write_config_uint8(bus, devfn, CYG_PCI_CFG_INT_LINE, dev_info->header.bridge.int_line); cyg_pcihw_write_config_uint16(bus, devfn, CYG_PCI_CFG_BRIDGE_CONTROL, dev_info->header.bridge.control); break; case CYG_PCI_HEADER_CARDBUS_BRIDGE: CYG_FAIL("PCI device header 'cardbus bridge' support not implemented"); break; default: CYG_FAIL("Unknown PCI device header type"); break; } // Update values in dev_info. cyg_pci_get_device_info(devid, dev_info); } //--------------------------------------------------------------------------- // Specific device configuration access functions void cyg_pci_read_config_uint8( cyg_pci_device_id devid, cyg_uint8 offset, cyg_uint8 *val) { cyg_pcihw_read_config_uint8(CYG_PCI_DEV_GET_BUS(devid), CYG_PCI_DEV_GET_DEVFN(devid), offset, val); } void cyg_pci_read_config_uint16( cyg_pci_device_id devid, cyg_uint8 offset, cyg_uint16 *val) { cyg_pcihw_read_config_uint16(CYG_PCI_DEV_GET_BUS(devid), CYG_PCI_DEV_GET_DEVFN(devid), offset, val); } void cyg_pci_read_config_uint32( cyg_pci_device_id devid, cyg_uint8 offset, cyg_uint32 *val) { cyg_pcihw_read_config_uint32(CYG_PCI_DEV_GET_BUS(devid), CYG_PCI_DEV_GET_DEVFN(devid), offset, val); } // Write functions void cyg_pci_write_config_uint8( cyg_pci_device_id devid, cyg_uint8 offset, cyg_uint8 val) { cyg_pcihw_write_config_uint8(CYG_PCI_DEV_GET_BUS(devid), CYG_PCI_DEV_GET_DEVFN(devid), offset, val); } void cyg_pci_write_config_uint16( cyg_pci_device_id devid, cyg_uint8 offset, cyg_uint16 val) { cyg_pcihw_write_config_uint16(CYG_PCI_DEV_GET_BUS(devid), CYG_PCI_DEV_GET_DEVFN(devid), offset, val); } void cyg_pci_write_config_uint32( cyg_pci_device_id devid, cyg_uint8 offset, cyg_uint32 val) { cyg_pcihw_write_config_uint32(CYG_PCI_DEV_GET_BUS(devid), CYG_PCI_DEV_GET_DEVFN(devid), offset, val); } //------------------------------------------------------------------------ // Device find functions cyg_bool cyg_pci_find_next( cyg_pci_device_id cur_devid, cyg_pci_device_id *next_devid ) { cyg_uint8 bus = CYG_PCI_DEV_GET_BUS(cur_devid); cyg_uint8 devfn = CYG_PCI_DEV_GET_DEVFN(cur_devid); cyg_uint8 dev = CYG_PCI_DEV_GET_DEV(devfn); cyg_uint8 fn = CYG_PCI_DEV_GET_FN(devfn); #ifdef CYGPKG_IO_PCI_DEBUG diag_printf("cyg_pci_find_next: start[%x] ...\n",(unsigned)cur_devid); #endif // If this is the initializer, start with 0/0/0 if (CYG_PCI_NULL_DEVID == cur_devid) { bus = dev = fn = 0; dev = CYG_PCI_MIN_DEV; } else if (CYG_PCI_NULL_DEVFN == (cur_devid & CYG_PCI_NULL_DEVFN)) { dev = fn = 0; dev = CYG_PCI_MIN_DEV; } else { // Otherwise, check multi-function bit of device's first function cyg_uint8 header; devfn = CYG_PCI_DEV_MAKE_DEVFN(dev, 0); cyg_pcihw_read_config_uint8(bus, devfn, CYG_PCI_CFG_HEADER_TYPE, &header); if (header & CYG_PCI_CFG_HEADER_TYPE_MF) { // Multi-function device. Increase fn. fn++; if (fn >= CYG_PCI_MAX_FN) { fn = 0; dev++; } } else { // Single-function device. Skip to next. dev++; } } // Note: Reset iterators in enclosing statement's "next" part. // Allows resuming scan with given input values. for (;bus < CYG_PCI_MAX_BUS; bus++, dev=CYG_PCI_MIN_DEV) { for (;dev < CYG_PCI_MAX_DEV; dev++, fn=0) { for (;fn < CYG_PCI_MAX_FN; fn++) { cyg_uint16 vendor; if (CYG_PCI_IGNORE_DEVICE(bus, dev, fn)) continue; devfn = CYG_PCI_DEV_MAKE_DEVFN(dev, fn); cyg_pcihw_read_config_uint16(bus, devfn, CYG_PCI_CFG_VENDOR, &vendor); if (CYG_PCI_VENDOR_UNDEFINED != vendor) { #ifdef CYGPKG_IO_PCI_DEBUG diag_printf(" Bus: %d, Dev: %d, Fn: %d, Vendor: %x\n", bus, dev, fn, vendor); #endif *next_devid = CYG_PCI_DEV_MAKE_ID(bus, devfn); return true; } } } } #ifdef CYGPKG_IO_PCI_DEBUG diag_printf("nothing.\n"); #endif return false; } // // Scan for a particular device, starting with 'devid' // 'devid' is updated with the next device if found. // is not changed if no suitable device is found. cyg_bool cyg_pci_find_device( cyg_uint16 vendor, cyg_uint16 device, cyg_pci_device_id *devid ) { cyg_pci_device_id new_devid = *devid; #ifdef CYGPKG_IO_PCI_DEBUG diag_printf("cyg_pci_find_device - vendor: %x, device: %x\n", vendor, device); #endif // Scan entire bus, check for matches on valid devices. while (cyg_pci_find_next(new_devid, &new_devid)) { cyg_uint8 bus = CYG_PCI_DEV_GET_BUS(new_devid); cyg_uint8 devfn = CYG_PCI_DEV_GET_DEVFN(new_devid); cyg_uint16 v, d; // Check that vendor matches. cyg_pcihw_read_config_uint16(bus, devfn, CYG_PCI_CFG_VENDOR, &v); cyg_pcihw_read_config_uint16(bus, devfn, CYG_PCI_CFG_DEVICE, &d); #ifdef CYGPKG_IO_PCI_DEBUG diag_printf("... PCI vendor = %x, device = %x\n", v, d); #endif if (v != vendor) continue; // Check that device matches. if (d == device) { *devid = new_devid; return true; } } return false; } cyg_bool cyg_pci_find_class( cyg_uint32 dev_class, cyg_pci_device_id *devid ) { // Scan entire bus, check for matches on valid devices. while (cyg_pci_find_next(*devid, devid)) { cyg_uint8 bus = CYG_PCI_DEV_GET_BUS(*devid); cyg_uint8 devfn = CYG_PCI_DEV_GET_DEVFN(*devid); cyg_uint32 c; // Check that class code matches. cyg_pcihw_read_config_uint32(bus, devfn, CYG_PCI_CFG_CLASS_REV, &c); c >>= 8; if (c == dev_class) return true; } return false; } cyg_bool cyg_pci_find_matching( cyg_pci_match_func *matchp, void * match_callback_data, cyg_pci_device_id *devid ) { cyg_pci_device_id new_devid = *devid; #ifdef CYGPKG_IO_PCI_DEBUG diag_printf("cyg_pci_find_matching - func is at %x\n", (unsigned int)matchp); #endif // Scan entire bus, check for matches on valid devices. while (cyg_pci_find_next(new_devid, &new_devid)) { cyg_uint8 bus = CYG_PCI_DEV_GET_BUS(new_devid); cyg_uint8 devfn = CYG_PCI_DEV_GET_DEVFN(new_devid); cyg_uint16 v, d; cyg_uint32 c; // Check that vendor, device and class match. cyg_pcihw_read_config_uint16(bus, devfn, CYG_PCI_CFG_VENDOR, &v); cyg_pcihw_read_config_uint16(bus, devfn, CYG_PCI_CFG_DEVICE, &d); cyg_pcihw_read_config_uint32(bus, devfn, CYG_PCI_CFG_CLASS_REV, &c); c >>= 8; #ifdef CYGPKG_IO_PCI_DEBUG diag_printf("... PCI vendor = %x, device = %x, class %x\n", v, d, c); #endif // Check that device matches as the caller desires: if ( (*matchp)(v, d, c, match_callback_data) ) { *devid = new_devid; return true; } } return false; } //------------------------------------------------------------------------ // Resource Allocation void cyg_pci_set_memory_base(CYG_PCI_ADDRESS64 base) { cyg_pci_memory_base = base; } void cyg_pci_set_io_base(CYG_PCI_ADDRESS32 base) { cyg_pci_io_base = base; } cyg_bool cyg_pci_configure_device( cyg_pci_device *dev_info ) { int bar; cyg_uint32 flags; cyg_bool ret = true; // If device is already active, just return true as // cyg_pci_get_device_info has presumably filled in // the base_map already. if ((dev_info->command & CYG_PCI_CFG_COMMAND_ACTIVE) != 0) return true; if (dev_info->num_bars > 0) { for (bar = 0; bar < CYG_PCI_MAX_BAR; bar++) { if (!dev_info->base_address[bar]) { continue; } flags = dev_info->base_size[bar]; ret = false; if ((flags & CYG_PCI_CFG_BAR_SPACE_MASK) == CYG_PCI_CFG_BAR_SPACE_MEM){ ret |= cyg_pci_allocate_memory(dev_info, bar, &cyg_pci_memory_base); // If this is a 64bit memory region, skip the next bar // since it will contain the top 32 bits. if (flags & CYG_PRI_CFG_BAR_MEM_TYPE_64) bar++; } else ret |= cyg_pci_allocate_io(dev_info, bar, &cyg_pci_io_base); if (!ret) return ret; } } cyg_pci_translate_interrupt(dev_info, &dev_info->hal_vector); return ret; } // This is the function that handles resource allocation. It doesn't // affect the device state. // Should not be called with top32bit-bar of a 64bit pair. inline cyg_bool cyg_pci_allocate_memory_priv( cyg_pci_device *dev_info, cyg_uint32 bar, CYG_PCI_ADDRESS64 *base, CYG_PCI_ADDRESS64 *assigned_addr) { cyg_uint32 mem_type, flags; CYG_PCI_ADDRESS64 size, aligned_addr; // Get the probed size and flags flags = dev_info->base_size[bar]; // Decode size size = (CYG_PCI_ADDRESS64) ((~(flags & CYG_PRI_CFG_BAR_MEM_MASK))+1); // Calculate address we will assign the device. // This can be made more clever, specifically: // 1) The lowest 1MB should be reserved for devices with 1M memory type. // : Needs to be handled. // 2) The low 32bit space should be reserved for devices with 32bit type. // : With the usual handful of devices it is unlikely that the // low 4GB space will become full. // 3) A bitmap can be used to avoid fragmentation. // : Again, unlikely to be necessary. // // For now, simply align to required size. aligned_addr = (*base+size-1) & ~(size-1); // Is the request for memory space? if (CYG_PCI_CFG_BAR_SPACE_MEM != (flags & CYG_PCI_CFG_BAR_SPACE_MASK)) return false; // Check type of memory requested... mem_type = CYG_PRI_CFG_BAR_MEM_TYPE_MASK & flags; // We don't handle <1MB devices optimally yet. if (CYG_PRI_CFG_BAR_MEM_TYPE_1M == mem_type && (aligned_addr + size) > 1024*1024) return false; // Update the resource pointer and return values. *base = aligned_addr+size; *assigned_addr = aligned_addr; dev_info->base_map[bar] = (cyg_uint32) (aligned_addr+HAL_PCI_PHYSICAL_MEMORY_BASE) & 0xffffffff; // If a 64bit region, store upper 32 bits in the next bar. // Note: The CPU is not necessarily able to access the region // linearly - it may have to do it in segments. Driver must handle that. if (CYG_PRI_CFG_BAR_MEM_TYPE_64 == mem_type) { dev_info->base_map[bar+1] = (cyg_uint32) ((aligned_addr+HAL_PCI_PHYSICAL_MEMORY_BASE) >> 32) & 0xffffffff; } return true; } cyg_bool cyg_pci_allocate_memory( cyg_pci_device *dev_info, cyg_uint32 bar, CYG_PCI_ADDRESS64 *base) { cyg_uint8 bus = CYG_PCI_DEV_GET_BUS(dev_info->devid); cyg_uint8 devfn = CYG_PCI_DEV_GET_DEVFN(dev_info->devid); CYG_PCI_ADDRESS64 assigned_addr; cyg_bool ret; // Check that device is inactive. if ((dev_info->command & CYG_PCI_CFG_COMMAND_ACTIVE) != 0) return false; // Allocate memory space for the device. ret = cyg_pci_allocate_memory_priv(dev_info, bar, base, &assigned_addr); if (ret) { // Map the device and update the BAR in the dev_info structure. cyg_pcihw_write_config_uint32(bus, devfn, CYG_PCI_CFG_BAR_BASE+4*bar, (cyg_uint32) (assigned_addr & 0xffffffff)); cyg_pcihw_read_config_uint32(bus, devfn, CYG_PCI_CFG_BAR_BASE+4*bar, &dev_info->base_address[bar]); // Handle upper 32 bits if necessary. if (dev_info->base_size[bar] & CYG_PRI_CFG_BAR_MEM_TYPE_64) { cyg_pcihw_write_config_uint32(bus, devfn, CYG_PCI_CFG_BAR_BASE+4*(bar+1), (cyg_uint32) ((assigned_addr >> 32)& 0xffffffff)); cyg_pcihw_read_config_uint32(bus, devfn, CYG_PCI_CFG_BAR_BASE+4*(bar+1), &dev_info->base_address[bar+1]); } } return ret; } cyg_bool cyg_pci_allocate_io_priv( cyg_pci_device *dev_info, cyg_uint32 bar, CYG_PCI_ADDRESS32 *base, CYG_PCI_ADDRESS32 *assigned_addr) { cyg_uint32 flags, size; CYG_PCI_ADDRESS32 aligned_addr; // Get the probed size and flags flags = dev_info->base_size[bar]; // Decode size size = (~(flags & CYG_PRI_CFG_BAR_IO_MASK))+1; // Calculate address we will assign the device. // This can be made more clever. // For now, simply align to required size. aligned_addr = (*base+size-1) & ~(size-1); // Is the request for IO space? if (CYG_PCI_CFG_BAR_SPACE_IO != (flags & CYG_PCI_CFG_BAR_SPACE_MASK)) return false; // Update the resource pointer and return values. *base = aligned_addr+size; dev_info->base_map[bar] = aligned_addr+HAL_PCI_PHYSICAL_IO_BASE; *assigned_addr = aligned_addr; return true; } cyg_bool cyg_pci_allocate_io( cyg_pci_device *dev_info, cyg_uint32 bar, CYG_PCI_ADDRESS32 *base) { cyg_uint8 bus = CYG_PCI_DEV_GET_BUS(dev_info->devid); cyg_uint8 devfn = CYG_PCI_DEV_GET_DEVFN(dev_info->devid); CYG_PCI_ADDRESS32 assigned_addr; cyg_bool ret; // Check that device is inactive. if ((dev_info->command & CYG_PCI_CFG_COMMAND_ACTIVE) != 0) return false; // Allocate IO space for the device. ret = cyg_pci_allocate_io_priv(dev_info, bar, base, &assigned_addr); if (ret) { // Map the device and update the BAR in the dev_info structure. cyg_pcihw_write_config_uint32(bus, devfn, CYG_PCI_CFG_BAR_BASE+4*bar, assigned_addr); cyg_pcihw_read_config_uint32(bus, devfn, CYG_PCI_CFG_BAR_BASE+4*bar, &dev_info->base_address[bar]); } return ret; } cyg_bool cyg_pci_translate_interrupt( cyg_pci_device *dev_info, CYG_ADDRWORD *vec ) { cyg_uint8 bus = CYG_PCI_DEV_GET_BUS(dev_info->devid); cyg_uint8 devfn = CYG_PCI_DEV_GET_DEVFN(dev_info->devid); return cyg_pcihw_translate_interrupt(bus, devfn, vec); } // Initialize devices on a given bus and all subordinate busses. cyg_bool cyg_pci_configure_bus( cyg_uint8 bus, cyg_uint8 *next_bus ) { cyg_uint8 devfn, header_type; cyg_pci_device_id devid; cyg_pci_device dev_info; CYG_PCI_ADDRESS64 mem_start, mem_limit, mem_base; CYG_PCI_ADDRESS32 io_start, io_limit, io_base; // Scan only this bus for valid devices. devid = CYG_PCI_DEV_MAKE_ID(bus, 0) | CYG_PCI_NULL_DEVFN; #ifdef CYGPKG_IO_PCI_DEBUG diag_printf("Configuring bus %d.\n", bus); #endif while (cyg_pci_find_next(devid, &devid) && bus == CYG_PCI_DEV_GET_BUS(devid)) { devfn = CYG_PCI_DEV_GET_DEVFN(devid); // Get the device info cyg_pci_get_device_info(devid, &dev_info); #ifdef CYGPKG_IO_PCI_DEBUG diag_printf("\n"); diag_printf("Configuring PCI Bus : %d\n", bus); diag_printf(" PCI Device: %d\n", CYG_PCI_DEV_GET_DEV(devfn)); diag_printf(" PCI Func : %d\n", CYG_PCI_DEV_GET_FN(devfn)); diag_printf(" Vendor Id : 0x%08X\n", dev_info.vendor); diag_printf(" Device Id : 0x%08X\n", dev_info.device); #endif header_type = dev_info.header_type & CYG_PCI_CFG_HEADER_TYPE_MASK; // Check for supported header types. if (header_type != CYG_PCI_HEADER_NORMAL && header_type != CYG_PCI_HEADER_BRIDGE) { CYG_FAIL("Unsupported PCI header type"); continue; } // Only PCI-to-PCI bridges if (header_type == CYG_PCI_HEADER_BRIDGE && (dev_info.class_rev >> 8) != CYG_PCI_CLASS_BRIDGE_PCI_PCI) { CYG_FAIL("Unsupported PCI bridge class"); continue; } // Configure the base registers if (!cyg_pci_configure_device(&dev_info)) { // Apparently out of resources. CYG_FAIL("cyg_pci_configure_device failed"); break; } // Activate non-bridge devices. if (header_type != CYG_PCI_HEADER_BRIDGE) { dev_info.command |= (CYG_PCI_CFG_COMMAND_IO // enable I/O space | CYG_PCI_CFG_COMMAND_MEMORY // enable memory space | CYG_PCI_CFG_COMMAND_MASTER); // enable bus master cyg_pci_write_config_uint16(dev_info.devid, CYG_PCI_CFG_COMMAND, dev_info.command); } else { // Bridge Configuration // Set up the bus numbers that define the bridge dev_info.header.bridge.pri_bus = bus; cyg_pcihw_write_config_uint8(bus, devfn, CYG_PCI_CFG_PRI_BUS, dev_info.header.bridge.pri_bus); dev_info.header.bridge.sec_bus = *next_bus; cyg_pcihw_write_config_uint8(bus, devfn, CYG_PCI_CFG_SEC_BUS, dev_info.header.bridge.sec_bus); // Temporarily set to maximum so config cycles get passed along. dev_info.header.bridge.sub_bus = CYG_PCI_MAX_BUS - 1; cyg_pcihw_write_config_uint8(bus, devfn, CYG_PCI_CFG_SUB_BUS, dev_info.header.bridge.sub_bus); // increment bus counter *next_bus += 1; // To figure the sizes of the memory and I/O windows, save the // current base of memory and I/O before configuring the bus // or busses on the secondary side of the bridge. After the // secondary side is configured, the difference between the // current values and saved values will tell the size. // For bridges, the memory window must start and end on a 1M // boundary and the I/O window must start and end on a 4K // boundary. We round up the mem and I/O allocation bases // to appropriate boundaries before configuring the secondary // bus. Save the pre-rounded values in case no mem or I/O // is needed we can recover any space lost due to rounding. // round up start of PCI memory space to a 1M boundary mem_base = cyg_pci_memory_base; cyg_pci_memory_base += 0xfffff; cyg_pci_memory_base &= ~0xfffff; mem_start = cyg_pci_memory_base; // round up start of PCI I/O space to a 4 Kbyte start address io_base = cyg_pci_io_base; cyg_pci_io_base += 0xfff; cyg_pci_io_base &= ~0xfff; io_start = cyg_pci_io_base; // configure the subordinate PCI bus cyg_pci_configure_bus (dev_info.header.bridge.sec_bus, next_bus); // set subordinate bus number to last assigned bus number dev_info.header.bridge.sub_bus = *next_bus - 1; cyg_pcihw_write_config_uint8(bus, devfn, CYG_PCI_CFG_SUB_BUS, dev_info.header.bridge.sub_bus); // Did sub bus configuration use any I/O? if (cyg_pci_io_base > io_start) { // round up end of bridge's I/O space to a 4K boundary cyg_pci_io_base += 0xfff; cyg_pci_io_base &= ~0xfff; io_limit = cyg_pci_io_base - 0x1000; // Enable I/O cycles across bridge dev_info.command |= CYG_PCI_CFG_COMMAND_IO; // 32 Bit I/O? if ((dev_info.header.bridge.io_base & 0x0f) == 0x01) { // I/O Base Upper 16 Bits Register dev_info.header.bridge.io_base_upper16 = io_start >> 16; cyg_pcihw_write_config_uint16(bus, devfn, CYG_PCI_CFG_IO_BASE_UPPER16, dev_info.header.bridge.io_base_upper16); // I/O Limit Upper 16 Bits Register dev_info.header.bridge.io_limit_upper16 = io_limit >> 16; cyg_pcihw_write_config_uint16(bus, devfn, CYG_PCI_CFG_IO_LIMIT_UPPER16, dev_info.header.bridge.io_limit_upper16); } // I/O Base Register dev_info.header.bridge.io_base = (io_start & 0xf000) >> 8; cyg_pcihw_write_config_uint8(bus, devfn, CYG_PCI_CFG_IO_BASE, dev_info.header.bridge.io_base); // I/O Limit Register dev_info.header.bridge.io_limit = (io_limit & 0xf000) >> 8; cyg_pcihw_write_config_uint8(bus, devfn, CYG_PCI_CFG_IO_LIMIT, dev_info.header.bridge.io_limit); } else { // No I/O space used on secondary bus. // Recover any space lost on unnecessary rounding cyg_pci_io_base = io_base; } // Did sub bus configuration use any memory? if (cyg_pci_memory_base > mem_start) { // round up end of bridge's PCI memory space to a 1M boundary cyg_pci_memory_base += 0xfffff; cyg_pci_memory_base &= ~0xfffff; mem_limit = cyg_pci_memory_base - 0x100000; // Enable memory cycles across bridge dev_info.command |= CYG_PCI_CFG_COMMAND_MEMORY; // Memory Base Register dev_info.header.bridge.mem_base = (mem_start >> 16) & 0xfff0; cyg_pcihw_write_config_uint16(bus, devfn, CYG_PCI_CFG_MEM_BASE, dev_info.header.bridge.mem_base); // Memory Limit Register dev_info.header.bridge.mem_limit = (mem_limit >> 16) & 0xfff0; cyg_pcihw_write_config_uint16(bus, devfn, CYG_PCI_CFG_MEM_LIMIT, dev_info.header.bridge.mem_limit); // Prefetchable memory not yet supported across bridges. // Disable by making limit < base { cyg_uint16 tmp_word; tmp_word = 0; cyg_pcihw_write_config_uint16(bus, devfn, CYG_PCI_CFG_PREFETCH_LIMIT, tmp_word); tmp_word = 0xfff0; cyg_pcihw_write_config_uint16(bus, devfn, CYG_PCI_CFG_PREFETCH_BASE, tmp_word); } } else { // No memory space used on secondary bus. // Recover any space lost on unnecessary rounding cyg_pci_memory_base = mem_base; } // Setup the bridge command register dev_info.command |= CYG_PCI_CFG_COMMAND_MASTER; dev_info.command |= CYG_PCI_CFG_COMMAND_SERR; cyg_pcihw_write_config_uint16(bus, devfn, CYG_PCI_CFG_COMMAND, dev_info.command); /* Setup the Bridge Control Register */ dev_info.header.bridge.control |= CYG_PCI_CFG_BRIDGE_CTL_PARITY; dev_info.header.bridge.control |= CYG_PCI_CFG_BRIDGE_CTL_SERR; dev_info.header.bridge.control |= CYG_PCI_CFG_BRIDGE_CTL_MASTER; cyg_pcihw_write_config_uint16(bus, devfn, CYG_PCI_CFG_BRIDGE_CONTROL, dev_info.header.bridge.control); } } #ifdef CYGPKG_IO_PCI_DEBUG diag_printf("Finished configuring bus %d.\n", bus); #endif return true; } #endif // ifdef CYG_PCI_PRESENT //----------------------------------------------------------------------------- // end of pci.c
