Merge branch 'pci/enumeration'

- Implement portdrv .shutdown() method that calls service driver .remove()
  methods (which disables interrupt generation as required by .shutdown()),
  but doesn't disable bus mastering (which hangs on Loongson LS7A because
  of a hardware defect) (Huacai Chen)

- Prevent MRRS increases for devices below Loongson LS7A to avoid hardware
  limitations (Huacai Chen)

- Ignore devices with a firmware (DT/ACPI) node that says the device is
  disabled (Rob Herring)

* pci/enumeration:
  PCI: Honor firmware's device disabled status
  PCI: loongson: Add more devices that need MRRS quirk
  PCI: loongson: Prevent LS7A MRRS increases
  PCI/portdrv: Prevent LS7A Bus Master clearing on shutdown
This commit is contained in:
Bjorn Helgaas 2023-02-22 13:47:24 -06:00
commit a17613298f
5 changed files with 62 additions and 36 deletions

View File

@ -15,9 +15,14 @@
#include "../pci.h"
/* Device IDs */
#define DEV_PCIE_PORT_0 0x7a09
#define DEV_PCIE_PORT_1 0x7a19
#define DEV_PCIE_PORT_2 0x7a29
#define DEV_LS2K_PCIE_PORT0 0x1a05
#define DEV_LS7A_PCIE_PORT0 0x7a09
#define DEV_LS7A_PCIE_PORT1 0x7a19
#define DEV_LS7A_PCIE_PORT2 0x7a29
#define DEV_LS7A_PCIE_PORT3 0x7a39
#define DEV_LS7A_PCIE_PORT4 0x7a49
#define DEV_LS7A_PCIE_PORT5 0x7a59
#define DEV_LS7A_PCIE_PORT6 0x7a69
#define DEV_LS2K_APB 0x7a02
#define DEV_LS7A_GMAC 0x7a03
@ -53,11 +58,11 @@ static void bridge_class_quirk(struct pci_dev *dev)
dev->class = PCI_CLASS_BRIDGE_PCI_NORMAL;
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
DEV_PCIE_PORT_0, bridge_class_quirk);
DEV_LS7A_PCIE_PORT0, bridge_class_quirk);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
DEV_PCIE_PORT_1, bridge_class_quirk);
DEV_LS7A_PCIE_PORT1, bridge_class_quirk);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
DEV_PCIE_PORT_2, bridge_class_quirk);
DEV_LS7A_PCIE_PORT2, bridge_class_quirk);
static void system_bus_quirk(struct pci_dev *pdev)
{
@ -75,37 +80,33 @@ 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_mrrs_quirk(struct pci_dev *dev)
static void loongson_mrrs_quirk(struct pci_dev *pdev)
{
struct pci_bus *bus = dev->bus;
struct pci_dev *bridge;
static const struct pci_device_id bridge_devids[] = {
{ PCI_VDEVICE(LOONGSON, DEV_PCIE_PORT_0) },
{ PCI_VDEVICE(LOONGSON, DEV_PCIE_PORT_1) },
{ PCI_VDEVICE(LOONGSON, DEV_PCIE_PORT_2) },
{ 0, },
};
/*
* Some Loongson PCIe ports have h/w limitations of maximum read
* request size. They can't handle anything larger than this. So
* force this limit on any devices attached under these ports.
*/
struct pci_host_bridge *bridge = pci_find_host_bridge(pdev->bus);
/* look for the matching bridge */
while (!pci_is_root_bus(bus)) {
bridge = bus->self;
bus = bus->parent;
/*
* Some Loongson PCIe ports have a h/w limitation of
* 256 bytes maximum read request size. They can't handle
* anything larger than this. So force this limit on
* any devices attached under these ports.
*/
if (pci_match_id(bridge_devids, bridge)) {
if (pcie_get_readrq(dev) > 256) {
pci_info(dev, "limiting MRRS to 256\n");
pcie_set_readrq(dev, 256);
}
break;
}
}
bridge->no_inc_mrrs = 1;
}
DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, loongson_mrrs_quirk);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
DEV_LS2K_PCIE_PORT0, loongson_mrrs_quirk);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
DEV_LS7A_PCIE_PORT0, loongson_mrrs_quirk);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
DEV_LS7A_PCIE_PORT1, loongson_mrrs_quirk);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
DEV_LS7A_PCIE_PORT2, loongson_mrrs_quirk);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
DEV_LS7A_PCIE_PORT3, loongson_mrrs_quirk);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
DEV_LS7A_PCIE_PORT4, loongson_mrrs_quirk);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
DEV_LS7A_PCIE_PORT5, loongson_mrrs_quirk);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
DEV_LS7A_PCIE_PORT6, loongson_mrrs_quirk);
static void loongson_pci_pin_quirk(struct pci_dev *pdev)
{

View File

@ -6033,6 +6033,7 @@ int pcie_set_readrq(struct pci_dev *dev, int rq)
{
u16 v;
int ret;
struct pci_host_bridge *bridge = pci_find_host_bridge(dev->bus);
if (rq < 128 || rq > 4096 || !is_power_of_2(rq))
return -EINVAL;
@ -6051,6 +6052,15 @@ int pcie_set_readrq(struct pci_dev *dev, int rq)
v = (ffs(rq) - 8) << 12;
if (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;
}
}
ret = pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
PCI_EXP_DEVCTL_READRQ, v);

View File

@ -501,7 +501,6 @@ static void pcie_port_device_remove(struct pci_dev *dev)
{
device_for_each_child(&dev->dev, NULL, remove_iter);
pci_free_irq_vectors(dev);
pci_disable_device(dev);
}
/**
@ -727,6 +726,19 @@ static void pcie_portdrv_remove(struct pci_dev *dev)
}
pcie_port_device_remove(dev);
pci_disable_device(dev);
}
static void pcie_portdrv_shutdown(struct pci_dev *dev)
{
if (pci_bridge_d3_possible(dev)) {
pm_runtime_forbid(&dev->dev);
pm_runtime_get_noresume(&dev->dev);
pm_runtime_dont_use_autosuspend(&dev->dev);
}
pcie_port_device_remove(dev);
}
static pci_ers_result_t pcie_portdrv_error_detected(struct pci_dev *dev,
@ -777,7 +789,7 @@ static struct pci_driver pcie_portdriver = {
.probe = pcie_portdrv_probe,
.remove = pcie_portdrv_remove,
.shutdown = pcie_portdrv_remove,
.shutdown = pcie_portdrv_shutdown,
.err_handler = &pcie_portdrv_err_handler,

View File

@ -1841,6 +1841,8 @@ int pci_setup_device(struct pci_dev *dev)
pci_set_of_node(dev);
pci_set_acpi_fwnode(dev);
if (dev->dev.fwnode && !fwnode_device_is_available(dev->dev.fwnode))
return -ENODEV;
pci_dev_assign_slot(dev);

View File

@ -572,6 +572,7 @@ struct pci_host_bridge {
void *release_data;
unsigned int ignore_reset_delay:1; /* For entire hierarchy */
unsigned int no_ext_tags:1; /* No Extended Tags */
unsigned int no_inc_mrrs:1; /* No Increase MRRS */
unsigned int native_aer:1; /* OS may use PCIe AER */
unsigned int native_pcie_hotplug:1; /* OS may use PCIe hotplug */
unsigned int native_shpc_hotplug:1; /* OS may use SHPC hotplug */