mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:04:41 +01:00
The PCI core's pci_scan_bridge_extend() contains convoluted logic specific to setting up bus numbers for legacy CardBus bridges. Extract the CardBus specific part out into setup-cardbus.c to make the core code cleaner and allow omitting CardBus bridge support from modern systems. Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Link: https://patch.msgid.link/20251219174036.16738-24-ilpo.jarvinen@linux.intel.com
306 lines
8.6 KiB
C
306 lines
8.6 KiB
C
// 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;
|
|
}
|