diff --git a/headers/os/drivers/PCI.h b/headers/os/drivers/PCI.h index 2490cd7a43..bda37c9be3 100644 --- a/headers/os/drivers/PCI.h +++ b/headers/os/drivers/PCI.h @@ -83,7 +83,7 @@ typedef struct pci_info { ulong rom_base_pci; /* rom base addr, viewed from pci */ uchar interrupt_line; /* interrupt line */ uchar interrupt_pin; /* interrupt pin */ - ushort bridge_control; + ushort bridge_control; ushort subsystem_id; /* subsystem (add-in card) id */ ushort subsystem_vendor_id; /* subsystem (add-in card) vendor id */ } h1; @@ -109,7 +109,7 @@ typedef struct pci_info { ushort secondary_status; ushort bridge_control; #endif /* __HAIKU_PCI_BUS_MANAGER_TESTING */ - } h2; + } h2; } u; } pci_info; @@ -221,7 +221,7 @@ struct pci_module_info { #define PCI_primary_bus 0x18 /* (1 byte) */ #define PCI_secondary_bus 0x19 /* (1 byte) */ #define PCI_subordinate_bus 0x1A /* (1 byte) */ -#define PCI_secondary_latency 0x1B /* (1 byte) latency of secondary bus */ +#define PCI_secondary_latency 0x1B /* (1 byte) latency of secondary bus */ #define PCI_io_base 0x1C /* (1 byte) io base address register for 2ndry bus*/ #define PCI_io_limit 0x1D /* (1 byte) */ #define PCI_secondary_status 0x1E /* (2 bytes) */ @@ -236,7 +236,7 @@ struct pci_module_info { #define PCI_sub_vendor_id_1 0x34 /* (2 bytes) */ #define PCI_sub_device_id_1 0x36 /* (2 bytes) */ #define PCI_bridge_rom_base 0x38 -#define PCI_bridge_control 0x3E /* (2 bytes) */ +#define PCI_bridge_control 0x3E /* (2 bytes) */ /* PCI type 2 header offsets */ @@ -245,7 +245,7 @@ struct pci_module_info { #define PCI_primary_bus_2 0x18 /* (1 byte) */ #define PCI_secondary_bus_2 0x19 /* (1 byte) */ #define PCI_subordinate_bus_2 0x1A /* (1 byte) */ -#define PCI_secondary_latency_2 0x1B /* (1 byte) latency of secondary bus */ +#define PCI_secondary_latency_2 0x1B /* (1 byte) latency of secondary bus */ #define PCI_memory_base0_2 0x1C /* (4 bytes) */ #define PCI_memory_limit0_2 0x20 /* (4 bytes) */ #define PCI_memory_base1_2 0x24 /* (4 bytes) */ @@ -588,8 +588,8 @@ struct pci_module_info { /** types of PCI header */ -#define PCI_header_type_generic 0x00 -#define PCI_header_type_PCI_to_PCI_bridge 0x01 +#define PCI_header_type_generic 0x00 +#define PCI_header_type_PCI_to_PCI_bridge 0x01 #define PCI_header_type_cardbus 0x02 @@ -677,6 +677,34 @@ struct pci_module_info { #define PCI_pm_state_d2 0x02 #define PCI_pm_state_d3 0x03 +/** MSI registers **/ +#define PCI_msi_control 0x02 +#define PCI_msi_address 0x04 +#define PCI_msi_address_high 0x08 +#define PCI_msi_data 0x08 +#define PCI_msi_data_64bit 0x0c +#define PCI_msi_mask 0x10 +#define PCI_msi_pending 0x14 + +/** MSI control register values **/ +#define PCI_msi_control_enable 0x0001 +#define PCI_msi_control_vector 0x0100 +#define PCI_msi_control_64bit 0x0080 +#define PCI_msi_control_mme_mask 0x0070 +#define PCI_msi_control_mme_1 0x0000 +#define PCI_msi_control_mme_2 0x0010 +#define PCI_msi_control_mme_4 0x0020 +#define PCI_msi_control_mme_8 0x0030 +#define PCI_msi_control_mme_16 0x0040 +#define PCI_msi_control_mme_32 0x0050 +#define PCI_msi_control_mmc_mask 0x000e +#define PCI_msi_control_mmc_1 0x0000 +#define PCI_msi_control_mmc_2 0x0002 +#define PCI_msi_control_mmc_4 0x0004 +#define PCI_msi_control_mmc_8 0x0006 +#define PCI_msi_control_mmc_16 0x0008 +#define PCI_msi_control_mmc_32 0x000a + #ifdef __cplusplus } #endif diff --git a/headers/os/drivers/PCI_x86.h b/headers/os/drivers/PCI_x86.h new file mode 100644 index 0000000000..6405c05d87 --- /dev/null +++ b/headers/os/drivers/PCI_x86.h @@ -0,0 +1,38 @@ +#ifndef PCI_X86_H +#define PCI_X86_H + +#include +#include + +typedef struct pci_x86_module_info { + module_info info; + + uint8 (*get_msi_count)( + uint8 bus, /* bus number */ + uint8 device, /* device # on bus */ + uint8 function); /* function # in device */ + + status_t (*configure_msi)( + uint8 bus, /* bus number */ + uint8 device, /* device # on bus */ + uint8 function, /* function # in device */ + uint8 count, /* count of vectors desired */ + uint8 *startVector); /* first configured vector */ + status_t (*unconfigure_msi)( + uint8 bus, /* bus number */ + uint8 device, /* device # on bus */ + uint8 function); /* function # in device */ + + status_t (*enable_msi)( + uint8 bus, /* bus number */ + uint8 device, /* device # on bus */ + uint8 function); /* function # in device */ + status_t (*disable_msi)( + uint8 bus, /* bus number */ + uint8 device, /* device # on bus */ + uint8 function); /* function # in device */ +} pci_x86_module_info; + +#define B_PCI_X86_MODULE_NAME "bus_managers/pci/x86/v1" + +#endif // PCI_X86_H diff --git a/src/add-ons/kernel/bus_managers/pci/Jamfile b/src/add-ons/kernel/bus_managers/pci/Jamfile index 101de8039a..79d1b0e00a 100644 --- a/src/add-ons/kernel/bus_managers/pci/Jamfile +++ b/src/add-ons/kernel/bus_managers/pci/Jamfile @@ -3,12 +3,13 @@ SubDir HAIKU_TOP src add-ons kernel bus_managers pci ; UsePrivateKernelHeaders ; UsePrivateHeaders shared ; UsePrivateHeaders [ FDirName kernel util ] ; +UseHeaders [ FDirName $(SUBDIR) arch $(TARGET_ARCH) ] ; KernelAddon pci : pci.cpp pci_fixup.cpp pci_info.cpp - pci_module.c + pci_module.cpp pci_root.cpp pci_device.cpp : pci_arch_bus_manager.a diff --git a/src/add-ons/kernel/bus_managers/pci/arch/x86/Jamfile b/src/add-ons/kernel/bus_managers/pci/arch/x86/Jamfile index 24fb9dbf77..9328c66541 100644 --- a/src/add-ons/kernel/bus_managers/pci/arch/x86/Jamfile +++ b/src/add-ons/kernel/bus_managers/pci/arch/x86/Jamfile @@ -2,12 +2,14 @@ SubDir HAIKU_TOP src add-ons kernel bus_managers pci arch x86 ; SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) $(DOTDOT) ] ; -UsePrivateHeaders kernel [ FDirName kernel arch x86 ] ; +UsePrivateHeaders kernel [ FDirName kernel arch x86 ] [ FDirName kernel util ] ; -KernelStaticLibrary pci_arch_bus_manager : +KernelStaticLibrary pci_arch_bus_manager : + pci_arch_info.cpp + pci_arch_module.cpp pci_bios.c pci_controller.c pci_io.c pci_irq.c + pci_msi.cpp ; - diff --git a/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_arch_info.cpp b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_arch_info.cpp new file mode 100644 index 0000000000..3f7ba1e5e3 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_arch_info.cpp @@ -0,0 +1,7 @@ +#include "pci_arch_info.h" + +void +pci_read_arch_info(PCIDev *dev) +{ + pci_read_msi_info(dev); +} diff --git a/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_arch_info.h b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_arch_info.h new file mode 100644 index 0000000000..f673edbcf8 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_arch_info.h @@ -0,0 +1,13 @@ +#ifndef _PCI_ARCH_INFO_H +#define _PCI_ARCH_INFO_H + +#include "pci_msi.h" + +typedef struct pci_arch_info { + msi_info msi; +} pci_arch_info; + + +void pci_read_arch_info(PCIDev *device); + +#endif // _PCI_ARCH_INFO_H diff --git a/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_arch_module.cpp b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_arch_module.cpp new file mode 100644 index 0000000000..eecd984d22 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_arch_module.cpp @@ -0,0 +1,30 @@ +#include +#include "pci_msi.h" + + +static int32 +pci_arch_module_std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + case B_MODULE_UNINIT: + return B_OK; + } + + return B_BAD_VALUE; +} + + +pci_x86_module_info gPCIArchModule = { + { + B_PCI_X86_MODULE_NAME, + 0, + pci_arch_module_std_ops + }, + + &pci_get_msi_count, + &pci_configure_msi, + &pci_unconfigure_msi, + &pci_enable_msi, + &pci_disable_msi +}; diff --git a/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_msi.cpp b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_msi.cpp new file mode 100644 index 0000000000..ad33a7b8fe --- /dev/null +++ b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_msi.cpp @@ -0,0 +1,222 @@ +#include "pci_msi.h" +#include "pci_arch_info.h" +#include "pci.h" +#include "pci_private.h" + +#include +#include + +extern PCI *gPCI; + + +uint8 +pci_get_msi_count(uint8 virtualBus, uint8 _device, uint8 function) +{ + if (!msi_supported()) + return 0; + + uint8 bus; + int domain; + if (gPCI->ResolveVirtualBus(virtualBus, &domain, &bus) != B_OK) + return 0; + + PCIDev *device = gPCI->FindDevice(domain, bus, _device, function); + if (device == NULL) + return 0; + + msi_info *info = &device->arch_info.msi; + if (!info->msi_capable) + return 0; + + return info->message_count; +} + + +status_t +pci_configure_msi(uint8 virtualBus, uint8 _device, uint8 function, + uint8 count, uint8 *startVector) +{ + if (!msi_supported()) + return B_UNSUPPORTED; + + if (count == 0 || startVector == NULL) + return B_BAD_VALUE; + + uint8 bus; + int domain; + status_t result = gPCI->ResolveVirtualBus(virtualBus, &domain, &bus); + if (result != B_OK) + return result; + + PCIDev *device = gPCI->FindDevice(domain, bus, _device, function); + if (device == NULL) + return B_ERROR; + + msi_info *info = &device->arch_info.msi; + if (!info->msi_capable) + return B_UNSUPPORTED; + + if (count > 32 || count > info->message_count + || ((count - 1) & count) != 0 /* needs to be a power of 2 */) { + return B_BAD_VALUE; + } + + if (info->configured_count != 0) + return B_BUSY; + + result = msi_allocate_vectors(count, &info->start_vector, + &info->address_value, &info->data_value); + if (result != B_OK) + return result; + + uint8 offset = info->capability_offset; + gPCI->WriteConfig(device, offset + PCI_msi_address, 4, + info->address_value & 0xffffffff); + if (info->control_value & PCI_msi_control_64bit) { + gPCI->WriteConfig(device, offset + PCI_msi_address_high, 4, + info->address_value >> 32); + gPCI->WriteConfig(device, offset + PCI_msi_data_64bit, 2, + info->data_value); + } else + gPCI->WriteConfig(device, offset + PCI_msi_data, 2, info->data_value); + + info->control_value &= ~PCI_msi_control_mme_mask; + info->control_value |= (ffs(count) - 1) << 4; + gPCI->WriteConfig(device, offset + PCI_msi_control, 2, info->control_value); + + info->configured_count = count; + *startVector = info->start_vector; + return B_OK; +} + + +status_t +pci_unconfigure_msi(uint8 virtualBus, uint8 _device, uint8 function) +{ + if (!msi_supported()) + return B_UNSUPPORTED; + + uint8 bus; + int domain; + status_t result = gPCI->ResolveVirtualBus(virtualBus, &domain, &bus); + if (result != B_OK) + return result; + + PCIDev *device = gPCI->FindDevice(domain, bus, _device, function); + if (device == NULL) + return B_ERROR; + + msi_info *info = &device->arch_info.msi; + if (!info->msi_capable) + return B_UNSUPPORTED; + + if (info->configured_count == 0) + return B_NO_INIT; + + msi_free_vectors(info->configured_count, info->start_vector); + + info->control_value &= ~PCI_msi_control_mme_mask; + gPCI->WriteConfig(device, info->capability_offset + PCI_msi_control, 2, + info->control_value); + + info->configured_count = 0; + info->address_value = 0; + info->data_value = 0; + return B_OK; +} + + +status_t +pci_enable_msi(uint8 virtualBus, uint8 _device, uint8 function) +{ + if (!msi_supported()) + return B_UNSUPPORTED; + + uint8 bus; + int domain; + status_t result = gPCI->ResolveVirtualBus(virtualBus, &domain, &bus); + if (result != B_OK) + return result; + + PCIDev *device = gPCI->FindDevice(domain, bus, _device, function); + if (device == NULL) + return B_ERROR; + + msi_info *info = &device->arch_info.msi; + if (!info->msi_capable) + return B_UNSUPPORTED; + + if (info->configured_count == 0) + return B_NO_INIT; + + // ensure the pinned interrupt is disabled + gPCI->WriteConfig(device, PCI_command, 2, + gPCI->ReadConfig(device, PCI_command, 2) | PCI_command_int_disable); + + // enable msi generation + info->control_value |= PCI_msi_control_enable; + gPCI->WriteConfig(device, info->capability_offset + PCI_msi_control, 2, + info->control_value); + + dprintf("msi enabled: 0x%04lx\n", + gPCI->ReadConfig(device, info->capability_offset + PCI_msi_control, 2)); + return B_OK; +} + + +status_t +pci_disable_msi(uint8 virtualBus, uint8 _device, uint8 function) +{ + if (!msi_supported()) + return B_UNSUPPORTED; + + uint8 bus; + int domain; + status_t result = gPCI->ResolveVirtualBus(virtualBus, &domain, &bus); + if (result != B_OK) + return result; + + PCIDev *device = gPCI->FindDevice(domain, bus, _device, function); + if (device == NULL) + return B_ERROR; + + msi_info *info = &device->arch_info.msi; + if (!info->msi_capable) + return B_UNSUPPORTED; + + if (info->configured_count == 0) + return B_NO_INIT; + + // disable msi generation + info->control_value &= ~PCI_msi_control_enable; + gPCI->WriteConfig(device, info->capability_offset + PCI_msi_control, 2, + info->control_value); + + return B_OK; +} + + +void +pci_read_msi_info(PCIDev *device) +{ + if (!msi_supported()) + return; + + msi_info *info = &device->arch_info.msi; + info->msi_capable = false; + status_t result = gPCI->FindCapability(device->domain, device->bus, + device->device, device->function, PCI_cap_id_msi, + &info->capability_offset); + if (result != B_OK) + return; + + info->msi_capable = true; + info->control_value = gPCI->ReadConfig(device->domain, device->bus, + device->device, device->function, + info->capability_offset + PCI_msi_control, 2); + info->message_count + = 1 << ((info->control_value & PCI_msi_control_mmc_mask) >> 1); + info->configured_count = 0; + info->data_value = 0; + info->address_value = 0; +} diff --git a/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_msi.h b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_msi.h new file mode 100644 index 0000000000..d80a8571d0 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/pci/arch/x86/pci_msi.h @@ -0,0 +1,29 @@ +#ifndef _PCI_x86_MSI_H +#define _PCI_x86_MSI_H + +#include + +class PCIDev; + +// Message Signaled Interrupts +typedef struct msi_info { + bool msi_capable; + uint8 capability_offset; + uint8 message_count; + uint8 configured_count; + uint8 start_vector; + uint16 control_value; + uint16 data_value; + uint64 address_value; +} msi_info; + + +uint8 pci_get_msi_count(uint8 virtualBus, uint8 _device, uint8 function); +status_t pci_configure_msi(uint8 virtualBus, uint8 _device, uint8 function, + uint8 count, uint8 *startVector); +status_t pci_unconfigure_msi(uint8 virtualBus, uint8 device, uint8 function); +status_t pci_enable_msi(uint8 virtualBus, uint8 device, uint8 function); +status_t pci_disable_msi(uint8 virtualBus, uint8 device, uint8 function); +void pci_read_msi_info(PCIDev *device); + +#endif // _PCI_x86_MSI_H diff --git a/src/add-ons/kernel/bus_managers/pci/pci.cpp b/src/add-ons/kernel/bus_managers/pci/pci.cpp index 75b1b84f3a..def68d6f82 100644 --- a/src/add-ons/kernel/bus_managers/pci/pci.cpp +++ b/src/add-ons/kernel/bus_managers/pci/pci.cpp @@ -103,7 +103,7 @@ pci_reserve_device(uchar virtualBus, uchar device, uchar function, if (gPCI->ResolveVirtualBus(virtualBus, &domain, &bus) != B_OK) return B_ERROR; - //TRACE(("%s(%d [%d:%d], %d, %d, %s, %p)\n", __FUNCTION__, virtualBus, + //TRACE(("%s(%d [%d:%d], %d, %d, %s, %p)\n", __FUNCTION__, virtualBus, // domain, bus, device, function, driverName, nodeCookie)); device_attr matchPCIRoot[] = { @@ -140,7 +140,7 @@ pci_reserve_device(uchar virtualBus, uchar device, uchar function, root = gDeviceManager->get_root_node(); if (!root) return status; - + pci = NULL; if (gDeviceManager->get_next_child_node(root, matchPCIRoot, &pci) < B_OK) goto err0; @@ -148,16 +148,16 @@ pci_reserve_device(uchar virtualBus, uchar device, uchar function, node = NULL; if (gDeviceManager->get_next_child_node(pci, matchThis, &node) < B_OK) goto err1; - + // common API for all legacy modules ? //status = legacy_driver_register(node, driverName, nodeCookie, PCI_LEGACY_DRIVER_MODULE_NAME); - status = gDeviceManager->register_node(node, PCI_LEGACY_DRIVER_MODULE_NAME, + status = gDeviceManager->register_node(node, PCI_LEGACY_DRIVER_MODULE_NAME, legacyAttrs, NULL, &legacy); if (status < B_OK) goto err2; - status = gDeviceManager->register_node(legacy, PCI_LEGACY_DRIVER_MODULE_NAME, + status = gDeviceManager->register_node(legacy, PCI_LEGACY_DRIVER_MODULE_NAME, drvAttrs, NULL, NULL); if (status < B_OK) goto err3; @@ -192,7 +192,7 @@ pci_unreserve_device(uchar virtualBus, uchar device, uchar function, if (gPCI->ResolveVirtualBus(virtualBus, &domain, &bus) != B_OK) return B_ERROR; - //TRACE(("%s(%d [%d:%d], %d, %d, %s, %p)\n", __FUNCTION__, virtualBus, + //TRACE(("%s(%d [%d:%d], %d, %d, %s, %p)\n", __FUNCTION__, virtualBus, // domain, bus, device, function, driverName, nodeCookie)); device_attr matchPCIRoot[] = { @@ -237,7 +237,7 @@ pci_unreserve_device(uchar virtualBus, uchar device, uchar function, node = NULL; if (gDeviceManager->get_next_child_node(pci, matchThis, &node) < B_OK) goto err1; - + // common API for all legacy modules ? //status = legacy_driver_unregister(node, driverName, nodeCookie); @@ -1325,6 +1325,9 @@ PCI::_RefreshDeviceInfo(PCIBus *bus) for (PCIDev *dev = bus->child; dev; dev = dev->next) { _ReadBasicInfo(dev); _ReadHeaderInfo(dev); +#ifdef __INTEL__ + pci_read_arch_info(dev); +#endif if (dev->child) _RefreshDeviceInfo(dev->child); } diff --git a/src/add-ons/kernel/bus_managers/pci/pci.h b/src/add-ons/kernel/bus_managers/pci/pci.h index ddaacb0bd4..e4c66d227d 100644 --- a/src/add-ons/kernel/bus_managers/pci/pci.h +++ b/src/add-ons/kernel/bus_managers/pci/pci.h @@ -15,6 +15,9 @@ #include "pci_controller.h" +#ifdef __INTEL__ +#include "pci_arch_info.h" +#endif #define TRACE_PCI #ifndef TRACE_PCI @@ -23,7 +26,6 @@ # define TRACE(x) dprintf x #endif - #ifdef __cplusplus struct PCIDev; @@ -45,6 +47,9 @@ struct PCIDev { uint8 device; uint8 function; pci_info info; +#ifdef __INTEL__ + pci_arch_info arch_info; +#endif }; diff --git a/src/add-ons/kernel/bus_managers/pci/pci_module.cpp b/src/add-ons/kernel/bus_managers/pci/pci_module.cpp new file mode 100644 index 0000000000..03e983230d --- /dev/null +++ b/src/add-ons/kernel/bus_managers/pci/pci_module.cpp @@ -0,0 +1,96 @@ +/* + * Copyright 2005-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Copyright 2003, Marcus Overhagen. All rights reserved. + * + * Distributed under the terms of the MIT License. + */ + + +#include + +#include "pci_private.h" +#include "pci_info.h" +#include "pci.h" + + +device_manager_info *gDeviceManager; +extern struct pci_arch_module gPCIArchModule; + +static int32 +pci_old_module_std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + { + status_t status; + + TRACE(("PCI: pci_module_init\n")); + + status = pci_init(); + if (status < B_OK) + return status; + + pci_print_info(); + + return B_OK; + } + + case B_MODULE_UNINIT: + TRACE(("PCI: pci_module_uninit\n")); + pci_uninit(); + return B_OK; + } + + return B_BAD_VALUE; +} + + +static struct pci_module_info sOldPCIModule = { + { + { + B_PCI_MODULE_NAME, + B_KEEP_LOADED, + pci_old_module_std_ops + }, + NULL + }, + &pci_read_io_8, + &pci_write_io_8, + &pci_read_io_16, + &pci_write_io_16, + &pci_read_io_32, + &pci_write_io_32, + &pci_get_nth_pci_info, + &pci_read_config, + &pci_write_config, + &pci_ram_address, + &pci_find_capability, + &pci_reserve_device, + &pci_unreserve_device +}; + +module_dependency module_dependencies[] = { + {B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&gDeviceManager}, + {} +}; + +driver_module_info gPCILegacyDriverModule = { + { + PCI_LEGACY_DRIVER_MODULE_NAME, + 0, + NULL, + }, + NULL +}; + +module_info *modules[] = { + (module_info *)&sOldPCIModule, + (module_info *)&gPCIRootModule, + (module_info *)&gPCIDeviceModule, + (module_info *)&gPCILegacyDriverModule, +#ifdef __INTEL__ + // add platforms when they provide an arch specific module + (module_info *)&gPCIArchModule, +#endif + NULL +};