[POWERPC] fix iSeries PCI resource management
The way iSeries manages PCI IO and Memory resources is a bit strange and is based on overriding the content of those resources with home cooked ones afterward. This changes it a bit to better integrate with the new resource handling so that the "virtual" tokens that iSeries replaces resources with are done from the proper per-device fixup hook, and bridge resources are set to enclose that token space. This fixes various things such as the output of /proc/iomem & ioports, among others. This also fixes up various boot messages as well. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Paul Mackerras <paulus@samba.org>
This commit is contained in:
committed by
Paul Mackerras
parent
3fd94c6b1a
commit
50c9bc2fc8
@@ -190,6 +190,20 @@ int pci_read_irq_line(struct pci_dev *pci_dev)
|
|||||||
struct of_irq oirq;
|
struct of_irq oirq;
|
||||||
unsigned int virq;
|
unsigned int virq;
|
||||||
|
|
||||||
|
/* The current device-tree that iSeries generates from the HV
|
||||||
|
* PCI informations doesn't contain proper interrupt routing,
|
||||||
|
* and all the fallback would do is print out crap, so we
|
||||||
|
* don't attempt to resolve the interrupts here at all, some
|
||||||
|
* iSeries specific fixup does it.
|
||||||
|
*
|
||||||
|
* In the long run, we will hopefully fix the generated device-tree
|
||||||
|
* instead.
|
||||||
|
*/
|
||||||
|
#ifdef CONFIG_PPC_ISERIES
|
||||||
|
if (firmware_has_feature(FW_FEATURE_ISERIES))
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
|
|
||||||
DBG("Try to map irq for %s...\n", pci_name(pci_dev));
|
DBG("Try to map irq for %s...\n", pci_name(pci_dev));
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
@@ -946,7 +960,7 @@ static void __init pcibios_allocate_bus_resources(struct list_head *bus_list)
|
|||||||
|| res->start > res->end)
|
|| res->start > res->end)
|
||||||
continue;
|
continue;
|
||||||
if (bus->parent == NULL)
|
if (bus->parent == NULL)
|
||||||
pr = (res->flags & IORESOURCE_IO)?
|
pr = (res->flags & IORESOURCE_IO) ?
|
||||||
&ioport_resource : &iomem_resource;
|
&ioport_resource : &iomem_resource;
|
||||||
else {
|
else {
|
||||||
/* Don't bother with non-root busses when
|
/* Don't bother with non-root busses when
|
||||||
|
@@ -359,7 +359,7 @@ void __devinit scan_phb(struct pci_controller *hose)
|
|||||||
int i, mode;
|
int i, mode;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
|
||||||
DBG("Scanning PHB %s\n", node ? node->full_name : "<NO NAME>");
|
DBG("PCI: Scanning PHB %s\n", node ? node->full_name : "<NO NAME>");
|
||||||
|
|
||||||
/* Create an empty bus for the toplevel */
|
/* Create an empty bus for the toplevel */
|
||||||
bus = pci_create_bus(hose->parent, hose->first_busno, hose->ops, node);
|
bus = pci_create_bus(hose->parent, hose->first_busno, hose->ops, node);
|
||||||
@@ -375,9 +375,22 @@ void __devinit scan_phb(struct pci_controller *hose)
|
|||||||
pcibios_map_io_space(bus);
|
pcibios_map_io_space(bus);
|
||||||
|
|
||||||
/* Wire up PHB bus resources */
|
/* Wire up PHB bus resources */
|
||||||
bus->resource[0] = res = &hose->io_resource;
|
if (hose->io_resource.flags) {
|
||||||
for (i = 0; i < 3; ++i)
|
DBG("PCI: PHB IO resource = %016lx-%016lx [%lx]\n",
|
||||||
|
hose->io_resource.start, hose->io_resource.end,
|
||||||
|
hose->io_resource.flags);
|
||||||
|
bus->resource[0] = res = &hose->io_resource;
|
||||||
|
}
|
||||||
|
for (i = 0; i < 3; ++i) {
|
||||||
|
DBG("PCI: PHB MEM resource %d = %016lx-%016lx [%lx]\n", i,
|
||||||
|
hose->mem_resources[i].start,
|
||||||
|
hose->mem_resources[i].end,
|
||||||
|
hose->mem_resources[i].flags);
|
||||||
bus->resource[i+1] = &hose->mem_resources[i];
|
bus->resource[i+1] = &hose->mem_resources[i];
|
||||||
|
}
|
||||||
|
DBG("PCI: PHB MEM offset = %016lx\n", hose->pci_mem_offset);
|
||||||
|
DBG("PCI: PHB IO offset = %08lx\n",
|
||||||
|
(unsigned long)hose->io_base_virt - _IO_BASE);
|
||||||
|
|
||||||
/* Get probe mode and perform scan */
|
/* Get probe mode and perform scan */
|
||||||
mode = PCI_PROBE_NORMAL;
|
mode = PCI_PROBE_NORMAL;
|
||||||
|
@@ -20,6 +20,9 @@
|
|||||||
* along with this program; if not, write to the Free Software
|
* along with this program; if not, write to the Free Software
|
||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#undef DEBUG
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
@@ -58,6 +61,7 @@ static int limit_pci_retries = 1; /* Set Retry Error on. */
|
|||||||
#define IOMM_TABLE_MAX_ENTRIES 1024
|
#define IOMM_TABLE_MAX_ENTRIES 1024
|
||||||
#define IOMM_TABLE_ENTRY_SIZE 0x0000000000400000UL
|
#define IOMM_TABLE_ENTRY_SIZE 0x0000000000400000UL
|
||||||
#define BASE_IO_MEMORY 0xE000000000000000UL
|
#define BASE_IO_MEMORY 0xE000000000000000UL
|
||||||
|
#define END_IO_MEMORY 0xEFFFFFFFFFFFFFFFUL
|
||||||
|
|
||||||
static unsigned long max_io_memory = BASE_IO_MEMORY;
|
static unsigned long max_io_memory = BASE_IO_MEMORY;
|
||||||
static long current_iomm_table_entry;
|
static long current_iomm_table_entry;
|
||||||
@@ -68,7 +72,6 @@ static long current_iomm_table_entry;
|
|||||||
static struct device_node *iomm_table[IOMM_TABLE_MAX_ENTRIES];
|
static struct device_node *iomm_table[IOMM_TABLE_MAX_ENTRIES];
|
||||||
static u8 iobar_table[IOMM_TABLE_MAX_ENTRIES];
|
static u8 iobar_table[IOMM_TABLE_MAX_ENTRIES];
|
||||||
|
|
||||||
static const char pci_io_text[] = "iSeries PCI I/O";
|
|
||||||
static DEFINE_SPINLOCK(iomm_table_lock);
|
static DEFINE_SPINLOCK(iomm_table_lock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -279,8 +282,8 @@ out_free:
|
|||||||
* PCI: Bus 0, Device 26, Vendor 0x12AE Frame 1, Card C10 Ethernet
|
* PCI: Bus 0, Device 26, Vendor 0x12AE Frame 1, Card C10 Ethernet
|
||||||
* controller
|
* controller
|
||||||
*/
|
*/
|
||||||
static void __init iseries_device_information(struct pci_dev *pdev, int count,
|
static void __init iseries_device_information(struct pci_dev *pdev,
|
||||||
u16 bus, HvSubBusNumber subbus)
|
u16 bus, HvSubBusNumber subbus)
|
||||||
{
|
{
|
||||||
u8 frame = 0;
|
u8 frame = 0;
|
||||||
char card[4];
|
char card[4];
|
||||||
@@ -290,10 +293,9 @@ static void __init iseries_device_information(struct pci_dev *pdev, int count,
|
|||||||
ISERIES_GET_FUNCTION_FROM_SUBBUS(subbus));
|
ISERIES_GET_FUNCTION_FROM_SUBBUS(subbus));
|
||||||
|
|
||||||
if (iseries_get_location_code(bus, agent, &frame, card)) {
|
if (iseries_get_location_code(bus, agent, &frame, card)) {
|
||||||
printk("%d. PCI: Bus%3d, Device%3d, Vendor %04X Frame%3d, "
|
printk(KERN_INFO "PCI: %s, Vendor %04X Frame%3d, "
|
||||||
"Card %4s 0x%04X\n", count, bus,
|
"Card %4s 0x%04X\n", pci_name(pdev), pdev->vendor,
|
||||||
PCI_SLOT(pdev->devfn), pdev->vendor, frame,
|
frame, card, (int)(pdev->class >> 8));
|
||||||
card, (int)(pdev->class >> 8));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,7 +325,6 @@ static void __init iomm_table_allocate_entry(struct pci_dev *dev, int bar_num)
|
|||||||
* Set Resource values.
|
* Set Resource values.
|
||||||
*/
|
*/
|
||||||
spin_lock(&iomm_table_lock);
|
spin_lock(&iomm_table_lock);
|
||||||
bar_res->name = pci_io_text;
|
|
||||||
bar_res->start = BASE_IO_MEMORY +
|
bar_res->start = BASE_IO_MEMORY +
|
||||||
IOMM_TABLE_ENTRY_SIZE * current_iomm_table_entry;
|
IOMM_TABLE_ENTRY_SIZE * current_iomm_table_entry;
|
||||||
bar_res->end = bar_res->start + bar_size - 1;
|
bar_res->end = bar_res->start + bar_size - 1;
|
||||||
@@ -392,62 +393,64 @@ static struct device_node *find_device_node(int bus, int devfn)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* iSeries_pcibios_fixup_resources
|
||||||
|
*
|
||||||
|
* Fixes up all resources for devices
|
||||||
|
*/
|
||||||
|
void __init iSeries_pcibios_fixup_resources(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
const u32 *agent;
|
||||||
|
const u32 *sub_bus;
|
||||||
|
unsigned char bus = pdev->bus->number;
|
||||||
|
struct device_node *node;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
node = find_device_node(bus, pdev->devfn);
|
||||||
|
pr_debug("PCI: iSeries %s, pdev %p, node %p\n",
|
||||||
|
pci_name(pdev), pdev, node);
|
||||||
|
if (!node) {
|
||||||
|
printk("PCI: %s disabled, device tree entry not found !\n",
|
||||||
|
pci_name(pdev));
|
||||||
|
for (i = 0; i <= PCI_ROM_RESOURCE; i++)
|
||||||
|
pdev->resource[i].flags = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sub_bus = of_get_property(node, "linux,subbus", NULL);
|
||||||
|
agent = of_get_property(node, "linux,agent-id", NULL);
|
||||||
|
if (agent && sub_bus) {
|
||||||
|
u8 irq = iSeries_allocate_IRQ(bus, 0, *sub_bus);
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = HvCallXm_connectBusUnit(bus, *sub_bus, *agent, irq);
|
||||||
|
if (err)
|
||||||
|
pci_log_error("Connect Bus Unit",
|
||||||
|
bus, *sub_bus, *agent, err);
|
||||||
|
else {
|
||||||
|
err = HvCallPci_configStore8(bus, *sub_bus,
|
||||||
|
*agent, PCI_INTERRUPT_LINE, irq);
|
||||||
|
if (err)
|
||||||
|
pci_log_error("PciCfgStore Irq Failed!",
|
||||||
|
bus, *sub_bus, *agent, err);
|
||||||
|
else
|
||||||
|
pdev->irq = irq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pdev->sysdata = node;
|
||||||
|
PCI_DN(node)->pcidev = pdev;
|
||||||
|
allocate_device_bars(pdev);
|
||||||
|
iseries_device_information(pdev, bus, *sub_bus);
|
||||||
|
iommu_devnode_init_iSeries(pdev, node);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* iSeries_pci_final_fixup(void)
|
* iSeries_pci_final_fixup(void)
|
||||||
*/
|
*/
|
||||||
void __init iSeries_pci_final_fixup(void)
|
void __init iSeries_pci_final_fixup(void)
|
||||||
{
|
{
|
||||||
struct pci_dev *pdev = NULL;
|
|
||||||
struct device_node *node;
|
|
||||||
int num_dev = 0;
|
|
||||||
|
|
||||||
/* Fix up at the device node and pci_dev relationship */
|
/* Fix up at the device node and pci_dev relationship */
|
||||||
mf_display_src(0xC9000100);
|
mf_display_src(0xC9000100);
|
||||||
|
|
||||||
printk("pcibios_final_fixup\n");
|
|
||||||
for_each_pci_dev(pdev) {
|
|
||||||
const u32 *agent;
|
|
||||||
const u32 *sub_bus;
|
|
||||||
unsigned char bus = pdev->bus->number;
|
|
||||||
|
|
||||||
node = find_device_node(bus, pdev->devfn);
|
|
||||||
printk("pci dev %p (%x.%x), node %p\n", pdev, bus,
|
|
||||||
pdev->devfn, node);
|
|
||||||
if (!node) {
|
|
||||||
printk("PCI: Device Tree not found for 0x%016lX\n",
|
|
||||||
(unsigned long)pdev);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
agent = of_get_property(node, "linux,agent-id", NULL);
|
|
||||||
sub_bus = of_get_property(node, "linux,subbus", NULL);
|
|
||||||
if (agent && sub_bus) {
|
|
||||||
u8 irq = iSeries_allocate_IRQ(bus, 0, *sub_bus);
|
|
||||||
int err;
|
|
||||||
|
|
||||||
err = HvCallXm_connectBusUnit(bus, *sub_bus,
|
|
||||||
*agent, irq);
|
|
||||||
if (err)
|
|
||||||
pci_log_error("Connect Bus Unit",
|
|
||||||
bus, *sub_bus, *agent, err);
|
|
||||||
else {
|
|
||||||
err = HvCallPci_configStore8(bus, *sub_bus,
|
|
||||||
*agent, PCI_INTERRUPT_LINE, irq);
|
|
||||||
if (err)
|
|
||||||
pci_log_error("PciCfgStore Irq Failed!",
|
|
||||||
bus, *sub_bus, *agent, err);
|
|
||||||
else
|
|
||||||
pdev->irq = irq;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
num_dev++;
|
|
||||||
pdev->sysdata = node;
|
|
||||||
PCI_DN(node)->pcidev = pdev;
|
|
||||||
allocate_device_bars(pdev);
|
|
||||||
iseries_device_information(pdev, num_dev, bus, *sub_bus);
|
|
||||||
iommu_devnode_init_iSeries(pdev, node);
|
|
||||||
}
|
|
||||||
iSeries_activate_IRQs();
|
iSeries_activate_IRQs();
|
||||||
mf_display_src(0xC9000200);
|
mf_display_src(0xC9000200);
|
||||||
}
|
}
|
||||||
@@ -894,10 +897,18 @@ void __init iSeries_pcibios_init(void)
|
|||||||
/* All legacy iSeries PHBs are in domain zero */
|
/* All legacy iSeries PHBs are in domain zero */
|
||||||
phb->global_number = 0;
|
phb->global_number = 0;
|
||||||
|
|
||||||
phb->pci_mem_offset = bus;
|
|
||||||
phb->first_busno = bus;
|
phb->first_busno = bus;
|
||||||
phb->last_busno = bus;
|
phb->last_busno = bus;
|
||||||
phb->ops = &iSeries_pci_ops;
|
phb->ops = &iSeries_pci_ops;
|
||||||
|
phb->io_base_virt = (void __iomem *)_IO_BASE;
|
||||||
|
phb->io_resource.flags = IORESOURCE_IO;
|
||||||
|
phb->io_resource.start = BASE_IO_MEMORY;
|
||||||
|
phb->io_resource.end = END_IO_MEMORY;
|
||||||
|
phb->io_resource.name = "iSeries PCI IO";
|
||||||
|
phb->mem_resources[0].flags = IORESOURCE_MEM;
|
||||||
|
phb->mem_resources[0].start = BASE_IO_MEMORY;
|
||||||
|
phb->mem_resources[0].end = END_IO_MEMORY;
|
||||||
|
phb->mem_resources[0].name = "Series PCI MEM";
|
||||||
}
|
}
|
||||||
|
|
||||||
of_node_put(root);
|
of_node_put(root);
|
||||||
|
@@ -43,12 +43,16 @@
|
|||||||
#define ISERIES_GET_DEVICE_FROM_SUBBUS(subbus) ((subbus >> 5) & 0x7)
|
#define ISERIES_GET_DEVICE_FROM_SUBBUS(subbus) ((subbus >> 5) & 0x7)
|
||||||
#define ISERIES_GET_FUNCTION_FROM_SUBBUS(subbus) ((subbus >> 2) & 0x7)
|
#define ISERIES_GET_FUNCTION_FROM_SUBBUS(subbus) ((subbus >> 2) & 0x7)
|
||||||
|
|
||||||
|
struct pci_dev;
|
||||||
|
|
||||||
#ifdef CONFIG_PCI
|
#ifdef CONFIG_PCI
|
||||||
extern void iSeries_pcibios_init(void);
|
extern void iSeries_pcibios_init(void);
|
||||||
extern void iSeries_pci_final_fixup(void);
|
extern void iSeries_pci_final_fixup(void);
|
||||||
|
extern void iSeries_pcibios_fixup_resources(struct pci_dev *dev);
|
||||||
#else
|
#else
|
||||||
static inline void iSeries_pcibios_init(void) { }
|
static inline void iSeries_pcibios_init(void) { }
|
||||||
static inline void iSeries_pci_final_fixup(void) { }
|
static inline void iSeries_pci_final_fixup(void) { }
|
||||||
|
static inline void iSeries_pcibios_fixup_resources(struct pci_dev *dev) {}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* _PLATFORMS_ISERIES_PCI_H */
|
#endif /* _PLATFORMS_ISERIES_PCI_H */
|
||||||
|
@@ -639,24 +639,25 @@ static int __init iseries_probe(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
define_machine(iseries) {
|
define_machine(iseries) {
|
||||||
.name = "iSeries",
|
.name = "iSeries",
|
||||||
.setup_arch = iSeries_setup_arch,
|
.setup_arch = iSeries_setup_arch,
|
||||||
.show_cpuinfo = iSeries_show_cpuinfo,
|
.show_cpuinfo = iSeries_show_cpuinfo,
|
||||||
.init_IRQ = iSeries_init_IRQ,
|
.init_IRQ = iSeries_init_IRQ,
|
||||||
.get_irq = iSeries_get_irq,
|
.get_irq = iSeries_get_irq,
|
||||||
.init_early = iSeries_init_early,
|
.init_early = iSeries_init_early,
|
||||||
.pcibios_fixup = iSeries_pci_final_fixup,
|
.pcibios_fixup = iSeries_pci_final_fixup,
|
||||||
.restart = mf_reboot,
|
.pcibios_fixup_resources= iSeries_pcibios_fixup_resources,
|
||||||
.power_off = mf_power_off,
|
.restart = mf_reboot,
|
||||||
.halt = mf_power_off,
|
.power_off = mf_power_off,
|
||||||
.get_boot_time = iSeries_get_boot_time,
|
.halt = mf_power_off,
|
||||||
.set_rtc_time = iSeries_set_rtc_time,
|
.get_boot_time = iSeries_get_boot_time,
|
||||||
.get_rtc_time = iSeries_get_rtc_time,
|
.set_rtc_time = iSeries_set_rtc_time,
|
||||||
.calibrate_decr = generic_calibrate_decr,
|
.get_rtc_time = iSeries_get_rtc_time,
|
||||||
.progress = iSeries_progress,
|
.calibrate_decr = generic_calibrate_decr,
|
||||||
.probe = iseries_probe,
|
.progress = iSeries_progress,
|
||||||
.ioremap = iseries_ioremap,
|
.probe = iseries_probe,
|
||||||
.iounmap = iseries_iounmap,
|
.ioremap = iseries_ioremap,
|
||||||
|
.iounmap = iseries_iounmap,
|
||||||
/* XXX Implement enable_pmcs for iSeries */
|
/* XXX Implement enable_pmcs for iSeries */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user