arch/tile: improve support for PCI hotplug
Note that this is not complete hot-plug support; hot-unplug is not included. Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
This commit is contained in:
Родитель
313ce674d3
Коммит
398fa5a931
|
@ -339,6 +339,14 @@ config NO_IOPORT
|
||||||
|
|
||||||
source "drivers/pci/Kconfig"
|
source "drivers/pci/Kconfig"
|
||||||
|
|
||||||
|
config HOTPLUG
|
||||||
|
bool "Support for hot-pluggable devices"
|
||||||
|
---help---
|
||||||
|
Say Y here if you want to plug devices into your computer while
|
||||||
|
the system is running, and be able to use them quickly. In many
|
||||||
|
cases, the devices can likewise be unplugged at any time too.
|
||||||
|
One well-known example of this is USB.
|
||||||
|
|
||||||
source "drivers/pci/hotplug/Kconfig"
|
source "drivers/pci/hotplug/Kconfig"
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
|
@ -46,7 +46,8 @@ struct pci_controller {
|
||||||
*/
|
*/
|
||||||
#define PCI_DMA_BUS_IS_PHYS 1
|
#define PCI_DMA_BUS_IS_PHYS 1
|
||||||
|
|
||||||
int __init tile_pci_init(void);
|
int __devinit tile_pci_init(void);
|
||||||
|
int __devinit pcibios_init(void);
|
||||||
|
|
||||||
void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max);
|
void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max);
|
||||||
static inline void pci_iounmap(struct pci_dev *dev, void __iomem *addr) {}
|
static inline void pci_iounmap(struct pci_dev *dev, void __iomem *addr) {}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2010 Tilera Corporation. All Rights Reserved.
|
* Copyright 2011 Tilera Corporation. All Rights Reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -59,6 +59,7 @@ int __write_once tile_plx_gen1;
|
||||||
|
|
||||||
static struct pci_controller controllers[TILE_NUM_PCIE];
|
static struct pci_controller controllers[TILE_NUM_PCIE];
|
||||||
static int num_controllers;
|
static int num_controllers;
|
||||||
|
static int pci_scan_flags[TILE_NUM_PCIE];
|
||||||
|
|
||||||
static struct pci_ops tile_cfg_ops;
|
static struct pci_ops tile_cfg_ops;
|
||||||
|
|
||||||
|
@ -79,7 +80,7 @@ EXPORT_SYMBOL(pcibios_align_resource);
|
||||||
* controller_id is the controller number, config type is 0 or 1 for
|
* controller_id is the controller number, config type is 0 or 1 for
|
||||||
* config0 or config1 operations.
|
* config0 or config1 operations.
|
||||||
*/
|
*/
|
||||||
static int __init tile_pcie_open(int controller_id, int config_type)
|
static int __devinit tile_pcie_open(int controller_id, int config_type)
|
||||||
{
|
{
|
||||||
char filename[32];
|
char filename[32];
|
||||||
int fd;
|
int fd;
|
||||||
|
@ -95,7 +96,7 @@ static int __init tile_pcie_open(int controller_id, int config_type)
|
||||||
/*
|
/*
|
||||||
* Get the IRQ numbers from the HV and set up the handlers for them.
|
* Get the IRQ numbers from the HV and set up the handlers for them.
|
||||||
*/
|
*/
|
||||||
static int __init tile_init_irqs(int controller_id,
|
static int __devinit tile_init_irqs(int controller_id,
|
||||||
struct pci_controller *controller)
|
struct pci_controller *controller)
|
||||||
{
|
{
|
||||||
char filename[32];
|
char filename[32];
|
||||||
|
@ -139,71 +140,80 @@ static int __init tile_init_irqs(int controller_id,
|
||||||
*
|
*
|
||||||
* Returns the number of controllers discovered.
|
* Returns the number of controllers discovered.
|
||||||
*/
|
*/
|
||||||
int __init tile_pci_init(void)
|
int __devinit tile_pci_init(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
pr_info("PCI: Searching for controllers...\n");
|
pr_info("PCI: Searching for controllers...\n");
|
||||||
|
|
||||||
|
/* Re-init number of PCIe controllers to support hot-plug feature. */
|
||||||
|
num_controllers = 0;
|
||||||
|
|
||||||
/* Do any configuration we need before using the PCIe */
|
/* Do any configuration we need before using the PCIe */
|
||||||
|
|
||||||
for (i = 0; i < TILE_NUM_PCIE; i++) {
|
for (i = 0; i < TILE_NUM_PCIE; i++) {
|
||||||
int hv_cfg_fd0 = -1;
|
|
||||||
int hv_cfg_fd1 = -1;
|
|
||||||
int hv_mem_fd = -1;
|
|
||||||
char name[32];
|
|
||||||
struct pci_controller *controller;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Open the fd to the HV. If it fails then this
|
* To see whether we need a real config op based on
|
||||||
* device doesn't exist.
|
* the results of pcibios_init(), to support PCIe hot-plug.
|
||||||
*/
|
*/
|
||||||
hv_cfg_fd0 = tile_pcie_open(i, 0);
|
if (pci_scan_flags[i] == 0) {
|
||||||
if (hv_cfg_fd0 < 0)
|
int hv_cfg_fd0 = -1;
|
||||||
|
int hv_cfg_fd1 = -1;
|
||||||
|
int hv_mem_fd = -1;
|
||||||
|
char name[32];
|
||||||
|
struct pci_controller *controller;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open the fd to the HV. If it fails then this
|
||||||
|
* device doesn't exist.
|
||||||
|
*/
|
||||||
|
hv_cfg_fd0 = tile_pcie_open(i, 0);
|
||||||
|
if (hv_cfg_fd0 < 0)
|
||||||
|
continue;
|
||||||
|
hv_cfg_fd1 = tile_pcie_open(i, 1);
|
||||||
|
if (hv_cfg_fd1 < 0) {
|
||||||
|
pr_err("PCI: Couldn't open config fd to HV "
|
||||||
|
"for controller %d\n", i);
|
||||||
|
goto err_cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(name, "pcie/%d/mem", i);
|
||||||
|
hv_mem_fd = hv_dev_open((HV_VirtAddr)name, 0);
|
||||||
|
if (hv_mem_fd < 0) {
|
||||||
|
pr_err("PCI: Could not open mem fd to HV!\n");
|
||||||
|
goto err_cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("PCI: Found PCI controller #%d\n", i);
|
||||||
|
|
||||||
|
controller = &controllers[i];
|
||||||
|
|
||||||
|
if (tile_init_irqs(i, controller)) {
|
||||||
|
pr_err("PCI: Could not initialize "
|
||||||
|
"IRQs, aborting.\n");
|
||||||
|
goto err_cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
controller->index = i;
|
||||||
|
controller->hv_cfg_fd[0] = hv_cfg_fd0;
|
||||||
|
controller->hv_cfg_fd[1] = hv_cfg_fd1;
|
||||||
|
controller->hv_mem_fd = hv_mem_fd;
|
||||||
|
controller->first_busno = 0;
|
||||||
|
controller->last_busno = 0xff;
|
||||||
|
controller->ops = &tile_cfg_ops;
|
||||||
|
|
||||||
|
num_controllers++;
|
||||||
continue;
|
continue;
|
||||||
hv_cfg_fd1 = tile_pcie_open(i, 1);
|
|
||||||
if (hv_cfg_fd1 < 0) {
|
|
||||||
pr_err("PCI: Couldn't open config fd to HV "
|
|
||||||
"for controller %d\n", i);
|
|
||||||
goto err_cont;
|
|
||||||
}
|
|
||||||
|
|
||||||
sprintf(name, "pcie/%d/mem", i);
|
|
||||||
hv_mem_fd = hv_dev_open((HV_VirtAddr)name, 0);
|
|
||||||
if (hv_mem_fd < 0) {
|
|
||||||
pr_err("PCI: Could not open mem fd to HV!\n");
|
|
||||||
goto err_cont;
|
|
||||||
}
|
|
||||||
|
|
||||||
pr_info("PCI: Found PCI controller #%d\n", i);
|
|
||||||
|
|
||||||
controller = &controllers[num_controllers];
|
|
||||||
|
|
||||||
if (tile_init_irqs(i, controller)) {
|
|
||||||
pr_err("PCI: Could not initialize "
|
|
||||||
"IRQs, aborting.\n");
|
|
||||||
goto err_cont;
|
|
||||||
}
|
|
||||||
|
|
||||||
controller->index = num_controllers;
|
|
||||||
controller->hv_cfg_fd[0] = hv_cfg_fd0;
|
|
||||||
controller->hv_cfg_fd[1] = hv_cfg_fd1;
|
|
||||||
controller->hv_mem_fd = hv_mem_fd;
|
|
||||||
controller->first_busno = 0;
|
|
||||||
controller->last_busno = 0xff;
|
|
||||||
controller->ops = &tile_cfg_ops;
|
|
||||||
|
|
||||||
num_controllers++;
|
|
||||||
continue;
|
|
||||||
|
|
||||||
err_cont:
|
err_cont:
|
||||||
if (hv_cfg_fd0 >= 0)
|
if (hv_cfg_fd0 >= 0)
|
||||||
hv_dev_close(hv_cfg_fd0);
|
hv_dev_close(hv_cfg_fd0);
|
||||||
if (hv_cfg_fd1 >= 0)
|
if (hv_cfg_fd1 >= 0)
|
||||||
hv_dev_close(hv_cfg_fd1);
|
hv_dev_close(hv_cfg_fd1);
|
||||||
if (hv_mem_fd >= 0)
|
if (hv_mem_fd >= 0)
|
||||||
hv_dev_close(hv_mem_fd);
|
hv_dev_close(hv_mem_fd);
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -232,7 +242,7 @@ static int tile_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void __init fixup_read_and_payload_sizes(void)
|
static void __devinit fixup_read_and_payload_sizes(void)
|
||||||
{
|
{
|
||||||
struct pci_dev *dev = NULL;
|
struct pci_dev *dev = NULL;
|
||||||
int smallest_max_payload = 0x1; /* Tile maxes out at 256 bytes. */
|
int smallest_max_payload = 0x1; /* Tile maxes out at 256 bytes. */
|
||||||
|
@ -282,7 +292,7 @@ static void __init fixup_read_and_payload_sizes(void)
|
||||||
* The controllers have been set up by the time we get here, by a call to
|
* The controllers have been set up by the time we get here, by a call to
|
||||||
* tile_pci_init.
|
* tile_pci_init.
|
||||||
*/
|
*/
|
||||||
static int __init pcibios_init(void)
|
int __devinit pcibios_init(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -296,25 +306,31 @@ static int __init pcibios_init(void)
|
||||||
mdelay(250);
|
mdelay(250);
|
||||||
|
|
||||||
/* Scan all of the recorded PCI controllers. */
|
/* Scan all of the recorded PCI controllers. */
|
||||||
for (i = 0; i < num_controllers; i++) {
|
for (i = 0; i < TILE_NUM_PCIE; i++) {
|
||||||
struct pci_controller *controller = &controllers[i];
|
|
||||||
struct pci_bus *bus;
|
|
||||||
|
|
||||||
pr_info("PCI: initializing controller #%d\n", i);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This comes from the generic Linux PCI driver.
|
* Do real pcibios init ops if the controller is initialized
|
||||||
*
|
* by tile_pci_init() successfully and not initialized by
|
||||||
* It reads the PCI tree for this bus into the Linux
|
* pcibios_init() yet to support PCIe hot-plug.
|
||||||
* data structures.
|
|
||||||
*
|
|
||||||
* This is inlined in linux/pci.h and calls into
|
|
||||||
* pci_scan_bus_parented() in probe.c.
|
|
||||||
*/
|
*/
|
||||||
bus = pci_scan_bus(0, controller->ops, controller);
|
if (pci_scan_flags[i] == 0 && controllers[i].ops != NULL) {
|
||||||
controller->root_bus = bus;
|
struct pci_controller *controller = &controllers[i];
|
||||||
controller->last_busno = bus->subordinate;
|
struct pci_bus *bus;
|
||||||
|
|
||||||
|
pr_info("PCI: initializing controller #%d\n", i);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This comes from the generic Linux PCI driver.
|
||||||
|
*
|
||||||
|
* It reads the PCI tree for this bus into the Linux
|
||||||
|
* data structures.
|
||||||
|
*
|
||||||
|
* This is inlined in linux/pci.h and calls into
|
||||||
|
* pci_scan_bus_parented() in probe.c.
|
||||||
|
*/
|
||||||
|
bus = pci_scan_bus(0, controller->ops, controller);
|
||||||
|
controller->root_bus = bus;
|
||||||
|
controller->last_busno = bus->subordinate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Do machine dependent PCI interrupt routing */
|
/* Do machine dependent PCI interrupt routing */
|
||||||
|
@ -326,34 +342,45 @@ static int __init pcibios_init(void)
|
||||||
* It allocates all of the resources (I/O memory, etc)
|
* It allocates all of the resources (I/O memory, etc)
|
||||||
* associated with the devices read in above.
|
* associated with the devices read in above.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pci_assign_unassigned_resources();
|
pci_assign_unassigned_resources();
|
||||||
|
|
||||||
/* Configure the max_read_size and max_payload_size values. */
|
/* Configure the max_read_size and max_payload_size values. */
|
||||||
fixup_read_and_payload_sizes();
|
fixup_read_and_payload_sizes();
|
||||||
|
|
||||||
/* Record the I/O resources in the PCI controller structure. */
|
/* Record the I/O resources in the PCI controller structure. */
|
||||||
for (i = 0; i < num_controllers; i++) {
|
for (i = 0; i < TILE_NUM_PCIE; i++) {
|
||||||
struct pci_bus *root_bus = controllers[i].root_bus;
|
/*
|
||||||
struct pci_bus *next_bus;
|
* Do real pcibios init ops if the controller is initialized
|
||||||
struct pci_dev *dev;
|
* by tile_pci_init() successfully and not initialized by
|
||||||
|
* pcibios_init() yet to support PCIe hot-plug.
|
||||||
|
*/
|
||||||
|
if (pci_scan_flags[i] == 0 && controllers[i].ops != NULL) {
|
||||||
|
struct pci_bus *root_bus = controllers[i].root_bus;
|
||||||
|
struct pci_bus *next_bus;
|
||||||
|
struct pci_dev *dev;
|
||||||
|
|
||||||
list_for_each_entry(dev, &root_bus->devices, bus_list) {
|
list_for_each_entry(dev, &root_bus->devices, bus_list) {
|
||||||
/* Find the PCI host controller, ie. the 1st bridge. */
|
/*
|
||||||
if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI &&
|
* Find the PCI host controller, ie. the 1st
|
||||||
(PCI_SLOT(dev->devfn) == 0)) {
|
* bridge.
|
||||||
next_bus = dev->subordinate;
|
*/
|
||||||
controllers[i].mem_resources[0] =
|
if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI &&
|
||||||
*next_bus->resource[0];
|
(PCI_SLOT(dev->devfn) == 0)) {
|
||||||
controllers[i].mem_resources[1] =
|
next_bus = dev->subordinate;
|
||||||
*next_bus->resource[1];
|
controllers[i].mem_resources[0] =
|
||||||
controllers[i].mem_resources[2] =
|
*next_bus->resource[0];
|
||||||
*next_bus->resource[2];
|
controllers[i].mem_resources[1] =
|
||||||
|
*next_bus->resource[1];
|
||||||
|
controllers[i].mem_resources[2] =
|
||||||
|
*next_bus->resource[2];
|
||||||
|
|
||||||
break;
|
/* Setup flags. */
|
||||||
|
pci_scan_flags[i] = 1;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -381,7 +408,7 @@ char __devinit *pcibios_setup(char *str)
|
||||||
/*
|
/*
|
||||||
* This is called from the generic Linux layer.
|
* This is called from the generic Linux layer.
|
||||||
*/
|
*/
|
||||||
void __init pcibios_update_irq(struct pci_dev *dev, int irq)
|
void __devinit pcibios_update_irq(struct pci_dev *dev, int irq)
|
||||||
{
|
{
|
||||||
pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
|
pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче