2026-01-29 22:25:33 +08:00

806 lines
22 KiB
C

// SPDX-License-Identifier: GPL-2.0
#include <linux/pci.h>
#include <linux/pci-acpi.h>
#include <linux/pci-ecam.h>
#include <asm/sw64_init.h>
void set_devint_wken(int node)
{
unsigned long val;
/* enable INTD wakeup */
val = 0x80;
sw64_io_write(node, DEVINT_WKEN, val);
sw64_io_write(node, DEVINTWK_INTEN, val);
}
#ifdef CONFIG_UNCORE_JUNZHANG
void set_adr_int(int node)
{
sw64_io_write(node, ADR_INT_CONFIG, (0x0 << 16 | 0x3f));
sw64_io_write(node, ADR_CTL, 0xc);
}
#endif
void set_pcieport_service_irq(int node, int index)
{
if (IS_ENABLED(CONFIG_PCIE_PME))
write_piu_ior0(node, index, PMEINTCONFIG, PME_ENABLE_INTD_CORE0);
if (IS_ENABLED(CONFIG_PCIEAER))
write_piu_ior0(node, index, AERERRINTCONFIG, AER_ENABLE_INTD_CORE0);
}
int chip_pcie_configure(struct pci_controller *hose)
{
struct pci_dev *dev;
struct pci_bus *bus, *top;
struct list_head *next;
unsigned int max_read_size, smallest_max_payload;
int max_payloadsize;
unsigned long rc_index, node;
unsigned long piuconfig0, value;
unsigned int pcie_caps_offset;
unsigned int rc_conf_value;
u16 devctl, new_values;
bool rc_ari_disabled = false, found = false;
unsigned char bus_max_num;
node = hose->node;
rc_index = hose->index;
smallest_max_payload = read_rc_conf(node, rc_index, RC_EXP_DEVCAP);
smallest_max_payload &= PCI_EXP_DEVCAP_PAYLOAD;
bus_max_num = hose->busn_space->start;
top = hose->bus;
bus = top;
next = top->devices.next;
for (;;) {
if (next == &bus->devices) {
/* end of this bus, go up or finish */
if (bus == top)
break;
next = bus->self->bus_list.next;
bus = bus->self->bus;
continue;
}
dev = list_entry(next, struct pci_dev, bus_list);
if (dev->subordinate) {
/* this is a pci-pci bridge, do its devices next */
next = dev->subordinate->devices.next;
bus = dev->subordinate;
} else
next = dev->bus_list.next;
if (!found) {
if (pci_is_root_bus(dev->bus)) {
if (list_empty(&dev->subordinate->devices))
rc_ari_disabled = true;
} else {
if (!pci_ari_enabled(dev->bus)) {
rc_ari_disabled = true;
found = true;
}
}
}
if (bus->busn_res.end > bus_max_num)
bus_max_num = bus->busn_res.end;
/* Query device PCIe capability register */
pcie_caps_offset = dev->pcie_cap;
if (pcie_caps_offset == 0)
continue;
max_payloadsize = dev->pcie_mpss;
if (max_payloadsize < smallest_max_payload)
smallest_max_payload = max_payloadsize;
}
if (rc_ari_disabled) {
rc_conf_value = read_rc_conf(node, rc_index, RC_EXP_DEVCTL2);
rc_conf_value &= ~PCI_EXP_DEVCTL2_ARI;
write_rc_conf(node, rc_index, RC_EXP_DEVCTL2, rc_conf_value);
} else {
rc_conf_value = read_rc_conf(node, rc_index, RC_EXP_DEVCTL2);
rc_conf_value |= PCI_EXP_DEVCTL2_ARI;
write_rc_conf(node, rc_index, RC_EXP_DEVCTL2, rc_conf_value);
}
rc_conf_value = read_rc_conf(node, rc_index, RC_EXP_DEVCAP);
rc_conf_value &= PCI_EXP_DEVCAP_PAYLOAD;
max_payloadsize = rc_conf_value;
if (max_payloadsize < smallest_max_payload)
smallest_max_payload = max_payloadsize;
max_read_size = 0x2; /* Limit to 512B */
value = read_rc_conf(node, rc_index, RC_EXP_DEVCTL);
value &= ~(PCI_EXP_DEVCTL_PAYLOAD | PCI_EXP_DEVCTL_READRQ);
value |= (max_read_size << 12) | (smallest_max_payload << 5);
write_rc_conf(node, rc_index, RC_EXP_DEVCTL, value);
new_values = (max_read_size << 12) | (smallest_max_payload << 5);
piuconfig0 = read_piu_ior0(node, rc_index, PIUCONFIG0);
piuconfig0 &= ~(0x7fUL << 9);
if (smallest_max_payload == 0x2) {
piuconfig0 |= (0x20UL << 9);
write_piu_ior0(node, rc_index, PIUCONFIG0, piuconfig0);
} else {
piuconfig0 |= (0x40UL << 9);
write_piu_ior0(node, rc_index, PIUCONFIG0, piuconfig0);
}
pr_info("Node%ld RC%ld MPSS %luB, MRRS %luB, Piuconfig0 %#lx, ARI %s\n",
node, rc_index, (1UL << smallest_max_payload) << 7,
(1UL << max_read_size) << 7, piuconfig0,
rc_ari_disabled ? "disabled" : "enabled");
/* Now, set the max_payload_size for all devices to that value. */
bus = top;
next = top->devices.next;
for (;;) {
if (next == &bus->devices) {
/* end of this bus, go up or finish */
if (bus == top)
break;
next = bus->self->bus_list.next;
bus = bus->self->bus;
continue;
}
dev = list_entry(next, struct pci_dev, bus_list);
if (dev->subordinate) {
/* this is a pci-pci bridge, do its devices next */
next = dev->subordinate->devices.next;
bus = dev->subordinate;
} else
next = dev->bus_list.next;
pcie_caps_offset = dev->pcie_cap;
if (pcie_caps_offset == 0)
continue;
pci_read_config_word(dev, pcie_caps_offset + PCI_EXP_DEVCTL, &devctl);
devctl &= ~(PCI_EXP_DEVCTL_PAYLOAD | PCI_EXP_DEVCTL_READRQ);
devctl |= new_values;
pci_write_config_word(dev, pcie_caps_offset + PCI_EXP_DEVCTL, devctl);
}
return bus_max_num;
}
static int check_pci_linkup(unsigned long node, unsigned long index)
{
unsigned long rc_debug;
if (is_guest_or_emul()) {
if (node == 0 && index == 0)
return 0;
else
return 1;
} else {
rc_debug = read_piu_ior1(node, index, RCDEBUGINF1);
}
return !(rc_debug == 0x111);
}
static void set_rc_piu(unsigned long node, unsigned long index)
{
unsigned int i __maybe_unused;
unsigned int value;
u32 rc_misc_ctrl;
if (is_guest_or_emul())
return;
/* configure RC, set PCI-E root controller */
write_rc_conf(node, index, RC_COMMAND, 0x00100007);
write_rc_conf(node, index, RC_PORT_LINK_CTL, 0x1f0020);
write_rc_conf(node, index, RC_EXP_DEVCTL, 0x2850);
write_rc_conf(node, index, RC_EXP_DEVCTL2, 0x6);
write_rc_conf(node, index, RC_ORDER_RULE_CTL, 0x0100);
/* enable DBI_RO_WR_EN */
rc_misc_ctrl = read_rc_conf(node, index, RC_MISC_CONTROL_1);
write_rc_conf(node, index, RC_MISC_CONTROL_1, rc_misc_ctrl | 0x1);
/* fix up DEVICE_ID_VENDOR_ID register */
value = (PCI_DEVICE_ID_SW64_ROOT_BRIDGE << 16) | PCI_VENDOR_ID_JN;
write_rc_conf(node, index, RC_VENDOR_ID, value);
/* set PCI-E root class code */
value = read_rc_conf(node, index, RC_REVISION_ID);
write_rc_conf(node, index, RC_REVISION_ID, (PCI_CLASS_BRIDGE_HOST << 16) | value);
/* disable DBI_RO_WR_EN */
write_rc_conf(node, index, RC_MISC_CONTROL_1, rc_misc_ctrl);
write_rc_conf(node, index, RC_PRIMARY_BUS, 0xffffff);
write_piu_ior0(node, index, PIUCONFIG0, PIUCONFIG0_INIT_VAL);
write_piu_ior1(node, index, PIUCONFIG1, 0x2);
write_piu_ior1(node, index, ERRENABLE, -1);
/* set DMA offset value PCITODMA_OFFSET */
write_piu_ior0(node, index, EPDMABAR, PCITODMA_OFFSET);
if (IS_ENABLED(CONFIG_PCI_MSI)) {
write_piu_ior0(node, index, MSIADDR, MSIX_MSG_ADDR);
#ifdef CONFIG_UNCORE_XUELANG
for (i = 0; i < 256; i++)
write_piu_ior0(node, index, MSICONFIG0 + (i << 7), 0);
#endif
}
}
static void set_intx(unsigned long node, unsigned long index,
unsigned long int_conf)
{
if (is_guest_or_emul())
return;
#if defined(CONFIG_UNCORE_XUELANG)
write_piu_ior0(node, index, INTACONFIG, int_conf | (0x8UL << 10));
write_piu_ior0(node, index, INTBCONFIG, int_conf | (0x4UL << 10));
write_piu_ior0(node, index, INTCCONFIG, int_conf | (0x2UL << 10));
write_piu_ior0(node, index, INTDCONFIG, int_conf | (0x1UL << 10));
#elif defined(CONFIG_UNCORE_JUNZHANG)
write_piu_ior0(node, index, INTACONFIG, int_conf | (0x1UL << 10));
write_piu_ior0(node, index, INTBCONFIG, int_conf | (0x2UL << 10));
write_piu_ior0(node, index, INTCCONFIG, int_conf | (0x4UL << 10));
write_piu_ior0(node, index, INTDCONFIG, int_conf | (0x8UL << 10));
#endif
}
static unsigned long get_rc_enable(unsigned long node)
{
unsigned long rc_enable;
if (is_guest_or_emul())
return 1;
rc_enable = sw64_io_read(node, IO_START);
return rc_enable;
}
static int map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
struct pci_controller *hose = pci_bus_to_pci_controller(dev->bus);
if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT)
return hose->service_irq;
else
return hose->int_irq;
}
static void hose_init(struct pci_controller *hose)
{
unsigned long pci_io_base;
hose->sparse_mem_base = 0;
hose->sparse_io_base = 0;
pci_io_base = IO_BASE | (hose->node << IO_NODE_SHIFT)
| PCI_BASE | (hose->index << IO_RC_SHIFT);
hose->dense_mem_base = pci_io_base;
hose->dense_io_base = pci_io_base | PCI_LEGACY_IO;
hose->ep_config_space_base = __va(pci_io_base | PCI_EP_CFG);
hose->rc_config_space_base = __va(pci_io_base | PCI_RC_CFG);
hose->mem_space->start = pci_io_base + PCI_32BIT_MEMIO;
hose->mem_space->end = hose->mem_space->start + PCI_32BIT_MEMIO_SIZE - 1;
hose->mem_space->name = "pci memory space";
hose->mem_space->flags = IORESOURCE_MEM;
if (request_resource(&iomem_resource, hose->mem_space) < 0)
pr_err("Failed to request MEM on hose %ld\n", hose->index);
hose->pre_mem_space->start = pci_io_base | PCI_64BIT_MEMIO;
hose->pre_mem_space->end = hose->pre_mem_space->start + PCI_64BIT_MEMIO_SIZE - 1;
hose->pre_mem_space->name = "pci pre mem space";
hose->pre_mem_space->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH | IORESOURCE_MEM_64;
if (request_resource(&iomem_resource, hose->pre_mem_space) < 0)
pr_err("Failed to request 64bit MEM on hose %ld\n", hose->index);
hose->io_space->start = pci_io_base | PCI_LEGACY_IO;
hose->io_space->end = hose->io_space->start + PCI_LEGACY_IO_SIZE - 1;
hose->io_space->name = "pci io space";
hose->io_space->flags = IORESOURCE_IO;
if (request_resource(&ioport_resource, hose->io_space) < 0)
pr_err("Failed to request IO on hose %ld\n", hose->index);
hose->busn_space->name = "PCI busn";
hose->busn_space->start = 0xff;
hose->busn_space->end = 0xff;
hose->busn_space->flags = IORESOURCE_BUS;
hose->first_busno = hose->self_busno = hose->busn_space->start;
hose->last_busno = hose->busn_space->end;
if (is_in_host()) {
if (IS_ENABLED(CONFIG_PCI_MSI))
memset(hose->piu_msiconfig, 0, 256/8);
}
};
static struct sw64_pci_init_ops chip_pci_init_ops = {
.map_irq = map_irq,
.get_rc_enable = get_rc_enable,
.hose_init = hose_init,
.set_rc_piu = set_rc_piu,
.check_pci_linkup = check_pci_linkup,
.set_intx = set_intx,
};
void __init setup_chip_pci_ops(void)
{
sw64_chip_init->pci_init = chip_pci_init_ops;
}
static unsigned long rc_linkup;
static struct pci_controller *head, **tail = &head;
static void pci_mark_rc_linkup(unsigned long node, unsigned long index)
{
set_bit(node * 8 + index, &rc_linkup);
}
static int pci_get_rc_linkup(unsigned long node, unsigned long index)
{
return test_bit(node * 8 + index, &rc_linkup);
}
/**
* Link the specified pci controller to list
*/
extern struct pci_controller *hose_head;
static void pci_link_controller(struct pci_controller *hose)
{
if (unlikely(!hose))
return;
*tail = hose;
tail = &hose->next;
if (!hose_head)
hose_head = head;
}
struct pci_controller *bus_num_to_pci_controller(unsigned long bus_num)
{
struct pci_controller *hose;
for (hose = head; hose; hose = hose->next) {
if (bus_num >= hose->first_busno && bus_num <= hose->last_busno)
return hose;
}
return NULL;
}
struct pci_controller *pci_bus_to_pci_controller(const struct pci_bus *bus)
{
struct pci_config_window *cfg = NULL;
if (unlikely(!bus))
return NULL;
if (acpi_disabled)
return (struct pci_controller *)(bus->sysdata);
cfg = (struct pci_config_window *)bus->sysdata;
return (struct pci_controller *)(cfg->priv);
}
/**
* PCIe Root Complex read config space operations
*/
static int sw64_pcie_read_rc_cfg(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val)
{
u32 data;
struct pci_controller *hose = pci_bus_to_pci_controller(bus);
void __iomem *cfg_iobase = hose->rc_config_space_base;
if (IS_ENABLED(CONFIG_PCI_DEBUG))
pr_debug("rc read addr:%px bus %d, devfn %#x, where %#x size=%d\t",
cfg_iobase + ((where & ~3) << 5), bus->number, devfn, where, size);
if ((uintptr_t)where & (size - 1)) {
*val = 0;
return PCIBIOS_BAD_REGISTER_NUMBER;
}
/**
* Workaround for sw6a chipset due to only support scan with devfn = 0,
* while sw6b does not have this limit.
*/
if (unlikely(devfn > 0)) {
*val = ~0;
return PCIBIOS_DEVICE_NOT_FOUND;
}
data = readl(cfg_iobase + ((where & ~3) << 5));
switch (size) {
case 1:
*val = (data >> (8 * (where & 0x3))) & 0xff;
break;
case 2:
*val = (data >> (8 * (where & 0x2))) & 0xffff;
break;
default:
*val = data;
break;
}
if (IS_ENABLED(CONFIG_PCI_DEBUG))
pr_debug("*val %#x\n ", *val);
return PCIBIOS_SUCCESSFUL;
}
/**
* PCIe Root Complex write config space operations
*/
int sw64_pcie_write_rc_cfg(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 val)
{
u32 data;
u32 shift = 8 * (where & 3);
struct pci_controller *hose = pci_bus_to_pci_controller(bus);
void __iomem *cfg_iobase = (void *)hose->rc_config_space_base;
if ((uintptr_t)where & (size - 1))
return PCIBIOS_BAD_REGISTER_NUMBER;
switch (size) {
case 1:
data = readl(cfg_iobase + ((where & ~3) << 5));
data &= ~(0xff << shift);
data |= (val & 0xff) << shift;
break;
case 2:
data = readl(cfg_iobase + ((where & ~3) << 5));
data &= ~(0xffff << shift);
data |= (val & 0xffff) << shift;
break;
default:
data = val;
break;
}
if (IS_ENABLED(CONFIG_PCI_DEBUG))
pr_debug("rc write addr:%px bus %d, devfn %#x, where %#x *val %#x size %d\n",
cfg_iobase + ((where & ~3) << 5), bus->number, devfn, where, val, size);
writel(data, cfg_iobase + ((where & ~3) << 5));
return PCIBIOS_SUCCESSFUL;
}
/**
* sw64_pcie_valid_device - check if a valid device is present on bus
* @bus : PCI bus structure
* @devfn: device/function
*
* @return: 'true' on success and 'false' if invalid device is found
*/
static bool sw64_pcie_valid_device(struct pci_bus *bus, unsigned int devfn)
{
struct pci_controller *hose = pci_bus_to_pci_controller(bus);
if (is_in_host()) {
/* Only one device down on each root complex */
if (bus->number == hose->self_busno && devfn > 0)
return false;
}
return true;
}
/**
* sw64_pcie_config_read - read val from config space of PCI host controller or device
* @bus : PCI bus structure
* @devfn: device/function
* @where: offset from base
* @size : size of val
* @val[out]: the value read from PCI host controller or device
*
* @return: Whether read operation success
*/
static int sw64_pcie_config_read(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val)
{
struct pci_controller *hose = pci_bus_to_pci_controller(bus);
int ret = PCIBIOS_DEVICE_NOT_FOUND;
if (is_guest_or_emul())
return pci_generic_config_read(bus, devfn, where, size, val);
hose->self_busno = hose->busn_space->start;
if (unlikely(bus->number == hose->self_busno)) {
ret = sw64_pcie_read_rc_cfg(bus, devfn, where, size, val);
} else {
if (pci_get_rc_linkup(hose->node, hose->index))
ret = pci_generic_config_read(bus, devfn, where, size, val);
else
return ret;
}
return ret;
}
/**
* sw64_pcie_config_write - write val to config space of PCI host controller or device
* @bus : PCI bus structure
* @devfn: device/function
* @where: offset from base
* @size : size of val
* @val : the value write to PCI host controller or device
*
* @return: Whether write operation success
*/
static int sw64_pcie_config_write(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 val)
{
struct pci_controller *hose = pci_bus_to_pci_controller(bus);
if (is_guest_or_emul())
return pci_generic_config_write(bus, devfn, where, size, val);
hose->self_busno = hose->busn_space->start;
if (unlikely(bus->number == hose->self_busno))
return sw64_pcie_write_rc_cfg(bus, devfn, where, size, val);
else
return pci_generic_config_write(bus, devfn, where, size, val);
}
/**
* sw64_pcie_map_bus - get configuration base address
* @bus : PCI bus structure
* @devfn: device/function
* @where: offset from base
*
* @return: base address of the configuration space needed to be
* accessed.
*/
static void __iomem *sw64_pcie_map_bus(struct pci_bus *bus,
unsigned int devfn, int where)
{
struct pci_controller *hose = pci_bus_to_pci_controller(bus);
void __iomem *cfg_iobase;
unsigned long relbus;
if (!sw64_pcie_valid_device(bus, devfn))
return NULL;
/**
* ECAM of Sunway PCI host controller is slightly
* different from the standrad:
* [31:24]: bus number
* [23:19]: device number
* [18:16]: function number
* [15:12]: reserved
* [11:8] : extended config space registers
* [7:2] : legacy config space registers
*/
relbus = (bus->number << 24) | (devfn << 16) | where;
cfg_iobase = hose->ep_config_space_base + relbus;
if (IS_ENABLED(CONFIG_PCI_DEBUG))
pr_debug("addr:%px bus %d, devfn %d, where %d\n",
cfg_iobase, bus->number, devfn, where);
return cfg_iobase;
}
#ifdef CONFIG_ACPI
int sw64_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
return map_irq(dev, slot, pin);
}
static void setup_intx_irqs(struct pci_controller *hose)
{
unsigned long int_conf, node, val_node;
unsigned long index, irq;
int rcid;
node = hose->node;
index = hose->index;
if (!node_online(node))
val_node = next_node_in(node, node_online_map);
else
val_node = node;
irq = irq_alloc_descs_from(NR_IRQS_LEGACY, 2, val_node);
WARN_ON(irq < 0);
irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_level_irq);
irq_set_status_flags(irq, IRQ_LEVEL);
hose->int_irq = irq;
irq_set_chip_and_handler(irq + 1, &dummy_irq_chip, handle_level_irq);
hose->service_irq = irq + 1;
rcid = cpu_to_rcid(0);
pr_info_once("INTx are directed to node %d core %d.\n",
((rcid >> 6) & 0x3), (rcid & 0x1f));
int_conf = 1UL << 62 | rcid; /* rebase all intx on the first logical cpu */
set_intx(node, index, int_conf);
set_pcieport_service_irq(node, index);
}
static int sw64_pci_prepare_controller(struct pci_controller *hose,
struct acpi_device *adev)
{
unsigned long long index, node;
unsigned long long rc_config_base_addr;
unsigned long long pci_io_base_addr;
unsigned long long ep_io_base_addr;
acpi_status rc;
/* Get node from ACPI namespace */
node = acpi_get_node(adev->handle);
if (node == NUMA_NO_NODE) {
dev_err(&adev->dev, "unable to get node ID\n");
return -EEXIST;
}
/* Get index from ACPI namespace */
rc = acpi_evaluate_integer(adev->handle, "INDX", NULL, &index);
if (rc != AE_OK) {
dev_err(&adev->dev, "unable to retrieve INDX\n");
return -EEXIST;
}
/**
* Get Root Complex config space base address.
*
* For sw64, Root Complex config space base addr is different
* from Endpoint config space base address. Use MCFG table to
* pass Endpoint config space base address, and define Root Complex
* config space base address("RCCB") separately in the ACPI namespace.
*/
rc = acpi_evaluate_integer(adev->handle, "RCCB", NULL, &rc_config_base_addr);
if (rc != AE_OK) {
dev_err(&adev->dev, "unable to retrieve RCCB\n");
return -EEXIST;
}
/* Get Root Complex I/O space base addr from ACPI namespace */
rc = acpi_evaluate_integer(adev->handle, "RCIO", NULL, &pci_io_base_addr);
if (rc != AE_OK) {
dev_err(&adev->dev, "unable to retrieve RCIO\n");
return -EEXIST;
}
/* Get Endpoint I/O space base addr from ACPI namespace */
rc = acpi_evaluate_integer(adev->handle, "EPIO", NULL, &ep_io_base_addr);
if (rc != AE_OK) {
dev_err(&adev->dev, "unable to retrieve EPIO\n");
return -EEXIST;
}
hose->iommu_enable = false;
hose->index = index;
hose->node = node;
hose->sparse_mem_base = 0;
hose->sparse_io_base = 0;
hose->dense_mem_base = pci_io_base_addr;
hose->dense_io_base = ep_io_base_addr;
hose->rc_config_space_base = __va(rc_config_base_addr);
hose->first_busno = 0xff;
hose->last_busno = 0xff;
hose->self_busno = 0xff;
hose->need_domain_info = 0;
#if IS_ENABLED(CONFIG_PCI_MSI)
if (is_in_host())
memset(hose->piu_msiconfig, 0, 256 / 8); /* 256 bits bitmap */
#endif
/**
* There are two prerequisites for Root Complex
* of Sunway to work:
* 1. Root Complex enable
* 2. Root Complex link up
*/
set_rc_piu(hose->node, hose->index);
if (check_pci_linkup(hose->node, hose->index)) {
/**
* Root Complex link up failed.
* This usually means that no device on the slot.
*/
dev_info(&adev->dev, "<Node [%ld], RC [%ld]>: failed to link up\n",
hose->node, hose->index);
} else {
pci_mark_rc_linkup(hose->node, hose->index);
dev_info(&adev->dev, "<Node [%ld], RC [%ld]>: successfully link up\n",
hose->node, hose->index);
}
setup_intx_irqs(hose);
pci_link_controller(hose);
return 0;
}
/**
* Use the info from ACPI to init pci_controller
*/
static int sw64_pci_ecam_init(struct pci_config_window *cfg)
{
struct pci_controller *hose = NULL;
struct device *dev = cfg->parent;
struct acpi_device *adev = to_acpi_device(dev);
phys_addr_t mcfg_addr;
int ret;
/**
* First, check whether Root Complex is enabled.
* If Root Complex disabled, there's no need to continue.
*
* In ACPI namespace, we use _STA method to indicate
* whether Root Complex is enabled.
*
* The _STA has been checked when creating acpi_device.
* Double check here to get the latest hardware status.
*/
ret = acpi_bus_get_status(adev);
if (ret) {
dev_err(dev, "unable to retrieve _STA\n");
return ret;
}
if (!adev->status.present) {
dev_err(dev, "RC is not enabled\n");
return -ENODEV;
}
hose = kzalloc(sizeof(*hose), GFP_KERNEL);
if (!hose) {
dev_err(dev, "out of memory when alloc mem for pci_controller\n");
return -ENOMEM;
}
/* Get Endpoint config space base address from MCFG table */
mcfg_addr = cfg->res.start - (cfg->busr.start << cfg->ops->bus_shift);
/**
* "__va(mcfg_addr)" is equal to "cfg->win", so we can also use
* "hose->ep_config_space_base = cfg->win" here
*/
hose->ep_config_space_base = __va(mcfg_addr);
/* Init pci_controller */
ret = sw64_pci_prepare_controller(hose, adev);
if (ret) {
kfree(hose);
dev_err(&adev->dev, "failed to init pci controller\n");
return ret;
}
cfg->priv = (void *)hose;
return 0;
}
const struct pci_ecam_ops sw64_pci_ecam_ops = {
.bus_shift = 24,
.init = sw64_pci_ecam_init,
.pci_ops = {
.map_bus = sw64_pcie_map_bus,
.read = sw64_pcie_config_read,
.write = sw64_pcie_config_write,
}
};
#endif