mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-04 20:19:47 +08:00

On MicroChip MPFS Icicle:
microchip-pcie 2000000000.pcie: host bridge /soc/pcie@2000000000 ranges:
microchip-pcie 2000000000.pcie: Parsing ranges property...
microchip-pcie 2000000000.pcie: MEM 0x2008000000..0x2087ffffff -> 0x0008000000
Unable to handle kernel NULL pointer dereference at virtual address 0000000000000368
Current swapper/0 pgtable: 4K pagesize, 39-bit VAs, pgdp=0x00000000814f1000
[0000000000000368] pgd=0000000000000000, p4d=0000000000000000, pud=0000000000000000
Oops [#1]
Modules linked in:
CPU: 0 UID: 0 PID: 1 Comm: swapper/0 Not tainted 6.15.0-rc1-icicle-00003-gafc0a570bb61 #232 NONE
Hardware name: Microchip PolarFire-SoC Icicle Kit (DT)
[...]
[<ffffffff803fb8a4>] plda_pcie_setup_iomems+0xe/0x78
[<ffffffff803fc246>] mc_platform_init+0x80/0x1d2
[<ffffffff803f9c88>] pci_ecam_create+0x104/0x1e2
[<ffffffff8000adbe>] pci_host_common_init+0x120/0x228
[<ffffffff8000af42>] pci_host_common_probe+0x7c/0x8a
The initialization of driver_data was moved after the call to
gen_pci_init(), while the pci_ecam_ops.init() callback
mc_platform_init() expects it has already been initialized.
Fix this by moving the initialization of driver_data up.
Fixes: afc0a570bb
("PCI: host-generic: Extract an ECAM bridge creation helper from pci_host_common_probe()")
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Link: https://lore.kernel.org/r/774290708a6f0f683711914fda110742c18a7fb2.1750787223.git.geert+renesas@glider.be
Link: https://patch.msgid.link/20250625111806.4153773-2-maz@kernel.org
109 lines
2.5 KiB
C
109 lines
2.5 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Common library for PCI host controller drivers
|
|
*
|
|
* Copyright (C) 2014 ARM Limited
|
|
*
|
|
* Author: Will Deacon <will.deacon@arm.com>
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_pci.h>
|
|
#include <linux/pci-ecam.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
#include "pci-host-common.h"
|
|
|
|
static void gen_pci_unmap_cfg(void *ptr)
|
|
{
|
|
pci_ecam_free((struct pci_config_window *)ptr);
|
|
}
|
|
|
|
static struct pci_config_window *gen_pci_init(struct device *dev,
|
|
struct pci_host_bridge *bridge, const struct pci_ecam_ops *ops)
|
|
{
|
|
int err;
|
|
struct resource cfgres;
|
|
struct resource_entry *bus;
|
|
struct pci_config_window *cfg;
|
|
|
|
err = of_address_to_resource(dev->of_node, 0, &cfgres);
|
|
if (err) {
|
|
dev_err(dev, "missing \"reg\" property\n");
|
|
return ERR_PTR(err);
|
|
}
|
|
|
|
bus = resource_list_first_type(&bridge->windows, IORESOURCE_BUS);
|
|
if (!bus)
|
|
return ERR_PTR(-ENODEV);
|
|
|
|
cfg = pci_ecam_create(dev, &cfgres, bus->res, ops);
|
|
if (IS_ERR(cfg))
|
|
return cfg;
|
|
|
|
err = devm_add_action_or_reset(dev, gen_pci_unmap_cfg, cfg);
|
|
if (err)
|
|
return ERR_PTR(err);
|
|
|
|
return cfg;
|
|
}
|
|
|
|
int pci_host_common_init(struct platform_device *pdev,
|
|
const struct pci_ecam_ops *ops)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct pci_host_bridge *bridge;
|
|
struct pci_config_window *cfg;
|
|
|
|
bridge = devm_pci_alloc_host_bridge(dev, 0);
|
|
if (!bridge)
|
|
return -ENOMEM;
|
|
|
|
of_pci_check_probe_only();
|
|
|
|
platform_set_drvdata(pdev, bridge);
|
|
|
|
/* Parse and map our Configuration Space windows */
|
|
cfg = gen_pci_init(dev, bridge, ops);
|
|
if (IS_ERR(cfg))
|
|
return PTR_ERR(cfg);
|
|
|
|
bridge->sysdata = cfg;
|
|
bridge->ops = (struct pci_ops *)&ops->pci_ops;
|
|
bridge->enable_device = ops->enable_device;
|
|
bridge->disable_device = ops->disable_device;
|
|
bridge->msi_domain = true;
|
|
|
|
return pci_host_probe(bridge);
|
|
}
|
|
EXPORT_SYMBOL_GPL(pci_host_common_init);
|
|
|
|
int pci_host_common_probe(struct platform_device *pdev)
|
|
{
|
|
const struct pci_ecam_ops *ops;
|
|
|
|
ops = of_device_get_match_data(&pdev->dev);
|
|
if (!ops)
|
|
return -ENODEV;
|
|
|
|
return pci_host_common_init(pdev, ops);
|
|
}
|
|
EXPORT_SYMBOL_GPL(pci_host_common_probe);
|
|
|
|
void pci_host_common_remove(struct platform_device *pdev)
|
|
{
|
|
struct pci_host_bridge *bridge = platform_get_drvdata(pdev);
|
|
|
|
pci_lock_rescan_remove();
|
|
pci_stop_root_bus(bridge->bus);
|
|
pci_remove_root_bus(bridge->bus);
|
|
pci_unlock_rescan_remove();
|
|
}
|
|
EXPORT_SYMBOL_GPL(pci_host_common_remove);
|
|
|
|
MODULE_DESCRIPTION("Common library for PCI host controller drivers");
|
|
MODULE_LICENSE("GPL v2");
|