mirror of
https://github.com/torvalds/linux.git
synced 2024-12-27 13:22:23 +00:00
c0464062bf
Fix the following crash, seen in dwc/pci-imx6.
Unable to handle kernel NULL pointer dereference at virtual address 00000070
pgd = c0004000
[00000070] *pgd=00000000
Internal error: Oops: 805 [#1] SMP ARM
Modules linked in:
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.10.0-09686-g9e31489 #1
Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree)
task: cb850000 task.stack: cb84e000
PC is at imx6_pcie_probe+0x2f4/0x414
...
While at it, fix the same problem in various drivers instead of waiting for
individual crash reports.
The change in the imx6 driver was tested with qemu. The changes in other
drivers are based on code inspection and have been compile tested only.
Fixes: 442ec4c04d
("PCI: dwc: all: Split struct pcie_port into host-only and core structures")
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Vivek Gautam <vivek.gautam@codeaurora.org> # designware-plat
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Kishon Vijay Abraham I <kishon@ti.com>
136 lines
2.9 KiB
C
136 lines
2.9 KiB
C
/*
|
|
* PCIe RC driver for Synopsys DesignWare Core
|
|
*
|
|
* Copyright (C) 2015-2016 Synopsys, Inc. (www.synopsys.com)
|
|
*
|
|
* Authors: Joao Pinto <Joao.Pinto@synopsys.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
#include <linux/clk.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/resource.h>
|
|
#include <linux/signal.h>
|
|
#include <linux/types.h>
|
|
|
|
#include "pcie-designware.h"
|
|
|
|
struct dw_plat_pcie {
|
|
struct dw_pcie *pci;
|
|
};
|
|
|
|
static irqreturn_t dw_plat_pcie_msi_irq_handler(int irq, void *arg)
|
|
{
|
|
struct pcie_port *pp = arg;
|
|
|
|
return dw_handle_msi_irq(pp);
|
|
}
|
|
|
|
static void dw_plat_pcie_host_init(struct pcie_port *pp)
|
|
{
|
|
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
|
|
|
dw_pcie_setup_rc(pp);
|
|
dw_pcie_wait_for_link(pci);
|
|
|
|
if (IS_ENABLED(CONFIG_PCI_MSI))
|
|
dw_pcie_msi_init(pp);
|
|
}
|
|
|
|
static struct dw_pcie_host_ops dw_plat_pcie_host_ops = {
|
|
.host_init = dw_plat_pcie_host_init,
|
|
};
|
|
|
|
static int dw_plat_add_pcie_port(struct pcie_port *pp,
|
|
struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
int ret;
|
|
|
|
pp->irq = platform_get_irq(pdev, 1);
|
|
if (pp->irq < 0)
|
|
return pp->irq;
|
|
|
|
if (IS_ENABLED(CONFIG_PCI_MSI)) {
|
|
pp->msi_irq = platform_get_irq(pdev, 0);
|
|
if (pp->msi_irq < 0)
|
|
return pp->msi_irq;
|
|
|
|
ret = devm_request_irq(dev, pp->msi_irq,
|
|
dw_plat_pcie_msi_irq_handler,
|
|
IRQF_SHARED, "dw-plat-pcie-msi", pp);
|
|
if (ret) {
|
|
dev_err(dev, "failed to request MSI IRQ\n");
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
pp->root_bus_nr = -1;
|
|
pp->ops = &dw_plat_pcie_host_ops;
|
|
|
|
ret = dw_pcie_host_init(pp);
|
|
if (ret) {
|
|
dev_err(dev, "failed to initialize host\n");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dw_plat_pcie_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct dw_plat_pcie *dw_plat_pcie;
|
|
struct dw_pcie *pci;
|
|
struct resource *res; /* Resource from DT */
|
|
int ret;
|
|
|
|
dw_plat_pcie = devm_kzalloc(dev, sizeof(*dw_plat_pcie), GFP_KERNEL);
|
|
if (!dw_plat_pcie)
|
|
return -ENOMEM;
|
|
|
|
pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
|
|
if (!pci)
|
|
return -ENOMEM;
|
|
|
|
pci->dev = dev;
|
|
|
|
dw_plat_pcie->pci = pci;
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
pci->dbi_base = devm_ioremap_resource(dev, res);
|
|
if (IS_ERR(pci->dbi_base))
|
|
return PTR_ERR(pci->dbi_base);
|
|
|
|
platform_set_drvdata(pdev, dw_plat_pcie);
|
|
|
|
ret = dw_plat_add_pcie_port(&pci->pp, pdev);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id dw_plat_pcie_of_match[] = {
|
|
{ .compatible = "snps,dw-pcie", },
|
|
{},
|
|
};
|
|
|
|
static struct platform_driver dw_plat_pcie_driver = {
|
|
.driver = {
|
|
.name = "dw-pcie",
|
|
.of_match_table = dw_plat_pcie_of_match,
|
|
},
|
|
.probe = dw_plat_pcie_probe,
|
|
};
|
|
builtin_platform_driver(dw_plat_pcie_driver);
|