rom.c 6.5 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11
/*
 * 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/config.h>
#include <linux/kernel.h>
#include <linux/pci.h>
T
Tim Schmielau 已提交
12
#include <linux/slab.h>
L
Linus Torvalds 已提交
13 14 15 16 17

#include "pci.h"

/**
 * pci_enable_rom - enable ROM decoding for a PCI device
18
 * @pdev: PCI device to enable
L
Linus Torvalds 已提交
19 20 21 22 23 24
 *
 * 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.
 */
25
static int pci_enable_rom(struct pci_dev *pdev)
L
Linus Torvalds 已提交
26
{
27 28
	struct resource *res = pdev->resource + PCI_ROM_RESOURCE;
	struct pci_bus_region region;
L
Linus Torvalds 已提交
29 30
	u32 rom_addr;

31 32 33 34
	if (!res->flags)
		return -1;

	pcibios_resource_to_bus(pdev, &region, res);
L
Linus Torvalds 已提交
35
	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
36 37
	rom_addr &= ~PCI_ROM_ADDRESS_MASK;
	rom_addr |= region.start | PCI_ROM_ADDRESS_ENABLE;
L
Linus Torvalds 已提交
38
	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
39
	return 0;
L
Linus Torvalds 已提交
40 41 42 43
}

/**
 * pci_disable_rom - disable ROM decoding for a PCI device
44
 * @pdev: PCI device to disable
L
Linus Torvalds 已提交
45 46 47 48 49 50 51 52 53 54 55 56 57 58
 *
 * Disable ROM decoding on a PCI device by turning off the last bit in the
 * ROM BAR.
 */
static void pci_disable_rom(struct pci_dev *pdev)
{
	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);
}

/**
 * pci_map_rom - map a PCI ROM to kernel space
59
 * @pdev: pointer to pci device struct
L
Linus Torvalds 已提交
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
 * @size: pointer to receive size of pci window over ROM
 * @return: kernel virtual pointer to image of ROM
 *
 * 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];
	loff_t start;
	void __iomem *rom;
	void __iomem *image;
	int last_image;

	/* IORESOURCE_ROM_SHADOW only set on x86 */
	if (res->flags & IORESOURCE_ROM_SHADOW) {
		/* primary video rom always starts here */
		start = (loff_t)0xC0000;
		*size = 0x20000; /* cover C000:0 through E000:0 */
	} else {
		if (res->flags & IORESOURCE_ROM_COPY) {
			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
83 84
			return (void __iomem *)(unsigned long)
				pci_resource_start(pdev, PCI_ROM_RESOURCE);
L
Linus Torvalds 已提交
85 86
		} else {
			/* assign the ROM an address if it doesn't have one */
87 88 89
			if (res->parent == NULL &&
			    pci_assign_resource(pdev,PCI_ROM_RESOURCE))
				return NULL;
L
Linus Torvalds 已提交
90 91 92 93 94 95
			start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
			if (*size == 0)
				return NULL;

			/* Enable ROM space decodes */
96 97
			if (pci_enable_rom(pdev))
				return NULL;
L
Linus Torvalds 已提交
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
		}
	}

	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.
	 */
	image = rom;
	do {
		void __iomem *pds;
		/* Standard PCI ROMs start out with these bytes 55 AA */
		if (readb(image) != 0x55)
			break;
		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);

139 140 141
	/* never return a size larger than the PCI resource window */
	/* there are known ROMs that get the size wrong */
	*size = min((size_t)(image - rom), *size);
L
Linus Torvalds 已提交
142 143 144 145 146 147

	return rom;
}

/**
 * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy
148
 * @pdev: pointer to pci device struct
L
Linus Torvalds 已提交
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
 * @size: pointer to receive size of pci window over ROM
 * @return: kernel virtual pointer to image of ROM
 *
 * 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_copy(struct pci_dev *pdev, size_t *size)
{
	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
	void __iomem *rom;

	rom = pci_map_rom(pdev, size);
	if (!rom)
		return NULL;

	if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW))
		return rom;

	res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
	if (!res->start)
		return rom;

	res->end = res->start + *size;
173
	memcpy_fromio((void*)(unsigned long)res->start, rom, *size);
L
Linus Torvalds 已提交
174 175 176
	pci_unmap_rom(pdev, rom);
	res->flags |= IORESOURCE_ROM_COPY;

177
	return (void __iomem *)(unsigned long)res->start;
L
Linus Torvalds 已提交
178 179 180 181
}

/**
 * pci_unmap_rom - unmap the ROM from kernel space
182
 * @pdev: pointer to pci device struct
L
Linus Torvalds 已提交
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
 * @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];

	if (res->flags & IORESOURCE_ROM_COPY)
		return;

	iounmap(rom);

	/* Disable again before continuing, leave enabled if pci=rom */
	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW)))
		pci_disable_rom(pdev);
}

/**
 * pci_remove_rom - disable the ROM and remove its sysfs attribute
203
 * @pdev: pointer to pci device struct
L
Linus Torvalds 已提交
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
 *
 * Remove the rom file in sysfs and disable ROM decoding.
 */
void pci_remove_rom(struct pci_dev *pdev)
{
	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];

	if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
		sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
	if (!(res->flags & (IORESOURCE_ROM_ENABLE |
			    IORESOURCE_ROM_SHADOW |
			    IORESOURCE_ROM_COPY)))
		pci_disable_rom(pdev);
}

/**
 * pci_cleanup_rom - internal routine for freeing the ROM copy created
 * by pci_map_rom_copy called from remove.c
222
 * @pdev: pointer to pci device struct
L
Linus Torvalds 已提交
223 224 225 226 227 228 229
 *
 * 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) {
230
		kfree((void*)(unsigned long)res->start);
L
Linus Torvalds 已提交
231 232 233 234 235 236 237 238 239 240
		res->flags &= ~IORESOURCE_ROM_COPY;
		res->start = 0;
		res->end = 0;
	}
}

EXPORT_SYMBOL(pci_map_rom);
EXPORT_SYMBOL(pci_map_rom_copy);
EXPORT_SYMBOL(pci_unmap_rom);
EXPORT_SYMBOL(pci_remove_rom);