linux/pci: reserve io/memory space for bridge reserve io/memory space for bridge which will be used later by PCI hotplug. Signed-off-by: Isaku Yamahata diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1259,6 +1259,13 @@ running once the system is up. IXP2000 systems where the bus has to be configured a certain way for adjunct CPUs. + pci_reserve= [PCI] + Format: [[+IO][+MEM]][,...] + Format of sbdf: [:]:. + Specifies the least reserved io size or memory size + which is assigned to PCI bridge even when no child + pci device exists. This is useful with PCI hotplug. + pcmv= [HW,PCMCIA] BadgePAD 4 pd. [PARIDE] diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -43,6 +43,14 @@ config PCI_IOMULTI default y help Say Y here if you need io multiplexing. + +config PCI_RESERVE + bool "PCI IO/MEMORY space reserve" + depends on PCI + default y + help + Say Y here if you need PCI IO/MEMORY space reserve + config PCI_IOV bool "PCI IOV support" depends on PCI diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -7,6 +7,7 @@ obj-y += access.o bus.o probe.o remove. obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_PCI_GUESTDEV) += guestdev.o obj-$(CONFIG_PCI_IOMULTI) += iomulti.o +obj-$(CONFIG_PCI_RESERVE) += reserve.o # Build PCI Express stuff if needed obj-$(CONFIG_PCIEPORTBUS) += pcie/ diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -189,3 +189,18 @@ static inline int pci_iov_bus_range(stru return 0; } #endif /* CONFIG_PCI_IOV */ + +#ifdef CONFIG_PCI_RESERVE +unsigned long pci_reserve_size_io(struct pci_bus *bus); +unsigned long pci_reserve_size_mem(struct pci_bus *bus); +#else +static inline unsigned long pci_reserve_size_io(struct pci_bus *bus) +{ + return 0; +} + +static inline unsigned long pci_reserve_size_mem(struct pci_bus *bus) +{ + return 0; +} +#endif /* CONFIG_PCI_RESERVE */ diff --git a/drivers/pci/reserve.c b/drivers/pci/reserve.c new file mode 100644 --- /dev/null +++ b/drivers/pci/reserve.c @@ -0,0 +1,143 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright (c) 2009 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + */ + +#include +#include + +#include + +static char pci_reserve_param[COMMAND_LINE_SIZE]; + +/* pci_reserve= [PCI] + * Format: [[+IO][+MEM]][,...] + * Format of sbdf: [:]:. + */ +static int pci_reserve_parse_size(const char *str, + unsigned long *io_size, + unsigned long *mem_size) +{ + if (sscanf(str, "io%lx", io_size) == 1 || + sscanf(str, "IO%lx", io_size) == 1) + return 0; + + if (sscanf(str, "mem%lx", mem_size) == 1 || + sscanf(str, "MEM%lx", mem_size) == 1) + return 0; + + return -EINVAL; +} + +static int pci_reserve_parse_one(const char *str, + int *seg, int *bus, int *dev, int *func, + unsigned long *io_size, + unsigned long *mem_size) +{ + char *p; + + *io_size = 0; + *mem_size = 0; + + if (sscanf(str, "%x:%x:%x.%x", seg, bus, dev, func) != 4) { + *seg = 0; + if (sscanf(str, "%x:%x.%x", bus, dev, func) != 3) { + return -EINVAL; + } + } + + p = strchr(str, '+'); + if (p == NULL) + return -EINVAL; + p++; + if (pci_reserve_parse_size(p, io_size, mem_size)) + return -EINVAL; + + p = strchr(str, '+'); + if (p != NULL) { + p++; + pci_reserve_parse_size(p, io_size, mem_size); + } + return 0; +} + +static unsigned long pci_reserve_size(struct pci_bus *pbus, int flags) +{ + char *sp; + char *ep; + + int seg; + int bus; + int dev; + int func; + + unsigned long io_size; + unsigned long mem_size; + + sp = pci_reserve_param; + + do { + ep = strchr(sp, ','); + if (ep) + *ep = '\0'; /* chomp */ + + if (pci_reserve_parse_one(sp, &seg, &bus, &dev, &func, + &io_size, &mem_size) == 0) { + if (pci_domain_nr(pbus) == seg && + pbus->number == bus && + PCI_SLOT(pbus->self->devfn) == dev && + PCI_FUNC(pbus->self->devfn) == func) { + switch (flags) { + case IORESOURCE_IO: + return io_size; + case IORESOURCE_MEM: + return mem_size; + default: + break; + } + } + } + + if (ep) { + *ep = ','; /* restore chomp'ed ',' for later */ + ep++; + } + sp = ep; + } while (ep); + + return 0; +} + +unsigned long pci_reserve_size_io(struct pci_bus *pbus) +{ + return pci_reserve_size(pbus, IORESOURCE_IO); +} + +unsigned long pci_reserve_size_mem(struct pci_bus *pbus) +{ + return pci_reserve_size(pbus, IORESOURCE_MEM); +} + +static int __init pci_reserve_setup(char *str) +{ + if (strlen(str) > sizeof(pci_reserve_param)) + return 0; + strcpy(pci_reserve_param, str); + return 1; +} +__setup("pci_reserve=", pci_reserve_setup); diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -315,7 +315,7 @@ pbus_size_io(struct pci_bus *bus) #if defined(CONFIG_ISA) || defined(CONFIG_EISA) size = (size & 0xff) + ((size & ~0xffUL) << 2); #endif - size = ROUND_UP(size + size1, 4096); + size = ROUND_UP(max(size + size1, pci_reserve_size_io(bus)), 4096); if (!size) { b_res->flags = 0; return; @@ -393,7 +393,7 @@ pbus_size_mem(struct pci_bus *bus, unsig min_align = align1 >> 1; align += aligns[order]; } - size = ROUND_UP(size, min_align); + size = ROUND_UP(max(size, pci_reserve_size_mem(bus)), min_align); if (!size) { b_res->flags = 0; return 1;