18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PCI ROM access routines 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com> 68c2ecf20Sopenharmony_ci * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/export.h> 108c2ecf20Sopenharmony_ci#include <linux/pci.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "pci.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/** 168c2ecf20Sopenharmony_ci * pci_enable_rom - enable ROM decoding for a PCI device 178c2ecf20Sopenharmony_ci * @pdev: PCI device to enable 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * Enable ROM decoding on @dev. This involves simply turning on the last 208c2ecf20Sopenharmony_ci * bit of the PCI ROM BAR. Note that some cards may share address decoders 218c2ecf20Sopenharmony_ci * between the ROM and other resources, so enabling it may disable access 228c2ecf20Sopenharmony_ci * to MMIO registers or other card memory. 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ciint pci_enable_rom(struct pci_dev *pdev) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci struct resource *res = &pdev->resource[PCI_ROM_RESOURCE]; 278c2ecf20Sopenharmony_ci struct pci_bus_region region; 288c2ecf20Sopenharmony_ci u32 rom_addr; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci if (!res->flags) 318c2ecf20Sopenharmony_ci return -1; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci /* Nothing to enable if we're using a shadow copy in RAM */ 348c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_ROM_SHADOW) 358c2ecf20Sopenharmony_ci return 0; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci /* 388c2ecf20Sopenharmony_ci * Ideally pci_update_resource() would update the ROM BAR address, 398c2ecf20Sopenharmony_ci * and we would only set the enable bit here. But apparently some 408c2ecf20Sopenharmony_ci * devices have buggy ROM BARs that read as zero when disabled. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_ci pcibios_resource_to_bus(pdev->bus, ®ion, res); 438c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr); 448c2ecf20Sopenharmony_ci rom_addr &= ~PCI_ROM_ADDRESS_MASK; 458c2ecf20Sopenharmony_ci rom_addr |= region.start | PCI_ROM_ADDRESS_ENABLE; 468c2ecf20Sopenharmony_ci pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr); 478c2ecf20Sopenharmony_ci return 0; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_enable_rom); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/** 528c2ecf20Sopenharmony_ci * pci_disable_rom - disable ROM decoding for a PCI device 538c2ecf20Sopenharmony_ci * @pdev: PCI device to disable 548c2ecf20Sopenharmony_ci * 558c2ecf20Sopenharmony_ci * Disable ROM decoding on a PCI device by turning off the last bit in the 568c2ecf20Sopenharmony_ci * ROM BAR. 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_civoid pci_disable_rom(struct pci_dev *pdev) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct resource *res = &pdev->resource[PCI_ROM_RESOURCE]; 618c2ecf20Sopenharmony_ci u32 rom_addr; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_ROM_SHADOW) 648c2ecf20Sopenharmony_ci return; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr); 678c2ecf20Sopenharmony_ci rom_addr &= ~PCI_ROM_ADDRESS_ENABLE; 688c2ecf20Sopenharmony_ci pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_disable_rom); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/** 738c2ecf20Sopenharmony_ci * pci_get_rom_size - obtain the actual size of the ROM image 748c2ecf20Sopenharmony_ci * @pdev: target PCI device 758c2ecf20Sopenharmony_ci * @rom: kernel virtual pointer to image of ROM 768c2ecf20Sopenharmony_ci * @size: size of PCI window 778c2ecf20Sopenharmony_ci * return: size of actual ROM image 788c2ecf20Sopenharmony_ci * 798c2ecf20Sopenharmony_ci * Determine the actual length of the ROM image. 808c2ecf20Sopenharmony_ci * The PCI window size could be much larger than the 818c2ecf20Sopenharmony_ci * actual image size. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_cistatic size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, 848c2ecf20Sopenharmony_ci size_t size) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci void __iomem *image; 878c2ecf20Sopenharmony_ci int last_image; 888c2ecf20Sopenharmony_ci unsigned length; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci image = rom; 918c2ecf20Sopenharmony_ci do { 928c2ecf20Sopenharmony_ci void __iomem *pds; 938c2ecf20Sopenharmony_ci /* Standard PCI ROMs start out with these bytes 55 AA */ 948c2ecf20Sopenharmony_ci if (readw(image) != 0xAA55) { 958c2ecf20Sopenharmony_ci pci_info(pdev, "Invalid PCI ROM header signature: expecting 0xaa55, got %#06x\n", 968c2ecf20Sopenharmony_ci readw(image)); 978c2ecf20Sopenharmony_ci break; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci /* get the PCI data structure and check its "PCIR" signature */ 1008c2ecf20Sopenharmony_ci pds = image + readw(image + 24); 1018c2ecf20Sopenharmony_ci if (readl(pds) != 0x52494350) { 1028c2ecf20Sopenharmony_ci pci_info(pdev, "Invalid PCI ROM data signature: expecting 0x52494350, got %#010x\n", 1038c2ecf20Sopenharmony_ci readl(pds)); 1048c2ecf20Sopenharmony_ci break; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci last_image = readb(pds + 21) & 0x80; 1078c2ecf20Sopenharmony_ci length = readw(pds + 16); 1088c2ecf20Sopenharmony_ci image += length * 512; 1098c2ecf20Sopenharmony_ci /* Avoid iterating through memory outside the resource window */ 1108c2ecf20Sopenharmony_ci if (image >= rom + size) 1118c2ecf20Sopenharmony_ci break; 1128c2ecf20Sopenharmony_ci if (!last_image) { 1138c2ecf20Sopenharmony_ci if (readw(image) != 0xAA55) { 1148c2ecf20Sopenharmony_ci pci_info(pdev, "No more image in the PCI ROM\n"); 1158c2ecf20Sopenharmony_ci break; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci } while (length && !last_image); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* never return a size larger than the PCI resource window */ 1218c2ecf20Sopenharmony_ci /* there are known ROMs that get the size wrong */ 1228c2ecf20Sopenharmony_ci return min((size_t)(image - rom), size); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/** 1268c2ecf20Sopenharmony_ci * pci_map_rom - map a PCI ROM to kernel space 1278c2ecf20Sopenharmony_ci * @pdev: pointer to pci device struct 1288c2ecf20Sopenharmony_ci * @size: pointer to receive size of pci window over ROM 1298c2ecf20Sopenharmony_ci * 1308c2ecf20Sopenharmony_ci * Return: kernel virtual pointer to image of ROM 1318c2ecf20Sopenharmony_ci * 1328c2ecf20Sopenharmony_ci * Map a PCI ROM into kernel space. If ROM is boot video ROM, 1338c2ecf20Sopenharmony_ci * the shadow BIOS copy will be returned instead of the 1348c2ecf20Sopenharmony_ci * actual ROM. 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_civoid __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct resource *res = &pdev->resource[PCI_ROM_RESOURCE]; 1398c2ecf20Sopenharmony_ci loff_t start; 1408c2ecf20Sopenharmony_ci void __iomem *rom; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* assign the ROM an address if it doesn't have one */ 1438c2ecf20Sopenharmony_ci if (res->parent == NULL && pci_assign_resource(pdev, PCI_ROM_RESOURCE)) 1448c2ecf20Sopenharmony_ci return NULL; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci start = pci_resource_start(pdev, PCI_ROM_RESOURCE); 1478c2ecf20Sopenharmony_ci *size = pci_resource_len(pdev, PCI_ROM_RESOURCE); 1488c2ecf20Sopenharmony_ci if (*size == 0) 1498c2ecf20Sopenharmony_ci return NULL; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* Enable ROM space decodes */ 1528c2ecf20Sopenharmony_ci if (pci_enable_rom(pdev)) 1538c2ecf20Sopenharmony_ci return NULL; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci rom = ioremap(start, *size); 1568c2ecf20Sopenharmony_ci if (!rom) 1578c2ecf20Sopenharmony_ci goto err_ioremap; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* 1608c2ecf20Sopenharmony_ci * Try to find the true size of the ROM since sometimes the PCI window 1618c2ecf20Sopenharmony_ci * size is much larger than the actual size of the ROM. 1628c2ecf20Sopenharmony_ci * True size is important if the ROM is going to be copied. 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_ci *size = pci_get_rom_size(pdev, rom, *size); 1658c2ecf20Sopenharmony_ci if (!*size) 1668c2ecf20Sopenharmony_ci goto invalid_rom; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return rom; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ciinvalid_rom: 1718c2ecf20Sopenharmony_ci iounmap(rom); 1728c2ecf20Sopenharmony_cierr_ioremap: 1738c2ecf20Sopenharmony_ci /* restore enable if ioremap fails */ 1748c2ecf20Sopenharmony_ci if (!(res->flags & IORESOURCE_ROM_ENABLE)) 1758c2ecf20Sopenharmony_ci pci_disable_rom(pdev); 1768c2ecf20Sopenharmony_ci return NULL; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pci_map_rom); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/** 1818c2ecf20Sopenharmony_ci * pci_unmap_rom - unmap the ROM from kernel space 1828c2ecf20Sopenharmony_ci * @pdev: pointer to pci device struct 1838c2ecf20Sopenharmony_ci * @rom: virtual address of the previous mapping 1848c2ecf20Sopenharmony_ci * 1858c2ecf20Sopenharmony_ci * Remove a mapping of a previously mapped ROM 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_civoid pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct resource *res = &pdev->resource[PCI_ROM_RESOURCE]; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci iounmap(rom); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* Disable again before continuing */ 1948c2ecf20Sopenharmony_ci if (!(res->flags & IORESOURCE_ROM_ENABLE)) 1958c2ecf20Sopenharmony_ci pci_disable_rom(pdev); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pci_unmap_rom); 198