URL
https://opencores.org/ocsvn/openrisc_me/openrisc_me/trunk
Subversion Repositories openrisc_me
[/] [openrisc/] [trunk/] [rtos/] [ecos-2.0/] [packages/] [io/] [pci/] [v2_0/] [src/] [pci.c] - Rev 428
Go to most recent revision | Compare with Previous | Blame | View Log
//============================================================================= // // 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; 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; 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; 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) { 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; // No reason to scan beyond first inactive BAR. if (size == 0) { dev_info->num_bars = i; break; } // 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; } } } } else { // If the device is already configured. Fill in the base_map. CYG_PCI_ADDRESS64 tmp_addr; cyg_uint32 bar; for (i = 0; i < dev_info->num_bars; i++){ dev_info->base_size[i] = 0; bar = dev_info->base_address[i]; // No reason to scan beyond first inactive BAR. if (bar == 0) { dev_info->num_bars = i; break; } 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; } 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; 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; } } } 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); for (i = 0; i < dev_info->num_bars; 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 static CYG_PCI_ADDRESS64 cyg_pci_memory_base; static CYG_PCI_ADDRESS32 cyg_pci_io_base; 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 < dev_info->num_bars; bar++) { 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
Go to most recent revision | Compare with Previous | Blame | View Log