 40bfed28cd
			
		
	
	
		40bfed28cd
		
	
	
	
	
		
			
			- every pci device which implements _PRT acpi method is considered to be a pci-to-pci bridge - acpi driver constructs a hierarchy of pci-to-pci bridges - when pci driver identifies a pci-to-pci bridge it tells acpi driver what is the primary and the secondary bus for this device - when pci requests IRQ routing information from acpi, it passes the bus number too to be able to identify the device accurately
		
			
				
	
	
		
			315 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			315 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include <minix/driver.h>
 | |
| #include <acpi.h>
 | |
| #include <assert.h>
 | |
| #include <minix/acpi.h>
 | |
| 
 | |
| #include "acpi_globals.h"
 | |
| 
 | |
| #define PCI_MAX_DEVICES	32
 | |
| #define PCI_MAX_PINS	4
 | |
| 
 | |
| #define IRQ_TABLE_ENTRIES	(PCI_MAX_DEVICES * PCI_MAX_PINS)
 | |
| 
 | |
| struct pci_bridge {
 | |
| 	ACPI_HANDLE		handle;
 | |
| 	int			irqtable[IRQ_TABLE_ENTRIES];
 | |
| 	int			primary_bus;
 | |
| 	int			secondary_bus;
 | |
| 	unsigned		device;
 | |
| 	struct pci_bridge 	* parent;
 | |
| 	struct pci_bridge	* children[PCI_MAX_DEVICES];
 | |
| };
 | |
| 
 | |
| PRIVATE struct pci_bridge pci_root_bridge;
 | |
| 
 | |
| struct irq_resource {
 | |
| 	struct pci_bridge	* bridge;
 | |
| 	ACPI_PCI_ROUTING_TABLE	* tbl;
 | |
| };
 | |
| 
 | |
| PRIVATE struct pci_bridge * find_bridge(struct pci_bridge * root,
 | |
| 					int pbnr,
 | |
| 					int dev,
 | |
| 					int sbnr)
 | |
| {
 | |
| 	if (!root)
 | |
| 		return NULL;
 | |
| 
 | |
| 	if (sbnr == -1) {
 | |
| 		if (root->secondary_bus == pbnr)
 | |
| 			return root->children[dev];
 | |
| 		else {
 | |
| 			/* serach all children */
 | |
| 			unsigned d;
 | |
| 			for (d = 0; d < PCI_MAX_DEVICES; d++) {
 | |
| 				struct pci_bridge * b;
 | |
| 				b = find_bridge(root->children[d],
 | |
| 						pbnr, dev, sbnr);
 | |
| 				if (b)
 | |
| 					return b;
 | |
| 			}
 | |
| 		}
 | |
| 	} else {
 | |
| 		if (root->secondary_bus == sbnr)
 | |
| 			return root;
 | |
| 		else {
 | |
| 			/* check all children */
 | |
| 			unsigned d;
 | |
| 			for (d = 0; d < PCI_MAX_DEVICES; d++) {
 | |
| 				struct pci_bridge * b;
 | |
| 				b = find_bridge(root->children[d],
 | |
| 						pbnr, dev, sbnr);
 | |
| 				if (b)
 | |
| 					return b;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| PUBLIC void do_map_bridge(message *m)
 | |
| {
 | |
| 	int err = OK;
 | |
| 	unsigned dev = ((struct acpi_map_bridge_req *)m)->device;
 | |
| 	unsigned pbnr = ((struct acpi_map_bridge_req *)m)->primary_bus;
 | |
| 	unsigned sbnr = ((struct acpi_map_bridge_req *)m)->secondary_bus;
 | |
| 
 | |
| 	struct pci_bridge * bridge;
 | |
| 
 | |
| 	bridge = find_bridge(&pci_root_bridge, pbnr, dev, -1);
 | |
| 
 | |
| 	if (!bridge) {
 | |
| 		err = ENODEV;
 | |
| 		goto map_error;
 | |
| 	}
 | |
| 
 | |
| 	bridge->primary_bus = pbnr;
 | |
| 	bridge->secondary_bus = sbnr;
 | |
| 
 | |
| map_error:
 | |
| 	((struct acpi_map_bridge_resp *)m)->err = err;
 | |
| }
 | |
| 
 | |
| PRIVATE ACPI_STATUS device_get_int(ACPI_HANDLE handle,
 | |
| 				char * name,
 | |
| 				ACPI_INTEGER * val)
 | |
| {
 | |
| 	ACPI_STATUS status;
 | |
| 	char buff[sizeof(ACPI_OBJECT)];
 | |
| 	ACPI_BUFFER abuff;
 | |
| 
 | |
| 	abuff.Length = sizeof(buff);
 | |
| 	abuff.Pointer = buff;
 | |
| 
 | |
| 	status =  AcpiEvaluateObjectTyped(handle, name, NULL,
 | |
| 			&abuff, ACPI_TYPE_INTEGER);
 | |
| 	if (ACPI_SUCCESS(status)) {
 | |
| 		*val = ((ACPI_OBJECT *)abuff.Pointer)->Integer.Value;
 | |
| 	}
 | |
| 
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| PUBLIC void do_get_irq(message *m)
 | |
| {
 | |
| 	struct pci_bridge * bridge;
 | |
| 	int irq;
 | |
| 
 | |
| 	unsigned bus = ((struct acpi_get_irq_req *)m)->bus;
 | |
| 	unsigned dev = ((struct acpi_get_irq_req *)m)->dev;
 | |
| 	unsigned pin = ((struct acpi_get_irq_req *)m)->pin;
 | |
| 
 | |
| 	assert(dev < PCI_MAX_DEVICES && pin < PCI_MAX_PINS);
 | |
| 
 | |
| 	bridge = find_bridge(&pci_root_bridge, -1, -1, bus);
 | |
| 
 | |
| 	if (!bridge)
 | |
| 		irq = -1;
 | |
| 	else
 | |
| 		irq = bridge->irqtable[dev * PCI_MAX_PINS + pin];
 | |
| 
 | |
| 	((struct acpi_get_irq_resp *)m)->irq = irq;
 | |
| }
 | |
| 
 | |
| PRIVATE void add_irq(struct pci_bridge * bridge,
 | |
| 			unsigned dev,
 | |
| 			unsigned pin,
 | |
| 			u8_t irq)
 | |
| {
 | |
| 	assert(dev < PCI_MAX_DEVICES && pin < PCI_MAX_PINS);
 | |
| 
 | |
| 	bridge->irqtable[dev * PCI_MAX_PINS + pin] = irq;
 | |
| }
 | |
| 
 | |
| PRIVATE ACPI_STATUS get_irq_resource(ACPI_RESOURCE *res, void *context)
 | |
| {
 | |
| 	struct irq_resource * ires = (struct irq_resource *) context;
 | |
| 
 | |
| 	if (res->Type == ACPI_RESOURCE_TYPE_IRQ) {
 | |
| 		ACPI_RESOURCE_IRQ *irq;
 | |
| 
 | |
| 		irq = &res->Data.Irq;
 | |
| 		add_irq(ires->bridge, ires->tbl->Address >> 16, ires->tbl->Pin,
 | |
| 				irq->Interrupts[ires->tbl->SourceIndex]);
 | |
| 	} else if (res->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
 | |
| 		ACPI_RESOURCE_EXTENDED_IRQ *irq;
 | |
| 
 | |
| 		add_irq(ires->bridge, ires->tbl->Address >> 16, ires->tbl->Pin,
 | |
| 				irq->Interrupts[ires->tbl->SourceIndex]);
 | |
| 	}
 | |
| 
 | |
| 	return AE_OK;
 | |
| }
 | |
| 
 | |
| PRIVATE ACPI_STATUS get_pci_irq_routing(struct pci_bridge * bridge)
 | |
| {
 | |
| 	ACPI_STATUS status;
 | |
| 	ACPI_BUFFER abuff;
 | |
| 	char buff[4096];
 | |
| 	ACPI_PCI_ROUTING_TABLE *tbl;
 | |
| 	ACPI_DEVICE_INFO *info;
 | |
| 	int i;
 | |
| 
 | |
| 	abuff.Length = sizeof(buff);
 | |
| 	abuff.Pointer = buff;
 | |
| 
 | |
| 	status = AcpiGetIrqRoutingTable(bridge->handle, &abuff);
 | |
| 	if (ACPI_FAILURE(status)) {
 | |
| 		return AE_OK;
 | |
| 	}
 | |
| 
 | |
| 	info = abuff.Pointer;
 | |
| 	status = AcpiGetObjectInfo(bridge->handle, &info);
 | |
| 	if (ACPI_FAILURE(status))
 | |
| 		return status;
 | |
| 	/*
 | |
| 	 * Decode the device number (upper half of the address) and attach the
 | |
| 	 * new bridge in the children list of its parent
 | |
| 	 */
 | |
| 	bridge->device = info->Address >> 16;
 | |
| 	if (bridge != &pci_root_bridge) {
 | |
| 		bridge->parent->children[bridge->device] = bridge;
 | |
| 		bridge->primary_bus = bridge->secondary_bus = -1;
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	for (i = 0; i < PCI_MAX_DEVICES; i++)
 | |
| 		bridge->children[i] = NULL;
 | |
| 	for (i = 0; i < IRQ_TABLE_ENTRIES; i++)
 | |
| 		bridge->irqtable[i] = -1;
 | |
| 
 | |
| 	for (tbl = (ACPI_PCI_ROUTING_TABLE *)abuff.Pointer; tbl->Length;
 | |
| 			tbl = (ACPI_PCI_ROUTING_TABLE *)
 | |
| 			((char *)tbl + tbl->Length)) {
 | |
| 		ACPI_HANDLE src_handle;
 | |
| 		struct irq_resource ires;
 | |
| 
 | |
| 		if (*(char*)tbl->Source == '\0') {
 | |
| 			add_irq(bridge, tbl->Address >> 16,
 | |
| 					tbl->Pin, tbl->SourceIndex);
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		status = AcpiGetHandle(bridge->handle, tbl->Source, &src_handle);
 | |
| 		if (ACPI_FAILURE(status)) {
 | |
| 			printf("Failed AcpiGetHandle\n");
 | |
| 			continue;
 | |
| 		}
 | |
| 		ires.bridge = bridge;
 | |
| 		ires,tbl = tbl;
 | |
| 		status = AcpiWalkResources(src_handle, METHOD_NAME__CRS,
 | |
| 				get_irq_resource, &ires);
 | |
| 		if (ACPI_FAILURE(status)) {
 | |
| 			printf("Failed IRQ resource\n");
 | |
| 			continue;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return AE_OK;
 | |
| }
 | |
| 
 | |
| PRIVATE ACPI_STATUS add_pci_dev(ACPI_HANDLE handle,
 | |
| 				UINT32 level,
 | |
| 				void *context,
 | |
| 				void **retval)
 | |
| {
 | |
| 	ACPI_STATUS status;
 | |
| 	ACPI_BUFFER abuff;
 | |
| 	char buff[4096];
 | |
| 	ACPI_HANDLE parent_handle;
 | |
| 	struct pci_bridge * bridge;
 | |
| 	struct pci_bridge * parent_bridge = (struct pci_bridge *) context;
 | |
| 
 | |
| 
 | |
| 	/* skip pci root when we get to it again */
 | |
| 	if (handle == pci_root_bridge.handle)
 | |
| 		return AE_OK;
 | |
| 
 | |
| 	status = AcpiGetParent(handle, &parent_handle);
 | |
| 	if (!ACPI_SUCCESS(status))
 | |
| 		return status;
 | |
| 	/* skip devices that have a different parent */
 | |
| 	if (parent_handle != parent_bridge->handle)
 | |
| 		return AE_OK;
 | |
| 
 | |
| 	abuff.Length = sizeof(buff);
 | |
| 	abuff.Pointer = buff;
 | |
| 
 | |
| 	bridge = malloc(sizeof(struct pci_bridge));
 | |
| 	if (!bridge)
 | |
| 		return AE_NO_MEMORY;
 | |
| 	bridge->handle = handle;
 | |
| 	bridge->parent = parent_bridge;
 | |
| 
 | |
| 	status =  get_pci_irq_routing(bridge);
 | |
| 	if (!(ACPI_SUCCESS(status))) {
 | |
| 		free(bridge);
 | |
| 		return status;
 | |
| 	}
 | |
| 
 | |
| 	/* get the pci bridges */
 | |
| 	status = AcpiGetDevices(NULL, add_pci_dev, bridge, NULL);
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| PRIVATE ACPI_STATUS add_pci_root_dev(ACPI_HANDLE handle,
 | |
| 				UINT32 level,
 | |
| 				void *context,
 | |
| 				void **retval)
 | |
| {
 | |
| 	static unsigned called;
 | |
| 	ACPI_STATUS status;
 | |
| 
 | |
| 	if (++called > 1) {
 | |
| 		printf("ACPI: Warning! Multi rooted PCI is not supported!\n");
 | |
| 		return AE_OK;
 | |
| 	}
 | |
| 
 | |
| 	pci_root_bridge.handle = handle;
 | |
| 	pci_root_bridge.primary_bus = -1; /* undefined */
 | |
| 	pci_root_bridge.secondary_bus = 0; /* root bus is 0 in a single root
 | |
| 					      system */
 | |
| 
 | |
| 	status = get_pci_irq_routing(&pci_root_bridge);
 | |
| 	if (!ACPI_SUCCESS(status))
 | |
| 		return status;
 | |
| 
 | |
| 	/* get the pci bridges */
 | |
| 	status = AcpiGetDevices(NULL, add_pci_dev, &pci_root_bridge, NULL);
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| PUBLIC void pci_scan_devices(void)
 | |
| {
 | |
| 	ACPI_STATUS status;
 | |
| 
 | |
| 	/* do not scan devices in PIC mode */
 | |
| 	if (!machine.apic_enabled)
 | |
| 		return;
 | |
| 
 | |
| 	/* get the root first */
 | |
| 	status = AcpiGetDevices("PNP0A03", add_pci_root_dev, NULL, NULL);
 | |
| 	assert(ACPI_SUCCESS(status));
 | |
| }
 |