From 035223fb28791f0eb0d5719727355d3f6817d228 Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Thu, 5 Oct 2006 11:35:10 -0700 Subject: [POWERPC] Make pSeries_lpar_hpte_insert static Change the powerpc hpte_insert routines now called through ppc_md to static scope. Signed-off-by: Geoff Levand Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/lpar.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arch/powerpc/platforms/pseries') diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c index 1820a0b0a8c6..721436db3ef0 100644 --- a/arch/powerpc/platforms/pseries/lpar.c +++ b/arch/powerpc/platforms/pseries/lpar.c @@ -282,7 +282,7 @@ void vpa_init(int cpu) } } -long pSeries_lpar_hpte_insert(unsigned long hpte_group, +static long pSeries_lpar_hpte_insert(unsigned long hpte_group, unsigned long va, unsigned long pa, unsigned long rflags, unsigned long vflags, int psize) @@ -506,7 +506,7 @@ static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va, * Take a spinlock around flushes to avoid bouncing the hypervisor tlbie * lock. */ -void pSeries_lpar_flush_hash_range(unsigned long number, int local) +static void pSeries_lpar_flush_hash_range(unsigned long number, int local) { int i; unsigned long flags = 0; -- cgit v1.2.3 From 5d2efba64b231a1733c4048d1708d77e07f26426 Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Mon, 30 Oct 2006 16:15:59 +1100 Subject: [POWERPC] Use 4kB iommu pages even on 64kB-page systems The 10Gigabit ethernet device drivers appear to be able to chew up all 256MB of TCE mappings on pSeries systems, as evidenced by numerous error messages: iommu_alloc failed, tbl c0000000010d5c48 vaddr c0000000d875eff0 npages 1 Some experimentation indicates that this is essentially because one 1500 byte ethernet MTU gets mapped as a 64K DMA region when the large 64K pages are enabled. Thus, it doesn't take much to exhaust all of the available DMA mappings for a high-speed card. This patch changes the iommu allocator to work with its own unique, distinct page size. Although the patch is long, its actually quite simple: it just #defines a distinct IOMMU_PAGE_SIZE and then uses this in all the places that matter. As a side effect, it also dramatically improves network performance on platforms with H-calls on iommu translation inserts/removes (since we no longer call it 16 times for a 1500 bytes packet when the iommu HW is still 4k). In the future, we might want to make the IOMMU_PAGE_SIZE a variable in the iommu_table instance, thus allowing support for different HW page sizes in the iommu itself. Signed-off-by: Linas Vepstas Signed-off-by: Benjamin Herrenschmidt Acked-by: Olof Johansson Acked-by: Stephen Rothwell Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/iommu.c | 35 ++++++++-------------------------- 1 file changed, 8 insertions(+), 27 deletions(-) (limited to 'arch/powerpc/platforms/pseries') diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index d24ba547e53f..556c279a789d 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -57,9 +57,6 @@ static void tce_build_pSeries(struct iommu_table *tbl, long index, u64 *tcep; u64 rpn; - index <<= TCE_PAGE_FACTOR; - npages <<= TCE_PAGE_FACTOR; - proto_tce = TCE_PCI_READ; // Read allowed if (direction != DMA_TO_DEVICE) @@ -82,9 +79,6 @@ static void tce_free_pSeries(struct iommu_table *tbl, long index, long npages) { u64 *tcep; - npages <<= TCE_PAGE_FACTOR; - index <<= TCE_PAGE_FACTOR; - tcep = ((u64 *)tbl->it_base) + index; while (npages--) @@ -95,7 +89,6 @@ static unsigned long tce_get_pseries(struct iommu_table *tbl, long index) { u64 *tcep; - index <<= TCE_PAGE_FACTOR; tcep = ((u64 *)tbl->it_base) + index; return *tcep; @@ -109,9 +102,6 @@ static void tce_build_pSeriesLP(struct iommu_table *tbl, long tcenum, u64 proto_tce, tce; u64 rpn; - tcenum <<= TCE_PAGE_FACTOR; - npages <<= TCE_PAGE_FACTOR; - rpn = (virt_to_abs(uaddr)) >> TCE_SHIFT; proto_tce = TCE_PCI_READ; if (direction != DMA_TO_DEVICE) @@ -146,7 +136,7 @@ static void tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, u64 rpn; long l, limit; - if (TCE_PAGE_FACTOR == 0 && npages == 1) + if (npages == 1) return tce_build_pSeriesLP(tbl, tcenum, npages, uaddr, direction); @@ -164,9 +154,6 @@ static void tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, __get_cpu_var(tce_page) = tcep; } - tcenum <<= TCE_PAGE_FACTOR; - npages <<= TCE_PAGE_FACTOR; - rpn = (virt_to_abs(uaddr)) >> TCE_SHIFT; proto_tce = TCE_PCI_READ; if (direction != DMA_TO_DEVICE) @@ -207,9 +194,6 @@ static void tce_free_pSeriesLP(struct iommu_table *tbl, long tcenum, long npages { u64 rc; - tcenum <<= TCE_PAGE_FACTOR; - npages <<= TCE_PAGE_FACTOR; - while (npages--) { rc = plpar_tce_put((u64)tbl->it_index, (u64)tcenum << 12, 0); @@ -229,9 +213,6 @@ static void tce_freemulti_pSeriesLP(struct iommu_table *tbl, long tcenum, long n { u64 rc; - tcenum <<= TCE_PAGE_FACTOR; - npages <<= TCE_PAGE_FACTOR; - rc = plpar_tce_stuff((u64)tbl->it_index, (u64)tcenum << 12, 0, npages); if (rc && printk_ratelimit()) { @@ -248,7 +229,6 @@ static unsigned long tce_get_pSeriesLP(struct iommu_table *tbl, long tcenum) u64 rc; unsigned long tce_ret; - tcenum <<= TCE_PAGE_FACTOR; rc = plpar_tce_get((u64)tbl->it_index, (u64)tcenum << 12, &tce_ret); if (rc && printk_ratelimit()) { @@ -289,7 +269,7 @@ static void iommu_table_setparms(struct pci_controller *phb, tbl->it_busno = phb->bus->number; /* Units of tce entries */ - tbl->it_offset = phb->dma_window_base_cur >> PAGE_SHIFT; + tbl->it_offset = phb->dma_window_base_cur >> IOMMU_PAGE_SHIFT; /* Test if we are going over 2GB of DMA space */ if (phb->dma_window_base_cur + phb->dma_window_size > 0x80000000ul) { @@ -300,7 +280,7 @@ static void iommu_table_setparms(struct pci_controller *phb, phb->dma_window_base_cur += phb->dma_window_size; /* Set the tce table size - measured in entries */ - tbl->it_size = phb->dma_window_size >> PAGE_SHIFT; + tbl->it_size = phb->dma_window_size >> IOMMU_PAGE_SHIFT; tbl->it_index = 0; tbl->it_blocksize = 16; @@ -325,8 +305,8 @@ static void iommu_table_setparms_lpar(struct pci_controller *phb, tbl->it_base = 0; tbl->it_blocksize = 16; tbl->it_type = TCE_PCI; - tbl->it_offset = offset >> PAGE_SHIFT; - tbl->it_size = size >> PAGE_SHIFT; + tbl->it_offset = offset >> IOMMU_PAGE_SHIFT; + tbl->it_size = size >> IOMMU_PAGE_SHIFT; } static void iommu_bus_setup_pSeries(struct pci_bus *bus) @@ -522,8 +502,6 @@ static void iommu_dev_setup_pSeriesLP(struct pci_dev *dev) const void *dma_window = NULL; struct pci_dn *pci; - DBG("iommu_dev_setup_pSeriesLP, dev %p (%s)\n", dev, pci_name(dev)); - /* dev setup for LPAR is a little tricky, since the device tree might * contain the dma-window properties per-device and not neccesarily * for the bus. So we need to search upwards in the tree until we @@ -532,6 +510,9 @@ static void iommu_dev_setup_pSeriesLP(struct pci_dev *dev) */ dn = pci_device_to_OF_node(dev); + DBG("iommu_dev_setup_pSeriesLP, dev %p (%s) %s\n", + dev, pci_name(dev), dn->full_name); + for (pdn = dn; pdn && PCI_DN(pdn) && !PCI_DN(pdn)->iommu_table; pdn = pdn->parent) { dma_window = get_property(pdn, "ibm,dma-window", NULL); -- cgit v1.2.3 From c4028958b6ecad064b1a6303a6a5906d4fe48d73 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 22 Nov 2006 14:57:56 +0000 Subject: WorkStruct: make allyesconfig Fix up for make allyesconfig. Signed-Off-By: David Howells --- arch/powerpc/platforms/pseries/eeh_event.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'arch/powerpc/platforms/pseries') diff --git a/arch/powerpc/platforms/pseries/eeh_event.c b/arch/powerpc/platforms/pseries/eeh_event.c index 137077451316..49037edf7d39 100644 --- a/arch/powerpc/platforms/pseries/eeh_event.c +++ b/arch/powerpc/platforms/pseries/eeh_event.c @@ -37,8 +37,8 @@ /* EEH event workqueue setup. */ static DEFINE_SPINLOCK(eeh_eventlist_lock); LIST_HEAD(eeh_eventlist); -static void eeh_thread_launcher(void *); -DECLARE_WORK(eeh_event_wq, eeh_thread_launcher, NULL); +static void eeh_thread_launcher(struct work_struct *); +DECLARE_WORK(eeh_event_wq, eeh_thread_launcher); /* Serialize reset sequences for a given pci device */ DEFINE_MUTEX(eeh_event_mutex); @@ -103,7 +103,7 @@ static int eeh_event_handler(void * dummy) * eeh_thread_launcher * @dummy - unused */ -static void eeh_thread_launcher(void *dummy) +static void eeh_thread_launcher(struct work_struct *dummy) { if (kernel_thread(eeh_event_handler, NULL, CLONE_KERNEL) < 0) printk(KERN_ERR "Failed to start EEH daemon\n"); -- cgit v1.2.3 From f90bb153b1493719d18b4529a46ebfe43220ea6c Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Sat, 11 Nov 2006 17:24:51 +1100 Subject: [POWERPC] Make pci_read_irq_line the default This patch reworks the way IRQs are fixed up on PCI for arch powerpc. It makes pci_read_irq_line() called by default in the PCI code for devices that are probed, and add an optional per-device fixup in ppc_md for platforms that really need to correct what they obtain from pci_read_irq_line(). It also removes ppc_md.irq_bus_setup which was only used by pSeries and should not be needed anymore. I've also removed the pSeries s7a workaround as it can't work with the current interrupt code anyway. I'm trying to get one of these machines working so I can test a proper fix for that problem. I also haven't updated the old-style fixup code from 85xx_cds.c because it's actually buggy :) It assigns pci_dev->irq hard coded numbers which is no good with the new IRQ mapping code. It should at least use irq_create_mapping(NULL, hard_coded_number); and possibly also set_irq_type() to set them as level low. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/pci.c | 35 ---------------------------------- arch/powerpc/platforms/pseries/setup.c | 1 - 2 files changed, 36 deletions(-) (limited to 'arch/powerpc/platforms/pseries') diff --git a/arch/powerpc/platforms/pseries/pci.c b/arch/powerpc/platforms/pseries/pci.c index 410a6bcc4ca0..715db5c89908 100644 --- a/arch/powerpc/platforms/pseries/pci.c +++ b/arch/powerpc/platforms/pseries/pci.c @@ -29,8 +29,6 @@ #include #include -static int __devinitdata s7a_workaround = -1; - #if 0 void pcibios_name_device(struct pci_dev *dev) { @@ -57,39 +55,6 @@ void pcibios_name_device(struct pci_dev *dev) DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pcibios_name_device); #endif -static void __devinit check_s7a(void) -{ - struct device_node *root; - const char *model; - - s7a_workaround = 0; - root = of_find_node_by_path("/"); - if (root) { - model = get_property(root, "model", NULL); - if (model && !strcmp(model, "IBM,7013-S7A")) - s7a_workaround = 1; - of_node_put(root); - } -} - -void __devinit pSeries_irq_bus_setup(struct pci_bus *bus) -{ - struct pci_dev *dev; - - if (s7a_workaround < 0) - check_s7a(); - list_for_each_entry(dev, &bus->devices, bus_list) { - pci_read_irq_line(dev); - if (s7a_workaround) { - if (dev->irq > 16) { - dev->irq -= 3; - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, - dev->irq); - } - } - } -} - static void __init pSeries_request_regions(void) { if (!isa_io_base) diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 89a8119f988d..a8f3812aa38e 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -553,7 +553,6 @@ define_machine(pseries) { .log_error = pSeries_log_error, .pcibios_fixup = pSeries_final_fixup, .pci_probe_mode = pSeries_pci_probe_mode, - .irq_bus_setup = pSeries_irq_bus_setup, .restart = rtas_restart, .power_off = rtas_power_off, .halt = rtas_halt, -- cgit v1.2.3 From 12d04eef927bf61328af2c7cbe756c96f98ac3bf Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Sat, 11 Nov 2006 17:25:02 +1100 Subject: [POWERPC] Refactor 64 bits DMA operations This patch completely refactors DMA operations for 64 bits powerpc. 32 bits is untouched for now. We use the new dev_archdata structure to add the dma operations pointer and associated data to struct device. While at it, we also add the OF node pointer and numa node. In the future, we might want to look into merging that with pci_dn as well. The old vio, pci-iommu and pci-direct DMA ops are gone. They are now replaced by a set of generic iommu and direct DMA ops (non PCI specific) that can be used by bus types. The toplevel implementation is now inline. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/iommu.c | 90 +++++++++++++++--------------- arch/powerpc/platforms/pseries/pci_dlpar.c | 4 +- 2 files changed, 48 insertions(+), 46 deletions(-) (limited to 'arch/powerpc/platforms/pseries') diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index 556c279a789d..3c95392f4f41 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -309,7 +309,7 @@ static void iommu_table_setparms_lpar(struct pci_controller *phb, tbl->it_size = size >> IOMMU_PAGE_SHIFT; } -static void iommu_bus_setup_pSeries(struct pci_bus *bus) +static void pci_dma_bus_setup_pSeries(struct pci_bus *bus) { struct device_node *dn; struct iommu_table *tbl; @@ -318,10 +318,9 @@ static void iommu_bus_setup_pSeries(struct pci_bus *bus) struct pci_dn *pci; int children; - DBG("iommu_bus_setup_pSeries, bus %p, bus->self %p\n", bus, bus->self); - dn = pci_bus_to_OF_node(bus); - pci = PCI_DN(dn); + + DBG("pci_dma_bus_setup_pSeries: setting up bus %s\n", dn->full_name); if (bus->self) { /* This is not a root bus, any setup will be done for the @@ -329,6 +328,7 @@ static void iommu_bus_setup_pSeries(struct pci_bus *bus) */ return; } + pci = PCI_DN(dn); /* Check if the ISA bus on the system is under * this PHB. @@ -390,17 +390,17 @@ static void iommu_bus_setup_pSeries(struct pci_bus *bus) } -static void iommu_bus_setup_pSeriesLP(struct pci_bus *bus) +static void pci_dma_bus_setup_pSeriesLP(struct pci_bus *bus) { struct iommu_table *tbl; struct device_node *dn, *pdn; struct pci_dn *ppci; const void *dma_window = NULL; - DBG("iommu_bus_setup_pSeriesLP, bus %p, bus->self %p\n", bus, bus->self); - dn = pci_bus_to_OF_node(bus); + DBG("pci_dma_bus_setup_pSeriesLP: setting up bus %s\n", dn->full_name); + /* Find nearest ibm,dma-window, walking up the device tree */ for (pdn = dn; pdn != NULL; pdn = pdn->parent) { dma_window = get_property(pdn, "ibm,dma-window", NULL); @@ -409,11 +409,15 @@ static void iommu_bus_setup_pSeriesLP(struct pci_bus *bus) } if (dma_window == NULL) { - DBG("iommu_bus_setup_pSeriesLP: bus %s seems to have no ibm,dma-window property\n", dn->full_name); + DBG(" no ibm,dma-window property !\n"); return; } ppci = PCI_DN(pdn); + + DBG(" parent is %s, iommu_table: 0x%p\n", + pdn->full_name, ppci->iommu_table); + if (!ppci->iommu_table) { /* Bussubno hasn't been copied yet. * Do it now because iommu_table_setparms_lpar needs it. @@ -427,6 +431,7 @@ static void iommu_bus_setup_pSeriesLP(struct pci_bus *bus) iommu_table_setparms_lpar(ppci->phb, pdn, tbl, dma_window); ppci->iommu_table = iommu_init_table(tbl, ppci->phb->node); + DBG(" created table: %p\n", ppci->iommu_table); } if (pdn != dn) @@ -434,27 +439,27 @@ static void iommu_bus_setup_pSeriesLP(struct pci_bus *bus) } -static void iommu_dev_setup_pSeries(struct pci_dev *dev) +static void pci_dma_dev_setup_pSeries(struct pci_dev *dev) { - struct device_node *dn, *mydn; + struct device_node *dn; struct iommu_table *tbl; - DBG("iommu_dev_setup_pSeries, dev %p (%s)\n", dev, pci_name(dev)); + DBG("pci_dma_dev_setup_pSeries: %s\n", pci_name(dev)); - mydn = dn = pci_device_to_OF_node(dev); + dn = dev->dev.archdata.of_node; /* If we're the direct child of a root bus, then we need to allocate * an iommu table ourselves. The bus setup code should have setup * the window sizes already. */ if (!dev->bus->self) { + struct pci_controller *phb = PCI_DN(dn)->phb; + DBG(" --> first child, no bridge. Allocating iommu table.\n"); tbl = kmalloc_node(sizeof(struct iommu_table), GFP_KERNEL, - PCI_DN(dn)->phb->node); - iommu_table_setparms(PCI_DN(dn)->phb, dn, tbl); - PCI_DN(dn)->iommu_table = iommu_init_table(tbl, - PCI_DN(dn)->phb->node); - + phb->node); + iommu_table_setparms(phb, dn, tbl); + dev->dev.archdata.dma_data = iommu_init_table(tbl, phb->node); return; } @@ -465,11 +470,11 @@ static void iommu_dev_setup_pSeries(struct pci_dev *dev) while (dn && PCI_DN(dn) && PCI_DN(dn)->iommu_table == NULL) dn = dn->parent; - if (dn && PCI_DN(dn)) { - PCI_DN(mydn)->iommu_table = PCI_DN(dn)->iommu_table; - } else { - DBG("iommu_dev_setup_pSeries, dev %p (%s) has no iommu table\n", dev, pci_name(dev)); - } + if (dn && PCI_DN(dn)) + dev->dev.archdata.dma_data = PCI_DN(dn)->iommu_table; + else + printk(KERN_WARNING "iommu: Device %s has no iommu table\n", + pci_name(dev)); } static int iommu_reconfig_notifier(struct notifier_block *nb, unsigned long action, void *node) @@ -495,13 +500,15 @@ static struct notifier_block iommu_reconfig_nb = { .notifier_call = iommu_reconfig_notifier, }; -static void iommu_dev_setup_pSeriesLP(struct pci_dev *dev) +static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev) { struct device_node *pdn, *dn; struct iommu_table *tbl; const void *dma_window = NULL; struct pci_dn *pci; + DBG("pci_dma_dev_setup_pSeriesLP: %s\n", pci_name(dev)); + /* dev setup for LPAR is a little tricky, since the device tree might * contain the dma-window properties per-device and not neccesarily * for the bus. So we need to search upwards in the tree until we @@ -509,9 +516,7 @@ static void iommu_dev_setup_pSeriesLP(struct pci_dev *dev) * already allocated. */ dn = pci_device_to_OF_node(dev); - - DBG("iommu_dev_setup_pSeriesLP, dev %p (%s) %s\n", - dev, pci_name(dev), dn->full_name); + DBG(" node is %s\n", dn->full_name); for (pdn = dn; pdn && PCI_DN(pdn) && !PCI_DN(pdn)->iommu_table; pdn = pdn->parent) { @@ -520,16 +525,17 @@ static void iommu_dev_setup_pSeriesLP(struct pci_dev *dev) break; } + DBG(" parent is %s\n", pdn->full_name); + /* Check for parent == NULL so we don't try to setup the empty EADS * slots on POWER4 machines. */ if (dma_window == NULL || pdn->parent == NULL) { - DBG("No dma window for device, linking to parent\n"); - PCI_DN(dn)->iommu_table = PCI_DN(pdn)->iommu_table; + DBG(" no dma window for device, linking to parent\n"); + dev->dev.archdata.dma_data = PCI_DN(pdn)->iommu_table; return; - } else { - DBG("Found DMA window, allocating table\n"); } + DBG(" found DMA window, table: %p\n", pci->iommu_table); pci = PCI_DN(pdn); if (!pci->iommu_table) { @@ -542,24 +548,20 @@ static void iommu_dev_setup_pSeriesLP(struct pci_dev *dev) iommu_table_setparms_lpar(pci->phb, pdn, tbl, dma_window); pci->iommu_table = iommu_init_table(tbl, pci->phb->node); + DBG(" created table: %p\n", pci->iommu_table); } - if (pdn != dn) - PCI_DN(dn)->iommu_table = pci->iommu_table; + dev->dev.archdata.dma_data = pci->iommu_table; } -static void iommu_bus_setup_null(struct pci_bus *b) { } -static void iommu_dev_setup_null(struct pci_dev *d) { } - /* These are called very early. */ void iommu_init_early_pSeries(void) { if (of_chosen && get_property(of_chosen, "linux,iommu-off", NULL)) { /* Direct I/O, IOMMU off */ - ppc_md.iommu_dev_setup = iommu_dev_setup_null; - ppc_md.iommu_bus_setup = iommu_bus_setup_null; - pci_direct_iommu_init(); - + ppc_md.pci_dma_dev_setup = NULL; + ppc_md.pci_dma_bus_setup = NULL; + pci_dma_ops = &dma_direct_ops; return; } @@ -572,19 +574,19 @@ void iommu_init_early_pSeries(void) ppc_md.tce_free = tce_free_pSeriesLP; } ppc_md.tce_get = tce_get_pSeriesLP; - ppc_md.iommu_bus_setup = iommu_bus_setup_pSeriesLP; - ppc_md.iommu_dev_setup = iommu_dev_setup_pSeriesLP; + ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_pSeriesLP; + ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_pSeriesLP; } else { ppc_md.tce_build = tce_build_pSeries; ppc_md.tce_free = tce_free_pSeries; ppc_md.tce_get = tce_get_pseries; - ppc_md.iommu_bus_setup = iommu_bus_setup_pSeries; - ppc_md.iommu_dev_setup = iommu_dev_setup_pSeries; + ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_pSeries; + ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_pSeries; } pSeries_reconfig_notifier_register(&iommu_reconfig_nb); - pci_iommu_init(); + pci_dma_ops = &dma_iommu_ops; } diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c index 6bfacc217085..bb0cb5c5b565 100644 --- a/arch/powerpc/platforms/pseries/pci_dlpar.c +++ b/arch/powerpc/platforms/pseries/pci_dlpar.c @@ -93,8 +93,8 @@ pcibios_fixup_new_pci_devices(struct pci_bus *bus, int fix_bus) if (list_empty(&dev->global_list)) { int i; - /* Need to setup IOMMU tables */ - ppc_md.iommu_dev_setup(dev); + /* Fill device archdata and setup iommu table */ + pcibios_setup_new_device(dev); if(fix_bus) pcibios_fixup_device_resources(dev, bus); -- cgit v1.2.3 From 4c9d2800be5dfabf26acdeb401cbabe9edc1dcf2 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Sat, 11 Nov 2006 17:25:08 +1100 Subject: [POWERPC] Generic OF platform driver for PCI host bridges. When enabled in Kconfig, it will pick up any of_platform_device matching it's match list (currently type "pci", "pcix", "pcie", or "ht" and setup a PHB for it. Platform must provide a ppc_md.pci_setup_phb() for it to work (for doing the necessary initialisations specific to a given PHB like setting up the config space ops). It's currently only available on 64 bits as the 32 bits PCI code can't quite cope with it in it's current form. I will fix that later. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/pci_dlpar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/powerpc/platforms/pseries') diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c index bb0cb5c5b565..ac56b868913a 100644 --- a/arch/powerpc/platforms/pseries/pci_dlpar.c +++ b/arch/powerpc/platforms/pseries/pci_dlpar.c @@ -195,7 +195,7 @@ struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn) phb = pcibios_alloc_controller(dn); if (!phb) return NULL; - setup_phb(dn, phb); + rtas_setup_phb(phb); pci_process_bridge_OF_ranges(phb, dn, 0); pci_setup_phb_io_dynamic(phb, primary); -- cgit v1.2.3 From 088df4d256227b3d927bb6ed57e66d138da0565c Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Thu, 16 Nov 2006 15:41:15 -0600 Subject: [POWERPC] Wrap cpu_die() with CONFIG_HOTPLUG_CPU Per email discussion, it appears that rtas_stop_self() and pSeries_mach_cpu_die() should not be compiled if CONFIG_HOTPLUG_CPU is not defined. This patch adds #ifdefs around these bits of code. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/setup.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'arch/powerpc/platforms/pseries') diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index a8f3812aa38e..0dc2548ca9bc 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -347,6 +347,7 @@ static int __init pSeries_init_panel(void) } arch_initcall(pSeries_init_panel); +#ifdef CONFIG_HOTPLUG_CPU static void pSeries_mach_cpu_die(void) { local_irq_disable(); @@ -357,6 +358,9 @@ static void pSeries_mach_cpu_die(void) BUG(); for(;;); } +#else +#define pSeries_mach_cpu_die NULL +#endif static int pseries_set_dabr(unsigned long dabr) { -- cgit v1.2.3 From a5715d6dfc85e002bfad68bb2858cf5a248e2060 Mon Sep 17 00:00:00 2001 From: Mohan Kumar M Date: Fri, 17 Nov 2006 17:42:24 +0530 Subject: [POWERPC] pSeries/kexec: Fix for interrupt distribution This allows any secondary CPU thread also to become boot cpu for POWER5. The patch is required to solve kdump boot issue when the kdump kernel is booted with parameter "maxcpus=1". XICS init code tries to match the current boot cpu id with "reg" property in each CPU node in the device tree. But CPU node is created only for primary thread CPU ids and "reg" property only reflects primary CPU ids. So when a kernel is booted on a secondary cpu thread above condition will never meet and the default distribution server is left as zero. This leads to route the interrupts to CPU 0, but which is not online at this time. We use ibm,ppc-interrupt-server#s to check for both primary and secondary CPU ids. Accordingly default distribution server value is initialized from "ibm,ppc-interrupt-gserver#s" property. We loop through ibm,ppc-interrupt-gserver#s property to find the global distribution server from the last entry that matches with boot cpuid. Signed-off-by: Mohan Kumar M Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/xics.c | 68 +++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 19 deletions(-) (limited to 'arch/powerpc/platforms/pseries') diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index d071abe78ab1..b5b2b1103de8 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -656,13 +656,38 @@ static void __init xics_setup_8259_cascade(void) set_irq_chained_handler(cascade, pseries_8259_cascade); } +static struct device_node *cpuid_to_of_node(int cpu) +{ + struct device_node *np; + u32 hcpuid = get_hard_smp_processor_id(cpu); + + for_each_node_by_type(np, "cpu") { + int i, len; + const u32 *intserv; + + intserv = get_property(np, "ibm,ppc-interrupt-server#s", &len); + + if (!intserv) + intserv = get_property(np, "reg", &len); + + i = len / sizeof(u32); + + while (i--) + if (intserv[i] == hcpuid) + return np; + } + + return NULL; +} + void __init xics_init_IRQ(void) { - int i; + int i, j; struct device_node *np; u32 ilen, indx = 0; - const u32 *ireg; + const u32 *ireg, *isize; int found = 0; + u32 hcpuid; ppc64_boot_msg(0x20, "XICS Init"); @@ -683,26 +708,31 @@ void __init xics_init_IRQ(void) xics_init_host(); /* Find the server numbers for the boot cpu. */ - for (np = of_find_node_by_type(NULL, "cpu"); - np; - np = of_find_node_by_type(np, "cpu")) { - ireg = get_property(np, "reg", &ilen); - if (ireg && ireg[0] == get_hard_smp_processor_id(boot_cpuid)) { - ireg = get_property(np, - "ibm,ppc-interrupt-gserver#s", &ilen); - i = ilen / sizeof(int); - if (ireg && i > 0) { - default_server = ireg[0]; - /* take last element */ - default_distrib_server = ireg[i-1]; - } - ireg = get_property(np, + np = cpuid_to_of_node(boot_cpuid); + BUG_ON(!np); + ireg = get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen); + if (!ireg) + goto skip_gserver_check; + i = ilen / sizeof(int); + hcpuid = get_hard_smp_processor_id(boot_cpuid); + + /* Global interrupt distribution server is specified in the last + * entry of "ibm,ppc-interrupt-gserver#s" property. Get the last + * entry fom this property for current boot cpu id and use it as + * default distribution server + */ + for (j = 0; j < i; j += 2) { + if (ireg[j] == hcpuid) { + default_server = hcpuid; + default_distrib_server = ireg[j+1]; + + isize = get_property(np, "ibm,interrupt-server#-size", NULL); - if (ireg) - interrupt_server_size = *ireg; - break; + if (isize) + interrupt_server_size = *isize; } } +skip_gserver_check: of_node_put(np); if (firmware_has_feature(FW_FEATURE_LPAR)) -- cgit v1.2.3 From f8485350c22b25f5b9804d89cb8fdf6626d0f5cb Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Sat, 2 Dec 2006 13:26:57 +0200 Subject: [POWERPC] Replace kmalloc+memset with kzalloc Replace kmalloc+memset with kzalloc. Signed-off-by: Yan Burman Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/reconfig.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'arch/powerpc/platforms/pseries') diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c index 1773103354be..4ad33e41b008 100644 --- a/arch/powerpc/platforms/pseries/reconfig.c +++ b/arch/powerpc/platforms/pseries/reconfig.c @@ -268,11 +268,10 @@ static char * parse_next_property(char *buf, char *end, char **name, int *length static struct property *new_property(const char *name, const int length, const unsigned char *value, struct property *last) { - struct property *new = kmalloc(sizeof(*new), GFP_KERNEL); + struct property *new = kzalloc(sizeof(*new), GFP_KERNEL); if (!new) return NULL; - memset(new, 0, sizeof(*new)); if (!(new->name = kmalloc(strlen(name) + 1, GFP_KERNEL))) goto cleanup; -- cgit v1.2.3 From 0332c2d447a7a20a4d744ba3814a349d0c1c6405 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 5 Dec 2006 17:52:36 +1100 Subject: [POWERPC] Move rtas_stop_self() into platforms/pseries/hotplug-cpu.c As the first step in consolidating the pseries hotplug cpu code, create platforms/pseries/hotplug-cpu.c and move rtas_stop_self() into it. Do the rtas token initialisation in a new initcall, rather than rtas_initialize(). Signed-off-by: Michael Ellerman Acked-by: Linas Vepstas Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/Makefile | 2 + arch/powerpc/platforms/pseries/hotplug-cpu.c | 58 ++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 arch/powerpc/platforms/pseries/hotplug-cpu.c (limited to 'arch/powerpc/platforms/pseries') diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile index 997243a91be8..69590fbf83da 100644 --- a/arch/powerpc/platforms/pseries/Makefile +++ b/arch/powerpc/platforms/pseries/Makefile @@ -10,6 +10,8 @@ obj-$(CONFIG_XICS) += xics.o obj-$(CONFIG_SCANLOG) += scanlog.o obj-$(CONFIG_EEH) += eeh.o eeh_cache.o eeh_driver.o eeh_event.o +obj-$(CONFIG_HOTPLUG_CPU) += hotplug-cpu.o + obj-$(CONFIG_HVC_CONSOLE) += hvconsole.o obj-$(CONFIG_HVCS) += hvcserver.o obj-$(CONFIG_HCALL_STATS) += hvCall_inst.o diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c new file mode 100644 index 000000000000..6e21ebdfcef7 --- /dev/null +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -0,0 +1,58 @@ +/* + * pseries CPU Hotplug infrastructure. + * + * Split out from arch/powerpc/kernel/rtas.c + * + * Peter Bergner, IBM March 2001. + * Copyright (C) 2001 IBM. + * + * Copyright (C) 2006 Michael Ellerman, IBM Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xics.h" + +/* This version can't take the spinlock, because it never returns */ +static struct rtas_args rtas_stop_self_args = { + .token = RTAS_UNKNOWN_SERVICE, + .nargs = 0, + .nret = 1, + .rets = &rtas_stop_self_args.args[0], +}; + +void rtas_stop_self(void) +{ + struct rtas_args *args = &rtas_stop_self_args; + + local_irq_disable(); + + BUG_ON(args->token == RTAS_UNKNOWN_SERVICE); + + printk("cpu %u (hwid %u) Ready to die...\n", + smp_processor_id(), hard_smp_processor_id()); + enter_rtas(__pa(args)); + + panic("Alas, I survived.\n"); +} + +static int __init pseries_cpu_hotplug_init(void) +{ + rtas_stop_self_args.token = rtas_token("stop-self"); + + return 0; +} +arch_initcall(pseries_cpu_hotplug_init); -- cgit v1.2.3 From 04da6af960194ecdee4c29cd3f86e766903418ca Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 5 Dec 2006 17:52:37 +1100 Subject: [POWERPC] Move pSeries_mach_cpu_die() into platforms/pseries/hotplug-cpu.c Move pSeries_mach_cpu_die() into platforms/pseries/hotplug-cpu.c, this allows rtas_stop_self() to be static so remove the prototype. Wire up pSeries_mach_cpu_die() in the initcall, rather than statically in setup.c, the initcall will still run prior to the cpu hotplug code being callable, so there should be no change in behaviour. Signed-off-by: Michael Ellerman Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/hotplug-cpu.c | 18 ++++++++++++++++-- arch/powerpc/platforms/pseries/setup.c | 16 ---------------- 2 files changed, 16 insertions(+), 18 deletions(-) (limited to 'arch/powerpc/platforms/pseries') diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index 6e21ebdfcef7..9e9b6b159fab 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -1,7 +1,8 @@ /* * pseries CPU Hotplug infrastructure. * - * Split out from arch/powerpc/kernel/rtas.c + * Split out from arch/powerpc/platforms/pseries/setup.c and + * arch/powerpc/kernel/rtas.c * * Peter Bergner, IBM March 2001. * Copyright (C) 2001 IBM. @@ -34,7 +35,7 @@ static struct rtas_args rtas_stop_self_args = { .rets = &rtas_stop_self_args.args[0], }; -void rtas_stop_self(void) +static void rtas_stop_self(void) { struct rtas_args *args = &rtas_stop_self_args; @@ -49,10 +50,23 @@ void rtas_stop_self(void) panic("Alas, I survived.\n"); } +static void pSeries_mach_cpu_die(void) +{ + local_irq_disable(); + idle_task_exit(); + xics_teardown_cpu(0); + rtas_stop_self(); + /* Should never get here... */ + BUG(); + for(;;); +} + static int __init pseries_cpu_hotplug_init(void) { rtas_stop_self_args.token = rtas_token("stop-self"); + ppc_md.cpu_die = pSeries_mach_cpu_die; + return 0; } arch_initcall(pseries_cpu_hotplug_init); diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 0dc2548ca9bc..6090d753c44c 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -347,21 +347,6 @@ static int __init pSeries_init_panel(void) } arch_initcall(pSeries_init_panel); -#ifdef CONFIG_HOTPLUG_CPU -static void pSeries_mach_cpu_die(void) -{ - local_irq_disable(); - idle_task_exit(); - xics_teardown_cpu(0); - rtas_stop_self(); - /* Should never get here... */ - BUG(); - for(;;); -} -#else -#define pSeries_mach_cpu_die NULL -#endif - static int pseries_set_dabr(unsigned long dabr) { return plpar_hcall_norets(H_SET_DABR, dabr); @@ -561,7 +546,6 @@ define_machine(pseries) { .power_off = rtas_power_off, .halt = rtas_halt, .panic = rtas_os_term, - .cpu_die = pSeries_mach_cpu_die, .get_boot_time = rtas_get_boot_time, .get_rtc_time = rtas_get_rtc_time, .set_rtc_time = rtas_set_rtc_time, -- cgit v1.2.3 From 413f7c405a342b0b9370ea7a652b9f0270183bf3 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 5 Dec 2006 17:52:38 +1100 Subject: [POWERPC] Move the rest of the hotplug cpu code into platforms/pseries/hotplug-cpu.c Move the rest of the hotplug cpu code from platforms/pseries/smp.c into platforms/pseries/hotplug-cpu.c. Wire up the smp_ops callbacks and the notifier in the hotplug cpu initcall, rather than in smp_init_pseries(). No change in behaviour. Signed-off-by: Michael Ellerman Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/hotplug-cpu.c | 201 ++++++++++++++++++++++++++- arch/powerpc/platforms/pseries/smp.c | 200 -------------------------- 2 files changed, 199 insertions(+), 202 deletions(-) (limited to 'arch/powerpc/platforms/pseries') diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index 9e9b6b159fab..12864d75126d 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -1,11 +1,14 @@ /* * pseries CPU Hotplug infrastructure. * - * Split out from arch/powerpc/platforms/pseries/setup.c and - * arch/powerpc/kernel/rtas.c + * Split out from arch/powerpc/platforms/pseries/setup.c + * arch/powerpc/kernel/rtas.c, and arch/powerpc/platforms/pseries/smp.c * * Peter Bergner, IBM March 2001. * Copyright (C) 2001 IBM. + * Dave Engebretsen, Peter Bergner, and + * Mike Corrigan {engebret|bergner|mikec}@us.ibm.com + * Plus various changes from other IBM teams... * * Copyright (C) 2006 Michael Ellerman, IBM Corporation * @@ -61,12 +64,206 @@ static void pSeries_mach_cpu_die(void) for(;;); } +/* Get state of physical CPU. + * Return codes: + * 0 - The processor is in the RTAS stopped state + * 1 - stop-self is in progress + * 2 - The processor is not in the RTAS stopped state + * -1 - Hardware Error + * -2 - Hardware Busy, Try again later. + */ +static int query_cpu_stopped(unsigned int pcpu) +{ + int cpu_status; + int status, qcss_tok; + + qcss_tok = rtas_token("query-cpu-stopped-state"); + if (qcss_tok == RTAS_UNKNOWN_SERVICE) + return -1; + status = rtas_call(qcss_tok, 1, 2, &cpu_status, pcpu); + if (status != 0) { + printk(KERN_ERR + "RTAS query-cpu-stopped-state failed: %i\n", status); + return status; + } + + return cpu_status; +} + +static int pSeries_cpu_disable(void) +{ + int cpu = smp_processor_id(); + + cpu_clear(cpu, cpu_online_map); + vdso_data->processorCount--; + + /*fix boot_cpuid here*/ + if (cpu == boot_cpuid) + boot_cpuid = any_online_cpu(cpu_online_map); + + /* FIXME: abstract this to not be platform specific later on */ + xics_migrate_irqs_away(); + return 0; +} + +static void pSeries_cpu_die(unsigned int cpu) +{ + int tries; + int cpu_status; + unsigned int pcpu = get_hard_smp_processor_id(cpu); + + for (tries = 0; tries < 25; tries++) { + cpu_status = query_cpu_stopped(pcpu); + if (cpu_status == 0 || cpu_status == -1) + break; + msleep(200); + } + if (cpu_status != 0) { + printk("Querying DEAD? cpu %i (%i) shows %i\n", + cpu, pcpu, cpu_status); + } + + /* Isolation and deallocation are definatly done by + * drslot_chrp_cpu. If they were not they would be + * done here. Change isolate state to Isolate and + * change allocation-state to Unusable. + */ + paca[cpu].cpu_start = 0; +} + +/* + * Update cpu_present_map and paca(s) for a new cpu node. The wrinkle + * here is that a cpu device node may represent up to two logical cpus + * in the SMT case. We must honor the assumption in other code that + * the logical ids for sibling SMT threads x and y are adjacent, such + * that x^1 == y and y^1 == x. + */ +static int pSeries_add_processor(struct device_node *np) +{ + unsigned int cpu; + cpumask_t candidate_map, tmp = CPU_MASK_NONE; + int err = -ENOSPC, len, nthreads, i; + const u32 *intserv; + + intserv = get_property(np, "ibm,ppc-interrupt-server#s", &len); + if (!intserv) + return 0; + + nthreads = len / sizeof(u32); + for (i = 0; i < nthreads; i++) + cpu_set(i, tmp); + + lock_cpu_hotplug(); + + BUG_ON(!cpus_subset(cpu_present_map, cpu_possible_map)); + + /* Get a bitmap of unoccupied slots. */ + cpus_xor(candidate_map, cpu_possible_map, cpu_present_map); + if (cpus_empty(candidate_map)) { + /* If we get here, it most likely means that NR_CPUS is + * less than the partition's max processors setting. + */ + printk(KERN_ERR "Cannot add cpu %s; this system configuration" + " supports %d logical cpus.\n", np->full_name, + cpus_weight(cpu_possible_map)); + goto out_unlock; + } + + while (!cpus_empty(tmp)) + if (cpus_subset(tmp, candidate_map)) + /* Found a range where we can insert the new cpu(s) */ + break; + else + cpus_shift_left(tmp, tmp, nthreads); + + if (cpus_empty(tmp)) { + printk(KERN_ERR "Unable to find space in cpu_present_map for" + " processor %s with %d thread(s)\n", np->name, + nthreads); + goto out_unlock; + } + + for_each_cpu_mask(cpu, tmp) { + BUG_ON(cpu_isset(cpu, cpu_present_map)); + cpu_set(cpu, cpu_present_map); + set_hard_smp_processor_id(cpu, *intserv++); + } + err = 0; +out_unlock: + unlock_cpu_hotplug(); + return err; +} + +/* + * Update the present map for a cpu node which is going away, and set + * the hard id in the paca(s) to -1 to be consistent with boot time + * convention for non-present cpus. + */ +static void pSeries_remove_processor(struct device_node *np) +{ + unsigned int cpu; + int len, nthreads, i; + const u32 *intserv; + + intserv = get_property(np, "ibm,ppc-interrupt-server#s", &len); + if (!intserv) + return; + + nthreads = len / sizeof(u32); + + lock_cpu_hotplug(); + for (i = 0; i < nthreads; i++) { + for_each_present_cpu(cpu) { + if (get_hard_smp_processor_id(cpu) != intserv[i]) + continue; + BUG_ON(cpu_online(cpu)); + cpu_clear(cpu, cpu_present_map); + set_hard_smp_processor_id(cpu, -1); + break; + } + if (cpu == NR_CPUS) + printk(KERN_WARNING "Could not find cpu to remove " + "with physical id 0x%x\n", intserv[i]); + } + unlock_cpu_hotplug(); +} + +static int pSeries_smp_notifier(struct notifier_block *nb, unsigned long action, void *node) +{ + int err = NOTIFY_OK; + + switch (action) { + case PSERIES_RECONFIG_ADD: + if (pSeries_add_processor(node)) + err = NOTIFY_BAD; + break; + case PSERIES_RECONFIG_REMOVE: + pSeries_remove_processor(node); + break; + default: + err = NOTIFY_DONE; + break; + } + return err; +} + +static struct notifier_block pSeries_smp_nb = { + .notifier_call = pSeries_smp_notifier, +}; + static int __init pseries_cpu_hotplug_init(void) { rtas_stop_self_args.token = rtas_token("stop-self"); ppc_md.cpu_die = pSeries_mach_cpu_die; + smp_ops->cpu_disable = pSeries_cpu_disable; + smp_ops->cpu_die = pSeries_cpu_die; + + /* Processors can be added/removed only on LPAR */ + if (firmware_has_feature(FW_FEATURE_LPAR)) + pSeries_reconfig_notifier_register(&pSeries_smp_nb); + return 0; } arch_initcall(pseries_cpu_hotplug_init); diff --git a/arch/powerpc/platforms/pseries/smp.c b/arch/powerpc/platforms/pseries/smp.c index c6624b8a0e77..4408518eaebe 100644 --- a/arch/powerpc/platforms/pseries/smp.c +++ b/arch/powerpc/platforms/pseries/smp.c @@ -64,197 +64,6 @@ static cpumask_t of_spin_map; extern void generic_secondary_smp_init(unsigned long); -#ifdef CONFIG_HOTPLUG_CPU - -/* Get state of physical CPU. - * Return codes: - * 0 - The processor is in the RTAS stopped state - * 1 - stop-self is in progress - * 2 - The processor is not in the RTAS stopped state - * -1 - Hardware Error - * -2 - Hardware Busy, Try again later. - */ -static int query_cpu_stopped(unsigned int pcpu) -{ - int cpu_status; - int status, qcss_tok; - - qcss_tok = rtas_token("query-cpu-stopped-state"); - if (qcss_tok == RTAS_UNKNOWN_SERVICE) - return -1; - status = rtas_call(qcss_tok, 1, 2, &cpu_status, pcpu); - if (status != 0) { - printk(KERN_ERR - "RTAS query-cpu-stopped-state failed: %i\n", status); - return status; - } - - return cpu_status; -} - -static int pSeries_cpu_disable(void) -{ - int cpu = smp_processor_id(); - - cpu_clear(cpu, cpu_online_map); - vdso_data->processorCount--; - - /*fix boot_cpuid here*/ - if (cpu == boot_cpuid) - boot_cpuid = any_online_cpu(cpu_online_map); - - /* FIXME: abstract this to not be platform specific later on */ - xics_migrate_irqs_away(); - return 0; -} - -static void pSeries_cpu_die(unsigned int cpu) -{ - int tries; - int cpu_status; - unsigned int pcpu = get_hard_smp_processor_id(cpu); - - for (tries = 0; tries < 25; tries++) { - cpu_status = query_cpu_stopped(pcpu); - if (cpu_status == 0 || cpu_status == -1) - break; - msleep(200); - } - if (cpu_status != 0) { - printk("Querying DEAD? cpu %i (%i) shows %i\n", - cpu, pcpu, cpu_status); - } - - /* Isolation and deallocation are definatly done by - * drslot_chrp_cpu. If they were not they would be - * done here. Change isolate state to Isolate and - * change allocation-state to Unusable. - */ - paca[cpu].cpu_start = 0; -} - -/* - * Update cpu_present_map and paca(s) for a new cpu node. The wrinkle - * here is that a cpu device node may represent up to two logical cpus - * in the SMT case. We must honor the assumption in other code that - * the logical ids for sibling SMT threads x and y are adjacent, such - * that x^1 == y and y^1 == x. - */ -static int pSeries_add_processor(struct device_node *np) -{ - unsigned int cpu; - cpumask_t candidate_map, tmp = CPU_MASK_NONE; - int err = -ENOSPC, len, nthreads, i; - const u32 *intserv; - - intserv = get_property(np, "ibm,ppc-interrupt-server#s", &len); - if (!intserv) - return 0; - - nthreads = len / sizeof(u32); - for (i = 0; i < nthreads; i++) - cpu_set(i, tmp); - - lock_cpu_hotplug(); - - BUG_ON(!cpus_subset(cpu_present_map, cpu_possible_map)); - - /* Get a bitmap of unoccupied slots. */ - cpus_xor(candidate_map, cpu_possible_map, cpu_present_map); - if (cpus_empty(candidate_map)) { - /* If we get here, it most likely means that NR_CPUS is - * less than the partition's max processors setting. - */ - printk(KERN_ERR "Cannot add cpu %s; this system configuration" - " supports %d logical cpus.\n", np->full_name, - cpus_weight(cpu_possible_map)); - goto out_unlock; - } - - while (!cpus_empty(tmp)) - if (cpus_subset(tmp, candidate_map)) - /* Found a range where we can insert the new cpu(s) */ - break; - else - cpus_shift_left(tmp, tmp, nthreads); - - if (cpus_empty(tmp)) { - printk(KERN_ERR "Unable to find space in cpu_present_map for" - " processor %s with %d thread(s)\n", np->name, - nthreads); - goto out_unlock; - } - - for_each_cpu_mask(cpu, tmp) { - BUG_ON(cpu_isset(cpu, cpu_present_map)); - cpu_set(cpu, cpu_present_map); - set_hard_smp_processor_id(cpu, *intserv++); - } - err = 0; -out_unlock: - unlock_cpu_hotplug(); - return err; -} - -/* - * Update the present map for a cpu node which is going away, and set - * the hard id in the paca(s) to -1 to be consistent with boot time - * convention for non-present cpus. - */ -static void pSeries_remove_processor(struct device_node *np) -{ - unsigned int cpu; - int len, nthreads, i; - const u32 *intserv; - - intserv = get_property(np, "ibm,ppc-interrupt-server#s", &len); - if (!intserv) - return; - - nthreads = len / sizeof(u32); - - lock_cpu_hotplug(); - for (i = 0; i < nthreads; i++) { - for_each_present_cpu(cpu) { - if (get_hard_smp_processor_id(cpu) != intserv[i]) - continue; - BUG_ON(cpu_online(cpu)); - cpu_clear(cpu, cpu_present_map); - set_hard_smp_processor_id(cpu, -1); - break; - } - if (cpu == NR_CPUS) - printk(KERN_WARNING "Could not find cpu to remove " - "with physical id 0x%x\n", intserv[i]); - } - unlock_cpu_hotplug(); -} - -static int pSeries_smp_notifier(struct notifier_block *nb, unsigned long action, void *node) -{ - int err = NOTIFY_OK; - - switch (action) { - case PSERIES_RECONFIG_ADD: - if (pSeries_add_processor(node)) - err = NOTIFY_BAD; - break; - case PSERIES_RECONFIG_REMOVE: - pSeries_remove_processor(node); - break; - default: - err = NOTIFY_DONE; - break; - } - return err; -} - -static struct notifier_block pSeries_smp_nb = { - .notifier_call = pSeries_smp_notifier, -}; - -#endif /* CONFIG_HOTPLUG_CPU */ - /** * smp_startup_cpu() - start the given cpu * @@ -422,15 +231,6 @@ static void __init smp_init_pseries(void) DBG(" -> smp_init_pSeries()\n"); -#ifdef CONFIG_HOTPLUG_CPU - smp_ops->cpu_disable = pSeries_cpu_disable; - smp_ops->cpu_die = pSeries_cpu_die; - - /* Processors can be added/removed only on LPAR */ - if (firmware_has_feature(FW_FEATURE_LPAR)) - pSeries_reconfig_notifier_register(&pSeries_smp_nb); -#endif - /* Mark threads which are still spinning in hold loops. */ if (cpu_has_feature(CPU_FTR_SMT)) { for_each_present_cpu(i) { -- cgit v1.2.3 From 674fa677c01ad0b90237f5cddc8d68502fea5156 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 5 Dec 2006 17:52:38 +1100 Subject: [POWERPC] Only enable cpu hotplug via RTAS if the required firmware support is found To support cpu hotplug on pseries we require two RTAS tokens. The cpu hotplug machinery should only be wired up if these tokens are found in the device tree. Signed-off-by: Michael Ellerman Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/hotplug-cpu.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'arch/powerpc/platforms/pseries') diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index 12864d75126d..e78e8ac95550 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -64,6 +64,8 @@ static void pSeries_mach_cpu_die(void) for(;;); } +static int qcss_tok; /* query-cpu-stopped-state token */ + /* Get state of physical CPU. * Return codes: * 0 - The processor is in the RTAS stopped state @@ -74,12 +76,8 @@ static void pSeries_mach_cpu_die(void) */ static int query_cpu_stopped(unsigned int pcpu) { - int cpu_status; - int status, qcss_tok; + int cpu_status, status; - qcss_tok = rtas_token("query-cpu-stopped-state"); - if (qcss_tok == RTAS_UNKNOWN_SERVICE) - return -1; status = rtas_call(qcss_tok, 1, 2, &cpu_status, pcpu); if (status != 0) { printk(KERN_ERR @@ -254,9 +252,16 @@ static struct notifier_block pSeries_smp_nb = { static int __init pseries_cpu_hotplug_init(void) { rtas_stop_self_args.token = rtas_token("stop-self"); + qcss_tok = rtas_token("query-cpu-stopped-state"); - ppc_md.cpu_die = pSeries_mach_cpu_die; + if (rtas_stop_self_args.token == RTAS_UNKNOWN_SERVICE || + qcss_tok == RTAS_UNKNOWN_SERVICE) { + printk(KERN_INFO "CPU Hotplug not supported by firmware " + "- disabling.\n"); + return 0; + } + ppc_md.cpu_die = pSeries_mach_cpu_die; smp_ops->cpu_disable = pSeries_cpu_disable; smp_ops->cpu_die = pSeries_cpu_die; -- cgit v1.2.3 From 06ba30b6bfb30c84b70fed681b8c920dbff5d5a4 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 5 Dec 2006 17:52:39 +1100 Subject: [POWERPC] Cleanup pass over platforms/pseries/hotplug-cpu.c Purely cosmetic. Change pSeries to pseries inline with other parts of the kernel, and fix an overly long line. Signed-off-by: Michael Ellerman Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/hotplug-cpu.c | 29 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'arch/powerpc/platforms/pseries') diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index e78e8ac95550..f460b9cbfd46 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -53,7 +53,7 @@ static void rtas_stop_self(void) panic("Alas, I survived.\n"); } -static void pSeries_mach_cpu_die(void) +static void pseries_mach_cpu_die(void) { local_irq_disable(); idle_task_exit(); @@ -88,7 +88,7 @@ static int query_cpu_stopped(unsigned int pcpu) return cpu_status; } -static int pSeries_cpu_disable(void) +static int pseries_cpu_disable(void) { int cpu = smp_processor_id(); @@ -104,7 +104,7 @@ static int pSeries_cpu_disable(void) return 0; } -static void pSeries_cpu_die(unsigned int cpu) +static void pseries_cpu_die(unsigned int cpu) { int tries; int cpu_status; @@ -136,7 +136,7 @@ static void pSeries_cpu_die(unsigned int cpu) * the logical ids for sibling SMT threads x and y are adjacent, such * that x^1 == y and y^1 == x. */ -static int pSeries_add_processor(struct device_node *np) +static int pseries_add_processor(struct device_node *np) { unsigned int cpu; cpumask_t candidate_map, tmp = CPU_MASK_NONE; @@ -197,7 +197,7 @@ out_unlock: * the hard id in the paca(s) to -1 to be consistent with boot time * convention for non-present cpus. */ -static void pSeries_remove_processor(struct device_node *np) +static void pseries_remove_processor(struct device_node *np) { unsigned int cpu; int len, nthreads, i; @@ -226,17 +226,18 @@ static void pSeries_remove_processor(struct device_node *np) unlock_cpu_hotplug(); } -static int pSeries_smp_notifier(struct notifier_block *nb, unsigned long action, void *node) +static int pseries_smp_notifier(struct notifier_block *nb, + unsigned long action, void *node) { int err = NOTIFY_OK; switch (action) { case PSERIES_RECONFIG_ADD: - if (pSeries_add_processor(node)) + if (pseries_add_processor(node)) err = NOTIFY_BAD; break; case PSERIES_RECONFIG_REMOVE: - pSeries_remove_processor(node); + pseries_remove_processor(node); break; default: err = NOTIFY_DONE; @@ -245,8 +246,8 @@ static int pSeries_smp_notifier(struct notifier_block *nb, unsigned long action, return err; } -static struct notifier_block pSeries_smp_nb = { - .notifier_call = pSeries_smp_notifier, +static struct notifier_block pseries_smp_nb = { + .notifier_call = pseries_smp_notifier, }; static int __init pseries_cpu_hotplug_init(void) @@ -261,13 +262,13 @@ static int __init pseries_cpu_hotplug_init(void) return 0; } - ppc_md.cpu_die = pSeries_mach_cpu_die; - smp_ops->cpu_disable = pSeries_cpu_disable; - smp_ops->cpu_die = pSeries_cpu_die; + ppc_md.cpu_die = pseries_mach_cpu_die; + smp_ops->cpu_disable = pseries_cpu_disable; + smp_ops->cpu_die = pseries_cpu_die; /* Processors can be added/removed only on LPAR */ if (firmware_has_feature(FW_FEATURE_LPAR)) - pSeries_reconfig_notifier_register(&pSeries_smp_nb); + pSeries_reconfig_notifier_register(&pseries_smp_nb); return 0; } -- cgit v1.2.3 From d0e70341c05f6c31375530e0ae29b319153004a7 Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Wed, 6 Dec 2006 12:32:20 -0600 Subject: [POWERPC] EEH recovery tweaks If one attempts to create a device driver recovery sequence that does not depend on a hard reset of the device, but simply just attempts to resume processing, then one discovers that the recovery sequence implemented on powerpc is not quite right. This patch fixes this up. Signed-off-by: Linas Vepstas Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/eeh.c | 1 + arch/powerpc/platforms/pseries/eeh_driver.c | 13 ++++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'arch/powerpc/platforms/pseries') diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 3c2d63ebf787..da6e5362e7cd 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -337,6 +337,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) printk (KERN_ERR "EEH: Device driver ignored %d bad reads, panicing\n", pdn->eeh_check_count); dump_stack(); + msleep(5000); /* re-read the slot reset state */ if (read_slot_reset_state(pdn, rets) != 0) diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c index c2bc9904f1cb..cbd6b0711ab4 100644 --- a/arch/powerpc/platforms/pseries/eeh_driver.c +++ b/arch/powerpc/platforms/pseries/eeh_driver.c @@ -170,14 +170,19 @@ static void eeh_report_reset(struct pci_dev *dev, void *userdata) static void eeh_report_resume(struct pci_dev *dev, void *userdata) { struct pci_driver *driver = dev->driver; + struct device_node *dn = pci_device_to_OF_node(dev); dev->error_state = pci_channel_io_normal; if (!driver) return; - if (!driver->err_handler) - return; - if (!driver->err_handler->resume) + + if ((PCI_DN(dn)->eeh_mode) & EEH_MODE_IRQ_DISABLED) { + PCI_DN(dn)->eeh_mode &= ~EEH_MODE_IRQ_DISABLED; + enable_irq(dev->irq); + } + if (!driver->err_handler || + !driver->err_handler->resume) return; driver->err_handler->resume(dev); @@ -407,6 +412,8 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event) if (rc) result = PCI_ERS_RESULT_NEED_RESET; + else + result = PCI_ERS_RESULT_RECOVERED; } /* If any device has a hard failure, then shut off everything. */ -- cgit v1.2.3 From b4d1ab58c016f4dffef4ff1eb392fa84e3c35024 Mon Sep 17 00:00:00 2001 From: Josef Sipek Date: Fri, 8 Dec 2006 02:37:30 -0800 Subject: [PATCH] struct path: convert powerpc Signed-off-by: Josef Sipek Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/powerpc/platforms/pseries/hvCall_inst.c | 2 +- arch/powerpc/platforms/pseries/scanlog.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'arch/powerpc/platforms/pseries') diff --git a/arch/powerpc/platforms/pseries/hvCall_inst.c b/arch/powerpc/platforms/pseries/hvCall_inst.c index 446e17d162a5..80181c4c49eb 100644 --- a/arch/powerpc/platforms/pseries/hvCall_inst.c +++ b/arch/powerpc/platforms/pseries/hvCall_inst.c @@ -85,7 +85,7 @@ static int hcall_inst_seq_open(struct inode *inode, struct file *file) rc = seq_open(file, &hcall_inst_seq_ops); seq = file->private_data; - seq->private = file->f_dentry->d_inode->i_private; + seq->private = file->f_path.dentry->d_inode->i_private; return rc; } diff --git a/arch/powerpc/platforms/pseries/scanlog.c b/arch/powerpc/platforms/pseries/scanlog.c index 77a5bb1d9c30..45368a57d7dd 100644 --- a/arch/powerpc/platforms/pseries/scanlog.c +++ b/arch/powerpc/platforms/pseries/scanlog.c @@ -47,7 +47,7 @@ static struct proc_dir_entry *proc_ppc64_scan_log_dump; /* The proc file */ static ssize_t scanlog_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - struct inode * inode = file->f_dentry->d_inode; + struct inode * inode = file->f_path.dentry->d_inode; struct proc_dir_entry *dp; unsigned int *data; int status; -- cgit v1.2.3 From 5773bbcdec54b7258cb9e2aa6f3459b4cbfd9dc5 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Fri, 8 Dec 2006 18:08:37 +1100 Subject: [POWERPC] micro optimise pSeries_probe We find the OF root the line before, we may as well use it. Signed-off-by: Anton Blanchard Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/setup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arch/powerpc/platforms/pseries') diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 6090d753c44c..3e2f7467057d 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -433,8 +433,8 @@ static int __init pSeries_probe_hypertas(unsigned long node, static int __init pSeries_probe(void) { unsigned long root = of_get_flat_dt_root(); - char *dtype = of_get_flat_dt_prop(of_get_flat_dt_root(), - "device_type", NULL); + char *dtype = of_get_flat_dt_prop(root, "device_type", NULL); + if (dtype == NULL) return 0; if (strcmp(dtype, "chrp")) -- cgit v1.2.3 From a223535425eb28082a0925b0ce2f02f962936cf4 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Fri, 8 Dec 2006 18:22:09 +1100 Subject: [POWERPC] dont allow pSeries_probe to succeed without initialising MMU pSeries_probe can decide that we are a pseries but then fail to initialise the MMU. If an rtas node doesnt exist, we continually fall out of pSeries_probe_hypertas early and never get to the MMU init code. While pseries without RTAS is an illegal combination, the way we currently fail is a pain to track down, and can happen if your flattened device tree code has issues (like mine did :). With the following patch we init the MMU, come up and print some warnings about RTAS not existing, instead of looping on 0x400 exceptions. Signed-off-by: Anton Blanchard Signed-off-by: Paul Mackerras --- arch/powerpc/platforms/pseries/setup.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'arch/powerpc/platforms/pseries') diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 3e2f7467057d..042ecae107ac 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -422,11 +422,6 @@ static int __init pSeries_probe_hypertas(unsigned long node, if (of_get_flat_dt_prop(node, "ibm,hypertas-functions", NULL) != NULL) powerpc_firmware_features |= FW_FEATURE_LPAR; - if (firmware_has_feature(FW_FEATURE_LPAR)) - hpte_init_lpar(); - else - hpte_init_native(); - return 1; } @@ -452,6 +447,11 @@ static int __init pSeries_probe(void) /* Now try to figure out if we are running on LPAR */ of_scan_flat_dt(pSeries_probe_hypertas, NULL); + if (firmware_has_feature(FW_FEATURE_LPAR)) + hpte_init_lpar(); + else + hpte_init_native(); + DBG("Machine is%s LPAR !\n", (powerpc_firmware_features & FW_FEATURE_LPAR) ? "" : " not"); -- cgit v1.2.3