/** * Write to PCI configuration space * * @v pci PCI device * @v location Encoded offset and width * @v value Value * @ret rc Return status code */ int efipci_write ( struct pci_device *pci, unsigned long location, unsigned long value ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root; EFI_HANDLE handle; EFI_STATUS efirc; int rc; /* Identify root bridge */ if ( ( rc = efipci_root ( pci, &handle, &root ) ) != 0 ) goto err_root; /* Read from configuration space */ if ( ( efirc = root->Pci.Write ( root, EFIPCI_WIDTH ( location ), efipci_address ( pci, location ), 1, &value ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( pci, "EFIPCI " PCI_FMT " config write to offset %02lx " "failed: %s\n", PCI_ARGS ( pci ), EFIPCI_OFFSET ( location ), strerror ( rc ) ); goto err_write; } err_write: bs->CloseProtocol ( handle, &efi_pci_root_bridge_io_protocol_guid, efi_image_handle, handle ); err_root: return rc; }
/** * Read from PCI configuration space * * @v pci PCI device * @v where Address within configuration space * @v value Data buffer * @v len Length to read * @ret rc Return status code */ int linux_pci_read ( struct pci_device *pci, unsigned long where, unsigned long *value, size_t len ) { uint32_t tmp = 0; int fd; int check_len; int rc; /* Return "missing device" in case of error */ *value = -1UL; /* Open configuration space */ fd = linux_pci_open ( pci, O_RDONLY, where ); if ( fd < 0 ) { rc = fd; goto err_open; } /* Read value */ check_len = linux_read ( fd, &tmp, len ); if ( check_len < 0 ) { DBGC ( pci, "PCI could not read from " PCI_FMT " %#02lx+%#zx: " "%s\n", PCI_ARGS ( pci ), where, len, linux_strerror ( linux_errno ) ); rc = -ELINUX ( linux_errno ); goto err_read; } if ( ( size_t ) check_len != len ) { DBGC ( pci, "PCI read only %#x bytes from " PCI_FMT " %#02lx+%#zx\n", check_len, PCI_ARGS ( pci ), where, len ); rc = -EIO; goto err_read; } /* Return value */ *value = le32_to_cpu ( tmp ); /* Success */ rc = 0; err_read: linux_close ( fd ); err_open: return rc; }
/** * Write to PCI configuration space * * @v pci PCI device * @v where Address within configuration space * @v value Value to write * @v len Length of value * @ret rc Return status code */ int linux_pci_write ( struct pci_device *pci, unsigned long where, unsigned long value, size_t len ) { uint32_t tmp; int fd; int check_len; int rc; /* Open configuration space */ fd = linux_pci_open ( pci, O_WRONLY, where ); if ( fd < 0 ) { rc = fd; goto err_open; } /* Prepare value for writing */ tmp = cpu_to_le32 ( value ); assert ( len <= sizeof ( tmp ) ); /* Write value */ check_len = linux_write ( fd, &tmp, len ); if ( check_len < 0 ) { DBGC ( pci, "PCI could not write to " PCI_FMT " %#02lx+%#zx: " "%s\n", PCI_ARGS ( pci ), where, len, linux_strerror ( linux_errno ) ); rc = -ELINUX ( linux_errno ); goto err_write; } if ( ( size_t ) check_len != len ) { DBGC ( pci, "PCI wrote only %#x bytes to " PCI_FMT " %#02lx+%#zx\n", check_len, PCI_ARGS ( pci ), where, len ); rc = -EIO; goto err_write; } /* Success */ rc = 0; err_write: linux_close ( fd ); err_open: return rc; }
/** * Check to see if driver supports a device * * @v device EFI device handle * @ret rc Return status code */ static int efipci_supported ( EFI_HANDLE device ) { struct pci_device pci; int rc; /* Get PCI device information */ if ( ( rc = efipci_info ( device, &pci ) ) != 0 ) return rc; /* Look for a driver */ if ( ( rc = pci_find_driver ( &pci ) ) != 0 ) { DBGC ( device, "EFIPCI " PCI_FMT " (%04x:%04x class %06x) " "has no driver\n", PCI_ARGS ( &pci ), pci.vendor, pci.device, pci.class ); return rc; }
int efipci_write ( struct pci_device *pci, unsigned long location, unsigned long value ) { EFI_STATUS efirc; int rc; if ( ( efirc = efipci->Pci.Write ( efipci, EFIPCI_WIDTH ( location ), efipci_address ( pci, location ), 1, &value ) ) != 0 ) { rc = -EEFI ( efirc ); DBG ( "EFIPCI config write to " PCI_FMT " offset %02lx " "failed: %s\n", PCI_ARGS ( pci ), EFIPCI_OFFSET ( location ), strerror ( rc ) ); return -EIO; } return 0; }
/** * Open EFI PCI device * * @v device EFI device handle * @v attributes Protocol opening attributes * @v pci PCI device to fill in * @ret rc Return status code */ int efipci_open ( EFI_HANDLE device, UINT32 attributes, struct pci_device *pci ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; union { EFI_PCI_IO_PROTOCOL *pci_io; void *interface; } pci_io; UINTN pci_segment, pci_bus, pci_dev, pci_fn; unsigned int busdevfn; EFI_STATUS efirc; int rc; /* See if device is a PCI device */ if ( ( efirc = bs->OpenProtocol ( device, &efi_pci_io_protocol_guid, &pci_io.interface, efi_image_handle, device, attributes ) ) != 0 ) { rc = -EEFI_PCI ( efirc ); DBGCP ( device, "EFIPCI %s cannot open PCI protocols: %s\n", efi_handle_name ( device ), strerror ( rc ) ); goto err_open_protocol; } /* Get PCI bus:dev.fn address */ if ( ( efirc = pci_io.pci_io->GetLocation ( pci_io.pci_io, &pci_segment, &pci_bus, &pci_dev, &pci_fn ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( device, "EFIPCI %s could not get PCI location: %s\n", efi_handle_name ( device ), strerror ( rc ) ); goto err_get_location; } busdevfn = PCI_BUSDEVFN ( pci_segment, pci_bus, pci_dev, pci_fn ); pci_init ( pci, busdevfn ); DBGCP ( device, "EFIPCI " PCI_FMT " is %s\n", PCI_ARGS ( pci ), efi_handle_name ( device ) ); /* Try to enable I/O cycles, memory cycles, and bus mastering. * Some platforms will 'helpfully' report errors if these bits * can't be enabled (for example, if the card doesn't actually * support I/O cycles). Work around any such platforms by * enabling bits individually and simply ignoring any errors. */ pci_io.pci_io->Attributes ( pci_io.pci_io, EfiPciIoAttributeOperationEnable, EFI_PCI_IO_ATTRIBUTE_IO, NULL ); pci_io.pci_io->Attributes ( pci_io.pci_io, EfiPciIoAttributeOperationEnable, EFI_PCI_IO_ATTRIBUTE_MEMORY, NULL ); pci_io.pci_io->Attributes ( pci_io.pci_io, EfiPciIoAttributeOperationEnable, EFI_PCI_IO_ATTRIBUTE_BUS_MASTER, NULL ); /* Populate PCI device */ if ( ( rc = pci_read_config ( pci ) ) != 0 ) { DBGC ( device, "EFIPCI " PCI_FMT " cannot read PCI " "configuration: %s\n", PCI_ARGS ( pci ), strerror ( rc ) ); goto err_pci_read_config; } return 0; err_pci_read_config: err_get_location: bs->CloseProtocol ( device, &efi_pci_io_protocol_guid, efi_image_handle, device ); err_open_protocol: return rc; }
struct pci_device pci; int rc; /* Get PCI device information */ if ( ( rc = efipci_info ( device, &pci ) ) != 0 ) return rc; /* Look for a driver */ if ( ( rc = pci_find_driver ( &pci ) ) != 0 ) { DBGC ( device, "EFIPCI " PCI_FMT " (%04x:%04x class %06x) " "has no driver\n", PCI_ARGS ( &pci ), pci.vendor, pci.device, pci.class ); return rc; } DBGC ( device, "EFIPCI " PCI_FMT " (%04x:%04x class %06x) has driver " "\"%s\"\n", PCI_ARGS ( &pci ), pci.vendor, pci.device, pci.class, pci.id->name ); return 0; } /** * Attach driver to device * * @v efidev EFI device * @ret rc Return status code */ static int efipci_start ( struct efi_device *efidev ) { EFI_HANDLE device = efidev->device; struct pci_device *pci; int rc;
/** * Locate EFI PCI root bridge I/O protocol * * @v pci PCI device * @ret handle EFI PCI root bridge handle * @ret root EFI PCI root bridge I/O protocol, or NULL if not found * @ret rc Return status code */ static int efipci_root ( struct pci_device *pci, EFI_HANDLE *handle, EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL **root ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_HANDLE *handles; UINTN num_handles; union { void *interface; EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root; } u; EFI_STATUS efirc; UINTN i; int rc; /* Enumerate all handles */ if ( ( efirc = bs->LocateHandleBuffer ( ByProtocol, &efi_pci_root_bridge_io_protocol_guid, NULL, &num_handles, &handles ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( pci, "EFIPCI " PCI_FMT " cannot locate root bridges: " "%s\n", PCI_ARGS ( pci ), strerror ( rc ) ); goto err_locate; } /* Look for matching root bridge I/O protocol */ for ( i = 0 ; i < num_handles ; i++ ) { *handle = handles[i]; if ( ( efirc = bs->OpenProtocol ( *handle, &efi_pci_root_bridge_io_protocol_guid, &u.interface, efi_image_handle, *handle, EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( pci, "EFIPCI " PCI_FMT " cannot open %s: %s\n", PCI_ARGS ( pci ), efi_handle_name ( *handle ), strerror ( rc ) ); continue; } if ( u.root->SegmentNumber == PCI_SEG ( pci->busdevfn ) ) { *root = u.root; bs->FreePool ( handles ); return 0; } bs->CloseProtocol ( *handle, &efi_pci_root_bridge_io_protocol_guid, efi_image_handle, *handle ); } DBGC ( pci, "EFIPCI " PCI_FMT " found no root bridge\n", PCI_ARGS ( pci ) ); rc = -ENOENT; bs->FreePool ( handles ); err_locate: return rc; }