PCI: mvebu: Add support for compiling driver as module
Now when driver uses devm_pci_remap_iospace() function, it is possible implement ->remove() callback for unbinding device from driver. Implement mvebu_pcie_remove() callback with proper cleanup phase, drop driver's suppress_bind_attrs flag and switch type of CONFIG_PCI_MVEBU option from bool to tristate. This allows to compile pci-mvebu.c driver as loadable module pci-mvebu.ko with ability to unload it. Link: https://lore.kernel.org/r/20211126144307.7568-3-pali@kernel.org Signed-off-by: Pali Rohár <pali@kernel.org> Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
This commit is contained in:
Родитель
859186e238
Коммит
0746ae1be1
|
@ -4,7 +4,7 @@ menu "PCI controller drivers"
|
||||||
depends on PCI
|
depends on PCI
|
||||||
|
|
||||||
config PCI_MVEBU
|
config PCI_MVEBU
|
||||||
bool "Marvell EBU PCIe controller"
|
tristate "Marvell EBU PCIe controller"
|
||||||
depends on ARCH_MVEBU || ARCH_DOVE || COMPILE_TEST
|
depends on ARCH_MVEBU || ARCH_DOVE || COMPILE_TEST
|
||||||
depends on MVEBU_MBUS
|
depends on MVEBU_MBUS
|
||||||
depends on ARM
|
depends on ARM
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
@ -154,6 +155,30 @@ static void mvebu_pcie_set_local_dev_nr(struct mvebu_pcie_port *port, int nr)
|
||||||
mvebu_writel(port, stat, PCIE_STAT_OFF);
|
mvebu_writel(port, stat, PCIE_STAT_OFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mvebu_pcie_disable_wins(struct mvebu_pcie_port *port)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
mvebu_writel(port, 0, PCIE_BAR_LO_OFF(0));
|
||||||
|
mvebu_writel(port, 0, PCIE_BAR_HI_OFF(0));
|
||||||
|
|
||||||
|
for (i = 1; i < 3; i++) {
|
||||||
|
mvebu_writel(port, 0, PCIE_BAR_CTRL_OFF(i));
|
||||||
|
mvebu_writel(port, 0, PCIE_BAR_LO_OFF(i));
|
||||||
|
mvebu_writel(port, 0, PCIE_BAR_HI_OFF(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 5; i++) {
|
||||||
|
mvebu_writel(port, 0, PCIE_WIN04_CTRL_OFF(i));
|
||||||
|
mvebu_writel(port, 0, PCIE_WIN04_BASE_OFF(i));
|
||||||
|
mvebu_writel(port, 0, PCIE_WIN04_REMAP_OFF(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
mvebu_writel(port, 0, PCIE_WIN5_CTRL_OFF);
|
||||||
|
mvebu_writel(port, 0, PCIE_WIN5_BASE_OFF);
|
||||||
|
mvebu_writel(port, 0, PCIE_WIN5_REMAP_OFF);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Setup PCIE BARs and Address Decode Wins:
|
* Setup PCIE BARs and Address Decode Wins:
|
||||||
* BAR[0] -> internal registers (needed for MSI)
|
* BAR[0] -> internal registers (needed for MSI)
|
||||||
|
@ -170,21 +195,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)
|
||||||
dram = mv_mbus_dram_info();
|
dram = mv_mbus_dram_info();
|
||||||
|
|
||||||
/* First, disable and clear BARs and windows. */
|
/* First, disable and clear BARs and windows. */
|
||||||
for (i = 1; i < 3; i++) {
|
mvebu_pcie_disable_wins(port);
|
||||||
mvebu_writel(port, 0, PCIE_BAR_CTRL_OFF(i));
|
|
||||||
mvebu_writel(port, 0, PCIE_BAR_LO_OFF(i));
|
|
||||||
mvebu_writel(port, 0, PCIE_BAR_HI_OFF(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < 5; i++) {
|
|
||||||
mvebu_writel(port, 0, PCIE_WIN04_CTRL_OFF(i));
|
|
||||||
mvebu_writel(port, 0, PCIE_WIN04_BASE_OFF(i));
|
|
||||||
mvebu_writel(port, 0, PCIE_WIN04_REMAP_OFF(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
mvebu_writel(port, 0, PCIE_WIN5_CTRL_OFF);
|
|
||||||
mvebu_writel(port, 0, PCIE_WIN5_BASE_OFF);
|
|
||||||
mvebu_writel(port, 0, PCIE_WIN5_REMAP_OFF);
|
|
||||||
|
|
||||||
/* Setup windows for DDR banks. Count total DDR size on the fly. */
|
/* Setup windows for DDR banks. Count total DDR size on the fly. */
|
||||||
size = 0;
|
size = 0;
|
||||||
|
@ -1327,6 +1338,52 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
|
||||||
return pci_host_probe(bridge);
|
return pci_host_probe(bridge);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mvebu_pcie_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct mvebu_pcie *pcie = platform_get_drvdata(pdev);
|
||||||
|
struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
|
||||||
|
u32 cmd;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Remove PCI bus with all devices. */
|
||||||
|
pci_lock_rescan_remove();
|
||||||
|
pci_stop_root_bus(bridge->bus);
|
||||||
|
pci_remove_root_bus(bridge->bus);
|
||||||
|
pci_unlock_rescan_remove();
|
||||||
|
|
||||||
|
for (i = 0; i < pcie->nports; i++) {
|
||||||
|
struct mvebu_pcie_port *port = &pcie->ports[i];
|
||||||
|
|
||||||
|
if (!port->base)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Disable Root Bridge I/O space, memory space and bus mastering. */
|
||||||
|
cmd = mvebu_readl(port, PCIE_CMD_OFF);
|
||||||
|
cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
|
||||||
|
mvebu_writel(port, cmd, PCIE_CMD_OFF);
|
||||||
|
|
||||||
|
/* Mask all interrupt sources. */
|
||||||
|
mvebu_writel(port, 0, PCIE_MASK_OFF);
|
||||||
|
|
||||||
|
/* Free config space for emulated root bridge. */
|
||||||
|
pci_bridge_emul_cleanup(&port->bridge);
|
||||||
|
|
||||||
|
/* Disable and clear BARs and windows. */
|
||||||
|
mvebu_pcie_disable_wins(port);
|
||||||
|
|
||||||
|
/* Delete PCIe IO and MEM windows. */
|
||||||
|
if (port->iowin.size)
|
||||||
|
mvebu_pcie_del_windows(port, port->iowin.base, port->iowin.size);
|
||||||
|
if (port->memwin.size)
|
||||||
|
mvebu_pcie_del_windows(port, port->memwin.base, port->memwin.size);
|
||||||
|
|
||||||
|
/* Power down card and disable clocks. Must be the last step. */
|
||||||
|
mvebu_pcie_powerdown(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct of_device_id mvebu_pcie_of_match_table[] = {
|
static const struct of_device_id mvebu_pcie_of_match_table[] = {
|
||||||
{ .compatible = "marvell,armada-xp-pcie", },
|
{ .compatible = "marvell,armada-xp-pcie", },
|
||||||
{ .compatible = "marvell,armada-370-pcie", },
|
{ .compatible = "marvell,armada-370-pcie", },
|
||||||
|
@ -1343,10 +1400,14 @@ static struct platform_driver mvebu_pcie_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "mvebu-pcie",
|
.name = "mvebu-pcie",
|
||||||
.of_match_table = mvebu_pcie_of_match_table,
|
.of_match_table = mvebu_pcie_of_match_table,
|
||||||
/* driver unloading/unbinding currently not supported */
|
|
||||||
.suppress_bind_attrs = true,
|
|
||||||
.pm = &mvebu_pcie_pm_ops,
|
.pm = &mvebu_pcie_pm_ops,
|
||||||
},
|
},
|
||||||
.probe = mvebu_pcie_probe,
|
.probe = mvebu_pcie_probe,
|
||||||
|
.remove = mvebu_pcie_remove,
|
||||||
};
|
};
|
||||||
builtin_platform_driver(mvebu_pcie_driver);
|
module_platform_driver(mvebu_pcie_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@bootlin.com>");
|
||||||
|
MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
|
||||||
|
MODULE_DESCRIPTION("Marvell EBU PCIe controller");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
|
Загрузка…
Ссылка в новой задаче