xref: /linux/drivers/pci/rom.c (revision 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2)
1*1da177e4SLinus Torvalds /*
2*1da177e4SLinus Torvalds  * drivers/pci/rom.c
3*1da177e4SLinus Torvalds  *
4*1da177e4SLinus Torvalds  * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
5*1da177e4SLinus Torvalds  * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
6*1da177e4SLinus Torvalds  *
7*1da177e4SLinus Torvalds  * PCI ROM access routines
8*1da177e4SLinus Torvalds  */
9*1da177e4SLinus Torvalds #include <linux/config.h>
10*1da177e4SLinus Torvalds #include <linux/kernel.h>
11*1da177e4SLinus Torvalds #include <linux/pci.h>
12*1da177e4SLinus Torvalds 
13*1da177e4SLinus Torvalds #include "pci.h"
14*1da177e4SLinus Torvalds 
15*1da177e4SLinus Torvalds /**
16*1da177e4SLinus Torvalds  * pci_enable_rom - enable ROM decoding for a PCI device
17*1da177e4SLinus Torvalds  * @dev: PCI device to enable
18*1da177e4SLinus Torvalds  *
19*1da177e4SLinus Torvalds  * Enable ROM decoding on @dev.  This involves simply turning on the last
20*1da177e4SLinus Torvalds  * bit of the PCI ROM BAR.  Note that some cards may share address decoders
21*1da177e4SLinus Torvalds  * between the ROM and other resources, so enabling it may disable access
22*1da177e4SLinus Torvalds  * to MMIO registers or other card memory.
23*1da177e4SLinus Torvalds  */
24*1da177e4SLinus Torvalds static void pci_enable_rom(struct pci_dev *pdev)
25*1da177e4SLinus Torvalds {
26*1da177e4SLinus Torvalds 	u32 rom_addr;
27*1da177e4SLinus Torvalds 
28*1da177e4SLinus Torvalds 	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
29*1da177e4SLinus Torvalds 	rom_addr |= PCI_ROM_ADDRESS_ENABLE;
30*1da177e4SLinus Torvalds 	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
31*1da177e4SLinus Torvalds }
32*1da177e4SLinus Torvalds 
33*1da177e4SLinus Torvalds /**
34*1da177e4SLinus Torvalds  * pci_disable_rom - disable ROM decoding for a PCI device
35*1da177e4SLinus Torvalds  * @dev: PCI device to disable
36*1da177e4SLinus Torvalds  *
37*1da177e4SLinus Torvalds  * Disable ROM decoding on a PCI device by turning off the last bit in the
38*1da177e4SLinus Torvalds  * ROM BAR.
39*1da177e4SLinus Torvalds  */
40*1da177e4SLinus Torvalds static void pci_disable_rom(struct pci_dev *pdev)
41*1da177e4SLinus Torvalds {
42*1da177e4SLinus Torvalds 	u32 rom_addr;
43*1da177e4SLinus Torvalds 	pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
44*1da177e4SLinus Torvalds 	rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
45*1da177e4SLinus Torvalds 	pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
46*1da177e4SLinus Torvalds }
47*1da177e4SLinus Torvalds 
48*1da177e4SLinus Torvalds /**
49*1da177e4SLinus Torvalds  * pci_map_rom - map a PCI ROM to kernel space
50*1da177e4SLinus Torvalds  * @dev: pointer to pci device struct
51*1da177e4SLinus Torvalds  * @size: pointer to receive size of pci window over ROM
52*1da177e4SLinus Torvalds  * @return: kernel virtual pointer to image of ROM
53*1da177e4SLinus Torvalds  *
54*1da177e4SLinus Torvalds  * Map a PCI ROM into kernel space. If ROM is boot video ROM,
55*1da177e4SLinus Torvalds  * the shadow BIOS copy will be returned instead of the
56*1da177e4SLinus Torvalds  * actual ROM.
57*1da177e4SLinus Torvalds  */
58*1da177e4SLinus Torvalds void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
59*1da177e4SLinus Torvalds {
60*1da177e4SLinus Torvalds 	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
61*1da177e4SLinus Torvalds 	loff_t start;
62*1da177e4SLinus Torvalds 	void __iomem *rom;
63*1da177e4SLinus Torvalds 	void __iomem *image;
64*1da177e4SLinus Torvalds 	int last_image;
65*1da177e4SLinus Torvalds 
66*1da177e4SLinus Torvalds 	/* IORESOURCE_ROM_SHADOW only set on x86 */
67*1da177e4SLinus Torvalds 	if (res->flags & IORESOURCE_ROM_SHADOW) {
68*1da177e4SLinus Torvalds 		/* primary video rom always starts here */
69*1da177e4SLinus Torvalds 		start = (loff_t)0xC0000;
70*1da177e4SLinus Torvalds 		*size = 0x20000; /* cover C000:0 through E000:0 */
71*1da177e4SLinus Torvalds 	} else {
72*1da177e4SLinus Torvalds 		if (res->flags & IORESOURCE_ROM_COPY) {
73*1da177e4SLinus Torvalds 			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
74*1da177e4SLinus Torvalds 			return (void __iomem *)pci_resource_start(pdev, PCI_ROM_RESOURCE);
75*1da177e4SLinus Torvalds 		} else {
76*1da177e4SLinus Torvalds 			/* assign the ROM an address if it doesn't have one */
77*1da177e4SLinus Torvalds 			if (res->parent == NULL)
78*1da177e4SLinus Torvalds 				pci_assign_resource(pdev, PCI_ROM_RESOURCE);
79*1da177e4SLinus Torvalds 
80*1da177e4SLinus Torvalds 			start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
81*1da177e4SLinus Torvalds 			*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
82*1da177e4SLinus Torvalds 			if (*size == 0)
83*1da177e4SLinus Torvalds 				return NULL;
84*1da177e4SLinus Torvalds 
85*1da177e4SLinus Torvalds 			/* Enable ROM space decodes */
86*1da177e4SLinus Torvalds 			pci_enable_rom(pdev);
87*1da177e4SLinus Torvalds 		}
88*1da177e4SLinus Torvalds 	}
89*1da177e4SLinus Torvalds 
90*1da177e4SLinus Torvalds 	rom = ioremap(start, *size);
91*1da177e4SLinus Torvalds 	if (!rom) {
92*1da177e4SLinus Torvalds 		/* restore enable if ioremap fails */
93*1da177e4SLinus Torvalds 		if (!(res->flags & (IORESOURCE_ROM_ENABLE |
94*1da177e4SLinus Torvalds 				    IORESOURCE_ROM_SHADOW |
95*1da177e4SLinus Torvalds 				    IORESOURCE_ROM_COPY)))
96*1da177e4SLinus Torvalds 			pci_disable_rom(pdev);
97*1da177e4SLinus Torvalds 		return NULL;
98*1da177e4SLinus Torvalds 	}
99*1da177e4SLinus Torvalds 
100*1da177e4SLinus Torvalds 	/*
101*1da177e4SLinus Torvalds 	 * Try to find the true size of the ROM since sometimes the PCI window
102*1da177e4SLinus Torvalds 	 * size is much larger than the actual size of the ROM.
103*1da177e4SLinus Torvalds 	 * True size is important if the ROM is going to be copied.
104*1da177e4SLinus Torvalds 	 */
105*1da177e4SLinus Torvalds 	image = rom;
106*1da177e4SLinus Torvalds 	do {
107*1da177e4SLinus Torvalds 		void __iomem *pds;
108*1da177e4SLinus Torvalds 		/* Standard PCI ROMs start out with these bytes 55 AA */
109*1da177e4SLinus Torvalds 		if (readb(image) != 0x55)
110*1da177e4SLinus Torvalds 			break;
111*1da177e4SLinus Torvalds 		if (readb(image + 1) != 0xAA)
112*1da177e4SLinus Torvalds 			break;
113*1da177e4SLinus Torvalds 		/* get the PCI data structure and check its signature */
114*1da177e4SLinus Torvalds 		pds = image + readw(image + 24);
115*1da177e4SLinus Torvalds 		if (readb(pds) != 'P')
116*1da177e4SLinus Torvalds 			break;
117*1da177e4SLinus Torvalds 		if (readb(pds + 1) != 'C')
118*1da177e4SLinus Torvalds 			break;
119*1da177e4SLinus Torvalds 		if (readb(pds + 2) != 'I')
120*1da177e4SLinus Torvalds 			break;
121*1da177e4SLinus Torvalds 		if (readb(pds + 3) != 'R')
122*1da177e4SLinus Torvalds 			break;
123*1da177e4SLinus Torvalds 		last_image = readb(pds + 21) & 0x80;
124*1da177e4SLinus Torvalds 		/* this length is reliable */
125*1da177e4SLinus Torvalds 		image += readw(pds + 16) * 512;
126*1da177e4SLinus Torvalds 	} while (!last_image);
127*1da177e4SLinus Torvalds 
128*1da177e4SLinus Torvalds 	*size = image - rom;
129*1da177e4SLinus Torvalds 
130*1da177e4SLinus Torvalds 	return rom;
131*1da177e4SLinus Torvalds }
132*1da177e4SLinus Torvalds 
133*1da177e4SLinus Torvalds /**
134*1da177e4SLinus Torvalds  * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy
135*1da177e4SLinus Torvalds  * @dev: pointer to pci device struct
136*1da177e4SLinus Torvalds  * @size: pointer to receive size of pci window over ROM
137*1da177e4SLinus Torvalds  * @return: kernel virtual pointer to image of ROM
138*1da177e4SLinus Torvalds  *
139*1da177e4SLinus Torvalds  * Map a PCI ROM into kernel space. If ROM is boot video ROM,
140*1da177e4SLinus Torvalds  * the shadow BIOS copy will be returned instead of the
141*1da177e4SLinus Torvalds  * actual ROM.
142*1da177e4SLinus Torvalds  */
143*1da177e4SLinus Torvalds void __iomem *pci_map_rom_copy(struct pci_dev *pdev, size_t *size)
144*1da177e4SLinus Torvalds {
145*1da177e4SLinus Torvalds 	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
146*1da177e4SLinus Torvalds 	void __iomem *rom;
147*1da177e4SLinus Torvalds 
148*1da177e4SLinus Torvalds 	rom = pci_map_rom(pdev, size);
149*1da177e4SLinus Torvalds 	if (!rom)
150*1da177e4SLinus Torvalds 		return NULL;
151*1da177e4SLinus Torvalds 
152*1da177e4SLinus Torvalds 	if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW))
153*1da177e4SLinus Torvalds 		return rom;
154*1da177e4SLinus Torvalds 
155*1da177e4SLinus Torvalds 	res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
156*1da177e4SLinus Torvalds 	if (!res->start)
157*1da177e4SLinus Torvalds 		return rom;
158*1da177e4SLinus Torvalds 
159*1da177e4SLinus Torvalds 	res->end = res->start + *size;
160*1da177e4SLinus Torvalds 	memcpy_fromio((void*)res->start, rom, *size);
161*1da177e4SLinus Torvalds 	pci_unmap_rom(pdev, rom);
162*1da177e4SLinus Torvalds 	res->flags |= IORESOURCE_ROM_COPY;
163*1da177e4SLinus Torvalds 
164*1da177e4SLinus Torvalds 	return (void __iomem *)res->start;
165*1da177e4SLinus Torvalds }
166*1da177e4SLinus Torvalds 
167*1da177e4SLinus Torvalds /**
168*1da177e4SLinus Torvalds  * pci_unmap_rom - unmap the ROM from kernel space
169*1da177e4SLinus Torvalds  * @dev: pointer to pci device struct
170*1da177e4SLinus Torvalds  * @rom: virtual address of the previous mapping
171*1da177e4SLinus Torvalds  *
172*1da177e4SLinus Torvalds  * Remove a mapping of a previously mapped ROM
173*1da177e4SLinus Torvalds  */
174*1da177e4SLinus Torvalds void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom)
175*1da177e4SLinus Torvalds {
176*1da177e4SLinus Torvalds 	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
177*1da177e4SLinus Torvalds 
178*1da177e4SLinus Torvalds 	if (res->flags & IORESOURCE_ROM_COPY)
179*1da177e4SLinus Torvalds 		return;
180*1da177e4SLinus Torvalds 
181*1da177e4SLinus Torvalds 	iounmap(rom);
182*1da177e4SLinus Torvalds 
183*1da177e4SLinus Torvalds 	/* Disable again before continuing, leave enabled if pci=rom */
184*1da177e4SLinus Torvalds 	if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW)))
185*1da177e4SLinus Torvalds 		pci_disable_rom(pdev);
186*1da177e4SLinus Torvalds }
187*1da177e4SLinus Torvalds 
188*1da177e4SLinus Torvalds /**
189*1da177e4SLinus Torvalds  * pci_remove_rom - disable the ROM and remove its sysfs attribute
190*1da177e4SLinus Torvalds  * @dev: pointer to pci device struct
191*1da177e4SLinus Torvalds  *
192*1da177e4SLinus Torvalds  * Remove the rom file in sysfs and disable ROM decoding.
193*1da177e4SLinus Torvalds  */
194*1da177e4SLinus Torvalds void pci_remove_rom(struct pci_dev *pdev)
195*1da177e4SLinus Torvalds {
196*1da177e4SLinus Torvalds 	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
197*1da177e4SLinus Torvalds 
198*1da177e4SLinus Torvalds 	if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
199*1da177e4SLinus Torvalds 		sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
200*1da177e4SLinus Torvalds 	if (!(res->flags & (IORESOURCE_ROM_ENABLE |
201*1da177e4SLinus Torvalds 			    IORESOURCE_ROM_SHADOW |
202*1da177e4SLinus Torvalds 			    IORESOURCE_ROM_COPY)))
203*1da177e4SLinus Torvalds 		pci_disable_rom(pdev);
204*1da177e4SLinus Torvalds }
205*1da177e4SLinus Torvalds 
206*1da177e4SLinus Torvalds /**
207*1da177e4SLinus Torvalds  * pci_cleanup_rom - internal routine for freeing the ROM copy created
208*1da177e4SLinus Torvalds  * by pci_map_rom_copy called from remove.c
209*1da177e4SLinus Torvalds  * @dev: pointer to pci device struct
210*1da177e4SLinus Torvalds  *
211*1da177e4SLinus Torvalds  * Free the copied ROM if we allocated one.
212*1da177e4SLinus Torvalds  */
213*1da177e4SLinus Torvalds void pci_cleanup_rom(struct pci_dev *pdev)
214*1da177e4SLinus Torvalds {
215*1da177e4SLinus Torvalds 	struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
216*1da177e4SLinus Torvalds 	if (res->flags & IORESOURCE_ROM_COPY) {
217*1da177e4SLinus Torvalds 		kfree((void*)res->start);
218*1da177e4SLinus Torvalds 		res->flags &= ~IORESOURCE_ROM_COPY;
219*1da177e4SLinus Torvalds 		res->start = 0;
220*1da177e4SLinus Torvalds 		res->end = 0;
221*1da177e4SLinus Torvalds 	}
222*1da177e4SLinus Torvalds }
223*1da177e4SLinus Torvalds 
224*1da177e4SLinus Torvalds EXPORT_SYMBOL(pci_map_rom);
225*1da177e4SLinus Torvalds EXPORT_SYMBOL(pci_map_rom_copy);
226*1da177e4SLinus Torvalds EXPORT_SYMBOL(pci_unmap_rom);
227*1da177e4SLinus Torvalds EXPORT_SYMBOL(pci_remove_rom);
228