mirror of
https://github.com/torvalds/linux.git
synced 2026-05-28 09:04:39 +02:00
Merge branch 'pci/resource'
- Build zero-sized resources when a BAR is larger than 4G but pci_bus_addr_t or resource_size_t can't represent 64-bit addresses (Ilpo Järvinen) - Fix bridge window alignment with optional resources, where we previously lost the additional alignment requirement (Ilpo Järvinen) - Stop over-estimating bridge window size since we now assign them without any gaps between them (Ilpo Järvinen) - Increase resource MAX_IORES_LEVEL to avoid /proc/iomem flattening for nested bridges and endpoints (Ilpo Järvinen) - Remove old_size limit from bridge window sizing (Ilpo Järvinen) - Push realloc check into pbus_size_mem() to simplify callers (Ilpo Järvinen) - Pass bridge window resource to pbus_size_mem() to avoid looking it up again (Ilpo Järvinen) - Use res_to_dev_res() instead of open-coding the same search (Ilpo Järvinen) - Add pci_resource_is_bridge_win() helper (Ilpo Järvinen) - Add more logging of resource assignment (Ilpo Järvinen) - Add pbus_mem_size_optional() to handle sizes of optional resources (SR-IOV VF BARs, expansion ROMs, bridge windows) (Ilpo Järvinen) - Move CardBus code to setup-cardbus.c and only build it when CONFIG_CARDBUS is set (Ilpo Järvinen) - Use scnprintf() instead of sprintf() (Ilpo Järvinen) - Add pbus_validate_busn() for Bus Number validation (Ilpo Järvinen) - Don't claim disabled bridge windows to avoid spurious claim failures (Ilpo Järvinen) * pci/resource: PCI: Don't claim disabled bridge windows PCI: Move CardBus bridge scanning to setup-cardbus.c PCI: Add pbus_validate_busn() for Bus Number validation PCI: Add dword #defines for Bus Number + Secondary Latency Timer PCI: Use scnprintf() instead of sprintf() PCI: Handle CardBus-specific params in setup-cardbus.c PCI: Separate CardBus setup & build it only with CONFIG_CARDBUS PCI: Add 'pci' prefix to struct pci_dev_resource handling functions PCI: Use resource_assigned() in setup-bus.c algorithm resource: Mark res given to resource_assigned() as const PCI: Add pbus_mem_size_optional() to handle optional sizes PCI: Check invalid align earlier in pbus_size_mem() PCI: Log reset and restore of resources PCI: Add pci_resource_is_bridge_win() PCI: Fetch dev_res to local var in __assign_resources_sorted() PCI: Use res_to_dev_res() in reassign_resources_sorted() PCI: Pass bridge window resource to pbus_size_mem() PCI: Push realloc check into pbus_size_mem() PCI: Remove old_size limit from bridge window sizing resource: Increase MAX_IORES_LEVEL to 8 PCI: Stop over-estimating bridge window size PCI: Rewrite bridge window head alignment function PCI: Fix bridge window alignment with optional resources PCI: Use resource_set_range() that correctly sets ->end
This commit is contained in:
commit
73b4779864
|
|
@ -39,6 +39,7 @@ obj-$(CONFIG_PCI_TSM) += tsm.o
|
|||
obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o
|
||||
obj-$(CONFIG_PCI_NPEM) += npem.o
|
||||
obj-$(CONFIG_PCIE_TPH) += tph.o
|
||||
obj-$(CONFIG_CARDBUS) += setup-cardbus.o
|
||||
|
||||
# Endpoint library must be initialized before its users
|
||||
obj-$(CONFIG_PCI_ENDPOINT) += endpoint/
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ static ssize_t resource_show(struct device *dev, struct device_attribute *attr,
|
|||
struct resource zerores = {};
|
||||
|
||||
/* For backwards compatibility */
|
||||
if (i >= PCI_BRIDGE_RESOURCES && i <= PCI_BRIDGE_RESOURCE_END &&
|
||||
if (pci_resource_is_bridge_win(i) &&
|
||||
res->flags & (IORESOURCE_UNSET | IORESOURCE_DISABLED))
|
||||
res = &zerores;
|
||||
|
||||
|
|
|
|||
|
|
@ -99,12 +99,6 @@ bool pci_reset_supported(struct pci_dev *dev)
|
|||
int pci_domains_supported = 1;
|
||||
#endif
|
||||
|
||||
#define DEFAULT_CARDBUS_IO_SIZE (256)
|
||||
#define DEFAULT_CARDBUS_MEM_SIZE (64*1024*1024)
|
||||
/* pci=cbmemsize=nnM,cbiosize=nn can override this */
|
||||
unsigned long pci_cardbus_io_size = DEFAULT_CARDBUS_IO_SIZE;
|
||||
unsigned long pci_cardbus_mem_size = DEFAULT_CARDBUS_MEM_SIZE;
|
||||
|
||||
#define DEFAULT_HOTPLUG_IO_SIZE (256)
|
||||
#define DEFAULT_HOTPLUG_MMIO_SIZE (2*1024*1024)
|
||||
#define DEFAULT_HOTPLUG_MMIO_PREF_SIZE (2*1024*1024)
|
||||
|
|
@ -6639,7 +6633,9 @@ static int __init pci_setup(char *str)
|
|||
if (k)
|
||||
*k++ = 0;
|
||||
if (*str && (str = pcibios_setup(str)) && *str) {
|
||||
if (!strcmp(str, "nomsi")) {
|
||||
if (!pci_setup_cardbus(str)) {
|
||||
/* Function handled the parameters */
|
||||
} else if (!strcmp(str, "nomsi")) {
|
||||
pci_no_msi();
|
||||
} else if (!strncmp(str, "noats", 5)) {
|
||||
pr_info("PCIe: ATS is disabled\n");
|
||||
|
|
@ -6658,10 +6654,6 @@ static int __init pci_setup(char *str)
|
|||
pcie_ari_disabled = true;
|
||||
} else if (!strncmp(str, "notph", 5)) {
|
||||
pci_no_tph();
|
||||
} else if (!strncmp(str, "cbiosize=", 9)) {
|
||||
pci_cardbus_io_size = memparse(str + 9, &str);
|
||||
} else if (!strncmp(str, "cbmemsize=", 10)) {
|
||||
pci_cardbus_mem_size = memparse(str + 10, &str);
|
||||
} else if (!strncmp(str, "resource_alignment=", 19)) {
|
||||
resource_alignment_param = str + 19;
|
||||
} else if (!strncmp(str, "ecrc=", 5)) {
|
||||
|
|
|
|||
|
|
@ -245,6 +245,7 @@ void pci_config_pm_runtime_put(struct pci_dev *dev);
|
|||
void pci_pm_power_up_and_verify_state(struct pci_dev *pci_dev);
|
||||
void pci_pm_init(struct pci_dev *dev);
|
||||
void pci_ea_init(struct pci_dev *dev);
|
||||
bool pci_ea_fixed_busnrs(struct pci_dev *dev, u8 *sec, u8 *sub);
|
||||
void pci_msi_init(struct pci_dev *dev);
|
||||
void pci_msix_init(struct pci_dev *dev);
|
||||
bool pci_bridge_d3_possible(struct pci_dev *dev);
|
||||
|
|
@ -379,8 +380,40 @@ extern unsigned long pci_hotplug_io_size;
|
|||
extern unsigned long pci_hotplug_mmio_size;
|
||||
extern unsigned long pci_hotplug_mmio_pref_size;
|
||||
extern unsigned long pci_hotplug_bus_size;
|
||||
extern unsigned long pci_cardbus_io_size;
|
||||
extern unsigned long pci_cardbus_mem_size;
|
||||
|
||||
static inline bool pci_is_cardbus_bridge(struct pci_dev *dev)
|
||||
{
|
||||
return dev->hdr_type == PCI_HEADER_TYPE_CARDBUS;
|
||||
}
|
||||
#ifdef CONFIG_CARDBUS
|
||||
unsigned long pci_cardbus_resource_alignment(struct resource *res);
|
||||
int pci_bus_size_cardbus_bridge(struct pci_bus *bus,
|
||||
struct list_head *realloc_head);
|
||||
int pci_cardbus_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
|
||||
u32 buses, int max,
|
||||
unsigned int available_buses, int pass);
|
||||
int pci_setup_cardbus(char *str);
|
||||
|
||||
#else
|
||||
static inline unsigned long pci_cardbus_resource_alignment(struct resource *res)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int pci_bus_size_cardbus_bridge(struct pci_bus *bus,
|
||||
struct list_head *realloc_head)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
static inline int pci_cardbus_scan_bridge_extend(struct pci_bus *bus,
|
||||
struct pci_dev *dev,
|
||||
u32 buses, int max,
|
||||
unsigned int available_buses,
|
||||
int pass)
|
||||
{
|
||||
return max;
|
||||
}
|
||||
static inline int pci_setup_cardbus(char *str) { return -ENOENT; }
|
||||
#endif /* CONFIG_CARDBUS */
|
||||
|
||||
/**
|
||||
* pci_match_one_device - Tell if a PCI device structure has a matching
|
||||
|
|
@ -443,6 +476,10 @@ void __pci_size_stdbars(struct pci_dev *dev, int count,
|
|||
int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
|
||||
struct resource *res, unsigned int reg, u32 *sizes);
|
||||
void pci_configure_ari(struct pci_dev *dev);
|
||||
|
||||
int pci_dev_res_add_to_list(struct list_head *head, struct pci_dev *dev,
|
||||
struct resource *res, resource_size_t add_size,
|
||||
resource_size_t min_align);
|
||||
void __pci_bus_size_bridges(struct pci_bus *bus,
|
||||
struct list_head *realloc_head);
|
||||
void __pci_bus_assign_resources(const struct pci_bus *bus,
|
||||
|
|
@ -455,6 +492,11 @@ void pci_walk_bus_locked(struct pci_bus *top,
|
|||
|
||||
const char *pci_resource_name(struct pci_dev *dev, unsigned int i);
|
||||
bool pci_resource_is_optional(const struct pci_dev *dev, int resno);
|
||||
static inline bool pci_resource_is_bridge_win(int resno)
|
||||
{
|
||||
return resno >= PCI_BRIDGE_RESOURCES &&
|
||||
resno <= PCI_BRIDGE_RESOURCE_END;
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_resource_num - Reverse lookup resource number from device resources
|
||||
|
|
@ -478,6 +520,7 @@ static inline int pci_resource_num(const struct pci_dev *dev,
|
|||
return resno;
|
||||
}
|
||||
|
||||
void pbus_validate_busn(struct pci_bus *bus);
|
||||
struct resource *pbus_select_window(struct pci_bus *bus,
|
||||
const struct resource *res);
|
||||
void pci_reassigndev_resource_alignment(struct pci_dev *dev);
|
||||
|
|
@ -927,8 +970,6 @@ static inline void pci_suspend_ptm(struct pci_dev *dev) { }
|
|||
static inline void pci_resume_ptm(struct pci_dev *dev) { }
|
||||
#endif
|
||||
|
||||
unsigned long pci_cardbus_resource_alignment(struct resource *);
|
||||
|
||||
static inline resource_size_t pci_resource_alignment(struct pci_dev *dev,
|
||||
struct resource *res)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/pci_hotplug.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sprintf.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/aer.h>
|
||||
|
|
@ -24,9 +25,6 @@
|
|||
#include <linux/bitfield.h>
|
||||
#include "pci.h"
|
||||
|
||||
#define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */
|
||||
#define CARDBUS_RESERVE_BUSNR 3
|
||||
|
||||
static struct resource busn_resource = {
|
||||
.name = "PCI busn",
|
||||
.start = 0,
|
||||
|
|
@ -287,8 +285,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
|
|||
if ((sizeof(pci_bus_addr_t) < 8 || sizeof(resource_size_t) < 8)
|
||||
&& sz64 > 0x100000000ULL) {
|
||||
res->flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED;
|
||||
res->start = 0;
|
||||
res->end = 0;
|
||||
resource_set_range(res, 0, 0);
|
||||
pci_err(dev, "%s: can't handle BAR larger than 4GB (size %#010llx)\n",
|
||||
res_name, (unsigned long long)sz64);
|
||||
goto out;
|
||||
|
|
@ -297,8 +294,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
|
|||
if ((sizeof(pci_bus_addr_t) < 8) && l) {
|
||||
/* Above 32-bit boundary; try to reallocate */
|
||||
res->flags |= IORESOURCE_UNSET;
|
||||
res->start = 0;
|
||||
res->end = sz64 - 1;
|
||||
resource_set_range(res, 0, sz64);
|
||||
pci_info(dev, "%s: can't handle BAR above 4GB (bus address %#010llx)\n",
|
||||
res_name, (unsigned long long)l64);
|
||||
goto out;
|
||||
|
|
@ -525,8 +521,8 @@ static void pci_read_bridge_windows(struct pci_dev *bridge)
|
|||
|
||||
pci_read_config_dword(bridge, PCI_PRIMARY_BUS, &buses);
|
||||
res.flags = IORESOURCE_BUS;
|
||||
res.start = (buses >> 8) & 0xff;
|
||||
res.end = (buses >> 16) & 0xff;
|
||||
res.start = FIELD_GET(PCI_SECONDARY_BUS_MASK, buses);
|
||||
res.end = FIELD_GET(PCI_SUBORDINATE_BUS_MASK, buses);
|
||||
pci_info(bridge, "PCI bridge to %pR%s\n", &res,
|
||||
bridge->transparent ? " (subtractive decode)" : "");
|
||||
|
||||
|
|
@ -1313,6 +1309,26 @@ static void pci_enable_rrs_sv(struct pci_dev *pdev)
|
|||
|
||||
static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus,
|
||||
unsigned int available_buses);
|
||||
|
||||
void pbus_validate_busn(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_bus *upstream = bus->parent;
|
||||
struct pci_dev *bridge = bus->self;
|
||||
|
||||
/* Check that all devices are accessible */
|
||||
while (upstream->parent) {
|
||||
if ((bus->busn_res.end > upstream->busn_res.end) ||
|
||||
(bus->number > upstream->busn_res.end) ||
|
||||
(bus->number < upstream->number) ||
|
||||
(bus->busn_res.end < upstream->number)) {
|
||||
pci_info(bridge, "devices behind bridge are unusable because %pR cannot be assigned for them\n",
|
||||
&bus->busn_res);
|
||||
break;
|
||||
}
|
||||
upstream = upstream->parent;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_ea_fixed_busnrs() - Read fixed Secondary and Subordinate bus
|
||||
* numbers from EA capability.
|
||||
|
|
@ -1324,7 +1340,7 @@ static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus,
|
|||
* and subordinate bus numbers, return true with the bus numbers in @sec
|
||||
* and @sub. Otherwise return false.
|
||||
*/
|
||||
static bool pci_ea_fixed_busnrs(struct pci_dev *dev, u8 *sec, u8 *sub)
|
||||
bool pci_ea_fixed_busnrs(struct pci_dev *dev, u8 *sec, u8 *sub)
|
||||
{
|
||||
int ea, offset;
|
||||
u32 dw;
|
||||
|
|
@ -1378,8 +1394,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
|
|||
int pass)
|
||||
{
|
||||
struct pci_bus *child;
|
||||
int is_cardbus = (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS);
|
||||
u32 buses, i, j = 0;
|
||||
u32 buses;
|
||||
u16 bctl;
|
||||
u8 primary, secondary, subordinate;
|
||||
int broken = 0;
|
||||
|
|
@ -1394,9 +1409,9 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
|
|||
pm_runtime_get_sync(&dev->dev);
|
||||
|
||||
pci_read_config_dword(dev, PCI_PRIMARY_BUS, &buses);
|
||||
primary = buses & 0xFF;
|
||||
secondary = (buses >> 8) & 0xFF;
|
||||
subordinate = (buses >> 16) & 0xFF;
|
||||
primary = FIELD_GET(PCI_PRIMARY_BUS_MASK, buses);
|
||||
secondary = FIELD_GET(PCI_SECONDARY_BUS_MASK, buses);
|
||||
subordinate = FIELD_GET(PCI_SUBORDINATE_BUS_MASK, buses);
|
||||
|
||||
pci_dbg(dev, "scanning [bus %02x-%02x] behind bridge, pass %d\n",
|
||||
secondary, subordinate, pass);
|
||||
|
|
@ -1423,8 +1438,15 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
|
|||
pci_write_config_word(dev, PCI_BRIDGE_CONTROL,
|
||||
bctl & ~PCI_BRIDGE_CTL_MASTER_ABORT);
|
||||
|
||||
if ((secondary || subordinate) && !pcibios_assign_all_busses() &&
|
||||
!is_cardbus && !broken) {
|
||||
if (pci_is_cardbus_bridge(dev)) {
|
||||
max = pci_cardbus_scan_bridge_extend(bus, dev, buses, max,
|
||||
available_buses,
|
||||
pass);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((secondary || subordinate) &&
|
||||
!pcibios_assign_all_busses() && !broken) {
|
||||
unsigned int cmax, buses;
|
||||
|
||||
/*
|
||||
|
|
@ -1466,7 +1488,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
|
|||
* do in the second pass.
|
||||
*/
|
||||
if (!pass) {
|
||||
if (pcibios_assign_all_busses() || broken || is_cardbus)
|
||||
if (pcibios_assign_all_busses() || broken)
|
||||
|
||||
/*
|
||||
* Temporarily disable forwarding of the
|
||||
|
|
@ -1477,7 +1499,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
|
|||
* ranges.
|
||||
*/
|
||||
pci_write_config_dword(dev, PCI_PRIMARY_BUS,
|
||||
buses & ~0xffffff);
|
||||
buses & PCI_SEC_LATENCY_TIMER_MASK);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -1508,59 +1530,16 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
|
|||
if (available_buses)
|
||||
available_buses--;
|
||||
|
||||
buses = (buses & 0xff000000)
|
||||
| ((unsigned int)(child->primary) << 0)
|
||||
| ((unsigned int)(child->busn_res.start) << 8)
|
||||
| ((unsigned int)(child->busn_res.end) << 16);
|
||||
|
||||
/*
|
||||
* yenta.c forces a secondary latency timer of 176.
|
||||
* Copy that behaviour here.
|
||||
*/
|
||||
if (is_cardbus) {
|
||||
buses &= ~0xff000000;
|
||||
buses |= CARDBUS_LATENCY_TIMER << 24;
|
||||
}
|
||||
buses = (buses & PCI_SEC_LATENCY_TIMER_MASK) |
|
||||
FIELD_PREP(PCI_PRIMARY_BUS_MASK, child->primary) |
|
||||
FIELD_PREP(PCI_SECONDARY_BUS_MASK, child->busn_res.start) |
|
||||
FIELD_PREP(PCI_SUBORDINATE_BUS_MASK, child->busn_res.end);
|
||||
|
||||
/* We need to blast all three values with a single write */
|
||||
pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses);
|
||||
|
||||
if (!is_cardbus) {
|
||||
child->bridge_ctl = bctl;
|
||||
max = pci_scan_child_bus_extend(child, available_buses);
|
||||
} else {
|
||||
|
||||
/*
|
||||
* For CardBus bridges, we leave 4 bus numbers as
|
||||
* cards with a PCI-to-PCI bridge can be inserted
|
||||
* later.
|
||||
*/
|
||||
for (i = 0; i < CARDBUS_RESERVE_BUSNR; i++) {
|
||||
struct pci_bus *parent = bus;
|
||||
if (pci_find_bus(pci_domain_nr(bus),
|
||||
max+i+1))
|
||||
break;
|
||||
while (parent->parent) {
|
||||
if ((!pcibios_assign_all_busses()) &&
|
||||
(parent->busn_res.end > max) &&
|
||||
(parent->busn_res.end <= max+i)) {
|
||||
j = 1;
|
||||
}
|
||||
parent = parent->parent;
|
||||
}
|
||||
if (j) {
|
||||
|
||||
/*
|
||||
* Often, there are two CardBus
|
||||
* bridges -- try to leave one
|
||||
* valid bus number for each one.
|
||||
*/
|
||||
i /= 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
max += i;
|
||||
}
|
||||
child->bridge_ctl = bctl;
|
||||
max = pci_scan_child_bus_extend(child, available_buses);
|
||||
|
||||
/*
|
||||
* Set subordinate bus number to its real value.
|
||||
|
|
@ -1572,23 +1551,10 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
|
|||
pci_bus_update_busn_res_end(child, max);
|
||||
pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max);
|
||||
}
|
||||
scnprintf(child->name, sizeof(child->name), "PCI Bus %04x:%02x",
|
||||
pci_domain_nr(bus), child->number);
|
||||
|
||||
sprintf(child->name,
|
||||
(is_cardbus ? "PCI CardBus %04x:%02x" : "PCI Bus %04x:%02x"),
|
||||
pci_domain_nr(bus), child->number);
|
||||
|
||||
/* Check that all devices are accessible */
|
||||
while (bus->parent) {
|
||||
if ((child->busn_res.end > bus->busn_res.end) ||
|
||||
(child->number > bus->busn_res.end) ||
|
||||
(child->number < bus->number) ||
|
||||
(child->busn_res.end < bus->number)) {
|
||||
dev_info(&dev->dev, "devices behind bridge are unusable because %pR cannot be assigned for them\n",
|
||||
&child->busn_res);
|
||||
break;
|
||||
}
|
||||
bus = bus->parent;
|
||||
}
|
||||
pbus_validate_busn(child);
|
||||
|
||||
out:
|
||||
/* Clear errors in the Secondary Status Register */
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
306
drivers/pci/setup-cardbus.c
Normal file
306
drivers/pci/setup-cardbus.c
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Cardbus bridge setup routines.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/sprintf.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "pci.h"
|
||||
|
||||
#define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */
|
||||
#define CARDBUS_RESERVE_BUSNR 3
|
||||
|
||||
#define DEFAULT_CARDBUS_IO_SIZE SZ_256
|
||||
#define DEFAULT_CARDBUS_MEM_SIZE SZ_64M
|
||||
/* pci=cbmemsize=nnM,cbiosize=nn can override this */
|
||||
static unsigned long pci_cardbus_io_size = DEFAULT_CARDBUS_IO_SIZE;
|
||||
static unsigned long pci_cardbus_mem_size = DEFAULT_CARDBUS_MEM_SIZE;
|
||||
|
||||
unsigned long pci_cardbus_resource_alignment(struct resource *res)
|
||||
{
|
||||
if (res->flags & IORESOURCE_IO)
|
||||
return pci_cardbus_io_size;
|
||||
if (res->flags & IORESOURCE_MEM)
|
||||
return pci_cardbus_mem_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pci_bus_size_cardbus_bridge(struct pci_bus *bus,
|
||||
struct list_head *realloc_head)
|
||||
{
|
||||
struct pci_dev *bridge = bus->self;
|
||||
struct resource *b_res;
|
||||
resource_size_t b_res_3_size = pci_cardbus_mem_size * 2;
|
||||
u16 ctrl;
|
||||
|
||||
b_res = &bridge->resource[PCI_CB_BRIDGE_IO_0_WINDOW];
|
||||
if (resource_assigned(b_res))
|
||||
goto handle_b_res_1;
|
||||
/*
|
||||
* Reserve some resources for CardBus. We reserve a fixed amount
|
||||
* of bus space for CardBus bridges.
|
||||
*/
|
||||
resource_set_range(b_res, pci_cardbus_io_size, pci_cardbus_io_size);
|
||||
b_res->flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN;
|
||||
if (realloc_head) {
|
||||
b_res->end -= pci_cardbus_io_size;
|
||||
pci_dev_res_add_to_list(realloc_head, bridge, b_res,
|
||||
pci_cardbus_io_size,
|
||||
pci_cardbus_io_size);
|
||||
}
|
||||
|
||||
handle_b_res_1:
|
||||
b_res = &bridge->resource[PCI_CB_BRIDGE_IO_1_WINDOW];
|
||||
if (resource_assigned(b_res))
|
||||
goto handle_b_res_2;
|
||||
resource_set_range(b_res, pci_cardbus_io_size, pci_cardbus_io_size);
|
||||
b_res->flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN;
|
||||
if (realloc_head) {
|
||||
b_res->end -= pci_cardbus_io_size;
|
||||
pci_dev_res_add_to_list(realloc_head, bridge, b_res,
|
||||
pci_cardbus_io_size,
|
||||
pci_cardbus_io_size);
|
||||
}
|
||||
|
||||
handle_b_res_2:
|
||||
/* MEM1 must not be pref MMIO */
|
||||
pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl);
|
||||
if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM1) {
|
||||
ctrl &= ~PCI_CB_BRIDGE_CTL_PREFETCH_MEM1;
|
||||
pci_write_config_word(bridge, PCI_CB_BRIDGE_CONTROL, ctrl);
|
||||
pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl);
|
||||
}
|
||||
|
||||
/* Check whether prefetchable memory is supported by this bridge. */
|
||||
pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl);
|
||||
if (!(ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0)) {
|
||||
ctrl |= PCI_CB_BRIDGE_CTL_PREFETCH_MEM0;
|
||||
pci_write_config_word(bridge, PCI_CB_BRIDGE_CONTROL, ctrl);
|
||||
pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl);
|
||||
}
|
||||
|
||||
b_res = &bridge->resource[PCI_CB_BRIDGE_MEM_0_WINDOW];
|
||||
if (resource_assigned(b_res))
|
||||
goto handle_b_res_3;
|
||||
/*
|
||||
* If we have prefetchable memory support, allocate two regions.
|
||||
* Otherwise, allocate one region of twice the size.
|
||||
*/
|
||||
if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0) {
|
||||
resource_set_range(b_res, pci_cardbus_mem_size,
|
||||
pci_cardbus_mem_size);
|
||||
b_res->flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH |
|
||||
IORESOURCE_STARTALIGN;
|
||||
if (realloc_head) {
|
||||
b_res->end -= pci_cardbus_mem_size;
|
||||
pci_dev_res_add_to_list(realloc_head, bridge, b_res,
|
||||
pci_cardbus_mem_size,
|
||||
pci_cardbus_mem_size);
|
||||
}
|
||||
|
||||
/* Reduce that to half */
|
||||
b_res_3_size = pci_cardbus_mem_size;
|
||||
}
|
||||
|
||||
handle_b_res_3:
|
||||
b_res = &bridge->resource[PCI_CB_BRIDGE_MEM_1_WINDOW];
|
||||
if (resource_assigned(b_res))
|
||||
goto handle_done;
|
||||
resource_set_range(b_res, pci_cardbus_mem_size, b_res_3_size);
|
||||
b_res->flags |= IORESOURCE_MEM | IORESOURCE_STARTALIGN;
|
||||
if (realloc_head) {
|
||||
b_res->end -= b_res_3_size;
|
||||
pci_dev_res_add_to_list(realloc_head, bridge, b_res,
|
||||
b_res_3_size, pci_cardbus_mem_size);
|
||||
}
|
||||
|
||||
handle_done:
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pci_setup_cardbus_bridge(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_dev *bridge = bus->self;
|
||||
struct resource *res;
|
||||
struct pci_bus_region region;
|
||||
|
||||
pci_info(bridge, "CardBus bridge to %pR\n",
|
||||
&bus->busn_res);
|
||||
|
||||
res = bus->resource[0];
|
||||
pcibios_resource_to_bus(bridge->bus, ®ion, res);
|
||||
if (resource_assigned(res) && res->flags & IORESOURCE_IO) {
|
||||
/*
|
||||
* The IO resource is allocated a range twice as large as it
|
||||
* would normally need. This allows us to set both IO regs.
|
||||
*/
|
||||
pci_info(bridge, " bridge window %pR\n", res);
|
||||
pci_write_config_dword(bridge, PCI_CB_IO_BASE_0,
|
||||
region.start);
|
||||
pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_0,
|
||||
region.end);
|
||||
}
|
||||
|
||||
res = bus->resource[1];
|
||||
pcibios_resource_to_bus(bridge->bus, ®ion, res);
|
||||
if (resource_assigned(res) && res->flags & IORESOURCE_IO) {
|
||||
pci_info(bridge, " bridge window %pR\n", res);
|
||||
pci_write_config_dword(bridge, PCI_CB_IO_BASE_1,
|
||||
region.start);
|
||||
pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_1,
|
||||
region.end);
|
||||
}
|
||||
|
||||
res = bus->resource[2];
|
||||
pcibios_resource_to_bus(bridge->bus, ®ion, res);
|
||||
if (resource_assigned(res) && res->flags & IORESOURCE_MEM) {
|
||||
pci_info(bridge, " bridge window %pR\n", res);
|
||||
pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_0,
|
||||
region.start);
|
||||
pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_0,
|
||||
region.end);
|
||||
}
|
||||
|
||||
res = bus->resource[3];
|
||||
pcibios_resource_to_bus(bridge->bus, ®ion, res);
|
||||
if (resource_assigned(res) && res->flags & IORESOURCE_MEM) {
|
||||
pci_info(bridge, " bridge window %pR\n", res);
|
||||
pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_1,
|
||||
region.start);
|
||||
pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_1,
|
||||
region.end);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(pci_setup_cardbus_bridge);
|
||||
|
||||
int pci_setup_cardbus(char *str)
|
||||
{
|
||||
if (!strncmp(str, "cbiosize=", 9)) {
|
||||
pci_cardbus_io_size = memparse(str + 9, &str);
|
||||
return 0;
|
||||
} else if (!strncmp(str, "cbmemsize=", 10)) {
|
||||
pci_cardbus_mem_size = memparse(str + 10, &str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int pci_cardbus_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
|
||||
u32 buses, int max,
|
||||
unsigned int available_buses, int pass)
|
||||
{
|
||||
struct pci_bus *child;
|
||||
bool fixed_buses;
|
||||
u8 fixed_sec, fixed_sub;
|
||||
int next_busnr;
|
||||
u32 i, j = 0;
|
||||
|
||||
/*
|
||||
* We need to assign a number to this bus which we always do in the
|
||||
* second pass.
|
||||
*/
|
||||
if (!pass) {
|
||||
/*
|
||||
* Temporarily disable forwarding of the configuration
|
||||
* cycles on all bridges in this bus segment to avoid
|
||||
* possible conflicts in the second pass between two bridges
|
||||
* programmed with overlapping bus ranges.
|
||||
*/
|
||||
pci_write_config_dword(dev, PCI_PRIMARY_BUS,
|
||||
buses & PCI_SEC_LATENCY_TIMER_MASK);
|
||||
return max;
|
||||
}
|
||||
|
||||
/* Clear errors */
|
||||
pci_write_config_word(dev, PCI_STATUS, 0xffff);
|
||||
|
||||
/* Read bus numbers from EA Capability (if present) */
|
||||
fixed_buses = pci_ea_fixed_busnrs(dev, &fixed_sec, &fixed_sub);
|
||||
if (fixed_buses)
|
||||
next_busnr = fixed_sec;
|
||||
else
|
||||
next_busnr = max + 1;
|
||||
|
||||
/*
|
||||
* Prevent assigning a bus number that already exists. This can
|
||||
* happen when a bridge is hot-plugged, so in this case we only
|
||||
* re-scan this bus.
|
||||
*/
|
||||
child = pci_find_bus(pci_domain_nr(bus), next_busnr);
|
||||
if (!child) {
|
||||
child = pci_add_new_bus(bus, dev, next_busnr);
|
||||
if (!child)
|
||||
return max;
|
||||
pci_bus_insert_busn_res(child, next_busnr, bus->busn_res.end);
|
||||
}
|
||||
max++;
|
||||
if (available_buses)
|
||||
available_buses--;
|
||||
|
||||
buses = (buses & PCI_SEC_LATENCY_TIMER_MASK) |
|
||||
FIELD_PREP(PCI_PRIMARY_BUS_MASK, child->primary) |
|
||||
FIELD_PREP(PCI_SECONDARY_BUS_MASK, child->busn_res.start) |
|
||||
FIELD_PREP(PCI_SUBORDINATE_BUS_MASK, child->busn_res.end);
|
||||
|
||||
/*
|
||||
* yenta.c forces a secondary latency timer of 176.
|
||||
* Copy that behaviour here.
|
||||
*/
|
||||
buses &= ~PCI_SEC_LATENCY_TIMER_MASK;
|
||||
buses |= FIELD_PREP(PCI_SEC_LATENCY_TIMER_MASK, CARDBUS_LATENCY_TIMER);
|
||||
|
||||
/* We need to blast all three values with a single write */
|
||||
pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses);
|
||||
|
||||
/*
|
||||
* For CardBus bridges, we leave 4 bus numbers as cards with a
|
||||
* PCI-to-PCI bridge can be inserted later.
|
||||
*/
|
||||
for (i = 0; i < CARDBUS_RESERVE_BUSNR; i++) {
|
||||
struct pci_bus *parent = bus;
|
||||
|
||||
if (pci_find_bus(pci_domain_nr(bus), max + i + 1))
|
||||
break;
|
||||
|
||||
while (parent->parent) {
|
||||
if (!pcibios_assign_all_busses() &&
|
||||
(parent->busn_res.end > max) &&
|
||||
(parent->busn_res.end <= max + i)) {
|
||||
j = 1;
|
||||
}
|
||||
parent = parent->parent;
|
||||
}
|
||||
if (j) {
|
||||
/*
|
||||
* Often, there are two CardBus bridges -- try to
|
||||
* leave one valid bus number for each one.
|
||||
*/
|
||||
i /= 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
max += i;
|
||||
|
||||
/*
|
||||
* Set subordinate bus number to its real value. If fixed
|
||||
* subordinate bus number exists from EA capability then use it.
|
||||
*/
|
||||
if (fixed_buses)
|
||||
max = fixed_sub;
|
||||
pci_bus_update_busn_res_end(child, max);
|
||||
pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max);
|
||||
|
||||
scnprintf(child->name, sizeof(child->name), "PCI CardBus %04x:%02x",
|
||||
pci_domain_nr(bus), child->number);
|
||||
|
||||
pbus_validate_busn(child);
|
||||
|
||||
return max;
|
||||
}
|
||||
|
|
@ -359,7 +359,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno)
|
|||
|
||||
res->flags &= ~IORESOURCE_UNSET;
|
||||
res->flags &= ~IORESOURCE_STARTALIGN;
|
||||
if (resno >= PCI_BRIDGE_RESOURCES && resno <= PCI_BRIDGE_RESOURCE_END)
|
||||
if (pci_resource_is_bridge_win(resno))
|
||||
res->flags &= ~IORESOURCE_DISABLED;
|
||||
|
||||
pci_info(dev, "%s %pR: assigned\n", res_name, res);
|
||||
|
|
|
|||
|
|
@ -779,7 +779,7 @@ static void yenta_allocate_resources(struct yenta_socket *socket)
|
|||
IORESOURCE_MEM,
|
||||
PCI_CB_MEMORY_BASE_1, PCI_CB_MEMORY_LIMIT_1);
|
||||
if (program)
|
||||
pci_setup_cardbus(socket->dev->subordinate);
|
||||
pci_setup_cardbus_bridge(socket->dev->subordinate);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -338,7 +338,7 @@ static inline bool resource_union(const struct resource *r1, const struct resour
|
|||
* Check if this resource is added to a resource tree or detached. Caller is
|
||||
* responsible for not racing assignment.
|
||||
*/
|
||||
static inline bool resource_assigned(struct resource *res)
|
||||
static inline bool resource_assigned(const struct resource *res)
|
||||
{
|
||||
return res->parent;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1248,7 +1248,11 @@ void pci_stop_and_remove_bus_device(struct pci_dev *dev);
|
|||
void pci_stop_and_remove_bus_device_locked(struct pci_dev *dev);
|
||||
void pci_stop_root_bus(struct pci_bus *bus);
|
||||
void pci_remove_root_bus(struct pci_bus *bus);
|
||||
void pci_setup_cardbus(struct pci_bus *bus);
|
||||
#ifdef CONFIG_CARDBUS
|
||||
void pci_setup_cardbus_bridge(struct pci_bus *bus);
|
||||
#else
|
||||
static inline void pci_setup_cardbus_bridge(struct pci_bus *bus) { }
|
||||
#endif
|
||||
void pcibios_setup_bridge(struct pci_bus *bus, unsigned long type);
|
||||
void pci_sort_breadthfirst(void);
|
||||
#define dev_is_pci(d) ((d)->bus == &pci_bus_type)
|
||||
|
|
|
|||
|
|
@ -132,6 +132,11 @@
|
|||
#define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */
|
||||
#define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */
|
||||
#define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */
|
||||
/* Masks for dword-sized processing of Bus Number and Sec Latency Timer fields */
|
||||
#define PCI_PRIMARY_BUS_MASK 0x000000ff
|
||||
#define PCI_SECONDARY_BUS_MASK 0x0000ff00
|
||||
#define PCI_SUBORDINATE_BUS_MASK 0x00ff0000
|
||||
#define PCI_SEC_LATENCY_TIMER_MASK 0xff000000
|
||||
#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */
|
||||
#define PCI_IO_LIMIT 0x1d
|
||||
#define PCI_IO_RANGE_TYPE_MASK 0x0fUL /* I/O bridging type */
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ static struct resource *next_resource(struct resource *p, bool skip_children,
|
|||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
||||
enum { MAX_IORES_LEVEL = 5 };
|
||||
enum { MAX_IORES_LEVEL = 8 };
|
||||
|
||||
static void *r_start(struct seq_file *m, loff_t *pos)
|
||||
__acquires(resource_lock)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user