mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
158 lines
3.9 KiB
158 lines
3.9 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
/* |
|
* Support for indirect PCI bridges. |
|
* |
|
* Copyright (C) 1998 Gabriel Paubert. |
|
*/ |
|
|
|
#include <linux/kernel.h> |
|
#include <linux/pci.h> |
|
#include <linux/delay.h> |
|
#include <linux/string.h> |
|
#include <linux/init.h> |
|
|
|
#include <linux/io.h> |
|
#include <asm/pci-bridge.h> |
|
|
|
static int |
|
indirect_read_config(struct pci_bus *bus, unsigned int devfn, int offset, |
|
int len, u32 *val) |
|
{ |
|
struct pci_controller *hose = pci_bus_to_host(bus); |
|
volatile void __iomem *cfg_data; |
|
u8 cfg_type = 0; |
|
u32 bus_no, reg; |
|
|
|
if (hose->indirect_type & INDIRECT_TYPE_NO_PCIE_LINK) { |
|
if (bus->number != hose->first_busno) |
|
return PCIBIOS_DEVICE_NOT_FOUND; |
|
if (devfn != 0) |
|
return PCIBIOS_DEVICE_NOT_FOUND; |
|
} |
|
|
|
if (hose->indirect_type & INDIRECT_TYPE_SET_CFG_TYPE) |
|
if (bus->number != hose->first_busno) |
|
cfg_type = 1; |
|
|
|
bus_no = (bus->number == hose->first_busno) ? |
|
hose->self_busno : bus->number; |
|
|
|
if (hose->indirect_type & INDIRECT_TYPE_EXT_REG) |
|
reg = ((offset & 0xf00) << 16) | (offset & 0xfc); |
|
else |
|
reg = offset & 0xfc; /* Only 3 bits for function */ |
|
|
|
if (hose->indirect_type & INDIRECT_TYPE_BIG_ENDIAN) |
|
out_be32(hose->cfg_addr, (0x80000000 | (bus_no << 16) | |
|
(devfn << 8) | reg | cfg_type)); |
|
else |
|
out_le32(hose->cfg_addr, (0x80000000 | (bus_no << 16) | |
|
(devfn << 8) | reg | cfg_type)); |
|
|
|
/* |
|
* Note: the caller has already checked that offset is |
|
* suitably aligned and that len is 1, 2 or 4. |
|
*/ |
|
cfg_data = hose->cfg_data + (offset & 3); /* Only 3 bits for function */ |
|
switch (len) { |
|
case 1: |
|
*val = in_8(cfg_data); |
|
break; |
|
case 2: |
|
*val = in_le16(cfg_data); |
|
break; |
|
default: |
|
*val = in_le32(cfg_data); |
|
break; |
|
} |
|
return PCIBIOS_SUCCESSFUL; |
|
} |
|
|
|
static int |
|
indirect_write_config(struct pci_bus *bus, unsigned int devfn, int offset, |
|
int len, u32 val) |
|
{ |
|
struct pci_controller *hose = pci_bus_to_host(bus); |
|
volatile void __iomem *cfg_data; |
|
u8 cfg_type = 0; |
|
u32 bus_no, reg; |
|
|
|
if (hose->indirect_type & INDIRECT_TYPE_NO_PCIE_LINK) { |
|
if (bus->number != hose->first_busno) |
|
return PCIBIOS_DEVICE_NOT_FOUND; |
|
if (devfn != 0) |
|
return PCIBIOS_DEVICE_NOT_FOUND; |
|
} |
|
|
|
if (hose->indirect_type & INDIRECT_TYPE_SET_CFG_TYPE) |
|
if (bus->number != hose->first_busno) |
|
cfg_type = 1; |
|
|
|
bus_no = (bus->number == hose->first_busno) ? |
|
hose->self_busno : bus->number; |
|
|
|
if (hose->indirect_type & INDIRECT_TYPE_EXT_REG) |
|
reg = ((offset & 0xf00) << 16) | (offset & 0xfc); |
|
else |
|
reg = offset & 0xfc; |
|
|
|
if (hose->indirect_type & INDIRECT_TYPE_BIG_ENDIAN) |
|
out_be32(hose->cfg_addr, (0x80000000 | (bus_no << 16) | |
|
(devfn << 8) | reg | cfg_type)); |
|
else |
|
out_le32(hose->cfg_addr, (0x80000000 | (bus_no << 16) | |
|
(devfn << 8) | reg | cfg_type)); |
|
|
|
/* suppress setting of PCI_PRIMARY_BUS */ |
|
if (hose->indirect_type & INDIRECT_TYPE_SURPRESS_PRIMARY_BUS) |
|
if ((offset == PCI_PRIMARY_BUS) && |
|
(bus->number == hose->first_busno)) |
|
val &= 0xffffff00; |
|
|
|
/* Workaround for PCI_28 Errata in 440EPx/GRx */ |
|
if ((hose->indirect_type & INDIRECT_TYPE_BROKEN_MRM) && |
|
offset == PCI_CACHE_LINE_SIZE) { |
|
val = 0; |
|
} |
|
|
|
/* |
|
* Note: the caller has already checked that offset is |
|
* suitably aligned and that len is 1, 2 or 4. |
|
*/ |
|
cfg_data = hose->cfg_data + (offset & 3); |
|
switch (len) { |
|
case 1: |
|
out_8(cfg_data, val); |
|
break; |
|
case 2: |
|
out_le16(cfg_data, val); |
|
break; |
|
default: |
|
out_le32(cfg_data, val); |
|
break; |
|
} |
|
|
|
return PCIBIOS_SUCCESSFUL; |
|
} |
|
|
|
static struct pci_ops indirect_pci_ops = { |
|
.read = indirect_read_config, |
|
.write = indirect_write_config, |
|
}; |
|
|
|
void __init |
|
setup_indirect_pci(struct pci_controller *hose, |
|
resource_size_t cfg_addr, |
|
resource_size_t cfg_data, u32 flags) |
|
{ |
|
resource_size_t base = cfg_addr & PAGE_MASK; |
|
void __iomem *mbase; |
|
|
|
mbase = ioremap(base, PAGE_SIZE); |
|
hose->cfg_addr = mbase + (cfg_addr & ~PAGE_MASK); |
|
if ((cfg_data & PAGE_MASK) != base) |
|
mbase = ioremap(cfg_data & PAGE_MASK, PAGE_SIZE); |
|
hose->cfg_data = mbase + (cfg_data & ~PAGE_MASK); |
|
hose->ops = &indirect_pci_ops; |
|
hose->indirect_type = flags; |
|
}
|
|
|