Skip to content
8 changes: 5 additions & 3 deletions arch/loongarch/pci/acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,11 @@ static int acpi_prepare_root_resources(struct acpi_pci_root_info *ci)
if (status > 0) {
resource_list_for_each_entry_safe(entry, tmp, &ci->resources) {
if (entry->res->flags & IORESOURCE_MEM) {
entry->offset = ci->root->mcfg_addr & GENMASK_ULL(63, 40);
entry->res->start |= entry->offset;
entry->res->end |= entry->offset;
if (!entry->offset) {
entry->offset = ci->root->mcfg_addr & GENMASK_ULL(63, 40);
entry->res->start |= entry->offset;
entry->res->end |= entry->offset;
}
}
}
return status;
Expand Down
147 changes: 144 additions & 3 deletions drivers/pci/controller/pci-loongson.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <linux/pci_ids.h>
#include <linux/pci-acpi.h>
#include <linux/pci-ecam.h>
#include <linux/vgaarb.h>

#include "../pci.h"

Expand All @@ -32,6 +33,7 @@
#define DEV_LS7A_CONF 0x7a10
#define DEV_LS7A_GNET 0x7a13
#define DEV_LS7A_EHCI 0x7a14
#define DEV_LS7A_OHCI 0x7a24
#define DEV_LS7A_DC2 0x7a36
#define DEV_LS7A_HDMI 0x7a37

Expand Down Expand Up @@ -80,6 +82,20 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
DEV_LS7A_LPC, system_bus_quirk);

static void loongson_d3_quirk(struct pci_dev *pdev)
{
pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3;
pdev->no_d1d2 = 1;
}
DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_LOONGSON,
DEV_LS7A_PCIE_PORT3, loongson_d3_quirk);
DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_LOONGSON,
DEV_LS7A_PCIE_PORT4, loongson_d3_quirk);
DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_LOONGSON,
DEV_LS7A_PCIE_PORT5, loongson_d3_quirk);
DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_LOONGSON,
DEV_LS7A_PCIE_PORT6, loongson_d3_quirk);

/*
* Some Loongson PCIe ports have hardware limitations on their Maximum Read
* Request Size. They can't handle anything larger than this. Sane
Expand Down Expand Up @@ -163,6 +179,98 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON,
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON,
DEV_LS7A_HDMI, loongson_pci_pin_quirk);

static void loongson_ohci_quirk(struct pci_dev *dev)
{
if (dev->revision == 0x2)
dev->resource[0].start += 0x1000;
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_OHCI, loongson_ohci_quirk);

static void loongson_display_quirk(struct pci_dev *dev)
{
u32 val;
u64 mask, size;
u64 max_size = 0;
int i, num;
struct pci_bus *bus = dev->bus;

if (!dev->bus->number) {
if (!(dev->vendor == PCI_VENDOR_ID_LOONGSON && dev->device == 0x7a25))
return;
} else {
while (!pci_is_root_bus(bus->parent))
bus = bus->parent;

/* ensure slot is 7a2000 */
if (bus->self->vendor != PCI_VENDOR_ID_LOONGSON || bus->self->device < 0x7a39)
return;
}
max_size = 0;
for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
if (dev->resource[i].flags & IORESOURCE_MEM) {
size = dev->resource[i].end - dev->resource[i].start;
if (size > max_size) {
max_size = size;
num = i;
}
}
}
mask = ~(dev->resource[num].end - dev->resource[num].start);
val = (dev->resource[num].start >> (24 - 16)) | ((mask >> 24) & 0xffff);
writel(val, (volatile void *)0x80000efdfb000174UL);
writel(0x80000000, (volatile void *)0x80000efdfb000170UL);
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, 0x7a25, loongson_display_quirk);
DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID,
PCI_BASE_CLASS_DISPLAY, 16, loongson_display_quirk);

static void pci_fixup_aspeed(struct pci_dev *pdev)
{
struct pci_dev *bridge;
struct pci_bus *bus;
struct pci_dev *vdevp = NULL;
u16 config;

bus = pdev->bus;
bridge = bus->self;

/* Is VGA routed to us? */
if (bridge && (pci_is_bridge(bridge))) {
pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &config);

/* Yes, this bridge is PCI bridge-to-bridge spec compliant,
* just return!
*/
if (config & PCI_BRIDGE_CTL_VGA)
return;

dev_warn(&pdev->dev, "VGA bridge control is not enabled\n");
}

/* Just return if the system already have a default device */
if (vga_default_device())
return;

/* No default vga device */
while ((vdevp = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, vdevp))) {
if (vdevp->vendor != 0x1a03) {
/* Have other vga devcie in the system, do nothing */
dev_info(&pdev->dev,
"Another boot vga device: 0x%x:0x%x\n",
vdevp->vendor, vdevp->device);
return;
}
}

vga_set_default_device(pdev);

dev_info(&pdev->dev,
"Boot vga device set as 0x%x:0x%x\n",
pdev->vendor, pdev->device);
}
DECLARE_PCI_FIXUP_CLASS_FINAL(0x1a03, 0x2000,
PCI_CLASS_DISPLAY_VGA, 8, pci_fixup_aspeed);

static struct loongson_pci *pci_bus_to_loongson_pci(struct pci_bus *bus)
{
struct pci_config_window *cfg;
Expand Down Expand Up @@ -242,6 +350,36 @@ static void __iomem *pci_loongson_map_bus(struct pci_bus *bus,
return NULL;
}

static int pci_loongson_config_read(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val)
{
void __iomem *addr;

addr = bus->ops->map_bus(bus, devfn, where);
if (!addr) {
*val = ~0;
return PCIBIOS_DEVICE_NOT_FOUND;
}

if (size == 1)
*val = readb(addr);
else if (size == 2)
*val = readw(addr);
else
*val = readl(addr);
/*
* fix some pcie card not scanning properly when bus number is
* inconsistent during firmware and kernel scan phases.
*/
if (*val == 0x0 && where == PCI_VENDOR_ID) {
writel(*val, addr);
*val = readl(addr);
}


return PCIBIOS_SUCCESSFUL;
}

#ifdef CONFIG_OF

static int loongson_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
Expand All @@ -265,7 +403,7 @@ static int loongson_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
/* LS2K/LS7A accept 8/16/32-bit PCI config operations */
static struct pci_ops loongson_pci_ops = {
.map_bus = pci_loongson_map_bus,
.read = pci_generic_config_read,
.read = pci_loongson_config_read,
.write = pci_generic_config_write,
};

Expand Down Expand Up @@ -308,6 +446,7 @@ static int loongson_pci_probe(struct platform_device *pdev)
struct device_node *node = dev->of_node;
struct pci_host_bridge *bridge;
struct resource *regs;
unsigned int num = 0;

if (!node)
return -ENODEV;
Expand All @@ -332,7 +471,9 @@ static int loongson_pci_probe(struct platform_device *pdev)
}

if (priv->data->flags & FLAG_CFG1) {
regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (priv->cfg0_base)
num = 1;
regs = platform_get_resource(pdev, IORESOURCE_MEM, num);
if (!regs)
dev_info(dev, "missing mem resource for cfg1\n");
else {
Expand Down Expand Up @@ -389,7 +530,7 @@ const struct pci_ecam_ops loongson_pci_ecam_ops = {
.init = loongson_pci_ecam_init,
.pci_ops = {
.map_bus = pci_loongson_map_bus,
.read = pci_generic_config_read,
.read = pci_loongson_config_read,
.write = pci_generic_config_write,
}
};
Expand Down
20 changes: 18 additions & 2 deletions drivers/pci/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
#include <asm/dma.h>
#include <linux/aer.h>
#include <linux/bitfield.h>
#ifdef CONFIG_MACH_LOONGSON64
#include <linux/suspend.h>
#endif
#include "pci.h"

DEFINE_MUTEX(pci_slot_mutex);
Expand Down Expand Up @@ -172,6 +175,15 @@ static bool pci_bridge_d3_disable;
/* Force bridge_d3 for all PCIe ports */
static bool pci_bridge_d3_force;

#ifdef CONFIG_MACH_LOONGSON64

#ifndef CONFIG_PM_SLEEP
suspend_state_t pm_suspend_target_state;
#define pm_suspend_target_state (PM_SUSPEND_ON)
#endif

#endif

static int __init pcie_port_pm_setup(char *str)
{
if (!strcmp(str, "off"))
Expand Down Expand Up @@ -6186,8 +6198,9 @@ int pcie_set_readrq(struct pci_dev *dev, int rq)
{
u16 v;
int ret;
#ifdef CONFIG_MACH_LOONGSON64
struct pci_host_bridge *bridge = pci_find_host_bridge(dev->bus);

#endif
if (rq < 128 || rq > 4096 || !is_power_of_2(rq))
return -EINVAL;

Expand All @@ -6205,14 +6218,17 @@ int pcie_set_readrq(struct pci_dev *dev, int rq)

v = (ffs(rq) - 8) << 12;

if (bridge->no_inc_mrrs) {
#ifdef CONFIG_MACH_LOONGSON64
if (pm_suspend_target_state == PM_SUSPEND_ON &&
bridge->no_inc_mrrs) {
int max_mrrs = pcie_get_readrq(dev);

if (rq > max_mrrs) {
pci_info(dev, "can't set Max_Read_Request_Size to %d; max is %d\n", rq, max_mrrs);
return -EINVAL;
}
}
#endif

ret = pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
PCI_EXP_DEVCTL_READRQ, v);
Expand Down