2005-04-17 02:20:36 +04:00
/*
* drivers / pci / rom . c
*
* ( C ) Copyright 2004 Jon Smirl < jonsmirl @ yahoo . com >
* ( C ) Copyright 2004 Silicon Graphics , Inc . Jesse Barnes < jbarnes @ sgi . com >
*
* PCI ROM access routines
*/
# include <linux/kernel.h>
2011-05-27 17:37:25 +04:00
# include <linux/export.h>
2005-04-17 02:20:36 +04:00
# include <linux/pci.h>
2005-10-31 02:03:48 +03:00
# include <linux/slab.h>
2005-04-17 02:20:36 +04:00
# include "pci.h"
/**
* pci_enable_rom - enable ROM decoding for a PCI device
2005-05-01 19:59:26 +04:00
* @ pdev : PCI device to enable
2005-04-17 02:20:36 +04:00
*
* Enable ROM decoding on @ dev . This involves simply turning on the last
* bit of the PCI ROM BAR . Note that some cards may share address decoders
* between the ROM and other resources , so enabling it may disable access
* to MMIO registers or other card memory .
*/
2008-09-23 20:25:10 +04:00
int pci_enable_rom ( struct pci_dev * pdev )
2005-04-17 02:20:36 +04:00
{
2005-08-31 08:16:53 +04:00
struct resource * res = pdev - > resource + PCI_ROM_RESOURCE ;
struct pci_bus_region region ;
2005-04-17 02:20:36 +04:00
u32 rom_addr ;
2005-08-31 08:16:53 +04:00
if ( ! res - > flags )
return - 1 ;
pcibios_resource_to_bus ( pdev , & region , res ) ;
2005-04-17 02:20:36 +04:00
pci_read_config_dword ( pdev , pdev - > rom_base_reg , & rom_addr ) ;
2005-08-31 08:16:53 +04:00
rom_addr & = ~ PCI_ROM_ADDRESS_MASK ;
rom_addr | = region . start | PCI_ROM_ADDRESS_ENABLE ;
2005-04-17 02:20:36 +04:00
pci_write_config_dword ( pdev , pdev - > rom_base_reg , rom_addr ) ;
2005-08-31 08:16:53 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/**
* pci_disable_rom - disable ROM decoding for a PCI device
2005-05-01 19:59:26 +04:00
* @ pdev : PCI device to disable
2005-04-17 02:20:36 +04:00
*
* Disable ROM decoding on a PCI device by turning off the last bit in the
* ROM BAR .
*/
2008-09-23 20:25:10 +04:00
void pci_disable_rom ( struct pci_dev * pdev )
2005-04-17 02:20:36 +04:00
{
u32 rom_addr ;
pci_read_config_dword ( pdev , pdev - > rom_base_reg , & rom_addr ) ;
rom_addr & = ~ PCI_ROM_ADDRESS_ENABLE ;
pci_write_config_dword ( pdev , pdev - > rom_base_reg , rom_addr ) ;
}
2007-07-09 22:42:24 +04:00
/**
* pci_get_rom_size - obtain the actual size of the ROM image
2009-02-09 20:31:20 +03:00
* @ pdev : target PCI device
2007-07-09 22:42:24 +04:00
* @ rom : kernel virtual pointer to image of ROM
* @ size : size of PCI window
* return : size of actual ROM image
*
* Determine the actual length of the ROM image .
* The PCI window size could be much larger than the
* actual image size .
*/
2009-01-29 22:12:47 +03:00
size_t pci_get_rom_size ( struct pci_dev * pdev , void __iomem * rom , size_t size )
2007-07-09 22:42:24 +04:00
{
void __iomem * image ;
int last_image ;
image = rom ;
do {
void __iomem * pds ;
/* Standard PCI ROMs start out with these bytes 55 AA */
2009-01-29 22:12:47 +03:00
if ( readb ( image ) ! = 0x55 ) {
dev_err ( & pdev - > dev , " Invalid ROM contents \n " ) ;
2007-07-09 22:42:24 +04:00
break ;
2009-01-29 22:12:47 +03:00
}
2007-07-09 22:42:24 +04:00
if ( readb ( image + 1 ) ! = 0xAA )
break ;
/* get the PCI data structure and check its signature */
pds = image + readw ( image + 24 ) ;
if ( readb ( pds ) ! = ' P ' )
break ;
if ( readb ( pds + 1 ) ! = ' C ' )
break ;
if ( readb ( pds + 2 ) ! = ' I ' )
break ;
if ( readb ( pds + 3 ) ! = ' R ' )
break ;
last_image = readb ( pds + 21 ) & 0x80 ;
/* this length is reliable */
image + = readw ( pds + 16 ) * 512 ;
} while ( ! last_image ) ;
/* never return a size larger than the PCI resource window */
/* there are known ROMs that get the size wrong */
return min ( ( size_t ) ( image - rom ) , size ) ;
}
2005-04-17 02:20:36 +04:00
/**
* pci_map_rom - map a PCI ROM to kernel space
2005-05-01 19:59:26 +04:00
* @ pdev : pointer to pci device struct
2005-04-17 02:20:36 +04:00
* @ size : pointer to receive size of pci window over ROM
2008-10-30 08:35:12 +03:00
*
* Return : kernel virtual pointer to image of ROM
2005-04-17 02:20:36 +04:00
*
* Map a PCI ROM into kernel space . If ROM is boot video ROM ,
* the shadow BIOS copy will be returned instead of the
* actual ROM .
*/
void __iomem * pci_map_rom ( struct pci_dev * pdev , size_t * size )
{
struct resource * res = & pdev - > resource [ PCI_ROM_RESOURCE ] ;
2013-03-27 01:25:54 +04:00
loff_t start ;
2005-04-17 02:20:36 +04:00
void __iomem * rom ;
2006-09-28 08:55:47 +04:00
/*
2006-10-23 10:14:07 +04:00
* IORESOURCE_ROM_SHADOW set on x86 , x86_64 and IA64 supports legacy
* memory map if the VGA enable bit of the Bridge Control register is
* set for embedded VGA .
2006-09-28 08:55:47 +04:00
*/
2013-03-20 01:26:57 +04:00
if ( res - > flags & IORESOURCE_ROM_SHADOW ) {
2005-04-17 02:20:36 +04:00
/* primary video rom always starts here */
start = ( loff_t ) 0xC0000 ;
* size = 0x20000 ; /* cover C000:0 through E000:0 */
} else {
2006-10-05 01:49:52 +04:00
if ( res - > flags &
( IORESOURCE_ROM_COPY | IORESOURCE_ROM_BIOS_COPY ) ) {
2005-04-17 02:20:36 +04:00
* size = pci_resource_len ( pdev , PCI_ROM_RESOURCE ) ;
2006-06-13 04:06:02 +04:00
return ( void __iomem * ) ( unsigned long )
pci_resource_start ( pdev , PCI_ROM_RESOURCE ) ;
2005-04-17 02:20:36 +04:00
} else {
2013-03-27 01:25:54 +04:00
/* assign the ROM an address if it doesn't have one */
if ( res - > parent = = NULL & &
pci_assign_resource ( pdev , PCI_ROM_RESOURCE ) )
return NULL ;
start = pci_resource_start ( pdev , PCI_ROM_RESOURCE ) ;
* size = pci_resource_len ( pdev , PCI_ROM_RESOURCE ) ;
if ( * size = = 0 )
return NULL ;
2005-04-17 02:20:36 +04:00
2013-03-27 01:25:54 +04:00
/* Enable ROM space decodes */
if ( pci_enable_rom ( pdev ) )
return NULL ;
}
2013-03-20 01:26:57 +04:00
}
2005-04-17 02:20:36 +04:00
rom = ioremap ( start , * size ) ;
if ( ! rom ) {
/* restore enable if ioremap fails */
if ( ! ( res - > flags & ( IORESOURCE_ROM_ENABLE |
IORESOURCE_ROM_SHADOW |
IORESOURCE_ROM_COPY ) ) )
pci_disable_rom ( pdev ) ;
return NULL ;
}
/*
* Try to find the true size of the ROM since sometimes the PCI window
* size is much larger than the actual size of the ROM .
* True size is important if the ROM is going to be copied .
*/
2009-01-29 22:12:47 +03:00
* size = pci_get_rom_size ( pdev , rom , * size ) ;
2005-04-17 02:20:36 +04:00
return rom ;
}
/**
* pci_unmap_rom - unmap the ROM from kernel space
2005-05-01 19:59:26 +04:00
* @ pdev : pointer to pci device struct
2005-04-17 02:20:36 +04:00
* @ rom : virtual address of the previous mapping
*
* Remove a mapping of a previously mapped ROM
*/
void pci_unmap_rom ( struct pci_dev * pdev , void __iomem * rom )
{
struct resource * res = & pdev - > resource [ PCI_ROM_RESOURCE ] ;
2006-10-05 01:49:52 +04:00
if ( res - > flags & ( IORESOURCE_ROM_COPY | IORESOURCE_ROM_BIOS_COPY ) )
2005-04-17 02:20:36 +04:00
return ;
2013-03-27 01:25:54 +04:00
iounmap ( rom ) ;
2005-04-17 02:20:36 +04:00
/* Disable again before continuing, leave enabled if pci=rom */
if ( ! ( res - > flags & ( IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW ) ) )
pci_disable_rom ( pdev ) ;
}
/**
2008-03-01 09:03:15 +03:00
* pci_cleanup_rom - free the ROM copy created by pci_map_rom_copy
2005-05-01 19:59:26 +04:00
* @ pdev : pointer to pci device struct
2005-04-17 02:20:36 +04:00
*
* Free the copied ROM if we allocated one .
*/
void pci_cleanup_rom ( struct pci_dev * pdev )
{
struct resource * res = & pdev - > resource [ PCI_ROM_RESOURCE ] ;
if ( res - > flags & IORESOURCE_ROM_COPY ) {
2006-06-13 04:06:02 +04:00
kfree ( ( void * ) ( unsigned long ) res - > start ) ;
2005-04-17 02:20:36 +04:00
res - > flags & = ~ IORESOURCE_ROM_COPY ;
res - > start = 0 ;
res - > end = 0 ;
}
}
2013-03-27 01:25:54 +04:00
/**
* pci_platform_rom - provides a pointer to any ROM image provided by the
* platform
* @ pdev : pointer to pci device struct
* @ size : pointer to receive size of pci window over ROM
*/
void __iomem * pci_platform_rom ( struct pci_dev * pdev , size_t * size )
{
if ( pdev - > rom & & pdev - > romlen ) {
* size = pdev - > romlen ;
return phys_to_virt ( ( phys_addr_t ) pdev - > rom ) ;
}
return NULL ;
}
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL ( pci_map_rom ) ;
EXPORT_SYMBOL ( pci_unmap_rom ) ;
2008-09-23 20:25:10 +04:00
EXPORT_SYMBOL_GPL ( pci_enable_rom ) ;
EXPORT_SYMBOL_GPL ( pci_disable_rom ) ;
2013-03-27 01:25:54 +04:00
EXPORT_SYMBOL ( pci_platform_rom ) ;