* [PATCH 14/31] powerpc: Refactor 64 bits DMA operations
From: Benjamin Herrenschmidt @ 2006-11-07 7:22 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1162884150.435786.961370643810.qpush@grosgo>
This patch completely refactors DMA operations for 64 bits powerpc. 32 bits
is untouched for now.
The basic idea is to define an auxilliary structure (struct device_ext) that
can be attached to any struct device in the system. We use the "firmware_data"
pointer to reference it for now, though I kept that very isolated so I can
change it in the future, especially if I get my way with Greg KH and add a
sysdata pointer to struct device instead or even better, embed it inside
struct device to limit the number of pointer indirections.
This structure holds the optional OF device node pointer, the DMA ops and
associated void *data (to be used by the ops, typically, the iommu table
pointer for machines with more than one), and a numa node id (if useful)
to constraint consistent memory allocs. In the future, I might also merge
the current pci_dn into it for PCI.
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.
In the case of busses we control completely, like vio, the bus layer will
create and fill this structure and all is easy (see the changes to vio.c
or ebus).
In the case of "foreign" busses like PCI, what I've done is that I create
the data structure in the PCI fixup code (which is run after discovery but
before the struct device is actually registered with the core, as PCI keeps
those two steps separate) and use the old ppc_md. callbacks that I renamed
for consistency, to setup the iommu table pointer if necessary. By default
the dma_ops are set to the content of a global pci_dma_ops that your platform
can set. So if you have no iommu, you can just set that dma_direct_ops and
not implement any of the callbacks.
I do have plans to move away from that model for PCI, and consolidate this
with pci_dn, while make everybody use the platform notifiers provided by
the device core upon device registration but this goes beyond the goal of
this patch.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
arch/powerpc/kernel/Makefile | 3
arch/powerpc/kernel/dma_64.c | 251 +++++++++++++++++------------
arch/powerpc/kernel/ibmebus.c | 7
arch/powerpc/kernel/iommu.c | 6
arch/powerpc/kernel/of_platform.c | 10 +
arch/powerpc/kernel/pci_64.c | 57 ++++++
arch/powerpc/kernel/pci_direct_iommu.c | 98 -----------
arch/powerpc/kernel/pci_iommu.c | 164 ------------------
arch/powerpc/kernel/setup_64.c | 5
arch/powerpc/kernel/vio.c | 104 +++---------
arch/powerpc/platforms/cell/iommu.c | 21 --
arch/powerpc/platforms/iseries/iommu.c | 20 +-
arch/powerpc/platforms/iseries/pci.c | 2
arch/powerpc/platforms/pseries/iommu.c | 105 +++++++-----
arch/powerpc/platforms/pseries/pci_dlpar.c | 4
arch/powerpc/sysdev/dart_iommu.c | 38 +---
include/asm-powerpc/device_ext.h | 43 ++++
include/asm-powerpc/dma-mapping.h | 185 ++++++++++++++++-----
include/asm-powerpc/ibmebus.h | 3
include/asm-powerpc/iommu.h | 20 +-
include/asm-powerpc/iseries/iommu.h | 4
include/asm-powerpc/machdep.h | 4
include/asm-powerpc/of_device.h | 4
include/asm-powerpc/pci-bridge.h | 3
include/asm-powerpc/pci.h | 8
include/asm-powerpc/vio.h | 3
26 files changed, 580 insertions(+), 592 deletions(-)
Index: linux-cell/include/asm-powerpc/device_ext.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-cell/include/asm-powerpc/device_ext.h 2006-11-07 16:56:21.000000000 +1100
@@ -0,0 +1,43 @@
+#ifndef _POWERPC_DEVICE_EXT_H
+#define _POWERPC_DEVICE_EXT_H
+#ifdef __KERNEL__
+
+struct dma_mapping_ops;
+struct device_node;
+
+/*
+ * Auxilliary data structure that can be attached to any struct device
+ * in the system. (Currently using firmware_data, though I'd like to
+ * see a system_data in there instead).
+ *
+ * The structure can be wrapped within a bus-type specific data structure.
+ *
+ * There are no strict lifetime rules for this structure, it's entirely up
+ * to the bus to manage it's lifetime.
+ */
+struct device_ext {
+ /* Optional pointer to an OF device node */
+ struct device_node *of_node;
+
+ /* DMA operations on that device */
+ struct dma_mapping_ops *dma_ops;
+ void *dma_data;
+
+ /* NUMA node if applicable */
+ int numa_node;
+};
+
+/* Provide accessors in case we move it to some other field */
+static inline struct device_ext *device_get_ext(const struct device *dev)
+{
+ return dev->firmware_data;
+}
+
+static inline void device_set_ext(struct device *dev, struct device_ext *ext)
+{
+ dev->firmware_data = ext;
+}
+
+
+#endif /* __KERNEL__ */
+#endif /* _POWERPC_DEVICE_EXT_H */
Index: linux-cell/arch/powerpc/kernel/dma_64.c
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/dma_64.c 2006-11-07 16:56:19.000000000 +1100
+++ linux-cell/arch/powerpc/kernel/dma_64.c 2006-11-07 16:56:21.000000000 +1100
@@ -1,151 +1,204 @@
/*
- * Copyright (C) 2004 IBM Corporation
+ * Copyright (C) 2006 Benjamin Herrenschmidt, IBM Corporation
*
- * Implements the generic device dma API for ppc64. Handles
- * the pci and vio busses
+ * Provide default implementations of the DMA mapping callbacks for
+ * directly mapped busses and busses using the iommu infrastructure
*/
#include <linux/device.h>
#include <linux/dma-mapping.h>
-/* Include the busses we support */
-#include <linux/pci.h>
-#include <asm/vio.h>
-#include <asm/ibmebus.h>
-#include <asm/scatterlist.h>
#include <asm/bug.h>
+#include <asm/iommu.h>
+#include <asm/abs_addr.h>
-static struct dma_mapping_ops *get_dma_ops(struct device *dev)
+/*
+ * Generic iommu implementation
+ */
+
+static inline unsigned long device_to_mask(struct device *dev)
{
-#ifdef CONFIG_PCI
- if (dev->bus == &pci_bus_type)
- return &pci_dma_ops;
-#endif
-#ifdef CONFIG_IBMVIO
- if (dev->bus == &vio_bus_type)
- return &vio_dma_ops;
-#endif
-#ifdef CONFIG_IBMEBUS
- if (dev->bus == &ibmebus_bus_type)
- return &ibmebus_dma_ops;
-#endif
- return NULL;
+ if (dev->dma_mask && *dev->dma_mask)
+ return *dev->dma_mask;
+ /* Assume devices without mask can take 32 bit addresses */
+ return 0xfffffffful;
}
-int dma_supported(struct device *dev, u64 mask)
-{
- struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
- BUG_ON(!dma_ops);
+/* Allocates a contiguous real buffer and creates mappings over it.
+ * Returns the virtual address of the buffer and sets dma_handle
+ * to the dma address (mapping) of the first page.
+ */
+static void *dma_iommu_alloc_coherent(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t flag)
+{
+ struct device_ext *dext = device_get_ext(dev);
- return dma_ops->dma_supported(dev, mask);
+ if (unlikely(dext == NULL))
+ return NULL;
+ return iommu_alloc_coherent(dext->dma_data, size, dma_handle,
+ device_to_mask(dev), flag,
+ dext->numa_node);
}
-EXPORT_SYMBOL(dma_supported);
-int dma_set_mask(struct device *dev, u64 dma_mask)
+static void dma_iommu_free_coherent(struct device *dev, size_t size,
+ void *vaddr, dma_addr_t dma_handle)
{
-#ifdef CONFIG_PCI
- if (dev->bus == &pci_bus_type)
- return pci_set_dma_mask(to_pci_dev(dev), dma_mask);
-#endif
-#ifdef CONFIG_IBMVIO
- if (dev->bus == &vio_bus_type)
- return -EIO;
-#endif /* CONFIG_IBMVIO */
-#ifdef CONFIG_IBMEBUS
- if (dev->bus == &ibmebus_bus_type)
- return -EIO;
-#endif
- BUG();
- return 0;
+ struct device_ext *dext = device_get_ext(dev);
+
+ if (unlikely(dext == NULL))
+ return;
+ iommu_free_coherent(dext->dma_data, size, vaddr, dma_handle);
}
-EXPORT_SYMBOL(dma_set_mask);
-void *dma_alloc_coherent(struct device *dev, size_t size,
- dma_addr_t *dma_handle, gfp_t flag)
+/* Creates TCEs for a user provided buffer. The user buffer must be
+ * contiguous real kernel storage (not vmalloc). The address of the buffer
+ * passed here is the kernel (virtual) address of the buffer. The buffer
+ * need not be page aligned, the dma_addr_t returned will point to the same
+ * byte within the page as vaddr.
+ */
+static dma_addr_t dma_iommu_map_single(struct device *dev, void *vaddr,
+ size_t size,
+ enum dma_data_direction direction)
{
- struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
+ struct device_ext *dext = device_get_ext(dev);
- BUG_ON(!dma_ops);
-
- return dma_ops->alloc_coherent(dev, size, dma_handle, flag);
+ if (unlikely(dext == NULL))
+ return DMA_ERROR_CODE;
+ return iommu_map_single(dext->dma_data, vaddr, size,
+ device_to_mask(dev), direction);
}
-EXPORT_SYMBOL(dma_alloc_coherent);
-void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr,
- dma_addr_t dma_handle)
-{
- struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
- BUG_ON(!dma_ops);
+static void dma_iommu_unmap_single(struct device *dev, dma_addr_t dma_handle,
+ size_t size,
+ enum dma_data_direction direction)
+{
+ struct device_ext *dext = device_get_ext(dev);
- dma_ops->free_coherent(dev, size, cpu_addr, dma_handle);
+ if (unlikely(dext == NULL))
+ return;
+ iommu_unmap_single(dext->dma_data, dma_handle, size, direction);
}
-EXPORT_SYMBOL(dma_free_coherent);
-dma_addr_t dma_map_single(struct device *dev, void *cpu_addr, size_t size,
- enum dma_data_direction direction)
-{
- struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
- BUG_ON(!dma_ops);
+static int dma_iommu_map_sg(struct device *dev, struct scatterlist *sglist,
+ int nelems, enum dma_data_direction direction)
+{
+ struct device_ext *dext = device_get_ext(dev);
- return dma_ops->map_single(dev, cpu_addr, size, direction);
+ if (unlikely(dext == NULL))
+ return -ENXIO;
+ return iommu_map_sg(dext->dma_data, sglist, nelems,
+ device_to_mask(dev), direction);
}
-EXPORT_SYMBOL(dma_map_single);
-void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,
- enum dma_data_direction direction)
+static void dma_iommu_unmap_sg(struct device *dev, struct scatterlist *sglist,
+ int nelems, enum dma_data_direction direction)
{
- struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
-
- BUG_ON(!dma_ops);
+ struct device_ext *dext = device_get_ext(dev);
- dma_ops->unmap_single(dev, dma_addr, size, direction);
+ if (unlikely(dext == NULL))
+ return;
+ iommu_unmap_sg(dext->dma_data, sglist, nelems, direction);
}
-EXPORT_SYMBOL(dma_unmap_single);
-dma_addr_t dma_map_page(struct device *dev, struct page *page,
- unsigned long offset, size_t size,
- enum dma_data_direction direction)
+/* We support DMA to/from any memory page via the iommu */
+static int dma_iommu_dma_supported(struct device *dev, u64 mask)
{
- struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
+ struct device_ext *dext = device_get_ext(dev);
+ struct iommu_table *tbl = dext->dma_data;
- BUG_ON(!dma_ops);
+ if (!tbl || tbl->it_offset > mask) {
+ printk(KERN_INFO "Warning: IOMMU table offset too big for device mask\n");
+ if (tbl)
+ printk(KERN_INFO "mask: 0x%08lx, table offset: 0x%08lx\n",
+ mask, tbl->it_offset);
+ else
+ printk(KERN_INFO "mask: 0x%08lx, table unavailable\n",
+ mask);
+ return 0;
+ } else
+ return 1;
+}
- return dma_ops->map_single(dev, page_address(page) + offset, size,
- direction);
+struct dma_mapping_ops dma_iommu_ops = {
+ .alloc_coherent = dma_iommu_alloc_coherent,
+ .free_coherent = dma_iommu_free_coherent,
+ .map_single = dma_iommu_map_single,
+ .unmap_single = dma_iommu_unmap_single,
+ .map_sg = dma_iommu_map_sg,
+ .unmap_sg = dma_iommu_unmap_sg,
+ .dma_supported = dma_iommu_dma_supported,
+};
+EXPORT_SYMBOL(dma_iommu_ops);
+
+/*
+ * Generic direct DMA implementation
+ */
+
+static void *dma_direct_alloc_coherent(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t flag)
+{
+ void *ret;
+
+ /* TODO: Maybe use the numa node here too ? */
+ ret = (void *)__get_free_pages(flag, get_order(size));
+ if (ret != NULL) {
+ memset(ret, 0, size);
+ *dma_handle = virt_to_abs(ret);
+ }
+ return ret;
}
-EXPORT_SYMBOL(dma_map_page);
-void dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size,
- enum dma_data_direction direction)
+static void dma_direct_free_coherent(struct device *dev, size_t size,
+ void *vaddr, dma_addr_t dma_handle)
{
- struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
+ free_pages((unsigned long)vaddr, get_order(size));
+}
- BUG_ON(!dma_ops);
+static dma_addr_t dma_direct_map_single(struct device *dev, void *ptr,
+ size_t size,
+ enum dma_data_direction direction)
+{
+ return virt_to_abs(ptr);
+}
- dma_ops->unmap_single(dev, dma_address, size, direction);
+static void dma_direct_unmap_single(struct device *dev, dma_addr_t dma_addr,
+ size_t size,
+ enum dma_data_direction direction)
+{
}
-EXPORT_SYMBOL(dma_unmap_page);
-int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
- enum dma_data_direction direction)
+static int dma_direct_map_sg(struct device *dev, struct scatterlist *sg,
+ int nents, enum dma_data_direction direction)
{
- struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
+ int i;
- BUG_ON(!dma_ops);
+ for (i = 0; i < nents; i++, sg++) {
+ sg->dma_address = page_to_phys(sg->page) + sg->offset;
+ sg->dma_length = sg->length;
+ }
- return dma_ops->map_sg(dev, sg, nents, direction);
+ return nents;
}
-EXPORT_SYMBOL(dma_map_sg);
-void dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries,
- enum dma_data_direction direction)
+static void dma_direct_unmap_sg(struct device *dev, struct scatterlist *sg,
+ int nents, enum dma_data_direction direction)
{
- struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
-
- BUG_ON(!dma_ops);
+}
- dma_ops->unmap_sg(dev, sg, nhwentries, direction);
+static int dma_direct_dma_supported(struct device *dev, u64 mask)
+{
+ return mask < 0x100000000ull;
}
-EXPORT_SYMBOL(dma_unmap_sg);
+
+struct dma_mapping_ops dma_direct_ops = {
+ .alloc_coherent = dma_direct_alloc_coherent,
+ .free_coherent = dma_direct_free_coherent,
+ .map_single = dma_direct_map_single,
+ .unmap_single = dma_direct_unmap_single,
+ .map_sg = dma_direct_map_sg,
+ .unmap_sg = dma_direct_unmap_sg,
+ .dma_supported = dma_direct_dma_supported,
+};
+EXPORT_SYMBOL(dma_direct_ops);
Index: linux-cell/arch/powerpc/kernel/iommu.c
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/iommu.c 2006-11-07 16:56:19.000000000 +1100
+++ linux-cell/arch/powerpc/kernel/iommu.c 2006-11-07 16:56:21.000000000 +1100
@@ -258,9 +258,9 @@ static void iommu_free(struct iommu_tabl
spin_unlock_irqrestore(&(tbl->it_lock), flags);
}
-int iommu_map_sg(struct device *dev, struct iommu_table *tbl,
- struct scatterlist *sglist, int nelems,
- unsigned long mask, enum dma_data_direction direction)
+int iommu_map_sg(struct iommu_table *tbl, struct scatterlist *sglist,
+ int nelems, unsigned long mask,
+ enum dma_data_direction direction)
{
dma_addr_t dma_next = 0, dma_addr;
unsigned long flags;
Index: linux-cell/arch/powerpc/kernel/pci_direct_iommu.c
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/pci_direct_iommu.c 2006-11-07 16:56:19.000000000 +1100
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -1,98 +0,0 @@
-/*
- * Support for DMA from PCI devices to main memory on
- * machines without an iommu or with directly addressable
- * RAM (typically a pmac with 2Gb of RAM or less)
- *
- * Copyright (C) 2003 Benjamin Herrenschmidt (benh@kernel.crashing.org)
- *
- * 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 <linux/kernel.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <linux/bootmem.h>
-#include <linux/mm.h>
-#include <linux/dma-mapping.h>
-
-#include <asm/sections.h>
-#include <asm/io.h>
-#include <asm/prom.h>
-#include <asm/pci-bridge.h>
-#include <asm/machdep.h>
-#include <asm/pmac_feature.h>
-#include <asm/abs_addr.h>
-#include <asm/ppc-pci.h>
-
-static void *pci_direct_alloc_coherent(struct device *hwdev, size_t size,
- dma_addr_t *dma_handle, gfp_t flag)
-{
- void *ret;
-
- ret = (void *)__get_free_pages(flag, get_order(size));
- if (ret != NULL) {
- memset(ret, 0, size);
- *dma_handle = virt_to_abs(ret);
- }
- return ret;
-}
-
-static void pci_direct_free_coherent(struct device *hwdev, size_t size,
- void *vaddr, dma_addr_t dma_handle)
-{
- free_pages((unsigned long)vaddr, get_order(size));
-}
-
-static dma_addr_t pci_direct_map_single(struct device *hwdev, void *ptr,
- size_t size, enum dma_data_direction direction)
-{
- return virt_to_abs(ptr);
-}
-
-static void pci_direct_unmap_single(struct device *hwdev, dma_addr_t dma_addr,
- size_t size, enum dma_data_direction direction)
-{
-}
-
-static int pci_direct_map_sg(struct device *hwdev, struct scatterlist *sg,
- int nents, enum dma_data_direction direction)
-{
- int i;
-
- for (i = 0; i < nents; i++, sg++) {
- sg->dma_address = page_to_phys(sg->page) + sg->offset;
- sg->dma_length = sg->length;
- }
-
- return nents;
-}
-
-static void pci_direct_unmap_sg(struct device *hwdev, struct scatterlist *sg,
- int nents, enum dma_data_direction direction)
-{
-}
-
-static int pci_direct_dma_supported(struct device *dev, u64 mask)
-{
- return mask < 0x100000000ull;
-}
-
-static struct dma_mapping_ops pci_direct_ops = {
- .alloc_coherent = pci_direct_alloc_coherent,
- .free_coherent = pci_direct_free_coherent,
- .map_single = pci_direct_map_single,
- .unmap_single = pci_direct_unmap_single,
- .map_sg = pci_direct_map_sg,
- .unmap_sg = pci_direct_unmap_sg,
- .dma_supported = pci_direct_dma_supported,
-};
-
-void __init pci_direct_iommu_init(void)
-{
- pci_dma_ops = pci_direct_ops;
-}
Index: linux-cell/arch/powerpc/kernel/pci_iommu.c
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/pci_iommu.c 2006-11-07 16:56:19.000000000 +1100
+++ /dev/null 1970-01-01 00:00:00.000000000 +0000
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen, IBM Corporation
- *
- * Rewrite, cleanup, new allocation schemes:
- * Copyright (C) 2004 Olof Johansson, IBM Corporation
- *
- * Dynamic DMA mapping support, platform-independent parts.
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/spinlock.h>
-#include <linux/string.h>
-#include <linux/pci.h>
-#include <linux/dma-mapping.h>
-#include <asm/io.h>
-#include <asm/prom.h>
-#include <asm/iommu.h>
-#include <asm/pci-bridge.h>
-#include <asm/machdep.h>
-#include <asm/ppc-pci.h>
-
-/*
- * We can use ->sysdata directly and avoid the extra work in
- * pci_device_to_OF_node since ->sysdata will have been initialised
- * in the iommu init code for all devices.
- */
-#define PCI_GET_DN(dev) ((struct device_node *)((dev)->sysdata))
-
-static inline struct iommu_table *device_to_table(struct device *hwdev)
-{
- struct pci_dev *pdev;
-
- if (!hwdev) {
- pdev = ppc64_isabridge_dev;
- if (!pdev)
- return NULL;
- } else
- pdev = to_pci_dev(hwdev);
-
- return PCI_DN(PCI_GET_DN(pdev))->iommu_table;
-}
-
-
-static inline unsigned long device_to_mask(struct device *hwdev)
-{
- struct pci_dev *pdev;
-
- if (!hwdev) {
- pdev = ppc64_isabridge_dev;
- if (!pdev) /* This is the best guess we can do */
- return 0xfffffffful;
- } else
- pdev = to_pci_dev(hwdev);
-
- if (pdev->dma_mask)
- return pdev->dma_mask;
-
- /* Assume devices without mask can take 32 bit addresses */
- return 0xfffffffful;
-}
-
-
-/* Allocates a contiguous real buffer and creates mappings over it.
- * Returns the virtual address of the buffer and sets dma_handle
- * to the dma address (mapping) of the first page.
- */
-static void *pci_iommu_alloc_coherent(struct device *hwdev, size_t size,
- dma_addr_t *dma_handle, gfp_t flag)
-{
- return iommu_alloc_coherent(device_to_table(hwdev), size, dma_handle,
- device_to_mask(hwdev), flag,
- pcibus_to_node(to_pci_dev(hwdev)->bus));
-}
-
-static void pci_iommu_free_coherent(struct device *hwdev, size_t size,
- void *vaddr, dma_addr_t dma_handle)
-{
- iommu_free_coherent(device_to_table(hwdev), size, vaddr, dma_handle);
-}
-
-/* Creates TCEs for a user provided buffer. The user buffer must be
- * contiguous real kernel storage (not vmalloc). The address of the buffer
- * passed here is the kernel (virtual) address of the buffer. The buffer
- * need not be page aligned, the dma_addr_t returned will point to the same
- * byte within the page as vaddr.
- */
-static dma_addr_t pci_iommu_map_single(struct device *hwdev, void *vaddr,
- size_t size, enum dma_data_direction direction)
-{
- return iommu_map_single(device_to_table(hwdev), vaddr, size,
- device_to_mask(hwdev), direction);
-}
-
-
-static void pci_iommu_unmap_single(struct device *hwdev, dma_addr_t dma_handle,
- size_t size, enum dma_data_direction direction)
-{
- iommu_unmap_single(device_to_table(hwdev), dma_handle, size, direction);
-}
-
-
-static int pci_iommu_map_sg(struct device *pdev, struct scatterlist *sglist,
- int nelems, enum dma_data_direction direction)
-{
- return iommu_map_sg(pdev, device_to_table(pdev), sglist,
- nelems, device_to_mask(pdev), direction);
-}
-
-static void pci_iommu_unmap_sg(struct device *pdev, struct scatterlist *sglist,
- int nelems, enum dma_data_direction direction)
-{
- iommu_unmap_sg(device_to_table(pdev), sglist, nelems, direction);
-}
-
-/* We support DMA to/from any memory page via the iommu */
-static int pci_iommu_dma_supported(struct device *dev, u64 mask)
-{
- struct iommu_table *tbl = device_to_table(dev);
-
- if (!tbl || tbl->it_offset > mask) {
- printk(KERN_INFO "Warning: IOMMU table offset too big for device mask\n");
- if (tbl)
- printk(KERN_INFO "mask: 0x%08lx, table offset: 0x%08lx\n",
- mask, tbl->it_offset);
- else
- printk(KERN_INFO "mask: 0x%08lx, table unavailable\n",
- mask);
- return 0;
- } else
- return 1;
-}
-
-struct dma_mapping_ops pci_iommu_ops = {
- .alloc_coherent = pci_iommu_alloc_coherent,
- .free_coherent = pci_iommu_free_coherent,
- .map_single = pci_iommu_map_single,
- .unmap_single = pci_iommu_unmap_single,
- .map_sg = pci_iommu_map_sg,
- .unmap_sg = pci_iommu_unmap_sg,
- .dma_supported = pci_iommu_dma_supported,
-};
-
-void pci_iommu_init(void)
-{
- pci_dma_ops = pci_iommu_ops;
-}
Index: linux-cell/include/asm-powerpc/dma-mapping.h
===================================================================
--- linux-cell.orig/include/asm-powerpc/dma-mapping.h 2006-11-07 16:56:19.000000000 +1100
+++ linux-cell/include/asm-powerpc/dma-mapping.h 2006-11-07 16:56:21.000000000 +1100
@@ -14,6 +14,7 @@
#include <linux/mm.h>
#include <asm/scatterlist.h>
#include <asm/io.h>
+#include <asm/device_ext.h>
#define DMA_ERROR_CODE (~(dma_addr_t)0x0)
@@ -44,26 +45,152 @@ extern void __dma_sync_page(struct page
#endif /* ! CONFIG_NOT_COHERENT_CACHE */
#ifdef CONFIG_PPC64
+/*
+ * DMA operations are abstracted for G5 vs. i/pSeries, PCI vs. VIO
+ */
+struct dma_mapping_ops {
+ void * (*alloc_coherent)(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t flag);
+ void (*free_coherent)(struct device *dev, size_t size,
+ void *vaddr, dma_addr_t dma_handle);
+ dma_addr_t (*map_single)(struct device *dev, void *ptr,
+ size_t size, enum dma_data_direction direction);
+ void (*unmap_single)(struct device *dev, dma_addr_t dma_addr,
+ size_t size, enum dma_data_direction direction);
+ int (*map_sg)(struct device *dev, struct scatterlist *sg,
+ int nents, enum dma_data_direction direction);
+ void (*unmap_sg)(struct device *dev, struct scatterlist *sg,
+ int nents, enum dma_data_direction direction);
+ int (*dma_supported)(struct device *dev, u64 mask);
+ int (*dac_dma_supported)(struct device *dev, u64 mask);
+ int (*set_dma_mask)(struct device *dev, u64 dma_mask);
+};
+
+static inline struct dma_mapping_ops *get_dma_ops(struct device *dev)
+{
+ struct device_ext *dext;
+
+ /* We don't handle the NULL dev case for ISA for now. We could
+ * do it via an out of line call but it is not needed for now
+ */
+ if (dev == NULL)
+ return NULL;
+
+ dext = device_get_ext(dev);
+ if (unlikely(dext == NULL))
+ return NULL;
+ return dext->dma_ops;
+}
+
+static inline int dma_supported(struct device *dev, u64 mask)
+{
+ struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
+
+ if (unlikely(dma_ops == NULL))
+ return 0;
+ if (dma_ops->dma_supported == NULL)
+ return 1;
+ return dma_ops->dma_supported(dev, mask);
+}
+
+static inline int dma_set_mask(struct device *dev, u64 dma_mask)
+{
+ struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
+
+ if (unlikely(dma_ops == NULL))
+ return -EIO;
+ if (dma_ops->set_dma_mask != NULL)
+ return dma_ops->set_dma_mask(dev, dma_mask);
+ if (!dev->dma_mask || !dma_supported(dev, *dev->dma_mask))
+ return -EIO;
+ *dev->dma_mask = dma_mask;
+ return 0;
+}
+
+static inline void *dma_alloc_coherent(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t flag)
+{
+ struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
+
+ BUG_ON(!dma_ops);
+ return dma_ops->alloc_coherent(dev, size, dma_handle, flag);
+}
+
+static inline void dma_free_coherent(struct device *dev, size_t size,
+ void *cpu_addr, dma_addr_t dma_handle)
+{
+ struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
+
+ BUG_ON(!dma_ops);
+ dma_ops->free_coherent(dev, size, cpu_addr, dma_handle);
+}
+
+static inline dma_addr_t dma_map_single(struct device *dev, void *cpu_addr,
+ size_t size,
+ enum dma_data_direction direction)
+{
+ struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
+
+ BUG_ON(!dma_ops);
+ return dma_ops->map_single(dev, cpu_addr, size, direction);
+}
+
+static inline void dma_unmap_single(struct device *dev, dma_addr_t dma_addr,
+ size_t size,
+ enum dma_data_direction direction)
+{
+ struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
+
+ BUG_ON(!dma_ops);
+ dma_ops->unmap_single(dev, dma_addr, size, direction);
+}
+
+static inline dma_addr_t dma_map_page(struct device *dev, struct page *page,
+ unsigned long offset, size_t size,
+ enum dma_data_direction direction)
+{
+ struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
+
+ BUG_ON(!dma_ops);
+ return dma_ops->map_single(dev, page_address(page) + offset, size,
+ direction);
+}
+
+static inline void dma_unmap_page(struct device *dev, dma_addr_t dma_address,
+ size_t size,
+ enum dma_data_direction direction)
+{
+ struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
+
+ BUG_ON(!dma_ops);
+ dma_ops->unmap_single(dev, dma_address, size, direction);
+}
+
+static inline int dma_map_sg(struct device *dev, struct scatterlist *sg,
+ int nents, enum dma_data_direction direction)
+{
+ struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
+
+ BUG_ON(!dma_ops);
+ return dma_ops->map_sg(dev, sg, nents, direction);
+}
+
+static inline void dma_unmap_sg(struct device *dev, struct scatterlist *sg,
+ int nhwentries,
+ enum dma_data_direction direction)
+{
+ struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
+
+ BUG_ON(!dma_ops);
+ dma_ops->unmap_sg(dev, sg, nhwentries, direction);
+}
-extern int dma_supported(struct device *dev, u64 mask);
-extern int dma_set_mask(struct device *dev, u64 dma_mask);
-extern void *dma_alloc_coherent(struct device *dev, size_t size,
- dma_addr_t *dma_handle, gfp_t flag);
-extern void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr,
- dma_addr_t dma_handle);
-extern dma_addr_t dma_map_single(struct device *dev, void *cpu_addr,
- size_t size, enum dma_data_direction direction);
-extern void dma_unmap_single(struct device *dev, dma_addr_t dma_addr,
- size_t size, enum dma_data_direction direction);
-extern dma_addr_t dma_map_page(struct device *dev, struct page *page,
- unsigned long offset, size_t size,
- enum dma_data_direction direction);
-extern void dma_unmap_page(struct device *dev, dma_addr_t dma_address,
- size_t size, enum dma_data_direction direction);
-extern int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
- enum dma_data_direction direction);
-extern void dma_unmap_sg(struct device *dev, struct scatterlist *sg,
- int nhwentries, enum dma_data_direction direction);
+
+/*
+ * Available generic sets of operations
+ */
+extern struct dma_mapping_ops dma_iommu_ops;
+extern struct dma_mapping_ops dma_direct_ops;
#else /* CONFIG_PPC64 */
@@ -261,25 +388,5 @@ static inline void dma_cache_sync(void *
__dma_sync(vaddr, size, (int)direction);
}
-/*
- * DMA operations are abstracted for G5 vs. i/pSeries, PCI vs. VIO
- */
-struct dma_mapping_ops {
- void * (*alloc_coherent)(struct device *dev, size_t size,
- dma_addr_t *dma_handle, gfp_t flag);
- void (*free_coherent)(struct device *dev, size_t size,
- void *vaddr, dma_addr_t dma_handle);
- dma_addr_t (*map_single)(struct device *dev, void *ptr,
- size_t size, enum dma_data_direction direction);
- void (*unmap_single)(struct device *dev, dma_addr_t dma_addr,
- size_t size, enum dma_data_direction direction);
- int (*map_sg)(struct device *dev, struct scatterlist *sg,
- int nents, enum dma_data_direction direction);
- void (*unmap_sg)(struct device *dev, struct scatterlist *sg,
- int nents, enum dma_data_direction direction);
- int (*dma_supported)(struct device *dev, u64 mask);
- int (*dac_dma_supported)(struct device *dev, u64 mask);
-};
-
#endif /* __KERNEL__ */
#endif /* _ASM_DMA_MAPPING_H */
Index: linux-cell/include/asm-powerpc/iommu.h
===================================================================
--- linux-cell.orig/include/asm-powerpc/iommu.h 2006-11-07 16:56:19.000000000 +1100
+++ linux-cell/include/asm-powerpc/iommu.h 2006-11-07 16:56:21.000000000 +1100
@@ -87,22 +87,22 @@ extern void iommu_free_table(struct devi
extern struct iommu_table *iommu_init_table(struct iommu_table * tbl,
int nid);
-extern int iommu_map_sg(struct device *dev, struct iommu_table *tbl,
- struct scatterlist *sglist, int nelems, unsigned long mask,
- enum dma_data_direction direction);
+extern int iommu_map_sg(struct iommu_table *tbl, struct scatterlist *sglist,
+ int nelems, unsigned long mask,
+ enum dma_data_direction direction);
extern void iommu_unmap_sg(struct iommu_table *tbl, struct scatterlist *sglist,
- int nelems, enum dma_data_direction direction);
+ int nelems, enum dma_data_direction direction);
extern void *iommu_alloc_coherent(struct iommu_table *tbl, size_t size,
- dma_addr_t *dma_handle, unsigned long mask,
- gfp_t flag, int node);
+ dma_addr_t *dma_handle, unsigned long mask,
+ gfp_t flag, int node);
extern void iommu_free_coherent(struct iommu_table *tbl, size_t size,
- void *vaddr, dma_addr_t dma_handle);
+ void *vaddr, dma_addr_t dma_handle);
extern dma_addr_t iommu_map_single(struct iommu_table *tbl, void *vaddr,
- size_t size, unsigned long mask,
- enum dma_data_direction direction);
+ size_t size, unsigned long mask,
+ enum dma_data_direction direction);
extern void iommu_unmap_single(struct iommu_table *tbl, dma_addr_t dma_handle,
- size_t size, enum dma_data_direction direction);
+ size_t size, enum dma_data_direction direction);
extern void iommu_init_early_pSeries(void);
extern void iommu_init_early_iSeries(void);
Index: linux-cell/arch/powerpc/kernel/vio.c
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/vio.c 2006-11-07 16:56:19.000000000 +1100
+++ linux-cell/arch/powerpc/kernel/vio.c 2006-11-07 16:58:12.000000000 +1100
@@ -81,15 +81,15 @@ static struct iommu_table *vio_build_iom
struct iommu_table *tbl;
unsigned long offset, size;
- dma_window = get_property(dev->dev.platform_data,
- "ibm,my-dma-window", NULL);
+ dma_window = get_property(dev->dext.of_node,
+ "ibm,my-dma-window", NULL);
if (!dma_window)
return NULL;
tbl = kmalloc(sizeof(*tbl), GFP_KERNEL);
- of_parse_dma_window(dev->dev.platform_data, dma_window,
- &tbl->it_index, &offset, &size);
+ of_parse_dma_window(dev->dext.of_node, dma_window,
+ &tbl->it_index, &offset, &size);
/* TCE table size - measured in tce entries */
tbl->it_size = size >> IOMMU_PAGE_SHIFT;
@@ -115,9 +115,11 @@ static struct iommu_table *vio_build_iom
static const struct vio_device_id *vio_match_device(
const struct vio_device_id *ids, const struct vio_dev *dev)
{
+ struct device_ext *dext = device_get_ext(&dev->dev);
+
while (ids->type[0] != '\0') {
if ((strncmp(dev->type, ids->type, strlen(ids->type)) == 0) &&
- device_is_compatible(dev->dev.platform_data, ids->compat))
+ device_is_compatible(dext->of_node, ids->compat))
return ids;
ids++;
}
@@ -198,9 +200,11 @@ EXPORT_SYMBOL(vio_unregister_driver);
/* vio_dev refcount hit 0 */
static void __devinit vio_dev_release(struct device *dev)
{
- if (dev->platform_data) {
+ struct device_ext *dext = device_get_ext(dev);
+
+ if (dext && dext->of_node) {
/* XXX free TCE table */
- of_node_put(dev->platform_data);
+ of_node_put(dext->of_node);
}
kfree(to_vio_dev(dev));
}
@@ -210,7 +214,7 @@ static void __devinit vio_dev_release(st
* @of_node: The OF node for this device.
*
* Creates and initializes a vio_dev structure from the data in
- * of_node (dev.platform_data) and adds it to the list of virtual devices.
+ * of_node and adds it to the list of virtual devices.
* Returns a pointer to the created vio_dev or NULL if node has
* NULL device_type or compatible fields.
*/
@@ -240,8 +244,6 @@ struct vio_dev * __devinit vio_register_
if (viodev == NULL)
return NULL;
- viodev->dev.platform_data = of_node_get(of_node);
-
viodev->irq = irq_of_parse_and_map(of_node, 0);
snprintf(viodev->dev.bus_id, BUS_ID_SIZE, "%x", *unit_address);
@@ -254,7 +256,11 @@ struct vio_dev * __devinit vio_register_
if (unit_address != NULL)
viodev->unit_address = *unit_address;
}
- viodev->iommu_table = vio_build_iommu_table(viodev);
+ device_set_ext(&viodev->dev, &viodev->dext);
+ viodev->dext.of_node = of_node_get(of_node);
+ viodev->dext.dma_ops = &dma_iommu_ops;
+ viodev->dext.dma_data = vio_build_iommu_table(viodev);
+ viodev->dext.numa_node = of_node_to_nid(of_node);
/* init generic 'struct device' fields: */
viodev->dev.parent = &vio_bus_device.dev;
@@ -282,13 +288,16 @@ static int __init vio_bus_init(void)
int err;
struct device_node *node_vroot;
+ device_set_ext(&vio_bus_device.dev, &vio_bus_device.dext);
+
#ifdef CONFIG_PPC_ISERIES
if (firmware_has_feature(FW_FEATURE_ISERIES)) {
iommu_vio_init();
- vio_bus_device.iommu_table = &vio_iommu_table;
+ vio_bus_device.dext.dma_ops = &dma_iommu_ops;
+ vio_bus_device.dext.dma_data = &vio_iommu_table;
iSeries_vio_dev = &vio_bus_device.dev;
}
-#endif
+#endif /* CONFIG_PPC_ISERIES */
err = bus_register(&vio_bus_type);
if (err) {
@@ -336,7 +345,8 @@ static ssize_t name_show(struct device *
static ssize_t devspec_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct device_node *of_node = dev->platform_data;
+ struct device_ext *dext = device_get_ext(dev);
+ struct device_node *of_node = dext->of_node;
return sprintf(buf, "%s\n", of_node ? of_node->full_name : "none");
}
@@ -353,62 +363,6 @@ void __devinit vio_unregister_device(str
}
EXPORT_SYMBOL(vio_unregister_device);
-static dma_addr_t vio_map_single(struct device *dev, void *vaddr,
- size_t size, enum dma_data_direction direction)
-{
- return iommu_map_single(to_vio_dev(dev)->iommu_table, vaddr, size,
- ~0ul, direction);
-}
-
-static void vio_unmap_single(struct device *dev, dma_addr_t dma_handle,
- size_t size, enum dma_data_direction direction)
-{
- iommu_unmap_single(to_vio_dev(dev)->iommu_table, dma_handle, size,
- direction);
-}
-
-static int vio_map_sg(struct device *dev, struct scatterlist *sglist,
- int nelems, enum dma_data_direction direction)
-{
- return iommu_map_sg(dev, to_vio_dev(dev)->iommu_table, sglist,
- nelems, ~0ul, direction);
-}
-
-static void vio_unmap_sg(struct device *dev, struct scatterlist *sglist,
- int nelems, enum dma_data_direction direction)
-{
- iommu_unmap_sg(to_vio_dev(dev)->iommu_table, sglist, nelems, direction);
-}
-
-static void *vio_alloc_coherent(struct device *dev, size_t size,
- dma_addr_t *dma_handle, gfp_t flag)
-{
- return iommu_alloc_coherent(to_vio_dev(dev)->iommu_table, size,
- dma_handle, ~0ul, flag, -1);
-}
-
-static void vio_free_coherent(struct device *dev, size_t size,
- void *vaddr, dma_addr_t dma_handle)
-{
- iommu_free_coherent(to_vio_dev(dev)->iommu_table, size, vaddr,
- dma_handle);
-}
-
-static int vio_dma_supported(struct device *dev, u64 mask)
-{
- return 1;
-}
-
-struct dma_mapping_ops vio_dma_ops = {
- .alloc_coherent = vio_alloc_coherent,
- .free_coherent = vio_free_coherent,
- .map_single = vio_map_single,
- .unmap_single = vio_unmap_single,
- .map_sg = vio_map_sg,
- .unmap_sg = vio_unmap_sg,
- .dma_supported = vio_dma_supported,
-};
-
static int vio_bus_match(struct device *dev, struct device_driver *drv)
{
const struct vio_dev *vio_dev = to_vio_dev(dev);
@@ -422,13 +376,17 @@ static int vio_hotplug(struct device *de
char *buffer, int buffer_size)
{
const struct vio_dev *vio_dev = to_vio_dev(dev);
- struct device_node *dn = dev->platform_data;
+ struct device_ext *dext = device_get_ext(dev);
+ struct device_node *dn;
const char *cp;
int length;
if (!num_envp)
return -ENOMEM;
+ if (!dext)
+ return -ENODEV;
+ dn = dext->of_node;
if (!dn)
return -ENODEV;
cp = get_property(dn, "compatible", &length);
@@ -465,7 +423,9 @@ struct bus_type vio_bus_type = {
*/
const void *vio_get_attribute(struct vio_dev *vdev, char *which, int *length)
{
- return get_property(vdev->dev.platform_data, which, length);
+ struct device_ext *dext = device_get_ext(&vdev->dev);
+
+ return get_property(dext->of_node, which, length);
}
EXPORT_SYMBOL(vio_get_attribute);
Index: linux-cell/include/asm-powerpc/vio.h
===================================================================
--- linux-cell.orig/include/asm-powerpc/vio.h 2006-11-07 16:56:19.000000000 +1100
+++ linux-cell/include/asm-powerpc/vio.h 2006-11-07 16:56:21.000000000 +1100
@@ -23,6 +23,7 @@
#include <asm/hvcall.h>
#include <asm/scatterlist.h>
+#include <asm/device_ext.h>
/*
* Architecture-specific constants for drivers to
@@ -45,12 +46,12 @@ struct iommu_table;
* The vio_dev structure is used to describe virtual I/O devices.
*/
struct vio_dev {
- struct iommu_table *iommu_table; /* vio_map_* uses this */
const char *name;
const char *type;
uint32_t unit_address;
unsigned int irq;
struct device dev;
+ struct device_ext dext;
};
struct vio_driver {
Index: linux-cell/arch/powerpc/kernel/pci_64.c
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/pci_64.c 2006-11-07 16:56:19.000000000 +1100
+++ linux-cell/arch/powerpc/kernel/pci_64.c 2006-11-07 16:59:03.000000000 +1100
@@ -31,6 +31,7 @@
#include <asm/machdep.h>
#include <asm/ppc-pci.h>
#include <asm/firmware.h>
+#include <asm/device_ext.h>
#ifdef DEBUG
#include <asm/udbg.h>
@@ -63,7 +64,7 @@ void iSeries_pcibios_init(void);
LIST_HEAD(hose_list);
-struct dma_mapping_ops pci_dma_ops;
+struct dma_mapping_ops *pci_dma_ops;
EXPORT_SYMBOL(pci_dma_ops);
int global_phb_number; /* Global phb counter */
@@ -1213,15 +1214,43 @@ void __devinit pcibios_fixup_device_reso
}
EXPORT_SYMBOL(pcibios_fixup_device_resources);
+void __devinit pcibios_setup_new_device(struct pci_dev *dev)
+{
+ struct device_ext *dext;
+
+ if (device_get_ext(&dev->dev) != NULL)
+ return;
+
+ dext = kzalloc(sizeof(struct device_ext), GFP_KERNEL);
+ if (dext == NULL) {
+ printk(KERN_ERR "Failed to allocate device extension"
+ " for PCI device %s\n", pci_name(dev));
+ return;
+ }
+ device_set_ext(&dev->dev, dext);
+ dext->of_node = pci_device_to_OF_node(dev);
+ DBG("PCI device %s OF node: %s\n", pci_name(dev),
+ dext->of_node ? dext->of_node->full_name : "<none>");
+ dext->dma_ops = pci_dma_ops;
+#ifdef CONFIG_NUMA
+ dext->numa_node = pcibus_to_node(dev->bus);
+#else
+ dext->numa_node = -1;
+#endif
+ if (ppc_md.pci_dma_dev_setup)
+ ppc_md.pci_dma_dev_setup(dev);
+}
+EXPORT_SYMBOL(pcibios_setup_new_device);
static void __devinit do_bus_setup(struct pci_bus *bus)
{
struct pci_dev *dev;
- ppc_md.iommu_bus_setup(bus);
+ if (ppc_md.pci_dma_bus_setup)
+ ppc_md.pci_dma_bus_setup(bus);
list_for_each_entry(dev, &bus->devices, bus_list)
- ppc_md.iommu_dev_setup(dev);
+ pcibios_setup_new_device(dev);
/* Read default IRQs and fixup if necessary */
list_for_each_entry(dev, &bus->devices, bus_list) {
@@ -1429,3 +1458,25 @@ int pcibus_to_node(struct pci_bus *bus)
}
EXPORT_SYMBOL(pcibus_to_node);
#endif
+
+int pci_platform_notify(struct device * dev)
+{
+ return 0;
+}
+
+int pci_platform_notify_remove(struct device * dev)
+{
+ struct device_ext *dext = device_get_ext(dev);
+
+ DBG("%s:%s platform notify remove !\n",
+ dev_driver_string(dev), dev->bus_id);
+
+ if (dev->bus != &pci_bus_type)
+ return 0;
+
+ device_set_ext(dev, NULL);
+ mb();
+ kfree(dext);
+
+ return 0;
+}
Index: linux-cell/arch/powerpc/kernel/setup_64.c
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/setup_64.c 2006-11-07 16:56:19.000000000 +1100
+++ linux-cell/arch/powerpc/kernel/setup_64.c 2006-11-07 16:56:21.000000000 +1100
@@ -33,6 +33,7 @@
#include <linux/serial.h>
#include <linux/serial_8250.h>
#include <linux/bootmem.h>
+#include <linux/pci.h>
#include <asm/io.h>
#include <asm/kdump.h>
#include <asm/prom.h>
@@ -530,6 +531,10 @@ void __init setup_arch(char **cmdline_p)
conswitchp = &dummy_con;
#endif
+#ifdef CONFIG_PCI
+ platform_notify = pci_platform_notify;
+ platform_notify_remove = pci_platform_notify_remove;
+#endif
ppc_md.setup_arch();
paging_init();
Index: linux-cell/arch/powerpc/platforms/pseries/iommu.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/pseries/iommu.c 2006-11-07 16:56:19.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/pseries/iommu.c 2006-11-07 16:56:21.000000000 +1100
@@ -44,6 +44,7 @@
#include <asm/tce.h>
#include <asm/ppc-pci.h>
#include <asm/udbg.h>
+#include <asm/device_ext.h>
#include "plpar_wrappers.h"
@@ -309,7 +310,7 @@ static void iommu_table_setparms_lpar(st
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 +319,9 @@ static void iommu_bus_setup_pSeries(stru
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 +329,7 @@ static void iommu_bus_setup_pSeries(stru
*/
return;
}
+ pci = PCI_DN(dn);
/* Check if the ISA bus on the system is under
* this PHB.
@@ -390,17 +391,17 @@ static void iommu_bus_setup_pSeries(stru
}
-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 +410,15 @@ static void iommu_bus_setup_pSeriesLP(st
}
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 +432,7 @@ static void iommu_bus_setup_pSeriesLP(st
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 +440,34 @@ static void iommu_bus_setup_pSeriesLP(st
}
-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_ext *dext = device_get_ext(&dev->dev);
+ 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));
+
+ if (dext == NULL) {
+ printk(KERN_ERR "iommu: Attempt to setup PCI dev %s "
+ "with no device extension !", pci_name(dev));
+ return;
+ }
- mydn = dn = pci_device_to_OF_node(dev);
+ dn = dext->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);
+ dext->dma_data = iommu_init_table(tbl, phb->node);
return;
}
@@ -465,11 +478,11 @@ static void iommu_dev_setup_pSeries(stru
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))
+ dext->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 +508,22 @@ static struct notifier_block iommu_recon
.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_ext *dext = device_get_ext(&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));
+
+ if (dext == NULL) {
+ printk(KERN_ERR "iommu: Attempt to setup PCI dev %s "
+ "with no device extension !", pci_name(dev));
+ return;
+ }
+
/* 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 +531,7 @@ static void iommu_dev_setup_pSeriesLP(st
* 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 +540,17 @@ static void iommu_dev_setup_pSeriesLP(st
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");
+ dext->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 +563,20 @@ static void iommu_dev_setup_pSeriesLP(st
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;
+ dext->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 +589,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;
}
Index: linux-cell/arch/powerpc/platforms/pseries/pci_dlpar.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/pseries/pci_dlpar.c 2006-11-07 16:56:19.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/pseries/pci_dlpar.c 2006-11-07 16:56:21.000000000 +1100
@@ -93,8 +93,8 @@ pcibios_fixup_new_pci_devices(struct pci
if (list_empty(&dev->global_list)) {
int i;
- /* Need to setup IOMMU tables */
- ppc_md.iommu_dev_setup(dev);
+ /* Create device extension and setup iommu table */
+ pcibios_setup_new_device(dev);
if(fix_bus)
pcibios_fixup_device_resources(dev, bus);
Index: linux-cell/arch/powerpc/sysdev/dart_iommu.c
===================================================================
--- linux-cell.orig/arch/powerpc/sysdev/dart_iommu.c 2006-11-07 16:56:19.000000000 +1100
+++ linux-cell/arch/powerpc/sysdev/dart_iommu.c 2006-11-07 16:56:21.000000000 +1100
@@ -45,6 +45,7 @@
#include <asm/cacheflush.h>
#include <asm/lmb.h>
#include <asm/ppc-pci.h>
+#include <asm/device_ext.h>
#include "dart.h"
@@ -289,24 +290,23 @@ static void iommu_table_dart_setup(void)
set_bit(iommu_table_dart.it_size - 1, iommu_table_dart.it_map);
}
-static void iommu_dev_setup_dart(struct pci_dev *dev)
+static void pci_dma_dev_setup_dart(struct pci_dev *dev)
{
- struct device_node *dn;
+ struct device_ext *dext = device_get_ext(&dev->dev);
+
+ if (dext == NULL) {
+ printk(KERN_ERR "dart: Attempt to setup PCI dev %s "
+ "with no device extension !", pci_name(dev));
+ return;
+ }
/* We only have one iommu table on the mac for now, which makes
* things simple. Setup all PCI devices to point to this table
- *
- * We must use pci_device_to_OF_node() to make sure that
- * we get the real "final" pointer to the device in the
- * pci_dev sysdata and not the temporary PHB one
*/
- dn = pci_device_to_OF_node(dev);
-
- if (dn)
- PCI_DN(dn)->iommu_table = &iommu_table_dart;
+ dext->dma_data = &iommu_table_dart;
}
-static void iommu_bus_setup_dart(struct pci_bus *bus)
+static void pci_dma_bus_setup_dart(struct pci_bus *bus)
{
struct device_node *dn;
@@ -321,9 +321,6 @@ static void iommu_bus_setup_dart(struct
PCI_DN(dn)->iommu_table = &iommu_table_dart;
}
-static void iommu_dev_setup_null(struct pci_dev *dev) { }
-static void iommu_bus_setup_null(struct pci_bus *bus) { }
-
void iommu_init_early_dart(void)
{
struct device_node *dn;
@@ -344,22 +341,21 @@ void iommu_init_early_dart(void)
/* Initialize the DART HW */
if (dart_init(dn) == 0) {
- ppc_md.iommu_dev_setup = iommu_dev_setup_dart;
- ppc_md.iommu_bus_setup = iommu_bus_setup_dart;
+ ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_dart;
+ ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_dart;
/* Setup pci_dma ops */
- pci_iommu_init();
-
+ pci_dma_ops = &dma_iommu_ops;
return;
}
bail:
/* If init failed, use direct iommu and null setup functions */
- ppc_md.iommu_dev_setup = iommu_dev_setup_null;
- ppc_md.iommu_bus_setup = iommu_bus_setup_null;
+ ppc_md.pci_dma_dev_setup = NULL;
+ ppc_md.pci_dma_bus_setup = NULL;
/* Setup pci_dma ops */
- pci_direct_iommu_init();
+ pci_dma_ops = &dma_direct_ops;
}
Index: linux-cell/include/asm-powerpc/machdep.h
===================================================================
--- linux-cell.orig/include/asm-powerpc/machdep.h 2006-11-07 16:56:19.000000000 +1100
+++ linux-cell/include/asm-powerpc/machdep.h 2006-11-07 16:56:21.000000000 +1100
@@ -84,8 +84,8 @@ struct machdep_calls {
unsigned long (*tce_get)(struct iommu_table *tbl,
long index);
void (*tce_flush)(struct iommu_table *tbl);
- void (*iommu_dev_setup)(struct pci_dev *dev);
- void (*iommu_bus_setup)(struct pci_bus *bus);
+ void (*pci_dma_dev_setup)(struct pci_dev *dev);
+ void (*pci_dma_bus_setup)(struct pci_bus *bus);
#endif /* CONFIG_PPC64 */
int (*probe)(void);
Index: linux-cell/include/asm-powerpc/pci-bridge.h
===================================================================
--- linux-cell.orig/include/asm-powerpc/pci-bridge.h 2006-11-07 16:56:19.000000000 +1100
+++ linux-cell/include/asm-powerpc/pci-bridge.h 2006-11-07 16:56:21.000000000 +1100
@@ -161,6 +161,9 @@ static inline unsigned long pci_address_
}
#endif
+extern int pci_platform_notify(struct device * dev);
+extern int pci_platform_notify_remove(struct device * dev);
+
/* Return values for ppc_md.pci_probe_mode function */
#define PCI_PROBE_NONE -1 /* Don't look at this bus at all */
#define PCI_PROBE_NORMAL 0 /* Do normal PCI probing */
Index: linux-cell/include/asm-powerpc/pci.h
===================================================================
--- linux-cell.orig/include/asm-powerpc/pci.h 2006-11-07 16:56:19.000000000 +1100
+++ linux-cell/include/asm-powerpc/pci.h 2006-11-07 16:56:21.000000000 +1100
@@ -76,15 +76,15 @@ static inline int pcibios_prep_mwi(struc
return 0;
}
-extern struct dma_mapping_ops pci_dma_ops;
+extern struct dma_mapping_ops *pci_dma_ops;
/* For DAC DMA, we currently don't support it by default, but
* we let 64-bit platforms override this.
*/
static inline int pci_dac_dma_supported(struct pci_dev *hwdev,u64 mask)
{
- if (pci_dma_ops.dac_dma_supported)
- return pci_dma_ops.dac_dma_supported(&hwdev->dev, mask);
+ if (pci_dma_ops && pci_dma_ops->dac_dma_supported)
+ return pci_dma_ops->dac_dma_supported(&hwdev->dev, mask);
return 0;
}
@@ -216,6 +216,8 @@ extern int remap_bus_range(struct pci_bu
extern void pcibios_fixup_device_resources(struct pci_dev *dev,
struct pci_bus *bus);
+extern void pcibios_setup_new_device(struct pci_dev *dev);
+
extern void pcibios_claim_one_bus(struct pci_bus *b);
extern struct pci_controller *init_phb_dynamic(struct device_node *dn);
Index: linux-cell/arch/powerpc/kernel/Makefile
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/Makefile 2006-11-07 16:56:19.000000000 +1100
+++ linux-cell/arch/powerpc/kernel/Makefile 2006-11-07 16:56:21.000000000 +1100
@@ -63,8 +63,7 @@ obj-$(CONFIG_PPC_UDBG_16550) += legacy_s
module-$(CONFIG_PPC64) += module_64.o
obj-$(CONFIG_MODULES) += $(module-y)
-pci64-$(CONFIG_PPC64) += pci_64.o pci_dn.o pci_iommu.o \
- pci_direct_iommu.o iomap.o
+pci64-$(CONFIG_PPC64) += pci_64.o pci_dn.o iomap.o
pci32-$(CONFIG_PPC32) := pci_32.o
obj-$(CONFIG_PCI) += $(pci64-y) $(pci32-y)
kexec-$(CONFIG_PPC64) := machine_kexec_64.o
Index: linux-cell/arch/powerpc/kernel/ibmebus.c
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/ibmebus.c 2006-11-07 16:56:19.000000000 +1100
+++ linux-cell/arch/powerpc/kernel/ibmebus.c 2006-11-07 16:57:53.000000000 +1100
@@ -112,7 +112,7 @@ static int ibmebus_dma_supported(struct
return 1;
}
-struct dma_mapping_ops ibmebus_dma_ops = {
+static struct dma_mapping_ops ibmebus_dma_ops = {
.alloc_coherent = ibmebus_alloc_coherent,
.free_coherent = ibmebus_free_coherent,
.map_single = ibmebus_map_single,
@@ -176,6 +176,11 @@ static struct ibmebus_dev* __devinit ibm
dev->ofdev.dev.bus = &ibmebus_bus_type;
dev->ofdev.dev.release = ibmebus_dev_release;
+ device_set_ext(&dev->ofdev.dev, &dev->dext);
+ dev->dext.of_node = dev->ofdev.node;
+ dev->dext.dma_ops = &ibmebus_dma_ops;
+ dev->dext.numa_node = of_node_to_nid(dev->ofdev.node);
+
/* An ibmebusdev is based on a of_device. We have to change the
* bus type to use our own DMA mapping operations.
*/
Index: linux-cell/arch/powerpc/platforms/cell/iommu.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/cell/iommu.c 2006-11-07 16:56:19.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/cell/iommu.c 2006-11-07 16:56:21.000000000 +1100
@@ -255,9 +255,6 @@ static void enable_mapping(void __iomem
set_iost_origin(mmio_base);
}
-static void iommu_dev_setup_null(struct pci_dev *d) { }
-static void iommu_bus_setup_null(struct pci_bus *b) { }
-
struct cell_iommu {
unsigned long base;
unsigned long mmio_base;
@@ -306,12 +303,15 @@ static void cell_do_map_iommu(struct cel
}
}
-static void iommu_devnode_setup(struct device_node *d)
+static void pci_dma_cell_bus_setup(struct pci_bus *b)
{
const unsigned int *ioid;
unsigned long map_start, map_size, token;
const unsigned long *dma_window;
struct cell_iommu *iommu;
+ struct device_node *d;
+
+ d = pci_bus_to_OF_node(b);
ioid = get_property(d, "ioid", NULL);
if (!ioid)
@@ -330,12 +330,6 @@ static void iommu_devnode_setup(struct d
cell_do_map_iommu(iommu, *ioid, map_start, map_size);
}
-static void iommu_bus_setup(struct pci_bus *b)
-{
- struct device_node *d = (struct device_node *)b->sysdata;
- iommu_devnode_setup(d);
-}
-
static int cell_map_iommu_hardcoded(int num_nodes)
{
@@ -499,16 +493,13 @@ void cell_init_iommu(void)
if (setup_bus) {
pr_debug("%s: IOMMU mapping activated\n", __FUNCTION__);
- ppc_md.iommu_dev_setup = iommu_dev_setup_null;
- ppc_md.iommu_bus_setup = iommu_bus_setup;
+ ppc_md.pci_dma_bus_setup = pci_dma_cell_bus_setup;
} else {
pr_debug("%s: IOMMU mapping activated, "
"no device action necessary\n", __FUNCTION__);
/* Direct I/O, IOMMU off */
- ppc_md.iommu_dev_setup = iommu_dev_setup_null;
- ppc_md.iommu_bus_setup = iommu_bus_setup_null;
}
}
- pci_dma_ops = cell_iommu_ops;
+ pci_dma_ops = &cell_iommu_ops;
}
Index: linux-cell/include/asm-powerpc/ibmebus.h
===================================================================
--- linux-cell.orig/include/asm-powerpc/ibmebus.h 2006-11-07 16:56:19.000000000 +1100
+++ linux-cell/include/asm-powerpc/ibmebus.h 2006-11-07 16:56:21.000000000 +1100
@@ -43,12 +43,13 @@
#include <linux/interrupt.h>
#include <linux/mod_devicetable.h>
#include <asm/of_device.h>
+#include <asm/device_ext.h>
-extern struct dma_mapping_ops ibmebus_dma_ops;
extern struct bus_type ibmebus_bus_type;
struct ibmebus_dev {
const char *name;
+ struct device_ext dext;
struct of_device ofdev;
};
Index: linux-cell/include/asm-powerpc/of_device.h
===================================================================
--- linux-cell.orig/include/asm-powerpc/of_device.h 2006-11-07 16:56:19.000000000 +1100
+++ linux-cell/include/asm-powerpc/of_device.h 2006-11-07 16:56:21.000000000 +1100
@@ -5,6 +5,7 @@
#include <linux/device.h>
#include <linux/mod_devicetable.h>
#include <asm/prom.h>
+#include <asm/device_ext.h>
/*
@@ -14,8 +15,9 @@
*/
struct of_device
{
- struct device_node *node; /* OF device node */
+ struct device_node *node; /* to be obsoleted */
u64 dma_mask; /* DMA mask */
+ struct device_ext dext;
struct device dev; /* Generic device interface */
};
#define to_of_device(d) container_of(d, struct of_device, dev)
Index: linux-cell/arch/powerpc/kernel/of_platform.c
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/of_platform.c 2006-11-07 16:56:19.000000000 +1100
+++ linux-cell/arch/powerpc/kernel/of_platform.c 2006-11-07 16:56:21.000000000 +1100
@@ -22,7 +22,7 @@
#include <asm/dcr.h>
#include <asm/of_device.h>
#include <asm/of_platform.h>
-
+#include <asm/topology.h>
/*
* The list of OF IDs below is used for matching bus types in the
@@ -221,6 +221,14 @@ struct of_device* of_platform_device_cre
dev->dev.parent = parent;
dev->dev.bus = &of_platform_bus_type;
dev->dev.release = of_release_dev;
+ device_set_ext(&dev->dev, &dev->dext);
+ dev->dext.of_node = np;
+ dev->dext.numa_node = of_node_to_nid(np);
+
+ /* We do not fill the DMA ops for platform devices by default.
+ * This is currently the responsibility of the platform code
+ * to do such, possibly using a device notifier
+ */
if (bus_id)
strlcpy(dev->dev.bus_id, bus_id, BUS_ID_SIZE);
Index: linux-cell/arch/powerpc/platforms/iseries/iommu.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/iseries/iommu.c 2006-11-07 16:56:19.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/iseries/iommu.c 2006-11-07 16:56:21.000000000 +1100
@@ -27,6 +27,7 @@
#include <linux/types.h>
#include <linux/dma-mapping.h>
#include <linux/list.h>
+#include <linux/pci.h>
#include <asm/iommu.h>
#include <asm/tce.h>
@@ -36,6 +37,7 @@
#include <asm/pci-bridge.h>
#include <asm/iseries/hv_call_xm.h>
#include <asm/iseries/iommu.h>
+#include <asm/device_ext.h>
static void tce_build_iSeries(struct iommu_table *tbl, long index, long npages,
unsigned long uaddr, enum dma_data_direction direction)
@@ -168,14 +170,21 @@ static struct iommu_table *iommu_table_f
}
-void iommu_devnode_init_iSeries(struct device_node *dn)
+void iommu_devnode_init_iSeries(struct pci_dev *pdev, struct device_node *dn)
{
struct iommu_table *tbl;
+ struct device_ext *dext = device_get_ext(&pdev->dev);
struct pci_dn *pdn = PCI_DN(dn);
const u32 *lsn = get_property(dn, "linux,logical-slot-number", NULL);
BUG_ON(lsn == NULL);
+ if (dext == NULL) {
+ printk(KERN_ERR "iommu: Attempt to setup PCI dev %s "
+ "with no device extension !", pci_name(pdev));
+ return;
+ }
+
tbl = kmalloc(sizeof(struct iommu_table), GFP_KERNEL);
iommu_table_getparms_iSeries(pdn->busno, *lsn, 0, tbl);
@@ -186,19 +195,14 @@ void iommu_devnode_init_iSeries(struct d
pdn->iommu_table = iommu_init_table(tbl, -1);
else
kfree(tbl);
+ dext->dma_data = pdn->iommu_table;
}
#endif
-static void iommu_dev_setup_iSeries(struct pci_dev *dev) { }
-static void iommu_bus_setup_iSeries(struct pci_bus *bus) { }
-
void iommu_init_early_iSeries(void)
{
ppc_md.tce_build = tce_build_iSeries;
ppc_md.tce_free = tce_free_iSeries;
- ppc_md.iommu_dev_setup = iommu_dev_setup_iSeries;
- ppc_md.iommu_bus_setup = iommu_bus_setup_iSeries;
-
- pci_iommu_init();
+ pci_dma_ops = &dma_iommu_ops;
}
Index: linux-cell/arch/powerpc/platforms/iseries/pci.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/iseries/pci.c 2006-11-07 16:56:19.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/iseries/pci.c 2006-11-07 16:56:21.000000000 +1100
@@ -253,7 +253,7 @@ void __init iSeries_pci_final_fixup(void
PCI_DN(node)->pcidev = pdev;
allocate_device_bars(pdev);
iSeries_Device_Information(pdev, DeviceCount);
- iommu_devnode_init_iSeries(node);
+ iommu_devnode_init_iSeries(pdev, node);
} else
printk("PCI: Device Tree not found for 0x%016lX\n",
(unsigned long)pdev);
Index: linux-cell/include/asm-powerpc/iseries/iommu.h
===================================================================
--- linux-cell.orig/include/asm-powerpc/iseries/iommu.h 2006-11-07 16:56:19.000000000 +1100
+++ linux-cell/include/asm-powerpc/iseries/iommu.h 2006-11-07 16:56:21.000000000 +1100
@@ -21,11 +21,13 @@
* Boston, MA 02111-1307 USA
*/
+struct pci_dev;
struct device_node;
struct iommu_table;
/* Creates table for an individual device node */
-extern void iommu_devnode_init_iSeries(struct device_node *dn);
+extern void iommu_devnode_init_iSeries(struct pci_dev *pdev,
+ struct device_node *dn);
/* Get table parameters from HV */
extern void iommu_table_getparms_iSeries(unsigned long busno,
^ permalink raw reply
* [PATCH 13/31] powerpc: Hook of_platform_bus_probe with cell
From: Benjamin Herrenschmidt @ 2006-11-07 7:22 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1162884150.435786.961370643810.qpush@grosgo>
Hook up of_platform_bus_probe with the cell platform in order to publish
the non-PCI devices in the device-tree of cell blades as of_platform_device(s)
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
arch/powerpc/platforms/cell/setup.c | 9 +++++++++
1 file changed, 9 insertions(+)
Index: linux-cell/arch/powerpc/platforms/cell/setup.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/cell/setup.c 2006-10-26 16:41:49.000000000 +1000
+++ linux-cell/arch/powerpc/platforms/cell/setup.c 2006-10-26 16:42:30.000000000 +1000
@@ -51,6 +51,7 @@
#include <asm/spu_priv1.h>
#include <asm/udbg.h>
#include <asm/mpic.h>
+#include <asm/of_platform.h>
#include "interrupt.h"
#include "iommu.h"
@@ -81,6 +82,14 @@ static void cell_progress(char *s, unsig
printk("*** %04x : %s\n", hex, s ? s : "");
}
+static int __init cell_publish_devices(void)
+{
+ if (machine_is(cell))
+ of_platform_bus_probe(NULL, NULL, NULL);
+ return 0;
+}
+device_initcall(cell_publish_devices);
+
static void cell_mpic_cascade(unsigned int irq, struct irq_desc *desc)
{
struct mpic *mpic = desc->handler_data;
^ permalink raw reply
* [PATCH 12/31] powerpc: Souped-up of_platform_device support
From: Benjamin Herrenschmidt @ 2006-11-07 7:22 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1162884150.435786.961370643810.qpush@grosgo>
This patch first splits of_device.c and of_platform.c, the later containing
the bits relative to of_platform_device's. On the "breaks" side of things,
drivers uisng of_platform_device(s) need to include asm/of_platform.h now
and of_(un)register_driver is now of_(un)register_platform_driver.
In addition to a few utility functions to locate of_platform_device(s),
the main new addition is of_platform_bus_probe() which allows the platform
code to trigger an automatic creation of of_platform_devices for a whole
tree of devices.
The function acts based on the type of the various "parent" devices encountered
from a provided root, using either a default known list of bus types that can be
"probed" or a passed-in list. It will only register devices on busses matching
that list, which mean that typically, it will not register PCI devices, as
expected (since they will be picked up by the PCI layer).
This will be used by Cell platforms using 4xx-type IOs in the Axon bridge
and can be used by any embedded-type device as well.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
arch/powerpc/kernel/Makefile | 2
arch/powerpc/kernel/of_device.c | 172 ++------------
arch/powerpc/kernel/of_platform.c | 372 ++++++++++++++++++++++++++++++++
arch/powerpc/platforms/powermac/setup.c | 1
drivers/macintosh/smu.c | 3
drivers/macintosh/therm_adt746x.c | 2
drivers/macintosh/therm_pm72.c | 5
drivers/macintosh/therm_windtunnel.c | 7
drivers/video/platinumfb.c | 5
include/asm-powerpc/of_device.h | 34 --
include/asm-powerpc/of_platform.h | 60 +++++
11 files changed, 476 insertions(+), 187 deletions(-)
Index: linux-cell/arch/powerpc/kernel/of_device.c
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/of_device.c 2006-10-06 13:47:54.000000000 +1000
+++ linux-cell/arch/powerpc/kernel/of_device.c 2006-11-06 15:16:35.000000000 +1100
@@ -9,30 +9,26 @@
#include <asm/of_device.h>
/**
- * of_match_device - Tell if an of_device structure has a matching
- * of_match structure
+ * of_match_node - Tell if an device_node has a matching of_match structure
* @ids: array of of device match structures to search in
- * @dev: the of device structure to match against
+ * @node: the of device structure to match against
*
- * Used by a driver to check whether an of_device present in the
- * system is in its list of supported devices.
+ * Low level utility function used by device matching.
*/
-const struct of_device_id *of_match_device(const struct of_device_id *matches,
- const struct of_device *dev)
+const struct of_device_id *of_match_node(const struct of_device_id *matches,
+ const struct device_node *node)
{
- if (!dev->node)
- return NULL;
while (matches->name[0] || matches->type[0] || matches->compatible[0]) {
int match = 1;
if (matches->name[0])
- match &= dev->node->name
- && !strcmp(matches->name, dev->node->name);
+ match &= node->name
+ && !strcmp(matches->name, node->name);
if (matches->type[0])
- match &= dev->node->type
- && !strcmp(matches->type, dev->node->type);
+ match &= node->type
+ && !strcmp(matches->type, node->type);
if (matches->compatible[0])
- match &= device_is_compatible(dev->node,
- matches->compatible);
+ match &= device_is_compatible(node,
+ matches->compatible);
if (match)
return matches;
matches++;
@@ -40,16 +36,21 @@ const struct of_device_id *of_match_devi
return NULL;
}
-static int of_platform_bus_match(struct device *dev, struct device_driver *drv)
+/**
+ * of_match_device - Tell if an of_device structure has a matching
+ * of_match structure
+ * @ids: array of of device match structures to search in
+ * @dev: the of device structure to match against
+ *
+ * Used by a driver to check whether an of_device present in the
+ * system is in its list of supported devices.
+ */
+const struct of_device_id *of_match_device(const struct of_device_id *matches,
+ const struct of_device *dev)
{
- struct of_device * of_dev = to_of_device(dev);
- struct of_platform_driver * of_drv = to_of_platform_driver(drv);
- const struct of_device_id * matches = of_drv->match_table;
-
- if (!matches)
- return 0;
-
- return of_match_device(matches, of_dev) != NULL;
+ if (!dev->node)
+ return NULL;
+ return of_match_node(matches, dev->node);
}
struct of_device *of_dev_get(struct of_device *dev)
@@ -71,96 +72,8 @@ void of_dev_put(struct of_device *dev)
put_device(&dev->dev);
}
-
-static int of_device_probe(struct device *dev)
-{
- int error = -ENODEV;
- struct of_platform_driver *drv;
- struct of_device *of_dev;
- const struct of_device_id *match;
-
- drv = to_of_platform_driver(dev->driver);
- of_dev = to_of_device(dev);
-
- if (!drv->probe)
- return error;
-
- of_dev_get(of_dev);
-
- match = of_match_device(drv->match_table, of_dev);
- if (match)
- error = drv->probe(of_dev, match);
- if (error)
- of_dev_put(of_dev);
-
- return error;
-}
-
-static int of_device_remove(struct device *dev)
-{
- struct of_device * of_dev = to_of_device(dev);
- struct of_platform_driver * drv = to_of_platform_driver(dev->driver);
-
- if (dev->driver && drv->remove)
- drv->remove(of_dev);
- return 0;
-}
-
-static int of_device_suspend(struct device *dev, pm_message_t state)
-{
- struct of_device * of_dev = to_of_device(dev);
- struct of_platform_driver * drv = to_of_platform_driver(dev->driver);
- int error = 0;
-
- if (dev->driver && drv->suspend)
- error = drv->suspend(of_dev, state);
- return error;
-}
-
-static int of_device_resume(struct device * dev)
-{
- struct of_device * of_dev = to_of_device(dev);
- struct of_platform_driver * drv = to_of_platform_driver(dev->driver);
- int error = 0;
-
- if (dev->driver && drv->resume)
- error = drv->resume(of_dev);
- return error;
-}
-
-struct bus_type of_platform_bus_type = {
- .name = "of_platform",
- .match = of_platform_bus_match,
- .probe = of_device_probe,
- .remove = of_device_remove,
- .suspend = of_device_suspend,
- .resume = of_device_resume,
-};
-
-static int __init of_bus_driver_init(void)
-{
- return bus_register(&of_platform_bus_type);
-}
-
-postcore_initcall(of_bus_driver_init);
-
-int of_register_driver(struct of_platform_driver *drv)
-{
- /* initialize common driver fields */
- drv->driver.name = drv->name;
- drv->driver.bus = &of_platform_bus_type;
-
- /* register with core */
- return driver_register(&drv->driver);
-}
-
-void of_unregister_driver(struct of_platform_driver *drv)
-{
- driver_unregister(&drv->driver);
-}
-
-
-static ssize_t dev_show_devspec(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t dev_show_devspec(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct of_device *ofdev;
@@ -208,41 +121,10 @@ void of_device_unregister(struct of_devi
device_unregister(&ofdev->dev);
}
-struct of_device* of_platform_device_create(struct device_node *np,
- const char *bus_id,
- struct device *parent)
-{
- struct of_device *dev;
-
- dev = kmalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev)
- return NULL;
- memset(dev, 0, sizeof(*dev));
-
- dev->node = of_node_get(np);
- dev->dma_mask = 0xffffffffUL;
- dev->dev.dma_mask = &dev->dma_mask;
- dev->dev.parent = parent;
- dev->dev.bus = &of_platform_bus_type;
- dev->dev.release = of_release_dev;
-
- strlcpy(dev->dev.bus_id, bus_id, BUS_ID_SIZE);
-
- if (of_device_register(dev) != 0) {
- kfree(dev);
- return NULL;
- }
-
- return dev;
-}
EXPORT_SYMBOL(of_match_device);
-EXPORT_SYMBOL(of_platform_bus_type);
-EXPORT_SYMBOL(of_register_driver);
-EXPORT_SYMBOL(of_unregister_driver);
EXPORT_SYMBOL(of_device_register);
EXPORT_SYMBOL(of_device_unregister);
EXPORT_SYMBOL(of_dev_get);
EXPORT_SYMBOL(of_dev_put);
-EXPORT_SYMBOL(of_platform_device_create);
EXPORT_SYMBOL(of_release_dev);
Index: linux-cell/include/asm-powerpc/of_device.h
===================================================================
--- linux-cell.orig/include/asm-powerpc/of_device.h 2006-10-06 13:48:23.000000000 +1000
+++ linux-cell/include/asm-powerpc/of_device.h 2006-11-06 15:45:18.000000000 +1100
@@ -6,12 +6,6 @@
#include <linux/mod_devicetable.h>
#include <asm/prom.h>
-/*
- * The of_platform_bus_type is a bus type used by drivers that do not
- * attach to a macio or similar bus but still use OF probing
- * mechanism
- */
-extern struct bus_type of_platform_bus_type;
/*
* The of_device is a kind of "base class" that is a superset of
@@ -26,40 +20,16 @@ struct of_device
};
#define to_of_device(d) container_of(d, struct of_device, dev)
+extern const struct of_device_id *of_match_node(
+ const struct of_device_id *matches, const struct device_node *node);
extern const struct of_device_id *of_match_device(
const struct of_device_id *matches, const struct of_device *dev);
extern struct of_device *of_dev_get(struct of_device *dev);
extern void of_dev_put(struct of_device *dev);
-/*
- * An of_platform_driver driver is attached to a basic of_device on
- * the "platform bus" (of_platform_bus_type)
- */
-struct of_platform_driver
-{
- char *name;
- struct of_device_id *match_table;
- struct module *owner;
-
- int (*probe)(struct of_device* dev, const struct of_device_id *match);
- int (*remove)(struct of_device* dev);
-
- int (*suspend)(struct of_device* dev, pm_message_t state);
- int (*resume)(struct of_device* dev);
- int (*shutdown)(struct of_device* dev);
-
- struct device_driver driver;
-};
-#define to_of_platform_driver(drv) container_of(drv,struct of_platform_driver, driver)
-
-extern int of_register_driver(struct of_platform_driver *drv);
-extern void of_unregister_driver(struct of_platform_driver *drv);
extern int of_device_register(struct of_device *ofdev);
extern void of_device_unregister(struct of_device *ofdev);
-extern struct of_device *of_platform_device_create(struct device_node *np,
- const char *bus_id,
- struct device *parent);
extern void of_release_dev(struct device *dev);
#endif /* __KERNEL__ */
Index: linux-cell/arch/powerpc/kernel/of_platform.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-cell/arch/powerpc/kernel/of_platform.c 2006-11-06 15:45:18.000000000 +1100
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2006 Benjamin Herrenschmidt, IBM Corp.
+ * <benh@kernel.crashing.org>
+ *
+ * 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.
+ *
+ */
+
+#undef DEBUG
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/slab.h>
+
+#include <asm/errno.h>
+#include <asm/dcr.h>
+#include <asm/of_device.h>
+#include <asm/of_platform.h>
+
+
+/*
+ * The list of OF IDs below is used for matching bus types in the
+ * system whose devices are to be exposed as of_platform_devices.
+ *
+ * This is the default list valid for most platforms. This file provides
+ * functions who can take an explicit list if necessary though
+ *
+ * The search is always performed recursively looking for children of
+ * the provided device_node and recursively if such a children matches
+ * a bus type in the list
+ */
+
+static struct of_device_id of_default_bus_ids[] = {
+ { .type = "soc", },
+ { .compatible = "soc", },
+ { .type = "spider", },
+ { .type = "axon", },
+ { .type = "plb5", },
+ { .type = "plb4", },
+ { .type = "opb", },
+ {},
+};
+
+/*
+ *
+ * OF platform device type definition & base infrastructure
+ *
+ */
+
+static int of_platform_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct of_device * of_dev = to_of_device(dev);
+ struct of_platform_driver * of_drv = to_of_platform_driver(drv);
+ const struct of_device_id * matches = of_drv->match_table;
+
+ if (!matches)
+ return 0;
+
+ return of_match_device(matches, of_dev) != NULL;
+}
+
+static int of_platform_device_probe(struct device *dev)
+{
+ int error = -ENODEV;
+ struct of_platform_driver *drv;
+ struct of_device *of_dev;
+ const struct of_device_id *match;
+
+ drv = to_of_platform_driver(dev->driver);
+ of_dev = to_of_device(dev);
+
+ if (!drv->probe)
+ return error;
+
+ of_dev_get(of_dev);
+
+ match = of_match_device(drv->match_table, of_dev);
+ if (match)
+ error = drv->probe(of_dev, match);
+ if (error)
+ of_dev_put(of_dev);
+
+ return error;
+}
+
+static int of_platform_device_remove(struct device *dev)
+{
+ struct of_device * of_dev = to_of_device(dev);
+ struct of_platform_driver * drv = to_of_platform_driver(dev->driver);
+
+ if (dev->driver && drv->remove)
+ drv->remove(of_dev);
+ return 0;
+}
+
+static int of_platform_device_suspend(struct device *dev, pm_message_t state)
+{
+ struct of_device * of_dev = to_of_device(dev);
+ struct of_platform_driver * drv = to_of_platform_driver(dev->driver);
+ int error = 0;
+
+ if (dev->driver && drv->suspend)
+ error = drv->suspend(of_dev, state);
+ return error;
+}
+
+static int of_platform_device_resume(struct device * dev)
+{
+ struct of_device * of_dev = to_of_device(dev);
+ struct of_platform_driver * drv = to_of_platform_driver(dev->driver);
+ int error = 0;
+
+ if (dev->driver && drv->resume)
+ error = drv->resume(of_dev);
+ return error;
+}
+
+struct bus_type of_platform_bus_type = {
+ .name = "of_platform",
+ .match = of_platform_bus_match,
+ .probe = of_platform_device_probe,
+ .remove = of_platform_device_remove,
+ .suspend = of_platform_device_suspend,
+ .resume = of_platform_device_resume,
+};
+EXPORT_SYMBOL(of_platform_bus_type);
+
+static int __init of_bus_driver_init(void)
+{
+ return bus_register(&of_platform_bus_type);
+}
+
+postcore_initcall(of_bus_driver_init);
+
+int of_register_platform_driver(struct of_platform_driver *drv)
+{
+ /* initialize common driver fields */
+ drv->driver.name = drv->name;
+ drv->driver.bus = &of_platform_bus_type;
+
+ /* register with core */
+ return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL(of_register_platform_driver);
+
+void of_unregister_platform_driver(struct of_platform_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL(of_unregister_platform_driver);
+
+static void of_platform_make_bus_id(struct of_device *dev)
+{
+ struct device_node *node = dev->node;
+ char *name = dev->dev.bus_id;
+ const u32 *reg;
+ u64 addr;
+
+ /*
+ * If it's a DCR based device, use 'd' for native DCRs
+ * and 'D' for MMIO DCRs.
+ */
+#ifdef CONFIG_PPC_DCR
+ reg = get_property(node, "dcr-reg", NULL);
+ if (reg) {
+#ifdef CONFIG_PPC_DCR_NATIVE
+ snprintf(name, BUS_ID_SIZE, "d%x.%s",
+ *reg, node->name);
+#else /* CONFIG_PPC_DCR_NATIVE */
+ addr = of_translate_dcr_address(node, *reg, NULL);
+ if (addr != OF_BAD_ADDR) {
+ snprintf(name, BUS_ID_SIZE,
+ "D%llx.%s", (unsigned long long)addr,
+ node->name);
+ return;
+ }
+#endif /* !CONFIG_PPC_DCR_NATIVE */
+ }
+#endif /* CONFIG_PPC_DCR */
+
+ /*
+ * For MMIO, get the physical address
+ */
+ reg = get_property(node, "reg", NULL);
+ if (reg) {
+ addr = of_translate_address(node, reg);
+ if (addr != OF_BAD_ADDR) {
+ snprintf(name, BUS_ID_SIZE,
+ "%llx.%s", (unsigned long long)addr,
+ node->name);
+ return;
+ }
+ }
+
+ /*
+ * No BusID, use the node name and pray
+ */
+ snprintf(name, BUS_ID_SIZE, "%s", node->name);
+}
+
+struct of_device* of_platform_device_create(struct device_node *np,
+ const char *bus_id,
+ struct device *parent)
+{
+ struct of_device *dev;
+
+ dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return NULL;
+ memset(dev, 0, sizeof(*dev));
+
+ dev->node = of_node_get(np);
+ dev->dma_mask = 0xffffffffUL;
+ dev->dev.dma_mask = &dev->dma_mask;
+ dev->dev.parent = parent;
+ dev->dev.bus = &of_platform_bus_type;
+ dev->dev.release = of_release_dev;
+
+ if (bus_id)
+ strlcpy(dev->dev.bus_id, bus_id, BUS_ID_SIZE);
+ else
+ of_platform_make_bus_id(dev);
+
+ if (of_device_register(dev) != 0) {
+ kfree(dev);
+ return NULL;
+ }
+
+ return dev;
+}
+EXPORT_SYMBOL(of_platform_device_create);
+
+
+
+/**
+ * of_platform_bus_create - Create an OF device for a bus node and all its
+ * children. Optionally recursively instanciate matching busses.
+ * @bus: device node of the bus to instanciate
+ * @matches: match table, NULL to use the default, OF_NO_DEEP_PROBE to
+ * disallow recursive creation of child busses
+ */
+static int of_platform_bus_create(struct device_node *bus,
+ struct of_device_id *matches,
+ struct device *parent)
+{
+ struct device_node *child;
+ struct of_device *dev;
+ int rc = 0;
+
+ for (child = NULL; (child = of_get_next_child(bus, child)); ) {
+ pr_debug(" create child: %s\n", child->full_name);
+ dev = of_platform_device_create(child, NULL, parent);
+ if (dev == NULL)
+ rc = -ENOMEM;
+ else if (!of_match_node(matches, child))
+ continue;
+ if (rc == 0) {
+ pr_debug(" and sub busses\n");
+ rc = of_platform_bus_create(child, matches, &dev->dev);
+ } if (rc) {
+ of_node_put(child);
+ break;
+ }
+ }
+ return rc;
+}
+
+/**
+ * of_platform_bus_probe - Probe the device-tree for platform busses
+ * @root: parent of the first level to probe or NULL for the root of the tree
+ * @matches: match table, NULL to use the default
+ * @parent: parent to hook devices from, NULL for toplevel
+ *
+ * Note that children of the provided root are not instanciated as devices
+ * unless the specified root itself matches the bus list and is not NULL.
+ */
+
+int of_platform_bus_probe(struct device_node *root,
+ struct of_device_id *matches,
+ struct device *parent)
+{
+ struct device_node *child;
+ struct of_device *dev;
+ int rc = 0;
+
+ if (matches == NULL)
+ matches = of_default_bus_ids;
+ if (matches == OF_NO_DEEP_PROBE)
+ return -EINVAL;
+ if (root == NULL)
+ root = of_find_node_by_path("/");
+ else
+ of_node_get(root);
+
+ pr_debug("of_platform_bus_probe()\n");
+ pr_debug(" starting at: %s\n", root->full_name);
+
+ /* Do a self check of bus type, if there's a match, create
+ * children
+ */
+ if (of_match_node(matches, root)) {
+ pr_debug(" root match, create all sub devices\n");
+ dev = of_platform_device_create(root, NULL, parent);
+ if (dev == NULL) {
+ rc = -ENOMEM;
+ goto bail;
+ }
+ pr_debug(" create all sub busses\n");
+ rc = of_platform_bus_create(root, matches, &dev->dev);
+ goto bail;
+ }
+ for (child = NULL; (child = of_get_next_child(root, child)); ) {
+ if (!of_match_node(matches, child))
+ continue;
+
+ pr_debug(" match: %s\n", child->full_name);
+ dev = of_platform_device_create(child, NULL, parent);
+ if (dev == NULL)
+ rc = -ENOMEM;
+ else
+ rc = of_platform_bus_create(child, matches, &dev->dev);
+ if (rc) {
+ of_node_put(child);
+ break;
+ }
+ }
+ bail:
+ of_node_put(root);
+ return rc;
+}
+EXPORT_SYMBOL(of_platform_bus_probe);
+
+static int of_dev_node_match(struct device *dev, void *data)
+{
+ return to_of_device(dev)->node == data;
+}
+
+struct of_device *of_find_device_by_node(struct device_node *np)
+{
+ struct device *dev;
+
+ dev = bus_find_device(&of_platform_bus_type,
+ NULL, np, of_dev_node_match);
+ if (dev)
+ return to_of_device(dev);
+ return NULL;
+}
+EXPORT_SYMBOL(of_find_device_by_node);
+
+static int of_dev_phandle_match(struct device *dev, void *data)
+{
+ phandle *ph = data;
+ return to_of_device(dev)->node->linux_phandle == *ph;
+}
+
+struct of_device *of_find_device_by_phandle(phandle ph)
+{
+ struct device *dev;
+
+ dev = bus_find_device(&of_platform_bus_type,
+ NULL, &ph, of_dev_phandle_match);
+ if (dev)
+ return to_of_device(dev);
+ return NULL;
+}
+EXPORT_SYMBOL(of_find_device_by_phandle);
Index: linux-cell/arch/powerpc/kernel/Makefile
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/Makefile 2006-11-06 15:05:40.000000000 +1100
+++ linux-cell/arch/powerpc/kernel/Makefile 2006-11-06 15:45:18.000000000 +1100
@@ -21,7 +21,7 @@ obj-$(CONFIG_PPC64) += setup_64.o binfm
obj-$(CONFIG_PPC64) += vdso64/
obj-$(CONFIG_ALTIVEC) += vecemu.o vector.o
obj-$(CONFIG_PPC_970_NAP) += idle_power4.o
-obj-$(CONFIG_PPC_OF) += of_device.o prom_parse.o
+obj-$(CONFIG_PPC_OF) += of_device.o of_platform.o prom_parse.o
procfs-$(CONFIG_PPC64) := proc_ppc64.o
obj-$(CONFIG_PROC_FS) += $(procfs-y)
rtaspci-$(CONFIG_PPC64) := rtas_pci.o
Index: linux-cell/drivers/macintosh/therm_pm72.c
===================================================================
--- linux-cell.orig/drivers/macintosh/therm_pm72.c 2006-10-06 13:48:04.000000000 +1000
+++ linux-cell/drivers/macintosh/therm_pm72.c 2006-11-06 15:16:35.000000000 +1100
@@ -129,6 +129,7 @@
#include <asm/sections.h>
#include <asm/of_device.h>
#include <asm/macio.h>
+#include <asm/of_platform.h>
#include "therm_pm72.h"
@@ -2236,14 +2237,14 @@ static int __init therm_pm72_init(void)
return -ENODEV;
}
- of_register_driver(&fcu_of_platform_driver);
+ of_register_platform_driver(&fcu_of_platform_driver);
return 0;
}
static void __exit therm_pm72_exit(void)
{
- of_unregister_driver(&fcu_of_platform_driver);
+ of_unregister_platform_driver(&fcu_of_platform_driver);
if (of_dev)
of_device_unregister(of_dev);
Index: linux-cell/include/asm-powerpc/of_platform.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-cell/include/asm-powerpc/of_platform.h 2006-11-06 15:16:35.000000000 +1100
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2006 Benjamin Herrenschmidt, IBM Corp.
+ * <benh@kernel.crashing.org>
+ *
+ * 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 <asm/of_device.h>
+
+/*
+ * The of_platform_bus_type is a bus type used by drivers that do not
+ * attach to a macio or similar bus but still use OF probing
+ * mechanism
+ */
+extern struct bus_type of_platform_bus_type;
+
+/*
+ * An of_platform_driver driver is attached to a basic of_device on
+ * the "platform bus" (of_platform_bus_type)
+ */
+struct of_platform_driver
+{
+ char *name;
+ struct of_device_id *match_table;
+ struct module *owner;
+
+ int (*probe)(struct of_device* dev,
+ const struct of_device_id *match);
+ int (*remove)(struct of_device* dev);
+
+ int (*suspend)(struct of_device* dev, pm_message_t state);
+ int (*resume)(struct of_device* dev);
+ int (*shutdown)(struct of_device* dev);
+
+ struct device_driver driver;
+};
+#define to_of_platform_driver(drv) \
+ container_of(drv,struct of_platform_driver, driver)
+
+/* Platform drivers register/unregister */
+extern int of_register_platform_driver(struct of_platform_driver *drv);
+extern void of_unregister_platform_driver(struct of_platform_driver *drv);
+
+/* Platform devices and busses creation */
+extern struct of_device *of_platform_device_create(struct device_node *np,
+ const char *bus_id,
+ struct device *parent);
+/* pseudo "matches" value to not do deep probe */
+#define OF_NO_DEEP_PROBE ((struct of_device_id *)-1)
+
+extern int of_platform_bus_probe(struct device_node *root,
+ struct of_device_id *matches,
+ struct device *parent);
+
+extern struct of_device *of_find_device_by_node(struct device_node *np);
+extern struct of_device *of_find_device_by_phandle(phandle ph);
Index: linux-cell/arch/powerpc/platforms/powermac/setup.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/powermac/setup.c 2006-11-06 15:02:21.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/powermac/setup.c 2006-11-06 15:18:34.000000000 +1100
@@ -70,6 +70,7 @@
#include <asm/pmac_feature.h>
#include <asm/time.h>
#include <asm/of_device.h>
+#include <asm/of_platform.h>
#include <asm/mmu_context.h>
#include <asm/iommu.h>
#include <asm/smu.h>
Index: linux-cell/drivers/macintosh/smu.c
===================================================================
--- linux-cell.orig/drivers/macintosh/smu.c 2006-10-09 12:03:33.000000000 +1000
+++ linux-cell/drivers/macintosh/smu.c 2006-11-06 15:16:35.000000000 +1100
@@ -46,6 +46,7 @@
#include <asm/abs_addr.h>
#include <asm/uaccess.h>
#include <asm/of_device.h>
+#include <asm/of_platform.h>
#define VERSION "0.7"
#define AUTHOR "(c) 2005 Benjamin Herrenschmidt, IBM Corp."
@@ -653,7 +654,7 @@ static int __init smu_init_sysfs(void)
* I'm a bit too far from figuring out how that works with those
* new chipsets, but that will come back and bite us
*/
- of_register_driver(&smu_of_platform_driver);
+ of_register_platform_driver(&smu_of_platform_driver);
return 0;
}
Index: linux-cell/drivers/video/platinumfb.c
===================================================================
--- linux-cell.orig/drivers/video/platinumfb.c 2006-10-23 14:41:38.000000000 +1000
+++ linux-cell/drivers/video/platinumfb.c 2006-11-06 15:16:35.000000000 +1100
@@ -34,6 +34,7 @@
#include <asm/prom.h>
#include <asm/pgtable.h>
#include <asm/of_device.h>
+#include <asm/of_platform.h>
#include "macmodes.h"
#include "platinumfb.h"
@@ -682,14 +683,14 @@ static int __init platinumfb_init(void)
return -ENODEV;
platinumfb_setup(option);
#endif
- of_register_driver(&platinum_driver);
+ of_register_platform_driver(&platinum_driver);
return 0;
}
static void __exit platinumfb_exit(void)
{
- of_unregister_driver(&platinum_driver);
+ of_unregister_platform_driver(&platinum_driver);
}
MODULE_LICENSE("GPL");
Index: linux-cell/drivers/macintosh/therm_adt746x.c
===================================================================
--- linux-cell.orig/drivers/macintosh/therm_adt746x.c 2006-10-06 13:48:04.000000000 +1000
+++ linux-cell/drivers/macintosh/therm_adt746x.c 2006-11-06 15:45:42.000000000 +1100
@@ -30,7 +30,7 @@
#include <asm/io.h>
#include <asm/system.h>
#include <asm/sections.h>
-#include <asm/of_device.h>
+#include <asm/of_platform.h>
#undef DEBUG
Index: linux-cell/drivers/macintosh/therm_windtunnel.c
===================================================================
--- linux-cell.orig/drivers/macintosh/therm_windtunnel.c 2006-10-06 13:48:04.000000000 +1000
+++ linux-cell/drivers/macintosh/therm_windtunnel.c 2006-11-06 15:46:39.000000000 +1100
@@ -36,12 +36,13 @@
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/init.h>
+
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/sections.h>
-#include <asm/of_device.h>
+#include <asm/of_platform.h>
#include <asm/macio.h>
#define LOG_TEMP 0 /* continously log temperature */
@@ -511,14 +512,14 @@ g4fan_init( void )
return -ENODEV;
}
- of_register_driver( &therm_of_driver );
+ of_register_platform_driver( &therm_of_driver );
return 0;
}
static void __exit
g4fan_exit( void )
{
- of_unregister_driver( &therm_of_driver );
+ of_unregister_platform_driver( &therm_of_driver );
if( x.of_dev )
of_device_unregister( x.of_dev );
^ permalink raw reply
* [PATCH 11/31] powerpc: Native cell support for MPIC in southbridge
From: Benjamin Herrenschmidt @ 2006-11-07 7:22 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1162884150.435786.961370643810.qpush@grosgo>
Add support for southbridges using the MPIC interrupt controller to
the native cell platforms.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
arch/powerpc/Kconfig | 1
arch/powerpc/platforms/cell/setup.c | 44 ++++++++++++++++++++++++++++++++++++
2 files changed, 45 insertions(+)
Index: linux-cell/arch/powerpc/Kconfig
===================================================================
--- linux-cell.orig/arch/powerpc/Kconfig 2006-11-06 15:05:40.000000000 +1100
+++ linux-cell/arch/powerpc/Kconfig 2006-11-06 15:15:45.000000000 +1100
@@ -461,6 +461,7 @@ config PPC_CELL_NATIVE
bool
select PPC_CELL
select PPC_DCR_MMIO
+ select MPIC
default n
config PPC_IBM_CELL_BLADE
Index: linux-cell/arch/powerpc/platforms/cell/setup.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/cell/setup.c 2006-11-06 15:02:21.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/cell/setup.c 2006-11-06 15:05:44.000000000 +1100
@@ -50,6 +50,7 @@
#include <asm/spu.h>
#include <asm/spu_priv1.h>
#include <asm/udbg.h>
+#include <asm/mpic.h>
#include "interrupt.h"
#include "iommu.h"
@@ -80,10 +81,53 @@ static void cell_progress(char *s, unsig
printk("*** %04x : %s\n", hex, s ? s : "");
}
+static void cell_mpic_cascade(unsigned int irq, struct irq_desc *desc)
+{
+ struct mpic *mpic = desc->handler_data;
+ unsigned int virq;
+
+ virq = mpic_get_one_irq(mpic);
+ if (virq != NO_IRQ)
+ generic_handle_irq(virq);
+ desc->chip->eoi(irq);
+}
+
+static void __init mpic_init_IRQ(void)
+{
+ struct device_node *dn;
+ struct mpic *mpic;
+ unsigned int virq;
+
+ for (dn = NULL;
+ (dn = of_find_node_by_name(dn, "interrupt-controller"));) {
+ if (!device_is_compatible(dn, "CBEA,platform-open-pic"))
+ continue;
+
+ /* The MPIC driver will get everything it needs from the
+ * device-tree, just pass 0 to all arguments
+ */
+ mpic = mpic_alloc(dn, 0, 0, 0, 0, " MPIC ");
+ if (mpic == NULL)
+ continue;
+ mpic_init(mpic);
+
+ virq = irq_of_parse_and_map(dn, 0);
+ if (virq == NO_IRQ)
+ continue;
+
+ printk(KERN_INFO "%s : hooking up to IRQ %d\n",
+ dn->full_name, virq);
+ set_irq_data(virq, mpic);
+ set_irq_chained_handler(virq, cell_mpic_cascade);
+ }
+}
+
+
static void __init cell_init_irq(void)
{
iic_init_IRQ();
spider_init_IRQ();
+ mpic_init_IRQ();
}
static void __init cell_setup_arch(void)
^ permalink raw reply
* [PATCH 10/31] powerpc: Improve MPIC driver auto-configuration from DT
From: Benjamin Herrenschmidt @ 2006-11-07 7:22 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1162884150.435786.961370643810.qpush@grosgo>
This patch applies on top of the MPIC DCR support. It makes the MPIC
driver capable of a lot more auto-configuration based on the device-tree,
for example, it can retreive it's own physical address if not passed as
an argument, find out if it's DCR or MMIO mapped, and set the BIG_ENDIAN
flag automatically in the presence of a "big-endian" property in the
device-tree node.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
arch/powerpc/sysdev/mpic.c | 50 +++++++++++++++++++++++++++++++++------------
include/asm-powerpc/mpic.h | 4 +--
2 files changed, 39 insertions(+), 15 deletions(-)
Index: linux-cell/arch/powerpc/sysdev/mpic.c
===================================================================
--- linux-cell.orig/arch/powerpc/sysdev/mpic.c 2006-11-06 14:34:17.000000000 +1100
+++ linux-cell/arch/powerpc/sysdev/mpic.c 2006-11-06 14:36:45.000000000 +1100
@@ -894,7 +894,7 @@ static struct irq_host_ops mpic_host_ops
*/
struct mpic * __init mpic_alloc(struct device_node *node,
- unsigned long phys_addr,
+ phys_addr_t phys_addr,
unsigned int flags,
unsigned int isu_size,
unsigned int irq_count,
@@ -904,6 +904,7 @@ struct mpic * __init mpic_alloc(struct d
u32 reg;
const char *vers;
int i;
+ u64 paddr = phys_addr;
mpic = alloc_bootmem(sizeof(struct mpic));
if (mpic == NULL)
@@ -943,6 +944,11 @@ struct mpic * __init mpic_alloc(struct d
mpic->irq_count = irq_count;
mpic->num_sources = 0; /* so far */
+ /* Check for "big-endian" in device-tree */
+ if (node && get_property(node, "big-endian", NULL) != NULL)
+ mpic->flags |= MPIC_BIG_ENDIAN;
+
+
#ifdef CONFIG_MPIC_WEIRD
mpic->hw_set = mpic_infos[MPIC_GET_REGSET(flags)];
#endif
@@ -951,11 +957,17 @@ struct mpic * __init mpic_alloc(struct d
mpic->reg_type = (flags & MPIC_BIG_ENDIAN) ?
mpic_access_mmio_be : mpic_access_mmio_le;
+ /* If no physical address is passed in, a device-node is mandatory */
+ BUG_ON(paddr == 0 && node == NULL);
+
+ /* If no physical address passed in, check if it's dcr based */
+ if (paddr == 0 && get_property(node, "dcr-reg", NULL) != NULL)
+ mpic->flags |= MPIC_USES_DCR;
+
#ifdef CONFIG_PPC_DCR
if (mpic->flags & MPIC_USES_DCR) {
const u32 *dbasep;
- BUG_ON(mpic->of_node == NULL);
- dbasep = get_property(mpic->of_node, "dcr-reg", NULL);
+ dbasep = get_property(node, "dcr-reg", NULL);
BUG_ON(dbasep == NULL);
mpic->dcr_base = *dbasep;
mpic->reg_type = mpic_access_dcr;
@@ -964,9 +976,20 @@ struct mpic * __init mpic_alloc(struct d
BUG_ON (mpic->flags & MPIC_USES_DCR);
#endif /* CONFIG_PPC_DCR */
+ /* If the MPIC is not DCR based, and no physical address was passed
+ * in, try to obtain one
+ */
+ if (paddr == 0 && !(mpic->flags & MPIC_USES_DCR)) {
+ const u32 *reg;
+ reg = get_property(node, "reg", NULL);
+ BUG_ON(reg == NULL);
+ paddr = of_translate_address(node, reg);
+ BUG_ON(paddr == OF_BAD_ADDR);
+ }
+
/* Map the global registers */
- mpic_map(mpic, phys_addr, &mpic->gregs, MPIC_INFO(GREG_BASE), 0x1000);
- mpic_map(mpic, phys_addr, &mpic->tmregs, MPIC_INFO(TIMER_BASE), 0x1000);
+ mpic_map(mpic, paddr, &mpic->gregs, MPIC_INFO(GREG_BASE), 0x1000);
+ mpic_map(mpic, paddr, &mpic->tmregs, MPIC_INFO(TIMER_BASE), 0x1000);
/* Reset */
if (flags & MPIC_WANTS_RESET) {
@@ -991,7 +1014,7 @@ struct mpic * __init mpic_alloc(struct d
/* Map the per-CPU registers */
for (i = 0; i < mpic->num_cpus; i++) {
- mpic_map(mpic, phys_addr, &mpic->cpuregs[i],
+ mpic_map(mpic, paddr, &mpic->cpuregs[i],
MPIC_INFO(CPU_BASE) + i * MPIC_INFO(CPU_STRIDE),
0x1000);
}
@@ -999,7 +1022,7 @@ struct mpic * __init mpic_alloc(struct d
/* Initialize main ISU if none provided */
if (mpic->isu_size == 0) {
mpic->isu_size = mpic->num_sources;
- mpic_map(mpic, phys_addr, &mpic->isus[0],
+ mpic_map(mpic, paddr, &mpic->isus[0],
MPIC_INFO(IRQ_BASE), MPIC_INFO(IRQ_STRIDE) * mpic->isu_size);
}
mpic->isu_shift = 1 + __ilog2(mpic->isu_size - 1);
@@ -1020,10 +1043,11 @@ struct mpic * __init mpic_alloc(struct d
vers = "<unknown>";
break;
}
- printk(KERN_INFO "mpic: Setting up MPIC \"%s\" version %s at %lx, max %d CPUs\n",
- name, vers, phys_addr, mpic->num_cpus);
- printk(KERN_INFO "mpic: ISU size: %d, shift: %d, mask: %x\n", mpic->isu_size,
- mpic->isu_shift, mpic->isu_mask);
+ printk(KERN_INFO "mpic: Setting up MPIC \"%s\" version %s at %llx,"
+ " max %d CPUs\n",
+ name, vers, (unsigned long long)paddr, mpic->num_cpus);
+ printk(KERN_INFO "mpic: ISU size: %d, shift: %d, mask: %x\n",
+ mpic->isu_size, mpic->isu_shift, mpic->isu_mask);
mpic->next = mpics;
mpics = mpic;
@@ -1037,13 +1061,13 @@ struct mpic * __init mpic_alloc(struct d
}
void __init mpic_assign_isu(struct mpic *mpic, unsigned int isu_num,
- unsigned long phys_addr)
+ phys_addr_t paddr)
{
unsigned int isu_first = isu_num * mpic->isu_size;
BUG_ON(isu_num >= MPIC_MAX_ISU);
- mpic_map(mpic, phys_addr, &mpic->isus[isu_num], 0,
+ mpic_map(mpic, paddr, &mpic->isus[isu_num], 0,
MPIC_INFO(IRQ_STRIDE) * mpic->isu_size);
if ((isu_first + mpic->isu_size) > mpic->num_sources)
mpic->num_sources = isu_first + mpic->isu_size;
Index: linux-cell/include/asm-powerpc/mpic.h
===================================================================
--- linux-cell.orig/include/asm-powerpc/mpic.h 2006-11-06 14:34:17.000000000 +1100
+++ linux-cell/include/asm-powerpc/mpic.h 2006-11-06 14:34:38.000000000 +1100
@@ -364,7 +364,7 @@ struct mpic
* that is senses[0] correspond to linux irq "irq_offset".
*/
extern struct mpic *mpic_alloc(struct device_node *node,
- unsigned long phys_addr,
+ phys_addr_t phys_addr,
unsigned int flags,
unsigned int isu_size,
unsigned int irq_count,
@@ -377,7 +377,7 @@ extern struct mpic *mpic_alloc(struct de
* @phys_addr: physical address of the ISU
*/
extern void mpic_assign_isu(struct mpic *mpic, unsigned int isu_num,
- unsigned long phys_addr);
+ phys_addr_t phys_addr);
/* Set default sense codes
*
^ permalink raw reply
* [PATCH 9/31] powerpc: Support for DCR based MPIC
From: Benjamin Herrenschmidt @ 2006-11-07 7:22 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1162884150.435786.961370643810.qpush@grosgo>
This patch implements support for DCR based MPIC implementations. Such
implementations have the MPIC_USES_DCR flag set and don't use the phys_addr
argument of mpic_alloc (they require a valid dcr mapping in the device node)
This version of the patch can use a little bif of cleanup still (I can
probably consolidate rb->dbase/doff, at least once I'm sure on how the
hardware is actually supposed to work vs. possible simulator issues) and
it should be possible to build a DCR-only version of the driver. I need
to cleanup a bit the CONFIG_* handling for that and probably introduce
CONFIG_MPIC_MMIO and CONFIG_MPIC_DCR.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
arch/powerpc/sysdev/mpic.c | 138 ++++++++++++++++++++++++++++++++-------------
include/asm-powerpc/mpic.h | 35 ++++++++++-
2 files changed, 132 insertions(+), 41 deletions(-)
Index: linux-cell/arch/powerpc/sysdev/mpic.c
===================================================================
--- linux-cell.orig/arch/powerpc/sysdev/mpic.c 2006-10-16 17:20:39.000000000 +1000
+++ linux-cell/arch/powerpc/sysdev/mpic.c 2006-10-16 17:29:50.000000000 +1000
@@ -147,33 +147,51 @@ static u32 mpic_infos[][MPIC_IDX_END] =
*/
-static inline u32 _mpic_read(unsigned int be, volatile u32 __iomem *base,
- unsigned int reg)
-{
- if (be)
- return in_be32(base + (reg >> 2));
- else
- return in_le32(base + (reg >> 2));
+static inline u32 _mpic_read(enum mpic_reg_type type,
+ struct mpic_reg_bank *rb,
+ unsigned int reg)
+{
+ switch(type) {
+#ifdef CONFIG_PPC_DCR
+ case mpic_access_dcr:
+ return dcr_read(rb->dhost,
+ rb->dbase + reg + rb->doff);
+#endif
+ case mpic_access_mmio_be:
+ return in_be32(rb->base + (reg >> 2));
+ case mpic_access_mmio_le:
+ default:
+ return in_le32(rb->base + (reg >> 2));
+ }
}
-static inline void _mpic_write(unsigned int be, volatile u32 __iomem *base,
- unsigned int reg, u32 value)
+static inline void _mpic_write(enum mpic_reg_type type,
+ struct mpic_reg_bank *rb,
+ unsigned int reg, u32 value)
{
- if (be)
- out_be32(base + (reg >> 2), value);
- else
- out_le32(base + (reg >> 2), value);
+ switch(type) {
+#ifdef CONFIG_PPC_DCR
+ case mpic_access_dcr:
+ return dcr_write(rb->dhost,
+ rb->dbase + reg + rb->doff, value);
+#endif
+ case mpic_access_mmio_be:
+ return out_be32(rb->base + (reg >> 2), value);
+ case mpic_access_mmio_le:
+ default:
+ return out_le32(rb->base + (reg >> 2), value);
+ }
}
static inline u32 _mpic_ipi_read(struct mpic *mpic, unsigned int ipi)
{
- unsigned int be = (mpic->flags & MPIC_BIG_ENDIAN) != 0;
+ enum mpic_reg_type type = mpic->reg_type;
unsigned int offset = MPIC_INFO(GREG_IPI_VECTOR_PRI_0) +
(ipi * MPIC_INFO(GREG_IPI_STRIDE));
- if (mpic->flags & MPIC_BROKEN_IPI)
- be = !be;
- return _mpic_read(be, mpic->gregs, offset);
+ if ((mpic->flags & MPIC_BROKEN_IPI) && type == mpic_access_mmio_le)
+ type = mpic_access_mmio_be;
+ return _mpic_read(type, &mpic->gregs, offset);
}
static inline void _mpic_ipi_write(struct mpic *mpic, unsigned int ipi, u32 value)
@@ -181,7 +199,7 @@ static inline void _mpic_ipi_write(struc
unsigned int offset = MPIC_INFO(GREG_IPI_VECTOR_PRI_0) +
(ipi * MPIC_INFO(GREG_IPI_STRIDE));
- _mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->gregs, offset, value);
+ _mpic_write(mpic->reg_type, &mpic->gregs, offset, value);
}
static inline u32 _mpic_cpu_read(struct mpic *mpic, unsigned int reg)
@@ -190,8 +208,7 @@ static inline u32 _mpic_cpu_read(struct
if (mpic->flags & MPIC_PRIMARY)
cpu = hard_smp_processor_id();
- return _mpic_read(mpic->flags & MPIC_BIG_ENDIAN,
- mpic->cpuregs[cpu], reg);
+ return _mpic_read(mpic->reg_type, &mpic->cpuregs[cpu], reg);
}
static inline void _mpic_cpu_write(struct mpic *mpic, unsigned int reg, u32 value)
@@ -201,7 +218,7 @@ static inline void _mpic_cpu_write(struc
if (mpic->flags & MPIC_PRIMARY)
cpu = hard_smp_processor_id();
- _mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->cpuregs[cpu], reg, value);
+ _mpic_write(mpic->reg_type, &mpic->cpuregs[cpu], reg, value);
}
static inline u32 _mpic_irq_read(struct mpic *mpic, unsigned int src_no, unsigned int reg)
@@ -209,7 +226,7 @@ static inline u32 _mpic_irq_read(struct
unsigned int isu = src_no >> mpic->isu_shift;
unsigned int idx = src_no & mpic->isu_mask;
- return _mpic_read(mpic->flags & MPIC_BIG_ENDIAN, mpic->isus[isu],
+ return _mpic_read(mpic->reg_type, &mpic->isus[isu],
reg + (idx * MPIC_INFO(IRQ_STRIDE)));
}
@@ -219,12 +236,12 @@ static inline void _mpic_irq_write(struc
unsigned int isu = src_no >> mpic->isu_shift;
unsigned int idx = src_no & mpic->isu_mask;
- _mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->isus[isu],
+ _mpic_write(mpic->reg_type, &mpic->isus[isu],
reg + (idx * MPIC_INFO(IRQ_STRIDE)), value);
}
-#define mpic_read(b,r) _mpic_read(mpic->flags & MPIC_BIG_ENDIAN,(b),(r))
-#define mpic_write(b,r,v) _mpic_write(mpic->flags & MPIC_BIG_ENDIAN,(b),(r),(v))
+#define mpic_read(b,r) _mpic_read(mpic->reg_type,&(b),(r))
+#define mpic_write(b,r,v) _mpic_write(mpic->reg_type,&(b),(r),(v))
#define mpic_ipi_read(i) _mpic_ipi_read(mpic,(i))
#define mpic_ipi_write(i,v) _mpic_ipi_write(mpic,(i),(v))
#define mpic_cpu_read(i) _mpic_cpu_read(mpic,(i))
@@ -238,6 +255,38 @@ static inline void _mpic_irq_write(struc
*/
+static void _mpic_map_mmio(struct mpic *mpic, unsigned long phys_addr,
+ struct mpic_reg_bank *rb, unsigned int offset,
+ unsigned int size)
+{
+ rb->base = ioremap(phys_addr + offset, size);
+ BUG_ON(rb->base == NULL);
+}
+
+#ifdef CONFIG_PPC_DCR
+static void _mpic_map_dcr(struct mpic *mpic, struct mpic_reg_bank *rb,
+ unsigned int offset, unsigned int size)
+{
+ rb->dbase = mpic->dcr_base;
+ rb->doff = offset;
+ rb->dhost = dcr_map(mpic->of_node, rb->dbase + rb->doff, size);
+ BUG_ON(!DCR_MAP_OK(rb->dhost));
+}
+
+static inline void mpic_map(struct mpic *mpic, unsigned long phys_addr,
+ struct mpic_reg_bank *rb, unsigned int offset,
+ unsigned int size)
+{
+ if (mpic->flags & MPIC_USES_DCR)
+ _mpic_map_dcr(mpic, rb, offset, size);
+ else
+ _mpic_map_mmio(mpic, phys_addr, rb, offset, size);
+}
+#else /* CONFIG_PPC_DCR */
+#define mpic_map(m,p,b,o,s) _mpic_map_mmio(m,p,b,o,s)
+#endif /* !CONFIG_PPC_DCR */
+
+
/* Check if we have one of those nice broken MPICs with a flipped endian on
* reads from IPI registers
@@ -883,6 +932,7 @@ struct mpic * __init mpic_alloc(struct d
if (flags & MPIC_PRIMARY)
mpic->hc_ht_irq.set_affinity = mpic_set_affinity;
#endif /* CONFIG_MPIC_BROKEN_U3 */
+
#ifdef CONFIG_SMP
mpic->hc_ipi = mpic_ipi_chip;
mpic->hc_ipi.typename = name;
@@ -897,11 +947,26 @@ struct mpic * __init mpic_alloc(struct d
mpic->hw_set = mpic_infos[MPIC_GET_REGSET(flags)];
#endif
+ /* default register type */
+ mpic->reg_type = (flags & MPIC_BIG_ENDIAN) ?
+ mpic_access_mmio_be : mpic_access_mmio_le;
+
+#ifdef CONFIG_PPC_DCR
+ if (mpic->flags & MPIC_USES_DCR) {
+ const u32 *dbasep;
+ BUG_ON(mpic->of_node == NULL);
+ dbasep = get_property(mpic->of_node, "dcr-reg", NULL);
+ BUG_ON(dbasep == NULL);
+ mpic->dcr_base = *dbasep;
+ mpic->reg_type = mpic_access_dcr;
+ }
+#else
+ BUG_ON (mpic->flags & MPIC_USES_DCR);
+#endif /* CONFIG_PPC_DCR */
+
/* Map the global registers */
- mpic->gregs = ioremap(phys_addr + MPIC_INFO(GREG_BASE), 0x1000);
- mpic->tmregs = mpic->gregs +
- ((MPIC_INFO(TIMER_BASE) - MPIC_INFO(GREG_BASE)) >> 2);
- BUG_ON(mpic->gregs == NULL);
+ mpic_map(mpic, phys_addr, &mpic->gregs, MPIC_INFO(GREG_BASE), 0x1000);
+ mpic_map(mpic, phys_addr, &mpic->tmregs, MPIC_INFO(TIMER_BASE), 0x1000);
/* Reset */
if (flags & MPIC_WANTS_RESET) {
@@ -926,17 +991,16 @@ struct mpic * __init mpic_alloc(struct d
/* Map the per-CPU registers */
for (i = 0; i < mpic->num_cpus; i++) {
- mpic->cpuregs[i] = ioremap(phys_addr + MPIC_INFO(CPU_BASE) +
- i * MPIC_INFO(CPU_STRIDE), 0x1000);
- BUG_ON(mpic->cpuregs[i] == NULL);
+ mpic_map(mpic, phys_addr, &mpic->cpuregs[i],
+ MPIC_INFO(CPU_BASE) + i * MPIC_INFO(CPU_STRIDE),
+ 0x1000);
}
/* Initialize main ISU if none provided */
if (mpic->isu_size == 0) {
mpic->isu_size = mpic->num_sources;
- mpic->isus[0] = ioremap(phys_addr + MPIC_INFO(IRQ_BASE),
- MPIC_INFO(IRQ_STRIDE) * mpic->isu_size);
- BUG_ON(mpic->isus[0] == NULL);
+ mpic_map(mpic, phys_addr, &mpic->isus[0],
+ MPIC_INFO(IRQ_BASE), MPIC_INFO(IRQ_STRIDE) * mpic->isu_size);
}
mpic->isu_shift = 1 + __ilog2(mpic->isu_size - 1);
mpic->isu_mask = (1 << mpic->isu_shift) - 1;
@@ -979,8 +1043,8 @@ void __init mpic_assign_isu(struct mpic
BUG_ON(isu_num >= MPIC_MAX_ISU);
- mpic->isus[isu_num] = ioremap(phys_addr,
- MPIC_INFO(IRQ_STRIDE) * mpic->isu_size);
+ mpic_map(mpic, phys_addr, &mpic->isus[isu_num], 0,
+ MPIC_INFO(IRQ_STRIDE) * mpic->isu_size);
if ((isu_first + mpic->isu_size) > mpic->num_sources)
mpic->num_sources = isu_first + mpic->isu_size;
}
Index: linux-cell/include/asm-powerpc/mpic.h
===================================================================
--- linux-cell.orig/include/asm-powerpc/mpic.h 2006-10-16 17:20:39.000000000 +1000
+++ linux-cell/include/asm-powerpc/mpic.h 2006-10-16 17:26:46.000000000 +1000
@@ -3,6 +3,7 @@
#ifdef __KERNEL__
#include <linux/irq.h>
+#include <asm/dcr.h>
/*
* Global registers
@@ -225,6 +226,23 @@ struct mpic_irq_fixup
#endif /* CONFIG_MPIC_BROKEN_U3 */
+enum mpic_reg_type {
+ mpic_access_mmio_le,
+ mpic_access_mmio_be,
+#ifdef CONFIG_PPC_DCR
+ mpic_access_dcr
+#endif
+};
+
+struct mpic_reg_bank {
+ u32 __iomem *base;
+#ifdef CONFIG_PPC_DCR
+ dcr_host_t dhost;
+ unsigned int dbase;
+ unsigned int doff;
+#endif /* CONFIG_PPC_DCR */
+};
+
/* The instance data of a given MPIC */
struct mpic
{
@@ -264,11 +282,18 @@ struct mpic
spinlock_t fixup_lock;
#endif
+ /* Register access method */
+ enum mpic_reg_type reg_type;
+
/* The various ioremap'ed bases */
- volatile u32 __iomem *gregs;
- volatile u32 __iomem *tmregs;
- volatile u32 __iomem *cpuregs[MPIC_MAX_CPUS];
- volatile u32 __iomem *isus[MPIC_MAX_ISU];
+ struct mpic_reg_bank gregs;
+ struct mpic_reg_bank tmregs;
+ struct mpic_reg_bank cpuregs[MPIC_MAX_CPUS];
+ struct mpic_reg_bank isus[MPIC_MAX_ISU];
+
+#ifdef CONFIG_PPC_DCR
+ unsigned int dcr_base;
+#endif
#ifdef CONFIG_MPIC_WEIRD
/* Pointer to HW info array */
@@ -305,6 +330,8 @@ struct mpic
#define MPIC_SPV_EOI 0x00000020
/* No passthrough disable */
#define MPIC_NO_PTHROU_DIS 0x00000040
+/* DCR based MPIC */
+#define MPIC_USES_DCR 0x00000080
/* MPIC HW modification ID */
#define MPIC_REGSET_MASK 0xf0000000
^ permalink raw reply
* [PATCH 8/31] powerpc: Make EMAC use generic DCR access methods
From: Benjamin Herrenschmidt @ 2006-11-07 7:22 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1162884150.435786.961370643810.qpush@grosgo>
This patch makes the EMAC driver use the new DCR access methods. It
doesn't yet uses dcr_map() and thus still only work with real DCRs.
This will be fixed in a later patch
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
drivers/net/ibm_emac/ibm_emac_mal.h | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
Index: linux-cell/drivers/net/ibm_emac/ibm_emac_mal.h
===================================================================
--- linux-cell.orig/drivers/net/ibm_emac/ibm_emac_mal.h 2006-10-11 13:01:59.000000000 +1000
+++ linux-cell/drivers/net/ibm_emac/ibm_emac_mal.h 2006-10-11 14:35:28.000000000 +1000
@@ -24,6 +24,7 @@
#include <linux/netdevice.h>
#include <asm/io.h>
+#include <asm/dcr.h>
/*
* These MAL "versions" probably aren't the real versions IBM uses for these
@@ -191,6 +192,7 @@ struct mal_commac {
struct ibm_ocp_mal {
int dcrbase;
+ dcr_host_t dcrhost;
struct list_head poll_list;
struct net_device poll_dev;
@@ -207,12 +209,12 @@ struct ibm_ocp_mal {
static inline u32 get_mal_dcrn(struct ibm_ocp_mal *mal, int reg)
{
- return mfdcr(mal->dcrbase + reg);
+ return dcr_read(mal->dcrhost, mal->dcrbase + reg);
}
static inline void set_mal_dcrn(struct ibm_ocp_mal *mal, int reg, u32 val)
{
- mtdcr(mal->dcrbase + reg, val);
+ dcr_write(mal->dcrhost, mal->dcrbase + reg, val);
}
/* Register MAL devices */
^ permalink raw reply
* [PATCH 7/31] powerpc: Generic DCR infrastructure
From: Benjamin Herrenschmidt @ 2006-11-07 7:22 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1162884150.435786.961370643810.qpush@grosgo>
This patch adds new dcr_map/dcr_read/dcr_write accessors for DCRs that
can be used by drivers to transparently address either native DCRs or
memory mapped DCRs. The implementation for memory mapped DCRs is done
after the binding being currently worked on for SLOF and the Axon
chipset. This patch enables it for the cell native platform
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
arch/powerpc/Kconfig | 16 ++++
arch/powerpc/kernel/Makefile | 1
arch/powerpc/sysdev/Makefile | 3
arch/powerpc/sysdev/dcr-low.S | 39 +++++++++++
arch/powerpc/sysdev/dcr.c | 137 +++++++++++++++++++++++++++++++++++++++
arch/ppc/Kconfig | 11 +++
include/asm-powerpc/dcr-mmio.h | 51 ++++++++++++++
include/asm-powerpc/dcr-native.h | 39 +++++++++++
include/asm-powerpc/dcr.h | 42 +++++++++++
9 files changed, 337 insertions(+), 2 deletions(-)
Index: linux-cell/include/asm-powerpc/dcr.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-cell/include/asm-powerpc/dcr.h 2006-11-02 13:21:14.000000000 +1100
@@ -0,0 +1,42 @@
+/*
+ * (c) Copyright 2006 Benjamin Herrenschmidt, IBM Corp.
+ * <benh@kernel.crashing.org>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _ASM_POWERPC_DCR_H
+#define _ASM_POWERPC_DCR_H
+#ifdef __KERNEL__
+
+#ifdef CONFIG_PPC_DCR_NATIVE
+#include <asm/dcr-native.h>
+#else
+#include <asm/dcr-mmio.h>
+#endif
+
+/*
+ * On CONFIG_PPC_MERGE, we have additional helpers to read the DCR
+ * base from the device-tree
+ */
+#ifdef CONFIG_PPC_MERGE
+extern unsigned int dcr_resource_start(struct device_node *np,
+ unsigned int index);
+extern unsigned int dcr_resource_len(struct device_node *np,
+ unsigned int index);
+#endif /* CONFIG_PPC_MERGE */
+
+#endif /* __KERNEL__ */
+#endif /* _ASM_POWERPC_DCR_H */
Index: linux-cell/arch/powerpc/Kconfig
===================================================================
--- linux-cell.orig/arch/powerpc/Kconfig 2006-10-23 14:41:37.000000000 +1000
+++ linux-cell/arch/powerpc/Kconfig 2006-11-02 13:21:14.000000000 +1100
@@ -160,9 +160,11 @@ config PPC_86xx
config 40x
bool "AMCC 40x"
+ select PPC_DCR_NATIVE
config 44x
bool "AMCC 44x"
+ select PPC_DCR_NATIVE
config 8xx
bool "Freescale 8xx"
@@ -208,6 +210,19 @@ config PPC_FPU
bool
default y if PPC64
+config PPC_DCR_NATIVE
+ bool
+ default n
+
+config PPC_DCR_MMIO
+ bool
+ default n
+
+config PPC_DCR
+ bool
+ depends on PPC_DCR_NATIVE || PPC_DCR_MMIO
+ default y
+
config BOOKE
bool
depends on E200 || E500
@@ -445,6 +460,7 @@ config PPC_CELL
config PPC_CELL_NATIVE
bool
select PPC_CELL
+ select PPC_DCR_MMIO
default n
config PPC_IBM_CELL_BLADE
Index: linux-cell/include/asm-powerpc/dcr-mmio.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-cell/include/asm-powerpc/dcr-mmio.h 2006-11-02 13:21:14.000000000 +1100
@@ -0,0 +1,51 @@
+/*
+ * (c) Copyright 2006 Benjamin Herrenschmidt, IBM Corp.
+ * <benh@kernel.crashing.org>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _ASM_POWERPC_DCR_MMIO_H
+#define _ASM_POWERPC_DCR_MMIO_H
+#ifdef __KERNEL__
+
+#include <asm/io.h>
+
+typedef struct { void __iomem *token; unsigned int stride; } dcr_host_t;
+
+#define DCR_MAP_OK(host) ((host).token != NULL)
+
+extern dcr_host_t dcr_map(struct device_node *dev, unsigned int dcr_n,
+ unsigned int dcr_c);
+extern void dcr_unmap(dcr_host_t host, unsigned int dcr_n, unsigned int dcr_c);
+
+static inline u32 dcr_read(dcr_host_t host, unsigned int dcr_n)
+{
+ return in_be32(host.token + dcr_n * host.stride);
+}
+
+static inline void dcr_write(dcr_host_t host, unsigned int dcr_n, u32 value)
+{
+ out_be32(host.token + dcr_n * host.stride, value);
+}
+
+extern u64 of_translate_dcr_address(struct device_node *dev,
+ unsigned int dcr_n,
+ unsigned int *stride);
+
+#endif /* __KERNEL__ */
+#endif /* _ASM_POWERPC_DCR_MMIO_H */
+
+
Index: linux-cell/include/asm-powerpc/dcr-native.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-cell/include/asm-powerpc/dcr-native.h 2006-11-02 13:21:14.000000000 +1100
@@ -0,0 +1,39 @@
+/*
+ * (c) Copyright 2006 Benjamin Herrenschmidt, IBM Corp.
+ * <benh@kernel.crashing.org>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _ASM_POWERPC_DCR_NATIVE_H
+#define _ASM_POWERPC_DCR_NATIVE_H
+#ifdef __KERNEL__
+
+#include <asm/reg.h>
+
+typedef struct {} dcr_host_t;
+
+#define DCR_MAP_OK(host) (1)
+
+#define dcr_map(dev, dcr_n, dcr_c) {}
+#define dcr_unmap(host, dcr_n, dcr_c) {}
+#define dcr_read(host, dcr_n) mfdcr(dcr_n)
+#define dcr_write(host, dcr_n, value) mtdcr(dcr_n, value)
+
+
+#endif /* __KERNEL__ */
+#endif /* _ASM_POWERPC_DCR_NATIVE_H */
+
+
Index: linux-cell/arch/ppc/Kconfig
===================================================================
--- linux-cell.orig/arch/ppc/Kconfig 2006-10-06 13:47:55.000000000 +1000
+++ linux-cell/arch/ppc/Kconfig 2006-11-02 13:21:14.000000000 +1100
@@ -77,9 +77,11 @@ config 6xx
config 40x
bool "40x"
+ select PPC_DCR_NATIVE
config 44x
bool "44x"
+ select PPC_DCR_NATIVE
config 8xx
bool "8xx"
@@ -95,6 +97,15 @@ endchoice
config PPC_FPU
bool
+config PPC_DCR_NATIVE
+ bool
+ default n
+
+config PPC_DCR
+ bool
+ depends on PPC_DCR_NATIVE
+ default y
+
config BOOKE
bool
depends on E200 || E500
Index: linux-cell/arch/powerpc/kernel/Makefile
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/Makefile 2006-11-02 13:16:16.000000000 +1100
+++ linux-cell/arch/powerpc/kernel/Makefile 2006-11-02 13:21:14.000000000 +1100
@@ -59,6 +59,7 @@ obj-$(CONFIG_BOOTX_TEXT) += btext.o
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_PPC_UDBG_16550) += legacy_serial.o udbg_16550.o
+
module-$(CONFIG_PPC64) += module_64.o
obj-$(CONFIG_MODULES) += $(module-y)
Index: linux-cell/arch/powerpc/sysdev/Makefile
===================================================================
--- linux-cell.orig/arch/powerpc/sysdev/Makefile 2006-10-06 13:47:54.000000000 +1000
+++ linux-cell/arch/powerpc/sysdev/Makefile 2006-11-02 13:21:14.000000000 +1100
@@ -5,8 +5,7 @@ endif
obj-$(CONFIG_MPIC) += mpic.o
obj-$(CONFIG_PPC_INDIRECT_PCI) += indirect_pci.o
obj-$(CONFIG_PPC_MPC106) += grackle.o
-obj-$(CONFIG_BOOKE) += dcr.o
-obj-$(CONFIG_40x) += dcr.o
+obj-$(CONFIG_PPC_DCR) += dcr.o dcr-low.o
obj-$(CONFIG_U3_DART) += dart_iommu.o
obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o
obj-$(CONFIG_FSL_SOC) += fsl_soc.o
Index: linux-cell/arch/powerpc/sysdev/dcr-low.S
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-cell/arch/powerpc/sysdev/dcr-low.S 2006-11-02 13:21:14.000000000 +1100
@@ -0,0 +1,39 @@
+/*
+ * "Indirect" DCR access
+ *
+ * Copyright (c) 2004 Eugene Surovegin <ebs@ebshome.net>
+ *
+ * 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 <asm/ppc_asm.h>
+#include <asm/processor.h>
+
+#define DCR_ACCESS_PROLOG(table) \
+ rlwinm r3,r3,4,18,27; \
+ lis r5,table@h; \
+ ori r5,r5,table@l; \
+ add r3,r3,r5; \
+ mtctr r3; \
+ bctr
+
+_GLOBAL(__mfdcr)
+ DCR_ACCESS_PROLOG(__mfdcr_table)
+
+_GLOBAL(__mtdcr)
+ DCR_ACCESS_PROLOG(__mtdcr_table)
+
+__mfdcr_table:
+ mfdcr r3,0; blr
+__mtdcr_table:
+ mtdcr 0,r4; blr
+
+dcr = 1
+ .rept 1023
+ mfdcr r3,dcr; blr
+ mtdcr dcr,r4; blr
+ dcr = dcr + 1
+ .endr
Index: linux-cell/arch/powerpc/sysdev/dcr.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-cell/arch/powerpc/sysdev/dcr.c 2006-11-02 13:21:14.000000000 +1100
@@ -0,0 +1,137 @@
+/*
+ * (c) Copyright 2006 Benjamin Herrenschmidt, IBM Corp.
+ * <benh@kernel.crashing.org>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#undef DEBUG
+
+#include <linux/kernel.h>
+#include <asm/prom.h>
+#include <asm/dcr.h>
+
+unsigned int dcr_resource_start(struct device_node *np, unsigned int index)
+{
+ unsigned int ds;
+ const u32 *dr = get_property(np, "dcr-reg", &ds);
+
+ if (dr == NULL || ds & 1 || index >= (ds / 8))
+ return 0;
+
+ return dr[index * 2];
+}
+
+unsigned int dcr_resource_len(struct device_node *np, unsigned int index)
+{
+ unsigned int ds;
+ const u32 *dr = get_property(np, "dcr-reg", &ds);
+
+ if (dr == NULL || ds & 1 || index >= (ds / 8))
+ return 0;
+
+ return dr[index * 2 + 1];
+}
+
+#ifndef CONFIG_PPC_DCR_NATIVE
+
+static struct device_node * find_dcr_parent(struct device_node * node)
+{
+ struct device_node *par, *tmp;
+ const u32 *p;
+
+ for (par = of_node_get(node); par;) {
+ if (get_property(par, "dcr-controller", NULL))
+ break;
+ p = get_property(par, "dcr-parent", NULL);
+ tmp = par;
+ if (p == NULL)
+ par = of_get_parent(par);
+ else
+ par = of_find_node_by_phandle(*p);
+ of_node_put(tmp);
+ }
+ return par;
+}
+
+u64 of_translate_dcr_address(struct device_node *dev,
+ unsigned int dcr_n,
+ unsigned int *out_stride)
+{
+ struct device_node *dp;
+ const u32 *p;
+ unsigned int stride;
+ u64 ret;
+
+ dp = find_dcr_parent(dev);
+ if (dp == NULL)
+ return OF_BAD_ADDR;
+
+ /* Stride is not properly defined yet, default to 0x10 for Axon */
+ p = get_property(dp, "dcr-mmio-stride", NULL);
+ stride = (p == NULL) ? 0x10 : *p;
+
+ /* XXX FIXME: Which property name is to use of the 2 following ? */
+ p = get_property(dp, "dcr-mmio-range", NULL);
+ if (p == NULL)
+ p = get_property(dp, "dcr-mmio-space", NULL);
+ if (p == NULL)
+ return OF_BAD_ADDR;
+
+ /* Maybe could do some better range checking here */
+ ret = of_translate_address(dp, p);
+ if (ret != OF_BAD_ADDR)
+ ret += (u64)(stride) * (u64)dcr_n;
+ if (out_stride)
+ *out_stride = stride;
+ return ret;
+}
+
+dcr_host_t dcr_map(struct device_node *dev, unsigned int dcr_n,
+ unsigned int dcr_c)
+{
+ dcr_host_t ret = { .token = NULL, .stride = 0 };
+ u64 addr;
+
+ pr_debug("dcr_map(%s, 0x%x, 0x%x)\n",
+ dev->full_name, dcr_n, dcr_c);
+
+ addr = of_translate_dcr_address(dev, dcr_n, &ret.stride);
+ pr_debug("translates to addr: 0x%lx, stride: 0x%x\n",
+ addr, ret.stride);
+ if (addr == OF_BAD_ADDR)
+ return ret;
+ pr_debug("mapping 0x%x bytes\n", dcr_c * ret.stride);
+ ret.token = ioremap(addr, dcr_c * ret.stride);
+ if (ret.token == NULL)
+ return ret;
+ pr_debug("mapped at 0x%p -> base is 0x%p\n",
+ ret.token, ret.token - dcr_n * ret.stride);
+ ret.token -= dcr_n * ret.stride;
+ return ret;
+}
+
+void dcr_unmap(dcr_host_t host, unsigned int dcr_n, unsigned int dcr_c)
+{
+ dcr_host_t h = host;
+
+ if (h.token == NULL)
+ return;
+ h.token -= dcr_n * h.stride;
+ iounmap(h.token);
+ h.token = NULL;
+}
+
+#endif /* !defined(CONFIG_PPC_DCR_NATIVE) */
^ permalink raw reply
* [PATCH 6/31] powerpc: Remove ppc_md.pci_map_irq & ppc_swizzle for ARCH=powerpc
From: Benjamin Herrenschmidt @ 2006-11-07 7:22 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1162884150.435786.961370643810.qpush@grosgo>
These were inherited from ARCH=ppc, but are not needed since parsing of interrupts
should be done via the of_* functions (who can do swizzling). If we ever need to
do non-standard swizzling on bridges without a device-node, then we might add
back a slightly different version of ppc_md.pci_swizzle but for now, that is not
the case.
I removed the couple of calls for these in 83xx. If that breaks something, then
there is a problem with the device-tree on these.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
arch/powerpc/kernel/pci_32.c | 23 -----------------------
arch/powerpc/platforms/83xx/mpc832x_mds.c | 2 --
arch/powerpc/platforms/83xx/mpc8360e_pb.c | 2 --
include/asm-powerpc/machdep.h | 4 ----
4 files changed, 31 deletions(-)
Index: linux-cell/arch/powerpc/kernel/pci_32.c
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/pci_32.c 2006-11-06 15:04:50.000000000 +1100
+++ linux-cell/arch/powerpc/kernel/pci_32.c 2006-11-06 15:05:34.000000000 +1100
@@ -1283,10 +1283,6 @@ pcibios_init(void)
if (pci_assign_all_buses && have_of)
pcibios_make_OF_bus_map();
- /* Do machine dependent PCI interrupt routing */
- if (ppc_md.pci_swizzle && ppc_md.pci_map_irq)
- pci_fixup_irqs(ppc_md.pci_swizzle, ppc_md.pci_map_irq);
-
/* Call machine dependent fixup */
if (ppc_md.pcibios_fixup)
ppc_md.pcibios_fixup();
@@ -1309,25 +1305,6 @@ pcibios_init(void)
subsys_initcall(pcibios_init);
-unsigned char __init
-common_swizzle(struct pci_dev *dev, unsigned char *pinp)
-{
- struct pci_controller *hose = dev->sysdata;
-
- if (dev->bus->number != hose->first_busno) {
- u8 pin = *pinp;
- do {
- pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn));
- /* Move up the chain of bridges. */
- dev = dev->bus->self;
- } while (dev->bus->self);
- *pinp = pin;
-
- /* The slot is the idsel of the last bridge. */
- }
- return PCI_SLOT(dev->devfn);
-}
-
unsigned long resource_fixup(struct pci_dev * dev, struct resource * res,
unsigned long start, unsigned long size)
{
Index: linux-cell/arch/powerpc/platforms/83xx/mpc832x_mds.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/83xx/mpc832x_mds.c 2006-11-06 15:01:52.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/83xx/mpc832x_mds.c 2006-11-06 15:05:34.000000000 +1100
@@ -96,8 +96,6 @@ static void __init mpc832x_sys_setup_arc
#ifdef CONFIG_PCI
for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;)
add_bridge(np);
-
- ppc_md.pci_swizzle = common_swizzle;
ppc_md.pci_exclude_device = mpc83xx_exclude_device;
#endif
Index: linux-cell/arch/powerpc/platforms/83xx/mpc8360e_pb.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/83xx/mpc8360e_pb.c 2006-11-06 15:01:52.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/83xx/mpc8360e_pb.c 2006-11-06 15:05:34.000000000 +1100
@@ -102,8 +102,6 @@ static void __init mpc8360_sys_setup_arc
#ifdef CONFIG_PCI
for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;)
add_bridge(np);
-
- ppc_md.pci_swizzle = common_swizzle;
ppc_md.pci_exclude_device = mpc83xx_exclude_device;
#endif
Index: linux-cell/include/asm-powerpc/machdep.h
===================================================================
--- linux-cell.orig/include/asm-powerpc/machdep.h 2006-11-06 15:02:21.000000000 +1100
+++ linux-cell/include/asm-powerpc/machdep.h 2006-11-06 15:05:34.000000000 +1100
@@ -199,10 +199,6 @@ struct machdep_calls {
* Returns 0 to allow assignment/enabling of the device. */
int (*pcibios_enable_device_hook)(struct pci_dev *, int initial);
- /* For interrupt routing */
- unsigned char (*pci_swizzle)(struct pci_dev *, unsigned char *);
- int (*pci_map_irq)(struct pci_dev *, unsigned char, unsigned char);
-
/* Called in indirect_* to avoid touching devices */
int (*pci_exclude_device)(unsigned char, unsigned char);
^ permalink raw reply
* [PATCH 5/31] powerpc: Make pci_read_irq_line the default
From: Benjamin Herrenschmidt @ 2006-11-07 7:22 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1162884150.435786.961370643810.qpush@grosgo>
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 <benh@kernel.crashing.org>
arch/powerpc/kernel/pci_32.c | 10 ++++
arch/powerpc/kernel/pci_64.c | 8 ++-
arch/powerpc/platforms/82xx/mpc82xx_ads.c | 13 ------
arch/powerpc/platforms/83xx/mpc834x_itx.c | 3 -
arch/powerpc/platforms/83xx/mpc834x_sys.c | 3 -
arch/powerpc/platforms/83xx/mpc83xx.h | 1
arch/powerpc/platforms/83xx/pci.c | 9 ----
arch/powerpc/platforms/85xx/mpc85xx_ads.c | 11 -----
arch/powerpc/platforms/86xx/mpc86xx_hpcn.c | 10 ----
arch/powerpc/platforms/cell/setup.c | 9 ----
arch/powerpc/platforms/chrp/chrp.h | 1
arch/powerpc/platforms/chrp/pci.c | 9 ----
arch/powerpc/platforms/chrp/setup.c | 1
arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c | 16 ++-----
arch/powerpc/platforms/maple/maple.h | 2
arch/powerpc/platforms/maple/pci.c | 45 ++++++++--------------
arch/powerpc/platforms/maple/setup.c | 2
arch/powerpc/platforms/pasemi/pasemi.h | 1
arch/powerpc/platforms/pasemi/pci.c | 8 ---
arch/powerpc/platforms/pasemi/setup.c | 1
arch/powerpc/platforms/powermac/pci.c | 35 ++++++-----------
arch/powerpc/platforms/powermac/pmac.h | 2
arch/powerpc/platforms/powermac/setup.c | 2
arch/powerpc/platforms/pseries/pci.c | 35 -----------------
arch/powerpc/platforms/pseries/setup.c | 1
include/asm-powerpc/machdep.h | 2
include/asm-powerpc/ppc-pci.h | 1
27 files changed, 58 insertions(+), 183 deletions(-)
Index: linux-cell/arch/powerpc/kernel/pci_32.c
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/pci_32.c 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/kernel/pci_32.c 2006-11-06 15:04:50.000000000 +1100
@@ -12,6 +12,7 @@
#include <linux/errno.h>
#include <linux/bootmem.h>
#include <linux/irq.h>
+#include <linux/list.h>
#include <asm/processor.h>
#include <asm/io.h>
@@ -1338,6 +1339,7 @@ void __init pcibios_fixup_bus(struct pci
struct pci_controller *hose = (struct pci_controller *) bus->sysdata;
unsigned long io_offset;
struct resource *res;
+ struct pci_dev *dev;
int i;
io_offset = (unsigned long)hose->io_base_virt - isa_io_base;
@@ -1390,8 +1392,16 @@ void __init pcibios_fixup_bus(struct pci
}
}
+ /* Platform specific bus fixups */
if (ppc_md.pcibios_fixup_bus)
ppc_md.pcibios_fixup_bus(bus);
+
+ /* Read default IRQs and fixup if necessary */
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ pci_read_irq_line(dev);
+ if (ppc_md.pci_irq_fixup)
+ ppc_md.pci_irq_fixup(dev);
+ }
}
char __init *pcibios_setup(char *str)
Index: linux-cell/arch/powerpc/kernel/pci_64.c
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/pci_64.c 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/kernel/pci_64.c 2006-11-06 15:02:21.000000000 +1100
@@ -1223,8 +1223,12 @@ static void __devinit do_bus_setup(struc
list_for_each_entry(dev, &bus->devices, bus_list)
ppc_md.iommu_dev_setup(dev);
- if (ppc_md.irq_bus_setup)
- ppc_md.irq_bus_setup(bus);
+ /* Read default IRQs and fixup if necessary */
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ pci_read_irq_line(dev);
+ if (ppc_md.pci_irq_fixup)
+ ppc_md.pci_irq_fixup(dev);
+ }
}
void __devinit pcibios_fixup_bus(struct pci_bus *bus)
Index: linux-cell/arch/powerpc/platforms/pseries/pci.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/pseries/pci.c 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/pseries/pci.c 2006-11-06 15:02:21.000000000 +1100
@@ -29,8 +29,6 @@
#include <asm/prom.h>
#include <asm/ppc-pci.h>
-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
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)
Index: linux-cell/arch/powerpc/platforms/pseries/setup.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/pseries/setup.c 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/pseries/setup.c 2006-11-06 15:02:21.000000000 +1100
@@ -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,
Index: linux-cell/include/asm-powerpc/machdep.h
===================================================================
--- linux-cell.orig/include/asm-powerpc/machdep.h 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/include/asm-powerpc/machdep.h 2006-11-06 15:02:21.000000000 +1100
@@ -86,7 +86,6 @@ struct machdep_calls {
void (*tce_flush)(struct iommu_table *tbl);
void (*iommu_dev_setup)(struct pci_dev *dev);
void (*iommu_bus_setup)(struct pci_bus *bus);
- void (*irq_bus_setup)(struct pci_bus *bus);
#endif /* CONFIG_PPC64 */
int (*probe)(void);
@@ -106,6 +105,7 @@ struct machdep_calls {
/* Called after scanning the bus, before allocating resources */
void (*pcibios_fixup)(void);
int (*pci_probe_mode)(struct pci_bus *);
+ void (*pci_irq_fixup)(struct pci_dev *dev);
void (*restart)(char *cmd);
void (*power_off)(void);
Index: linux-cell/include/asm-powerpc/ppc-pci.h
===================================================================
--- linux-cell.orig/include/asm-powerpc/ppc-pci.h 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/include/asm-powerpc/ppc-pci.h 2006-11-06 15:02:21.000000000 +1100
@@ -47,7 +47,6 @@ unsigned long get_phb_buid (struct devic
/* From pSeries_pci.h */
extern void pSeries_final_fixup(void);
-extern void pSeries_irq_bus_setup(struct pci_bus *bus);
extern unsigned long pci_probe_only;
Index: linux-cell/arch/powerpc/platforms/cell/setup.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/cell/setup.c 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/cell/setup.c 2006-11-06 15:02:21.000000000 +1100
@@ -80,14 +80,6 @@ static void cell_progress(char *s, unsig
printk("*** %04x : %s\n", hex, s ? s : "");
}
-static void __init cell_pcibios_fixup(void)
-{
- struct pci_dev *dev = NULL;
-
- for_each_pci_dev(dev)
- pci_read_irq_line(dev);
-}
-
static void __init cell_init_irq(void)
{
iic_init_IRQ();
@@ -180,7 +172,6 @@ define_machine(cell) {
.check_legacy_ioport = cell_check_legacy_ioport,
.progress = cell_progress,
.init_IRQ = cell_init_irq,
- .pcibios_fixup = cell_pcibios_fixup,
#ifdef CONFIG_KEXEC
.machine_kexec = default_machine_kexec,
.machine_kexec_prepare = default_machine_kexec_prepare,
Index: linux-cell/arch/powerpc/platforms/maple/maple.h
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/maple/maple.h 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/maple/maple.h 2006-11-06 15:02:21.000000000 +1100
@@ -8,5 +8,5 @@ extern void maple_get_rtc_time(struct rt
extern unsigned long maple_get_boot_time(void);
extern void maple_calibrate_decr(void);
extern void maple_pci_init(void);
-extern void maple_pcibios_fixup(void);
+extern void maple_pci_irq_fixup(struct pci_dev *dev);
extern int maple_pci_get_legacy_ide_irq(struct pci_dev *dev, int channel);
Index: linux-cell/arch/powerpc/platforms/maple/pci.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/maple/pci.c 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/maple/pci.c 2006-11-06 15:02:21.000000000 +1100
@@ -502,38 +502,29 @@ static int __init add_bridge(struct devi
}
-void __init maple_pcibios_fixup(void)
+void __devinit maple_pci_irq_fixup(struct pci_dev *dev)
{
- struct pci_dev *dev = NULL;
+ DBG(" -> maple_pci_irq_fixup\n");
- DBG(" -> maple_pcibios_fixup\n");
-
- for_each_pci_dev(dev) {
- /* Fixup IRQ for PCIe host */
- if (u4_pcie != NULL && dev->bus->number == 0 &&
- pci_bus_to_host(dev->bus) == u4_pcie) {
- printk(KERN_DEBUG "Fixup U4 PCIe IRQ\n");
- dev->irq = irq_create_mapping(NULL, 1);
- if (dev->irq != NO_IRQ)
- set_irq_type(dev->irq, IRQ_TYPE_LEVEL_LOW);
- continue;
- }
-
- /* Hide AMD8111 IDE interrupt when in legacy mode so
- * the driver calls pci_get_legacy_ide_irq()
- */
- if (dev->vendor == PCI_VENDOR_ID_AMD &&
- dev->device == PCI_DEVICE_ID_AMD_8111_IDE &&
- (dev->class & 5) != 5) {
- dev->irq = NO_IRQ;
- continue;
- }
+ /* Fixup IRQ for PCIe host */
+ if (u4_pcie != NULL && dev->bus->number == 0 &&
+ pci_bus_to_host(dev->bus) == u4_pcie) {
+ printk(KERN_DEBUG "Fixup U4 PCIe IRQ\n");
+ dev->irq = irq_create_mapping(NULL, 1);
+ if (dev->irq != NO_IRQ)
+ set_irq_type(dev->irq, IRQ_TYPE_LEVEL_LOW);
+ }
- /* For all others, map the interrupt from the device-tree */
- pci_read_irq_line(dev);
+ /* Hide AMD8111 IDE interrupt when in legacy mode so
+ * the driver calls pci_get_legacy_ide_irq()
+ */
+ if (dev->vendor == PCI_VENDOR_ID_AMD &&
+ dev->device == PCI_DEVICE_ID_AMD_8111_IDE &&
+ (dev->class & 5) != 5) {
+ dev->irq = NO_IRQ;
}
- DBG(" <- maple_pcibios_fixup\n");
+ DBG(" <- maple_pci_irq_fixup\n");
}
static void __init maple_fixup_phb_resources(void)
Index: linux-cell/arch/powerpc/platforms/maple/setup.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/maple/setup.c 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/maple/setup.c 2006-11-06 15:02:21.000000000 +1100
@@ -312,7 +312,7 @@ define_machine(maple_md) {
.setup_arch = maple_setup_arch,
.init_early = maple_init_early,
.init_IRQ = maple_init_IRQ,
- .pcibios_fixup = maple_pcibios_fixup,
+ .pci_irq_fixup = maple_pci_irq_fixup,
.pci_get_legacy_ide_irq = maple_pci_get_legacy_ide_irq,
.restart = maple_restart,
.power_off = maple_power_off,
Index: linux-cell/arch/powerpc/platforms/powermac/pci.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/powermac/pci.c 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/powermac/pci.c 2006-11-06 15:02:21.000000000 +1100
@@ -985,30 +985,23 @@ static int __init add_bridge(struct devi
return 0;
}
-void __init pmac_pcibios_fixup(void)
+void __devinit pmac_pci_irq_fixup(struct pci_dev *dev)
{
- struct pci_dev* dev = NULL;
-
- for_each_pci_dev(dev) {
- /* Read interrupt from the device-tree */
- pci_read_irq_line(dev);
-
#ifdef CONFIG_PPC32
- /* Fixup interrupt for the modem/ethernet combo controller.
- * on machines with a second ohare chip.
- * The number in the device tree (27) is bogus (correct for
- * the ethernet-only board but not the combo ethernet/modem
- * board). The real interrupt is 28 on the second controller
- * -> 28+32 = 60.
- */
- if (has_second_ohare &&
- dev->vendor == PCI_VENDOR_ID_DEC &&
- dev->device == PCI_DEVICE_ID_DEC_TULIP_PLUS) {
- dev->irq = irq_create_mapping(NULL, 60);
- set_irq_type(dev->irq, IRQ_TYPE_LEVEL_LOW);
- }
-#endif /* CONFIG_PPC32 */
+ /* Fixup interrupt for the modem/ethernet combo controller.
+ * on machines with a second ohare chip.
+ * The number in the device tree (27) is bogus (correct for
+ * the ethernet-only board but not the combo ethernet/modem
+ * board). The real interrupt is 28 on the second controller
+ * -> 28+32 = 60.
+ */
+ if (has_second_ohare &&
+ dev->vendor == PCI_VENDOR_ID_DEC &&
+ dev->device == PCI_DEVICE_ID_DEC_TULIP_PLUS) {
+ dev->irq = irq_create_mapping(NULL, 60);
+ set_irq_type(dev->irq, IRQ_TYPE_LEVEL_LOW);
}
+#endif /* CONFIG_PPC32 */
}
#ifdef CONFIG_PPC64
Index: linux-cell/arch/powerpc/platforms/powermac/pmac.h
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/powermac/pmac.h 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/powermac/pmac.h 2006-11-06 15:02:21.000000000 +1100
@@ -20,7 +20,7 @@ extern void pmac_get_rtc_time(struct rtc
extern int pmac_set_rtc_time(struct rtc_time *);
extern void pmac_read_rtc_time(void);
extern void pmac_calibrate_decr(void);
-extern void pmac_pcibios_fixup(void);
+extern void pmac_pci_irq_fixup(struct pci_dev *);
extern void pmac_pci_init(void);
extern unsigned long pmac_ide_get_base(int index);
extern void pmac_ide_init_hwif_ports(hw_regs_t *hw,
Index: linux-cell/arch/powerpc/platforms/powermac/setup.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/powermac/setup.c 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/powermac/setup.c 2006-11-06 15:02:21.000000000 +1100
@@ -727,7 +727,7 @@ define_machine(powermac) {
.show_cpuinfo = pmac_show_cpuinfo,
.init_IRQ = pmac_pic_init,
.get_irq = NULL, /* changed later */
- .pcibios_fixup = pmac_pcibios_fixup,
+ .pci_irq_fixup = pmac_pci_irq_fixup,
.restart = pmac_restart,
.power_off = pmac_power_off,
.halt = pmac_halt,
Index: linux-cell/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c 2006-11-06 15:02:21.000000000 +1100
@@ -89,7 +89,7 @@ u8 find_slot_by_devfn(unsigned int *inte
/*
* Scans the interrupt map for pci device
*/
-void mpc7448_hpc2_fixup_irq(struct pci_dev *dev)
+void __devinit mpc7448_hpc2_fixup_irq(struct pci_dev *dev)
{
struct pci_controller *hose;
struct device_node *node;
@@ -117,19 +117,13 @@ void mpc7448_hpc2_fixup_irq(struct pci_d
pin = 1;
pin--;
dev->irq = interrupt[slot*4*7 + pin*7 + 5];
+
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
+
DBG("TSI_PCI: dev->irq = 0x%x\n", dev->irq);
}
/* temporary pci irq map fixup*/
-void __init mpc7448_hpc2_pcibios_fixup(void)
-{
- struct pci_dev *dev = NULL;
- for_each_pci_dev(dev) {
- mpc7448_hpc2_fixup_irq(dev);
- pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
- }
-}
-
static void __init mpc7448_hpc2_setup_arch(void)
{
struct device_node *cpu;
@@ -300,7 +294,7 @@ define_machine(mpc7448_hpc2){
.init_IRQ = mpc7448_hpc2_init_IRQ,
.show_cpuinfo = mpc7448_hpc2_show_cpuinfo,
.get_irq = mpic_get_irq,
- .pcibios_fixup = mpc7448_hpc2_pcibios_fixup,
+ .pci_irq_fixup = mpc7448_hpc2_fixup_irq,
.restart = mpc7448_hpc2_restart,
.calibrate_decr = generic_calibrate_decr,
.machine_check_exception= mpc7448_machine_check_exception,
Index: linux-cell/arch/powerpc/platforms/pasemi/pasemi.h
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/pasemi/pasemi.h 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/pasemi/pasemi.h 2006-11-06 15:02:21.000000000 +1100
@@ -3,6 +3,5 @@
extern unsigned long pas_get_boot_time(void);
extern void pas_pci_init(void);
-extern void pas_pcibios_fixup(void);
#endif /* _PASEMI_PASEMI_H */
Index: linux-cell/arch/powerpc/platforms/pasemi/pci.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/pasemi/pci.c 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/pasemi/pci.c 2006-11-06 15:02:21.000000000 +1100
@@ -148,14 +148,6 @@ static int __init add_bridge(struct devi
}
-void __init pas_pcibios_fixup(void)
-{
- struct pci_dev *dev = NULL;
-
- for_each_pci_dev(dev)
- pci_read_irq_line(dev);
-}
-
static void __init pas_fixup_phb_resources(void)
{
struct pci_controller *hose, *tmp;
Index: linux-cell/arch/powerpc/platforms/pasemi/setup.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/pasemi/setup.c 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/pasemi/setup.c 2006-11-06 15:02:21.000000000 +1100
@@ -176,7 +176,6 @@ define_machine(pas) {
.init_early = pas_init_early,
.init_IRQ = pas_init_IRQ,
.get_irq = mpic_get_irq,
- .pcibios_fixup = pas_pcibios_fixup,
.restart = pas_restart,
.power_off = pas_power_off,
.halt = pas_halt,
Index: linux-cell/arch/powerpc/platforms/82xx/mpc82xx_ads.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/82xx/mpc82xx_ads.c 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/82xx/mpc82xx_ads.c 2006-11-06 15:02:21.000000000 +1100
@@ -515,16 +515,6 @@ static int m82xx_pci_exclude_device(u_ch
return PCIBIOS_SUCCESSFUL;
}
-static void
-__init mpc82xx_pcibios_fixup(void)
-{
- struct pci_dev *dev = NULL;
-
- for_each_pci_dev(dev) {
- pci_read_irq_line(dev);
- }
-}
-
void __init add_bridge(struct device_node *np)
{
int len;
@@ -597,9 +587,6 @@ static void __init mpc82xx_ads_setup_arc
add_bridge(np);
of_node_put(np);
- ppc_md.pci_map_irq = NULL;
- ppc_md.pcibios_fixup = mpc82xx_pcibios_fixup;
- ppc_md.pcibios_fixup_bus = NULL;
#endif
#ifdef CONFIG_ROOT_NFS
Index: linux-cell/arch/powerpc/platforms/83xx/mpc834x_itx.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/83xx/mpc834x_itx.c 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/83xx/mpc834x_itx.c 2006-11-06 15:02:21.000000000 +1100
@@ -122,7 +122,4 @@ define_machine(mpc834x_itx) {
.time_init = mpc83xx_time_init,
.calibrate_decr = generic_calibrate_decr,
.progress = udbg_progress,
-#ifdef CONFIG_PCI
- .pcibios_fixup = mpc83xx_pcibios_fixup,
-#endif
};
Index: linux-cell/arch/powerpc/platforms/83xx/mpc834x_sys.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/83xx/mpc834x_sys.c 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/83xx/mpc834x_sys.c 2006-11-06 15:02:21.000000000 +1100
@@ -137,7 +137,4 @@ define_machine(mpc834x_sys) {
.time_init = mpc83xx_time_init,
.calibrate_decr = generic_calibrate_decr,
.progress = udbg_progress,
-#ifdef CONFIG_PCI
- .pcibios_fixup = mpc83xx_pcibios_fixup,
-#endif
};
Index: linux-cell/arch/powerpc/platforms/83xx/mpc83xx.h
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/83xx/mpc83xx.h 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/83xx/mpc83xx.h 2006-11-06 15:02:21.000000000 +1100
@@ -11,7 +11,6 @@
extern int add_bridge(struct device_node *dev);
extern int mpc83xx_exclude_device(u_char bus, u_char devfn);
-extern void mpc83xx_pcibios_fixup(void);
extern void mpc83xx_restart(char *cmd);
extern long mpc83xx_time_init(void);
Index: linux-cell/arch/powerpc/platforms/83xx/pci.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/83xx/pci.c 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/83xx/pci.c 2006-11-06 15:02:21.000000000 +1100
@@ -45,15 +45,6 @@ int mpc83xx_exclude_device(u_char bus, u
return PCIBIOS_SUCCESSFUL;
}
-void __init mpc83xx_pcibios_fixup(void)
-{
- struct pci_dev *dev = NULL;
-
- /* map all the PCI irqs */
- for_each_pci_dev(dev)
- pci_read_irq_line(dev);
-}
-
int __init add_bridge(struct device_node *dev)
{
int len;
Index: linux-cell/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/86xx/mpc86xx_hpcn.c 2006-11-06 15:02:21.000000000 +1100
@@ -398,15 +398,6 @@ mpc86xx_hpcn_show_cpuinfo(struct seq_fil
}
-void __init mpc86xx_hpcn_pcibios_fixup(void)
-{
- struct pci_dev *dev = NULL;
-
- for_each_pci_dev(dev)
- pci_read_irq_line(dev);
-}
-
-
/*
* Called very early, device-tree isn't unflattened
*/
@@ -461,7 +452,6 @@ define_machine(mpc86xx_hpcn) {
.setup_arch = mpc86xx_hpcn_setup_arch,
.init_IRQ = mpc86xx_hpcn_init_irq,
.show_cpuinfo = mpc86xx_hpcn_show_cpuinfo,
- .pcibios_fixup = mpc86xx_hpcn_pcibios_fixup,
.get_irq = mpic_get_irq,
.restart = mpc86xx_restart,
.time_init = mpc86xx_time_init,
Index: linux-cell/arch/powerpc/platforms/chrp/chrp.h
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/chrp/chrp.h 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/chrp/chrp.h 2006-11-06 15:02:21.000000000 +1100
@@ -9,4 +9,3 @@ extern long chrp_time_init(void);
extern void chrp_find_bridges(void);
extern void chrp_event_scan(unsigned long);
-extern void chrp_pcibios_fixup(void);
Index: linux-cell/arch/powerpc/platforms/chrp/setup.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/chrp/setup.c 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/chrp/setup.c 2006-11-06 15:02:21.000000000 +1100
@@ -600,7 +600,6 @@ define_machine(chrp) {
.init = chrp_init2,
.show_cpuinfo = chrp_show_cpuinfo,
.init_IRQ = chrp_init_IRQ,
- .pcibios_fixup = chrp_pcibios_fixup,
.restart = rtas_restart,
.power_off = rtas_power_off,
.halt = rtas_halt,
Index: linux-cell/arch/powerpc/platforms/85xx/mpc85xx_ads.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/85xx/mpc85xx_ads.c 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/85xx/mpc85xx_ads.c 2006-11-06 15:02:21.000000000 +1100
@@ -53,15 +53,6 @@ mpc85xx_exclude_device(u_char bus, u_cha
else
return PCIBIOS_SUCCESSFUL;
}
-
-void __init
-mpc85xx_pcibios_fixup(void)
-{
- struct pci_dev *dev = NULL;
-
- for_each_pci_dev(dev)
- pci_read_irq_line(dev);
-}
#endif /* CONFIG_PCI */
#ifdef CONFIG_CPM2
@@ -253,8 +244,6 @@ static void __init mpc85xx_ads_setup_arc
#ifdef CONFIG_PCI
for (np = NULL; (np = of_find_node_by_type(np, "pci")) != NULL;)
add_bridge(np);
-
- ppc_md.pcibios_fixup = mpc85xx_pcibios_fixup;
ppc_md.pci_exclude_device = mpc85xx_exclude_device;
#endif
Index: linux-cell/arch/powerpc/platforms/chrp/pci.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/chrp/pci.c 2006-11-06 15:02:16.000000000 +1100
+++ linux-cell/arch/powerpc/platforms/chrp/pci.c 2006-11-06 15:02:21.000000000 +1100
@@ -156,15 +156,6 @@ hydra_init(void)
return 1;
}
-void __init
-chrp_pcibios_fixup(void)
-{
- struct pci_dev *dev = NULL;
-
- for_each_pci_dev(dev)
- pci_read_irq_line(dev);
-}
-
#define PRG_CL_RESET_VALID 0x00010000
static void __init
^ permalink raw reply
* [PATCH 4/31] arch provides generic iomap missing accessors
From: Benjamin Herrenschmidt @ 2006-11-07 7:22 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1162884150.435786.961370643810.qpush@grosgo>
From: Linus Torvalds <torvalds@osdl.org>
Allow architectures to provide their own implementation of the big endian MMIO
accessors and "repeat" MMIO accessors for use by the generic iomap.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
More-or-less-tested-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
lib/iomap.c | 32 ++++++++++++++++++++++++++++----
1 file changed, 28 insertions(+), 4 deletions(-)
Index: linux-cell/lib/iomap.c
===================================================================
--- linux-cell.orig/lib/iomap.c 2006-11-05 16:19:32.000000000 +1100
+++ linux-cell/lib/iomap.c 2006-11-05 16:19:40.000000000 +1100
@@ -50,6 +50,16 @@
} \
} while (0)
+#ifndef pio_read16be
+#define pio_read16be(port) swab16(inw(port))
+#define pio_read32be(port) swab32(inl(port))
+#endif
+
+#ifndef mmio_read16be
+#define mmio_read16be(addr) be16_to_cpu(__raw_readw(addr))
+#define mmio_read32be(addr) be32_to_cpu(__raw_readl(addr))
+#endif
+
unsigned int fastcall ioread8(void __iomem *addr)
{
IO_COND(addr, return inb(port), return readb(addr));
@@ -60,7 +70,7 @@
}
unsigned int fastcall ioread16be(void __iomem *addr)
{
- IO_COND(addr, return inw(port), return be16_to_cpu(__raw_readw(addr)));
+ IO_COND(addr, return pio_read16be(port), return mmio_read16be(addr));
}
unsigned int fastcall ioread32(void __iomem *addr)
{
@@ -68,7 +78,7 @@
}
unsigned int fastcall ioread32be(void __iomem *addr)
{
- IO_COND(addr, return inl(port), return be32_to_cpu(__raw_readl(addr)));
+ IO_COND(addr, return pio_read32be(port), return mmio_read32be(addr));
}
EXPORT_SYMBOL(ioread8);
EXPORT_SYMBOL(ioread16);
@@ -76,6 +86,16 @@
EXPORT_SYMBOL(ioread32);
EXPORT_SYMBOL(ioread32be);
+#ifndef pio_write16be
+#define pio_write16be(val,port) outw(swab16(val),port)
+#define pio_write32be(val,port) outl(swab32(val),port)
+#endif
+
+#ifndef mmio_write16be
+#define mmio_write16be(val,port) __raw_writew(be16_to_cpu(val),port)
+#define mmio_write32be(val,port) __raw_writel(be32_to_cpu(val),port)
+#endif
+
void fastcall iowrite8(u8 val, void __iomem *addr)
{
IO_COND(addr, outb(val,port), writeb(val, addr));
@@ -86,7 +106,7 @@
}
void fastcall iowrite16be(u16 val, void __iomem *addr)
{
- IO_COND(addr, outw(val,port), __raw_writew(cpu_to_be16(val), addr));
+ IO_COND(addr, pio_write16be(val,port), mmio_write16be(val, addr));
}
void fastcall iowrite32(u32 val, void __iomem *addr)
{
@@ -94,7 +114,7 @@
}
void fastcall iowrite32be(u32 val, void __iomem *addr)
{
- IO_COND(addr, outl(val,port), __raw_writel(cpu_to_be32(val), addr));
+ IO_COND(addr, pio_write32be(val,port), mmio_write32be(val, addr));
}
EXPORT_SYMBOL(iowrite8);
EXPORT_SYMBOL(iowrite16);
@@ -108,6 +128,7 @@
* convert to CPU byte order. We write in "IO byte
* order" (we also don't have IO barriers).
*/
+#ifndef mmio_insb
static inline void mmio_insb(void __iomem *addr, u8 *dst, int count)
{
while (--count >= 0) {
@@ -132,7 +153,9 @@
dst++;
}
}
+#endif
+#ifndef mmio_outsb
static inline void mmio_outsb(void __iomem *addr, const u8 *src, int count)
{
while (--count >= 0) {
@@ -154,6 +177,7 @@
src++;
}
}
+#endif
void fastcall ioread8_rep(void __iomem *addr, void *dst, unsigned long count)
{
^ permalink raw reply
* [PATCH 3/31] Driver core: add notification of bus events
From: Benjamin Herrenschmidt @ 2006-11-07 7:22 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1162884150.435786.961370643810.qpush@grosgo>
I finally did as you suggested and added the notifier to the struct
bus_type itself. There are still problems to be expected is something
attaches to a bus type where the code can hook in different struct
device sub-classes (which is imho a big bogosity but I won't even try to
argue that case now) but it will solve nicely a number of issues I've
had so far.
That also means that clients interested in registering for such
notifications have to do it before devices are added and after bus types
are registered. Fortunately, most bus types that matter for the various
usage scenarios I have in mind are registerd at postcore_initcall time,
which means I have a really nice spot at arch_initcall time to add my
notifiers.
There are 4 notifications provided. Device being added (before hooked to
the bus) and removed (failure of previous case or after being unhooked
from the bus), along with driver being bound to a device and about to be
unbound.
The usage I have for these are:
- The 2 first ones are used to maintain a struct device_ext that is
hooked to struct device.firmware_data. This structure contains for now a
pointer to the Open Firmware node related to the device (if any), the
NUMA node ID (for quick access to it) and the DMA operations pointers &
iommu table instance for DMA to/from this device. For bus types I own
(like IBM VIO or EBUS), I just maintain that structure directly from the
bus code when creating the devices. But for bus types managed by generic
code like PCI or platform (actually, of_platform which is a variation of
platform linked to Open Firmware device-tree), I need this notifier.
- The other two ones have a completely different usage scenario. I have
cases where multiple devices and their drivers depend on each other. For
example, the IBM EMAC network driver needs to attach to a MAL DMA engine
which is a separate device, and a PHY interface which is also a separate
device. They are all of_platform_device's (well, about to be with my
upcoming patches) but there is no say in what precise order the core
will "probe" them and instanciate the various modules. The solution I
found for that is to have the drivers for emac to use multithread_probe,
and wait for a driver to be bound to the target MAL and PHY control
devices (the device-tree contains reference to the MAL and PHY interface
nodes, which I can then match to of_platform_devices). Right now, I've
been polling, but with that notifier, I can more cleanly wait (with a
timeout of course).
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
This patch is already in Greg KH tree under the name
driver-core-add-notification-of-bus-events.patch
drivers/base/bus.c | 15 +++++++++++++++
drivers/base/core.c | 12 ++++++++++++
drivers/base/dd.c | 10 ++++++++++
include/linux/device.h | 25 ++++++++++++++++++++++++-
4 files changed, 61 insertions(+), 1 deletion(-)
Index: linux-cell/drivers/base/core.c
===================================================================
--- linux-cell.orig/drivers/base/core.c 2006-11-02 13:20:21.000000000 +1100
+++ linux-cell/drivers/base/core.c 2006-11-02 13:20:24.000000000 +1100
@@ -17,6 +17,7 @@
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/kdev_t.h>
+#include <linux/notifier.h>
#include <asm/semaphore.h>
@@ -428,6 +429,11 @@ int device_add(struct device *dev)
if (platform_notify)
platform_notify(dev);
+ /* notify clients of device entry (new way) */
+ if (dev->bus)
+ blocking_notifier_call_chain(&dev->bus->bus_notifier,
+ BUS_NOTIFY_ADD_DEVICE, dev);
+
dev->uevent_attr.attr.name = "uevent";
dev->uevent_attr.attr.mode = S_IWUSR;
if (dev->driver)
@@ -504,6 +510,9 @@ int device_add(struct device *dev)
BusError:
device_pm_remove(dev);
PMError:
+ if (dev->bus)
+ blocking_notifier_call_chain(&dev->bus->bus_notifier,
+ BUS_NOTIFY_DEL_DEVICE, dev);
device_remove_groups(dev);
GroupError:
device_remove_attrs(dev);
@@ -622,6 +631,9 @@ void device_del(struct device * dev)
*/
if (platform_notify_remove)
platform_notify_remove(dev);
+ if (dev->bus)
+ blocking_notifier_call_chain(&dev->bus->bus_notifier,
+ BUS_NOTIFY_DEL_DEVICE, dev);
device_pm_remove(dev);
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
kobject_del(&dev->kobj);
Index: linux-cell/include/linux/device.h
===================================================================
--- linux-cell.orig/include/linux/device.h 2006-10-25 12:50:17.000000000 +1000
+++ linux-cell/include/linux/device.h 2006-11-02 13:20:24.000000000 +1100
@@ -42,6 +42,8 @@ struct bus_type {
struct klist klist_devices;
struct klist klist_drivers;
+ struct blocking_notifier_head bus_notifier;
+
struct bus_attribute * bus_attrs;
struct device_attribute * dev_attrs;
struct driver_attribute * drv_attrs;
@@ -75,6 +77,28 @@ int __must_check bus_for_each_drv(struct
struct device_driver *start, void *data,
int (*fn)(struct device_driver *, void *));
+/* Bus notifiers: Get notified of addition/removal of devices
+ * and binding/unbinding of drivers to devices.
+ * In the long run, it should be a replacement for the platform
+ * notify hooks.
+ */
+struct notifier_block;
+
+extern int register_bus_notifier(struct bus_type *bus,
+ struct notifier_block *nb);
+extern int unregister_bus_notifier(struct bus_type *bus,
+ struct notifier_block *nb);
+
+/* All 4 notifers below get called with the target struct device *
+ * as an argument. Note that those functions are likely to be called
+ * with the device semaphore held in the core, so be careful.
+ */
+#define BUS_NOTIFY_ADD_DEVICE 0x00000001 /* device added */
+#define BUS_NOTIFY_DEL_DEVICE 0x00000002 /* device removed */
+#define BUS_NOTIFY_BOUND_DRIVER 0x00000003 /* driver bound to device */
+#define BUS_NOTIFY_UNBIND_DRIVER 0x00000004 /* driver about to be
+ unbound */
+
/* driverfs interface for exporting bus attributes */
struct bus_attribute {
@@ -427,7 +451,6 @@ extern int (*platform_notify)(struct dev
extern int (*platform_notify_remove)(struct device * dev);
-
/**
* get_device - atomically increment the reference count for the device.
*
Index: linux-cell/drivers/base/bus.c
===================================================================
--- linux-cell.orig/drivers/base/bus.c 2006-10-23 14:41:37.000000000 +1000
+++ linux-cell/drivers/base/bus.c 2006-11-02 13:20:24.000000000 +1100
@@ -724,6 +724,8 @@ int bus_register(struct bus_type * bus)
{
int retval;
+ BLOCKING_INIT_NOTIFIER_HEAD(&bus->bus_notifier);
+
retval = kobject_set_name(&bus->subsys.kset.kobj, "%s", bus->name);
if (retval)
goto out;
@@ -782,6 +784,16 @@ void bus_unregister(struct bus_type * bu
subsystem_unregister(&bus->subsys);
}
+int register_bus_notifier(struct bus_type *bus, struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&bus->bus_notifier, nb);
+}
+
+int unregister_bus_notifier(struct bus_type *bus, struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&bus->bus_notifier, nb);
+}
+
int __init buses_init(void)
{
return subsystem_register(&bus_subsys);
@@ -798,3 +810,6 @@ EXPORT_SYMBOL_GPL(bus_rescan_devices);
EXPORT_SYMBOL_GPL(bus_create_file);
EXPORT_SYMBOL_GPL(bus_remove_file);
+
+EXPORT_SYMBOL_GPL(register_bus_notifier);
+EXPORT_SYMBOL_GPL(unregister_bus_notifier);
Index: linux-cell/drivers/base/dd.c
===================================================================
--- linux-cell.orig/drivers/base/dd.c 2006-10-30 08:00:39.000000000 +1100
+++ linux-cell/drivers/base/dd.c 2006-11-02 13:20:24.000000000 +1100
@@ -52,6 +52,11 @@ int device_bind_driver(struct device *de
pr_debug("bound device '%s' to driver '%s'\n",
dev->bus_id, dev->driver->name);
+
+ if (dev->bus)
+ blocking_notifier_call_chain(&dev->bus->bus_notifier,
+ BUS_NOTIFY_BOUND_DRIVER, dev);
+
klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices);
ret = sysfs_create_link(&dev->driver->kobj, &dev->kobj,
kobject_name(&dev->kobj));
@@ -288,6 +293,11 @@ static void __device_release_driver(stru
sysfs_remove_link(&dev->kobj, "driver");
klist_remove(&dev->knode_driver);
+ if (dev->bus)
+ blocking_notifier_call_chain(&dev->bus->bus_notifier,
+ BUS_NOTIFY_UNBIND_DRIVER,
+ dev);
+
if (dev->bus && dev->bus->remove)
dev->bus->remove(dev);
else if (drv->remove)
^ permalink raw reply
* [PATCH 2/31] Call platform_notify_remove later
From: Benjamin Herrenschmidt @ 2006-11-07 7:22 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1162884150.435786.961370643810.qpush@grosgo>
Move the call to platform_notify_remove() to after the call to
bus_remove_device(), where it belongs. It's bogus to notify the platform
of removal while drivers are still attached to the device and possibly
still operating since the platform might use this callback to tear down
some resources used by the driver (ACPI bits, iommu table, ...)
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: "Brown, Len" <len.brown@intel.com>
Cc: Greg KH <greg@kroah.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---
This patch is already in -mm under the name
call-platform_notify_remove-later.patch
drivers/base/core.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
Index: linux-cell/drivers/base/core.c
===================================================================
--- linux-cell.orig/drivers/base/core.c 2006-10-25 13:04:40.000000000 +1000
+++ linux-cell/drivers/base/core.c 2006-10-25 13:04:56.000000000 +1000
@@ -615,12 +615,13 @@ void device_del(struct device * dev)
device_remove_groups(dev);
device_remove_attrs(dev);
+ bus_remove_device(dev);
+
/* Notify the platform of the removal, in case they
* need to do anything...
*/
if (platform_notify_remove)
platform_notify_remove(dev);
- bus_remove_device(dev);
device_pm_remove(dev);
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
kobject_del(&dev->kobj);
^ permalink raw reply
* [PATCH 1/31] ibmveth: Remove ibmveth "liobn" field
From: Benjamin Herrenschmidt @ 2006-11-07 7:22 UTC (permalink / raw)
To: linuxppc-dev
In-Reply-To: <1162884150.435786.961370643810.qpush@grosgo>
Remove the now unused "liobn" field in ibmveth which also avoids
having insider knowledge of the iommu table in that driver.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Santiago Leon <santil@us.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
---
Already in Jeff's queue.
drivers/net/ibmveth.c | 4 ----
drivers/net/ibmveth.h | 1 -
2 files changed, 5 deletions(-)
Index: linux-cell/drivers/net/ibmveth.c
===================================================================
--- linux-cell.orig/drivers/net/ibmveth.c 2006-10-23 14:41:38.000000000 +1000
+++ linux-cell/drivers/net/ibmveth.c 2006-10-25 14:12:05.000000000 +1000
@@ -50,7 +50,6 @@
#include <asm/semaphore.h>
#include <asm/hvcall.h>
#include <asm/atomic.h>
-#include <asm/iommu.h>
#include <asm/vio.h>
#include <asm/uaccess.h>
#include <linux/seq_file.h>
@@ -1000,8 +999,6 @@ static int __devinit ibmveth_probe(struc
adapter->mac_addr = 0;
memcpy(&adapter->mac_addr, mac_addr_p, 6);
- adapter->liobn = dev->iommu_table->it_index;
-
netdev->irq = dev->irq;
netdev->open = ibmveth_open;
netdev->poll = ibmveth_poll;
@@ -1115,7 +1112,6 @@ static int ibmveth_seq_show(struct seq_f
seq_printf(seq, "%s %s\n\n", ibmveth_driver_string, ibmveth_driver_version);
seq_printf(seq, "Unit Address: 0x%x\n", adapter->vdev->unit_address);
- seq_printf(seq, "LIOBN: 0x%lx\n", adapter->liobn);
seq_printf(seq, "Current MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
current_mac[0], current_mac[1], current_mac[2],
current_mac[3], current_mac[4], current_mac[5]);
Index: linux-cell/drivers/net/ibmveth.h
===================================================================
--- linux-cell.orig/drivers/net/ibmveth.h 2006-10-06 13:48:09.000000000 +1000
+++ linux-cell/drivers/net/ibmveth.h 2006-10-25 14:10:39.000000000 +1000
@@ -118,7 +118,6 @@ struct ibmveth_adapter {
struct net_device_stats stats;
unsigned int mcastFilterSize;
unsigned long mac_addr;
- unsigned long liobn;
void * buffer_list_addr;
void * filter_list_addr;
dma_addr_t buffer_list_dma;
^ permalink raw reply
* [PATCH 0/31] My current serie of patches for 2.6.20 for review
From: Benjamin Herrenschmidt @ 2006-11-07 7:22 UTC (permalink / raw)
To: linuxppc-dev
[ I'm re-posting it today as there have been a number of significant
changes. I expect that unless I get a lot of feedback, however,
your mailboxes will be safe from such another flood for the next
few days as I'll be working on a couple of other things I have
pending and I'm fairly happy with the patches in their current
shape
]
This is the whole of my current patch set that I intend to merge
as soon as 2.6.20 merge window opens. I'm posting them here for
review, some of them already had a few iterations on the list by
their own, some didn't show up publically at all yet.
Note that the first 4 ones require specific comments:
- powerpc-ibmveth-remove-liobn.diff should already be queued with
Jeff Garzik
- platform-notify-remove-change.diff
- device-notifier.diff should both be already queued with Greg KH
- iomap-arch-accessors.diff was written by Linus but is not merged
yet. He asked me to resend it when needed, so I'll include it with
the other patches when the merge window opens.
This patch set started its life as the support for the new "Axon"
southbridge for cell (which includes such things as MMIO mapped DCR
and 4xx-type devices). However, as things evolved, I ended up doing
a lot more than just the basic support. Among other highlights of
this patch serie are:
- Generic DCR support for both 4xx-type DCRs and MMIO mapped DCR and
MPIC & EMAC changes to work with that
- Souped up of_platform_driver to make it easier to register entire
trees of devices at once, along with PCI host bridge detection support
for 64 bits architectures. Note that some name changes might break
compile of some platforms. I hope to have fixed everything before I
merge but don't forget to tell me if I missed something.
- Completely reworked 64 bits DMA operations
- Possibility to "hook" on PCI MMIO and PCI operations from the platform
code for use by iSeries, Cell and possibly others who have to deal with
weird hypervisor or PCI host bridge erratas. Currently supported only on
64 bits but would be easy to make it work on 32 bits if ever needed.
- Merged 32 and 64 bits io.h. ARCH=powerpc now uses asm-powerpc/io.h
for both. ARCH=ppc still uses the old one in asm-ppc as I really don't
feel like dealing with some of the cruft in there.
- As a consequence of the 2 above, new accessors are available that match
what is done by other architectures: {read,write}{w,l,q}_be for big endian
PCI MMIO accesses, {read,write}s{b,w,l} for MMIO "repeat" operations, and
a significant cleanup of the EEH stuff.
- Cell IOMMU support (mostly written by Jeremy Kerr)
^ permalink raw reply
* [RFC/PATCH 7/7] Enable MSI on Powerpc
From: Michael Ellerman @ 2006-11-07 7:21 UTC (permalink / raw)
To: linuxppc-dev
Cc: Eric W. Biederman, Greg Kroah-Hartman, linux-pci, David S. Miller
In-Reply-To: <1162884080.585336.70559261997.qpush@cradle>
Allow PCI_MSI to build on Powerpc. Hook up a few platforms to use
the appropriate MSI backend.
We still need CONFIG_POWERPC.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
---
arch/powerpc/kernel/Makefile | 6 ++++++
arch/powerpc/platforms/maple/setup.c | 3 +++
arch/powerpc/platforms/powermac/pic.c | 3 +++
arch/powerpc/platforms/pseries/setup.c | 3 +++
drivers/pci/Kconfig | 2 +-
drivers/pci/Makefile | 4 +++-
6 files changed, 19 insertions(+), 2 deletions(-)
Index: msi/arch/powerpc/kernel/Makefile
===================================================================
--- msi.orig/arch/powerpc/kernel/Makefile
+++ msi/arch/powerpc/kernel/Makefile
@@ -66,6 +66,12 @@ pci64-$(CONFIG_PPC64) += pci_64.o pci_d
pci_direct_iommu.o iomap.o
pci32-$(CONFIG_PPC32) := pci_32.o
obj-$(CONFIG_PCI) += $(pci64-y) $(pci32-y)
+
+msiobj-y := msi.o
+msiobj-$(CONFIG_PPC_RTAS) += msi-rtas.o
+msiobj-$(CONFIG_MPIC) += msi-mpic.o
+obj-$(CONFIG_PCI_MSI) += $(msiobj-y)
+
kexec-$(CONFIG_PPC64) := machine_kexec_64.o
kexec-$(CONFIG_PPC32) := machine_kexec_32.o
obj-$(CONFIG_KEXEC) += machine_kexec.o crash.o $(kexec-y)
Index: msi/arch/powerpc/platforms/maple/setup.c
===================================================================
--- msi.orig/arch/powerpc/platforms/maple/setup.c
+++ msi/arch/powerpc/platforms/maple/setup.c
@@ -61,6 +61,7 @@
#include <asm/lmb.h>
#include <asm/mpic.h>
#include <asm/udbg.h>
+#include <asm/msi.h>
#include "maple.h"
@@ -275,6 +276,8 @@ static void __init maple_init_IRQ(void)
ppc_md.get_irq = mpic_get_irq;
of_node_put(mpic_node);
of_node_put(root);
+
+ msi_mpic_init();
}
static void __init maple_progress(char *s, unsigned short hex)
Index: msi/arch/powerpc/platforms/powermac/pic.c
===================================================================
--- msi.orig/arch/powerpc/platforms/powermac/pic.c
+++ msi/arch/powerpc/platforms/powermac/pic.c
@@ -34,6 +34,7 @@
#include <asm/time.h>
#include <asm/pmac_feature.h>
#include <asm/mpic.h>
+#include <asm/msi.h>
#include "pmac.h"
@@ -562,6 +563,8 @@ static int __init pmac_pic_probe_mpic(vo
set_irq_data(cascade, mpic2);
set_irq_chained_handler(cascade, pmac_u3_cascade);
+ msi_mpic_init();
+
of_node_put(slave);
return 0;
}
Index: msi/arch/powerpc/platforms/pseries/setup.c
===================================================================
--- msi.orig/arch/powerpc/platforms/pseries/setup.c
+++ msi/arch/powerpc/platforms/pseries/setup.c
@@ -65,6 +65,7 @@
#include <asm/i8259.h>
#include <asm/udbg.h>
#include <asm/smp.h>
+#include <asm/msi.h>
#include "plpar_wrappers.h"
#include "ras.h"
@@ -275,6 +276,7 @@ static void __init pseries_discover_pic(
#ifdef CONFIG_SMP
smp_init_pseries_mpic();
#endif
+ msi_mpic_init();
return;
} else if (strstr(typep, "ppc-xicp")) {
ppc_md.init_IRQ = xics_init_IRQ;
@@ -284,6 +286,7 @@ static void __init pseries_discover_pic(
#ifdef CONFIG_SMP
smp_init_pseries_xics();
#endif
+ msi_rtas_init();
return;
}
}
Index: msi/drivers/pci/Kconfig
===================================================================
--- msi.orig/drivers/pci/Kconfig
+++ msi/drivers/pci/Kconfig
@@ -4,7 +4,7 @@
config PCI_MSI
bool "Message Signaled Interrupts (MSI and MSI-X)"
depends on PCI
- depends on (X86_LOCAL_APIC && X86_IO_APIC) || IA64
+ depends on (X86_LOCAL_APIC && X86_IO_APIC) || IA64 || PPC_MERGE
help
This allows device drivers to enable MSI (Message Signaled
Interrupts). Message Signaled Interrupts enable a device to
Index: msi/drivers/pci/Makefile
===================================================================
--- msi.orig/drivers/pci/Makefile
+++ msi/drivers/pci/Makefile
@@ -14,8 +14,10 @@ obj-$(CONFIG_HOTPLUG) += hotplug.o
# Build the PCI Hotplug drivers if we were asked to
obj-$(CONFIG_HOTPLUG_PCI) += hotplug/
-# Build the PCI MSI interrupt support
+# Build the PCI MSI interrupt support, but not for arch/powerpc
+ifndef CONFIG_PPC_MERGE
obj-$(CONFIG_PCI_MSI) += msi.o
+endif
# Build the Hypertransport interrupt support
obj-$(CONFIG_HT_IRQ) += htirq.o
^ permalink raw reply
* [RFC/PATCH 6/7] MPIC MSI backend
From: Michael Ellerman @ 2006-11-07 7:21 UTC (permalink / raw)
To: linuxppc-dev
Cc: Eric W. Biederman, Greg Kroah-Hartman, linux-pci, David S. Miller
In-Reply-To: <1162884080.585336.70559261997.qpush@cradle>
MPIC MSI backend. Based on code from Segher, heavily hacked by me.
This version discovers the magic address by reading the config space.
Still slightly hacky in that we reuse pdev->irq, pending an irq
allocator for MPIC.
Tested, succesfully getting MSIs from the tg3 via HT/PCI-X on a JS21
running SLOF.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
---
arch/powerpc/kernel/msi-mpic.c | 138 +++++++++++++++++++++++++++++++++++++++++
include/asm-powerpc/msi.h | 2
2 files changed, 140 insertions(+)
Index: msi/arch/powerpc/kernel/msi-mpic.c
===================================================================
--- /dev/null
+++ msi/arch/powerpc/kernel/msi-mpic.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2006 Segher Boessenkool, IBM Corp.
+ * Copyright (C) 2006 Michael Ellerman, IBM Corp.
+ *
+ * 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; version 2 of the
+ * License.
+ *
+ */
+
+#undef DEBUG
+
+#include <linux/irq.h>
+#include <asm/msi.h>
+#include <asm/hw_irq.h>
+#include <asm/ppc-pci.h>
+
+static unsigned int find_ht_msi_capability(struct pci_dev *pdev)
+{
+ u8 subcap;
+ unsigned int pos = pci_find_capability(pdev, PCI_CAP_ID_HT);
+
+ while (pos) {
+ pci_read_config_byte(pdev, pos + HT_SUBCAP_OFFSET, &subcap);
+ if (subcap == HT_CAPTYPE_MSI_MAPPING)
+ return pos;
+ pos = pci_find_next_capability(pdev, pos, PCI_CAP_ID_HT);
+ }
+
+ return 0;
+}
+
+static u64 read_ht_magic_addr(struct pci_dev *pdev, unsigned int pos)
+{
+ u8 flags;
+ u32 tmp;
+ u64 addr;
+
+ pci_read_config_byte(pdev, pos + HT_MSI_FLAGS, &flags);
+
+ if (flags & HT_MSI_FLAGS_FIXED)
+ return HT_MSI_FIXED_ADDR;
+
+ pci_read_config_dword(pdev, pos + HT_MSI_ADDR_LO, &tmp);
+ addr = tmp & HT_MSI_ADDR_LO_MASK;
+ pci_read_config_dword(pdev, pos + HT_MSI_ADDR_HI, &tmp);
+ addr = addr | ((u64)tmp << 32);
+
+ return addr;
+}
+
+static u64 find_ht_magic_addr(struct pci_dev *pdev)
+{
+ struct pci_bus *bus;
+ unsigned int pos;
+
+ for (bus = pdev->bus; bus; bus = bus->parent) {
+ pos = find_ht_msi_capability(bus->self);
+ if (pos)
+ return read_ht_magic_addr(bus->self, pos);
+ }
+
+ return 0;
+}
+
+static int msi_mpic_check(struct pci_dev *pdev, int num,
+ struct msix_entry *entries, int type)
+{
+ /* We need an irq allocator before we can support multi-MSI & MSI-X */
+ if (num != 1 || type == PCI_CAP_ID_MSIX) {
+ msi_debug("precondition failure for %s\n", pci_name(pdev));
+ return 1;
+ }
+
+ /* If we can't find a magic address then MSI ain't gonna work */
+ if (find_ht_magic_addr(pdev) == 0) {
+ msi_debug("no magic address found for %s\n", pci_name(pdev));
+ return 1;
+ }
+
+ return 0;
+}
+
+static void msi_mpic_free(struct pci_dev *pdev, int num,
+ struct msix_entry *entries, int type)
+{
+ /* We borrowed the LSI irq, so don't unmap it! */
+ return;
+}
+
+static int msi_mpic_alloc(struct pci_dev *pdev, int num,
+ struct msix_entry *entries, int type)
+{
+ /* For now we reuse the assigned LSI, this is a hack. */
+ set_irq_type(pdev->irq, IRQ_TYPE_EDGE_RISING);
+ entries[0].vector = pdev->irq;
+
+ return 0;
+}
+
+static int msi_mpic_setup_msi_msg(struct pci_dev *pdev,
+ struct msix_entry *entry, struct msi_msg *msg, int type)
+{
+ u64 addr;
+
+ addr = find_ht_magic_addr(pdev);
+ msg->address_lo = addr & 0xFFFFFFFF;
+ msg->address_hi = addr >> 32;
+ msg->data = irq_map[pdev->irq].hwirq;
+
+ msi_debug("allocated irq %d at 0x%lx for %s\n", pdev->irq,
+ addr, pci_name(pdev));
+
+ return 0;
+}
+
+static struct ppc_msi_ops mpic_msi_ops = {
+ .check = msi_mpic_check,
+ .alloc = msi_mpic_alloc,
+ .free = msi_mpic_free,
+ .enable = msi_raw_enable,
+ .disable = msi_raw_disable,
+ .setup_msi_msg = msi_mpic_setup_msi_msg,
+};
+
+static struct ppc_msi_ops *mpic_get_msi_ops(struct pci_dev *pdev)
+{
+ return &mpic_msi_ops;
+}
+
+int msi_mpic_init(void)
+{
+ pr_debug("mpic_msi_init: Registering MPIC MSI ops.\n");
+ ppc_md.get_msi_ops = mpic_get_msi_ops;
+
+ return 0;
+}
Index: msi/include/asm-powerpc/msi.h
===================================================================
--- msi.orig/include/asm-powerpc/msi.h
+++ msi/include/asm-powerpc/msi.h
@@ -170,8 +170,10 @@ extern void msi_raw_disable(struct pci_d
#ifdef CONFIG_PCI_MSI
extern int msi_rtas_init(void);
+extern int msi_mpic_init(void);
#else
static inline int msi_rtas_init(void) { return -1; };
+static inline int msi_mpic_init(void) { return -1; };
#endif
#endif /* __ASSEMBLY__ */
^ permalink raw reply
* [RFC/PATCH 5/7] RTAS MSI implementation
From: Michael Ellerman @ 2006-11-07 7:21 UTC (permalink / raw)
To: linuxppc-dev
Cc: Eric W. Biederman, Greg Kroah-Hartman, linux-pci, David S. Miller
In-Reply-To: <1162884080.585336.70559261997.qpush@cradle>
Powerpc MSI support via RTAS. Based on Jake's code.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
---
arch/powerpc/kernel/msi-rtas.c | 265 +++++++++++++++++++++++++++++++++++++++++
include/asm-powerpc/msi.h | 6
2 files changed, 271 insertions(+)
Index: msi/arch/powerpc/kernel/msi-rtas.c
===================================================================
--- /dev/null
+++ msi/arch/powerpc/kernel/msi-rtas.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2006 Jake Moilanen <moilanen@austin.ibm.com>, IBM Corp.
+ * Copyright (C) 2006 Michael Ellerman, IBM Corp.
+ *
+ * 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; version 2 of the
+ * License.
+ *
+ */
+
+#undef DEBUG
+
+#include <linux/irq.h>
+#include <asm/msi.h>
+#include <asm/rtas.h>
+#include <asm/hw_irq.h>
+#include <asm/ppc-pci.h>
+
+static int query_token, change_token;
+
+#define RTAS_QUERY_MSI_FN 0
+#define RTAS_CHANGE_MSI_FN 1
+#define RTAS_RESET_MSI_FN 2
+
+
+static struct pci_dn *get_pdn(struct pci_dev *pdev)
+{
+ struct device_node *dn;
+ struct pci_dn *pdn;
+
+ dn = pci_device_to_OF_node(pdev);
+ if (!dn) {
+ msi_debug("No OF device node for %s\n", pci_name(pdev));
+ return NULL;
+ }
+
+ pdn = PCI_DN(dn);
+ if (!pdn) {
+ msi_debug("No PCI DN for %s\n", pci_name(pdev));
+ return NULL;
+ }
+
+ return pdn;
+}
+
+/* RTAS Helpers */
+
+static int rtas_change_msi(struct pci_dn *pdn, u32 function, u32 num_irqs)
+{
+ u32 addr, seq_num, rtas_ret[2];
+ unsigned long buid;
+ int rc;
+
+ addr = rtas_config_addr(pdn->busno, pdn->devfn, 0);
+ buid = pdn->phb->buid;
+
+ seq_num = 1;
+ do {
+ rc = rtas_call(change_token, 6, 3, rtas_ret, addr,
+ BUID_HI(buid), BUID_LO(buid),
+ function, num_irqs, seq_num);
+
+ seq_num = rtas_ret[1];
+ } while (rtas_busy_delay(rc));
+
+ if (rc) {
+ msi_debug("error (%d) for %s\n", rc, pci_name(pdn->pcidev));
+ return rc;
+ }
+
+ return rtas_ret[0];
+}
+
+static int rtas_query_irq_number(struct pci_dn *pdn, int offset)
+{
+ u32 addr, rtas_ret[2];
+ unsigned long buid;
+ int rc;
+
+ addr = rtas_config_addr(pdn->busno, pdn->devfn, 0);
+ buid = pdn->phb->buid;
+
+ do {
+ rc = rtas_call(query_token, 4, 3, rtas_ret, addr,
+ BUID_HI(buid), BUID_LO(buid), offset);
+ } while (rtas_busy_delay(rc));
+
+ if (rc) {
+ msi_debug("error (%d) querying source number for %s\n",
+ rc, pci_name(pdn->pcidev));
+ return rc;
+ }
+
+ return rtas_ret[0];
+}
+
+/*
+ * The spec gives firmware the option to enable either MSI or MSI-X,
+ * this doesn't wash with the Linux API. For the time beinging, we
+ * kludge around that by checking ourselves the right type is enabled.
+ */
+static int check_msi_type(struct pci_dev *pdev, int type)
+{
+ int pos, msi_enabled, msix_enabled;
+ u16 reg;
+
+ pos = pci_find_capability(pdev, PCI_CAP_ID_MSI);
+ if (!pos) {
+ msi_debug("cap (%d) not found for %s\n", type, pci_name(pdev));
+ return -1;
+ }
+
+ pci_read_config_word(pdev, pos + PCI_MSI_FLAGS, ®);
+
+ msi_enabled = msix_enabled = 0;
+
+ if (reg & PCI_MSI_FLAGS_ENABLE)
+ msi_enabled = 1;
+
+ if (reg & PCI_MSIX_FLAGS_ENABLE)
+ msix_enabled = 1;
+
+ if (type == PCI_CAP_ID_MSI && (msix_enabled || !msi_enabled)) {
+ msi_debug("Expected MSI but got %s.\n",
+ msix_enabled ? "MSI-X" : "none");
+ return -1;
+ }
+
+ if (type == PCI_CAP_ID_MSIX && (msi_enabled || !msix_enabled)) {
+ msi_debug("Expected MSI-X but got %s.\n",
+ msi_enabled ? "MSI" : "none");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void msi_rtas_free(struct pci_dev *pdev, int num,
+ struct msix_entry *entries, int type)
+{
+ struct pci_dn *pdn;
+ int i;
+
+ pdn = get_pdn(pdev);
+ if (!pdn)
+ return;
+
+ for (i = 0; i < num; i++) {
+ irq_dispose_mapping(entries[i].vector);
+ }
+
+ if (rtas_change_msi(pdn, RTAS_CHANGE_MSI_FN, 0) != 0) {
+ msi_debug("Setting MSIs to 0 failed!\n");
+ BUG();
+ }
+}
+
+static int msi_rtas_check(struct pci_dev *pdev, int num,
+ struct msix_entry *entries, int type)
+{
+ struct device_node *dn;
+ int i;
+
+ /* We must have a pci_dn for the RTAS code. */
+ if (!get_pdn(pdev))
+ return -1;
+
+ dn = pci_device_to_OF_node(pdev);
+ if (!of_find_property(dn, "ibm,req#msi", NULL)) {
+ msi_debug("No ibm,req#msi for %s\n", pci_name(pdev));
+ return -1;
+ }
+
+ /*
+ * Firmware gives us no control over which entries are allocated
+ * for MSI-X, it seems to assume we want 0 - n. For now just insist
+ * that the entries array entry members are 0 - n.
+ */
+ for (i = 0; i < num; i++) {
+ if (entries[i].entry != i) {
+ msi_debug("entries[%d].entry (%d) != %d\n", i
+ entries[i].entry, i);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int msi_rtas_alloc(struct pci_dev *pdev, int num,
+ struct msix_entry *entries, int type)
+{
+ struct pci_dn *pdn;
+ int hwirq, virq, i;
+
+ pdn = get_pdn(pdev);
+ if (!pdn)
+ return -1;
+
+ /*
+ * In the case of an error it's not clear whether the device is left
+ * with MSI enabled or not, I think we should explicitly disable.
+ */
+ if (rtas_change_msi(pdn, RTAS_CHANGE_MSI_FN, num) != num) {
+ msi_debug("rtas_change_msi() failed for %s\n", pci_name(pdev));
+ goto out_free;
+ }
+
+ if (check_msi_type(pdev, type))
+ goto out_free;
+
+ for (i = 0; i < num; i++) {
+ hwirq = rtas_query_irq_number(pdn, i);
+ if (hwirq < 0) {
+ msi_debug("error (%d) getting hwirq for %s\n",
+ hwirq, pci_name(pdev));
+ goto out_free;
+ }
+
+ virq = irq_create_mapping(NULL, hwirq);
+
+ if (virq == NO_IRQ) {
+ msi_debug("Failed mapping hwirq %d\n", hwirq);
+ goto out_free;
+ }
+
+ entries[i].vector = virq;
+ }
+
+ return 0;
+
+ out_free:
+ msi_rtas_free(pdev, num, entries, type);
+ return -1;
+}
+
+static struct ppc_msi_ops rtas_msi_ops = {
+ .check = msi_rtas_check,
+ .alloc = msi_rtas_alloc,
+ .free = msi_rtas_free
+};
+
+static struct ppc_msi_ops *rtas_get_msi_ops(struct pci_dev *pdev)
+{
+ return &rtas_msi_ops;
+}
+
+int msi_rtas_init(void)
+{
+ query_token = rtas_token("ibm,query-interrupt-source-number");
+ change_token = rtas_token("ibm,change-msi");
+
+ if ((query_token == RTAS_UNKNOWN_SERVICE) ||
+ (change_token == RTAS_UNKNOWN_SERVICE)) {
+ msi_debug("Couldn't find RTAS tokens, no MSI support.\n");
+ return -1;
+ }
+
+ msi_debug("Registering RTAS MSI ops.\n");
+
+ ppc_md.get_msi_ops = rtas_get_msi_ops;
+
+ return 0;
+}
Index: msi/include/asm-powerpc/msi.h
===================================================================
--- msi.orig/include/asm-powerpc/msi.h
+++ msi/include/asm-powerpc/msi.h
@@ -168,6 +168,12 @@ extern void msi_raw_disable(struct pci_d
#define msi_debug(fmt, args...) \
pr_debug("MSI:%s:%d: " fmt, __FUNCTION__, __LINE__, ## args)
+#ifdef CONFIG_PCI_MSI
+extern int msi_rtas_init(void);
+#else
+static inline int msi_rtas_init(void) { return -1; };
+#endif
+
#endif /* __ASSEMBLY__ */
#endif /* __KERNEL__ */
#endif /* _ASM_POWERPC_MSI_H */
^ permalink raw reply
* [RFC/PATCH 4/7] Powerpc MSI implementation
From: Michael Ellerman @ 2006-11-07 7:21 UTC (permalink / raw)
To: linuxppc-dev
Cc: Eric W. Biederman, Greg Kroah-Hartman, linux-pci, David S. Miller
In-Reply-To: <1162884080.585336.70559261997.qpush@cradle>
Powerpc MSI implementation, based on a collection of "ops" callbacks.
We have to take the ops approach to accomodate RTAS, where firmware
handles almost all details of MSI setup/teardown. Bare-metal MSI
can be accomodated also.
We currently (ab)use the pci_dev to store our msi_info structure. We
were hoping to stash it in the pci_dn, but that's not a goer, so the
pci_dev seems like the right place, even though it seems naughty to
bloat generic structs.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
---
arch/powerpc/kernel/msi.c | 316 ++++++++++++++++++++++++++++++++++++++++++
include/asm-powerpc/machdep.h | 6
include/asm-powerpc/msi.h | 173 ++++++++++++++++++++++
include/linux/pci.h | 7
4 files changed, 502 insertions(+)
Index: msi/arch/powerpc/kernel/msi.c
===================================================================
--- /dev/null
+++ msi/arch/powerpc/kernel/msi.c
@@ -0,0 +1,316 @@
+/*
+ * Copyright 2006 (C), 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.
+ */
+
+#undef DEBUG
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <asm/msi.h>
+#include <asm/machdep.h>
+
+static struct ppc_msi_ops *get_msi_ops(struct pci_dev *pdev)
+{
+ if (ppc_md.get_msi_ops)
+ return ppc_md.get_msi_ops(pdev);
+
+ return NULL;
+}
+
+/* Activated by pci=nomsi on the command line. */
+static int no_msi;
+
+void pci_no_msi(void)
+{
+ msi_debug("MSI disabled by user\n");
+ no_msi = 1;
+}
+
+
+/* msi_info helpers */
+
+static int alloc_msi_info(struct pci_dev *pdev, int num,
+ struct msix_entry *entries, int type)
+{
+ struct msi_info *info;
+ unsigned int entries_size;
+
+ entries_size = sizeof(struct msix_entry) * num;
+
+ info = kzalloc(sizeof(struct msi_info) + entries_size, GFP_KERNEL);
+ if (!info) {
+ msi_debug("kzalloc failed for %s\n", pci_name(pdev));
+ return -ENOMEM;
+ }
+
+ info->type = type;
+ info->num = num;
+ info->entries = (struct msix_entry *)(info + 1);
+ memcpy(info->entries, entries, entries_size);
+
+ BUG_ON(pdev->msi_info); /* don't leak info structs */
+ pdev->msi_info = info;
+
+ return 0;
+}
+
+static struct msi_info *get_msi_info(struct pci_dev *pdev)
+{
+ return pdev->msi_info;
+}
+
+static void free_msi_info(struct pci_dev *pdev)
+{
+ kfree(pdev->msi_info);
+ pdev->msi_info = NULL;
+}
+
+
+/* Generic helpers */
+
+static int generic_msi_enable(struct pci_dev *pdev, int nvec,
+ struct msix_entry *entries, int type)
+{
+ struct ppc_msi_ops *ops;
+ int i, rc;
+
+ if (no_msi || !pdev || !entries || !nvec || get_msi_info(pdev)) {
+ msi_debug("predcondition failed for %p\n", pdev);
+ return -EINVAL;
+ }
+
+ ops = get_msi_ops(pdev);
+ if (!ops) {
+ msi_debug("no ops for %s\n", pci_name(pdev));
+ return -EINVAL;
+ }
+
+ for (i = 0; i < nvec; i++)
+ entries[i].vector = NO_IRQ;
+
+ rc = ops->check(pdev, nvec, entries, type);
+ if (rc) {
+ msi_debug("check failed (%d) for %s\n", rc, pci_name(pdev));
+ return rc;
+ }
+
+ rc = ops->alloc(pdev, nvec, entries, type);
+ if (rc) {
+ msi_debug("alloc failed (%d) for %s\n", rc, pci_name(pdev));
+ return rc;
+ }
+
+ if (ops->enable) {
+ rc = ops->enable(pdev, nvec, entries, type);
+ if (rc) {
+ msi_debug("enable failed (%d) for %s\n", rc,
+ pci_name(pdev));
+ goto out_free;
+ }
+ }
+
+ rc = alloc_msi_info(pdev, nvec, entries, type);
+ if (rc)
+ goto out_free;
+
+ return 0;
+
+ out_free:
+ ops->free(pdev, nvec, entries, type);
+
+ return rc;
+}
+
+static int generic_msi_disable(struct pci_dev *pdev, int type)
+{
+ struct ppc_msi_ops *ops;
+ struct msi_info *info;
+
+ if (no_msi || !pdev) {
+ msi_debug("predcondition failed for %p\n", pdev);
+ return -1;
+ }
+
+ info = get_msi_info(pdev);
+ if (!info) {
+ msi_debug("No info for %s\n", pci_name(pdev));
+ return -1;
+ }
+
+ ops = get_msi_ops(pdev);
+ if (!ops) {
+ msi_debug("no ops for %s\n", pci_name(pdev));
+ return -1;
+ }
+
+ if (ops->disable)
+ ops->disable(pdev, info->num, info->entries, type);
+
+ ops->free(pdev, info->num, info->entries, type);
+
+ return 0;
+}
+
+
+/* MSI */
+
+int pci_enable_msi(struct pci_dev *pdev)
+{
+ struct msix_entry entry;
+ int rc;
+
+ entry.entry = 0;
+
+ rc = generic_msi_enable(pdev, 1, &entry, PCI_CAP_ID_MSI);
+ if (rc)
+ return rc;
+
+ get_msi_info(pdev)->saved_irq = pdev->irq;
+ pdev->irq = entry.vector;
+ pdev->msi_enabled = 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_enable_msi);
+
+void pci_disable_msi(struct pci_dev *pdev)
+{
+ if (generic_msi_disable(pdev, PCI_CAP_ID_MSI) != 0)
+ return;
+
+ pdev->irq = get_msi_info(pdev)->saved_irq;
+ free_msi_info(pdev);
+ pdev->msi_enabled = 0;
+}
+EXPORT_SYMBOL_GPL(pci_disable_msi);
+
+
+/* MSI-X */
+
+int pci_enable_msix(struct pci_dev *pdev, struct msix_entry *entries, int nvec)
+{
+ int rc;
+
+ rc = generic_msi_enable(pdev, nvec, entries, PCI_CAP_ID_MSIX);
+ if (rc)
+ return rc;
+
+ pdev->msix_enabled = 1;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_enable_msix);
+
+void pci_disable_msix(struct pci_dev *pdev)
+{
+ if (generic_msi_disable(pdev, PCI_CAP_ID_MSIX) != 0)
+ return;
+
+ free_msi_info(pdev);
+ pdev->msix_enabled = 0;
+}
+EXPORT_SYMBOL_GPL(pci_disable_msix);
+
+
+/* Stubs for now */
+
+void disable_msi_mode(struct pci_dev *dev, int pos, int type)
+{
+ return;
+}
+
+void pci_scan_msi_device(struct pci_dev *dev)
+{
+ return;
+}
+
+void msi_remove_pci_irq_vectors(struct pci_dev* dev)
+{
+ return;
+}
+
+
+/* Bare metal enable/disable */
+
+int msi_raw_enable(struct pci_dev *pdev, int num,
+ struct msix_entry *entries, int type)
+{
+ struct ppc_msi_ops *ops;
+ struct msi_msg msg;
+ int pos;
+ u16 control;
+
+ pos = pci_find_capability(pdev, type);
+ if (!pos) {
+ msi_debug("cap (%d) not found for %s\n", type, pci_name(pdev));
+ return -1;
+ }
+
+ ops = get_msi_ops(pdev);
+ BUG_ON(!ops);
+
+ pci_read_config_word(pdev, pos + PCI_MSI_FLAGS, &control);
+
+ switch (type) {
+ case PCI_CAP_ID_MSI:
+ BUG_ON(!ops->setup_msi_msg);
+
+ ops->setup_msi_msg(pdev, &entries[0], &msg, type);
+
+ pci_write_config_dword(pdev, pos + PCI_MSI_ADDRESS_LO,
+ msg.address_lo);
+
+ if (control & PCI_MSI_FLAGS_64BIT) {
+ pci_write_config_dword(pdev, pos + PCI_MSI_ADDRESS_HI,
+ msg.address_hi);
+ pci_write_config_dword(pdev, pos + PCI_MSI_DATA_64,
+ msg.data);
+ } else {
+ pci_write_config_dword(pdev, pos + PCI_MSI_DATA_32,
+ msg.data);
+ }
+
+ control |= PCI_MSI_FLAGS_ENABLE;
+ break;
+ case PCI_CAP_ID_MSIX:
+ WARN_ON(1); /* XXX implement me */
+ return -1;
+ default:
+ BUG();
+ }
+
+ pci_write_config_word(pdev, pos + PCI_MSI_FLAGS, control);
+
+ return 0;
+}
+
+void msi_raw_disable(struct pci_dev *pdev, int num,
+ struct msix_entry *entries, int type)
+{
+ int pos;
+ u16 control;
+
+ pos = pci_find_capability(pdev, type);
+ BUG_ON(!pos);
+
+ pci_read_config_word(pdev, pos + PCI_MSI_FLAGS, &control);
+
+ switch (type) {
+ case PCI_CAP_ID_MSI:
+ control &= ~PCI_MSI_FLAGS_ENABLE;
+ break;
+ case PCI_CAP_ID_MSIX:
+ control &= ~PCI_MSIX_FLAGS_ENABLE;
+ break;
+ default:
+ BUG();
+ }
+
+ pci_write_config_word(pdev, pos + PCI_MSI_FLAGS, control);
+
+ return;
+}
Index: msi/include/asm-powerpc/machdep.h
===================================================================
--- msi.orig/include/asm-powerpc/machdep.h
+++ msi/include/asm-powerpc/machdep.h
@@ -29,6 +29,9 @@ struct file;
#ifdef CONFIG_KEXEC
struct kimage;
#endif
+#ifdef CONFIG_PCI_MSI
+struct ppc_msi_ops;
+#endif
#ifdef CONFIG_SMP
struct smp_ops_t {
@@ -106,6 +109,9 @@ struct machdep_calls {
/* Called after scanning the bus, before allocating resources */
void (*pcibios_fixup)(void);
int (*pci_probe_mode)(struct pci_bus *);
+#ifdef CONFIG_PCI_MSI
+ struct ppc_msi_ops* (*get_msi_ops)(struct pci_dev *pdev);
+#endif
void (*restart)(char *cmd);
void (*power_off)(void);
Index: msi/include/asm-powerpc/msi.h
===================================================================
--- /dev/null
+++ msi/include/asm-powerpc/msi.h
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+#ifndef _ASM_POWERPC_MSI_H
+#define _ASM_POWERPC_MSI_H
+
+#ifdef __KERNEL__
+#ifndef __ASSEMBLY__
+
+#include <linux/pci.h>
+#include <linux/msi.h>
+
+/*
+ * MSI and MSI-X although different in some details, are also similar in
+ * many respects, and ultimately achieve the same end. Given that, this code
+ * tries as far as possible to implement both MSI and MSI-X with a minimum
+ * of code duplication. We will use "MSI" to refer to both MSI and MSI-X,
+ * except where it is important to differentiate between the two.
+ *
+ * Enabling MSI for a device can be broken down into:
+ * 1) Checking the device can support the type/number of MSIs requested.
+ * 2) Allocating irqs for the MSIs and setting up the irq_descs.
+ * 3) Writing the appropriate configuration to the device and enabling MSIs.
+ *
+ * To implement that we have the following callbacks:
+ * 1) check(pdev, num, msix_entries, type)
+ * 2) alloc(pdev, num, msix_entries, type)
+ * 3) enable(pdev, num, msix_entries, type)
+ * a) setup_msi_msg(pdev, msix_entry, msi_msg, type)
+ *
+ * We give platforms full control over the enable step. However many
+ * platforms will simply want to program the device using standard PCI
+ * accessors. These platforms can use a generic enable callback and define
+ * a setup_msi_msg() callback which simply fills in the "magic" address and
+ * data values. Other platforms may leave setup_msi_msg() empty.
+ *
+ * Disabling MSI requires:
+ * 1) Disabling MSI on the device.
+ * 2) Freeing the irqs and any associated accounting information.
+ *
+ * Which maps directly to the two callbacks:
+ * 1) disable(pdev, num, msix_entries, type)
+ * 2) free(pdev, num, msix_entries, type)
+ */
+
+struct ppc_msi_ops
+{
+ /* check - Check that the requested MSI allocation is OK.
+ *
+ * @pdev: PCI device structure.
+ * @num: The number of MSIs being requested.
+ * @entries: An array of @num msix_entry structures.
+ * @type: The type, MSI or MSI-X.
+ *
+ * This routine is responsible for checking that the given PCI device
+ * can be allocated the requested type and number of MSIs.
+ *
+ * It is up to this routine to determine if the requested number of
+ * MSIs is valid for the device in question. If the number of MSIs,
+ * or the particular MSI entries, can not be supported for any
+ * reason this routine must return non-zero.
+ *
+ * If the check is succesful this routine must return 0.
+ */
+ int (*check) (struct pci_dev *pdev, int num,
+ struct msix_entry *entries, int type);
+
+ /* alloc - Allocate MSIs for the given device.
+ *
+ * @pdev: PCI device structure.
+ * @num: The number of MSIs being requested.
+ * @entries: An array of @num msix_entry structures.
+ * @type: The type, MSI or MSI-X.
+ *
+ * This routine is responsible for allocating the number of
+ * MSIs to the given PCI device.
+ *
+ * Upon completion there must be @num MSIs assigned to this device,
+ * the "vector" member of each struct msix_entry must be filled in
+ * with the Linux irq number allocated to it. The corresponding
+ * irq_descs must also be setup with an appropriate handler if
+ * required.
+ *
+ * If the allocation completes succesfully this routine must return 0.
+ */
+ int (*alloc) (struct pci_dev *pdev, int num,
+ struct msix_entry *entries, int type);
+
+ /* enable - Enable the MSIs on the given device.
+ *
+ * @pdev: PCI device structure.
+ * @num: The number of MSIs being requested.
+ * @entries: An array of @num msix_entry structures.
+ * @type: The type, MSI or MSI-X.
+ *
+ * This routine enables the MSIs on the given PCI device.
+ *
+ * If the enable completes succesfully this routine must return 0.
+ *
+ * This callback is optional.
+ */
+ int (*enable) (struct pci_dev *pdev, int num,
+ struct msix_entry *entries, int type);
+
+ /* setup_msi_msg - Setup an MSI message for the given device.
+ *
+ * @pdev: PCI device structure.
+ * @entry: The MSI entry to create a msi_msg for.
+ * @msg: Written with the magic address and data.
+ * @type: The type, MSI or MSI-X.
+ *
+ * Returns the "magic address and data" used to trigger the msi.
+ * If the setup is succesful this routine must return 0.
+ *
+ * This callback is optional.
+ */
+ int (*setup_msi_msg) (struct pci_dev *pdev, struct msix_entry *entry,
+ struct msi_msg *msg, int type);
+
+ /* disable - disable the MSI for the given device.
+ *
+ * @pdev: PCI device structure.
+ * @num: The number of MSIs to disable.
+ * @entries: An array of @num msix_entry structures.
+ * @type: The type, MSI or MSI-X.
+ *
+ * This routine should perform the inverse of enable.
+ */
+ void (*disable) (struct pci_dev *pdev, int num,
+ struct msix_entry *entries, int type);
+
+ /* free - free the MSIs assigned to the device.
+ *
+ * @pdev: PCI device structure.
+ * @num: The number of MSIs.
+ * @entries: An array of @num msix_entry structures.
+ * @type: The type, MSI or MSI-X.
+ *
+ * Free all MSIs and associated resources for the device. If any
+ * MSIs have been enabled they will have been disabled already by
+ * the generic code.
+ */
+ void (*free) (struct pci_dev *pdev, int num,
+ struct msix_entry *entries, int type);
+};
+
+
+/* Used by the MSI code to track MSI info for a pci_dev */
+struct msi_info {
+ int type;
+ unsigned int saved_irq;
+ unsigned int num;
+ struct msix_entry *entries;
+ void __iomem *msix_base;
+};
+
+extern int msi_raw_enable(struct pci_dev *pdev, int num,
+ struct msix_entry *entries, int type);
+extern void msi_raw_disable(struct pci_dev *pdev, int num,
+ struct msix_entry *entries, int type);
+
+#define msi_debug(fmt, args...) \
+ pr_debug("MSI:%s:%d: " fmt, __FUNCTION__, __LINE__, ## args)
+
+#endif /* __ASSEMBLY__ */
+#endif /* __KERNEL__ */
+#endif /* _ASM_POWERPC_MSI_H */
Index: msi/include/linux/pci.h
===================================================================
--- msi.orig/include/linux/pci.h
+++ msi/include/linux/pci.h
@@ -106,6 +106,10 @@ struct pci_cap_saved_state {
u32 data[0];
};
+#if defined(CONFIG_PCI_MSI) && defined(CONFIG_PPC_MERGE)
+struct msi_info;
+#endif
+
/*
* The pci_dev structure is used to describe PCI devices.
*/
@@ -173,6 +177,9 @@ struct pci_dev {
struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */
int rom_attr_enabled; /* has display of the rom attribute been enabled? */
struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */
+#if defined(CONFIG_PCI_MSI) && defined(CONFIG_PPC_MERGE)
+ struct msi_info *msi_info;
+#endif
};
#define pci_dev_g(n) list_entry(n, struct pci_dev, global_list)
^ permalink raw reply
* [RFC/PATCH 3/7] Rip out the existing powerpc msi stubs
From: Michael Ellerman @ 2006-11-07 7:21 UTC (permalink / raw)
To: linuxppc-dev
Cc: Eric W. Biederman, Greg Kroah-Hartman, linux-pci, David S. Miller
In-Reply-To: <1162884080.585336.70559261997.qpush@cradle>
Rip out the existing powerpc msi stubs. These were the start of an
implementation based on ppc_md calls, but were never used.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
---
arch/powerpc/kernel/irq.c | 28 ----------------------------
include/asm-powerpc/machdep.h | 5 -----
2 files changed, 33 deletions(-)
Index: msi/arch/powerpc/kernel/irq.c
===================================================================
--- msi.orig/arch/powerpc/kernel/irq.c
+++ msi/arch/powerpc/kernel/irq.c
@@ -871,34 +871,6 @@ arch_initcall(irq_late_init);
#endif /* CONFIG_PPC_MERGE */
-#ifdef CONFIG_PCI_MSI
-int pci_enable_msi(struct pci_dev * pdev)
-{
- if (ppc_md.enable_msi)
- return ppc_md.enable_msi(pdev);
- else
- return -1;
-}
-EXPORT_SYMBOL(pci_enable_msi);
-
-void pci_disable_msi(struct pci_dev * pdev)
-{
- if (ppc_md.disable_msi)
- ppc_md.disable_msi(pdev);
-}
-EXPORT_SYMBOL(pci_disable_msi);
-
-void pci_scan_msi_device(struct pci_dev *dev) {}
-int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) {return -1;}
-void pci_disable_msix(struct pci_dev *dev) {}
-void msi_remove_pci_irq_vectors(struct pci_dev *dev) {}
-void disable_msi_mode(struct pci_dev *dev, int pos, int type) {}
-void pci_no_msi(void) {}
-EXPORT_SYMBOL(pci_enable_msix);
-EXPORT_SYMBOL(pci_disable_msix);
-
-#endif
-
#ifdef CONFIG_PPC64
static int __init setup_noirqdistrib(char *str)
{
Index: msi/include/asm-powerpc/machdep.h
===================================================================
--- msi.orig/include/asm-powerpc/machdep.h
+++ msi/include/asm-powerpc/machdep.h
@@ -239,11 +239,6 @@ struct machdep_calls {
*/
void (*machine_kexec)(struct kimage *image);
#endif /* CONFIG_KEXEC */
-
-#ifdef CONFIG_PCI_MSI
- int (*enable_msi)(struct pci_dev *pdev);
- void (*disable_msi)(struct pci_dev *pdev);
-#endif /* CONFIG_PCI_MSI */
};
extern void power4_idle(void);
^ permalink raw reply
* [RFC/PATCH 2/7] Make some MSI-X #defines generic
From: Michael Ellerman @ 2006-11-07 7:21 UTC (permalink / raw)
To: linuxppc-dev
Cc: Eric W. Biederman, Greg Kroah-Hartman, linux-pci, David S. Miller
In-Reply-To: <1162884080.585336.70559261997.qpush@cradle>
Move some MSI-X #defines into pci_regs.h so they can be used
outside of drivers/pci.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
---
drivers/pci/msi.h | 8 --------
include/linux/pci_regs.h | 6 ++++++
2 files changed, 6 insertions(+), 8 deletions(-)
Index: msi/drivers/pci/msi.h
===================================================================
--- msi.orig/drivers/pci/msi.h
+++ msi/drivers/pci/msi.h
@@ -6,14 +6,6 @@
#ifndef MSI_H
#define MSI_H
-/*
- * MSI-X Address Register
- */
-#define PCI_MSIX_FLAGS_QSIZE 0x7FF
-#define PCI_MSIX_FLAGS_ENABLE (1 << 15)
-#define PCI_MSIX_FLAGS_BIRMASK (7 << 0)
-#define PCI_MSIX_FLAGS_BITMASK (1 << 0)
-
#define PCI_MSIX_ENTRY_SIZE 16
#define PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET 0
#define PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET 4
Index: msi/include/linux/pci_regs.h
===================================================================
--- msi.orig/include/linux/pci_regs.h
+++ msi/include/linux/pci_regs.h
@@ -292,6 +292,12 @@
#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */
#define PCI_MSI_MASK_BIT 16 /* Mask bits register */
+/* MSI-X registers (these are at offset PCI_MSI_FLAGS) */
+#define PCI_MSIX_FLAGS_QSIZE 0x7FF
+#define PCI_MSIX_FLAGS_ENABLE (1 << 15)
+#define PCI_MSIX_FLAGS_BIRMASK (7 << 0)
+#define PCI_MSIX_FLAGS_BITMASK (1 << 0)
+
/* CompactPCI Hotswap Register */
#define PCI_CHSWP_CSR 2 /* Control and Status Register */
^ permalink raw reply
* [RFC/PATCH 1/7] Add #defines for Hypertransport MSI fields
From: Michael Ellerman @ 2006-11-07 7:21 UTC (permalink / raw)
To: linuxppc-dev
Cc: Eric W. Biederman, Greg Kroah-Hartman, linux-pci, David S. Miller
In-Reply-To: <1162884080.585336.70559261997.qpush@cradle>
Add a few #defines for grabbing and working with the address fields
in a HT_CAPTYPE_MSI_MAPPING capability. All from the HT spec v3.00.
Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
---
include/linux/pci_regs.h | 7 +++++++
1 file changed, 7 insertions(+)
Index: msi/include/linux/pci_regs.h
===================================================================
--- msi.orig/include/linux/pci_regs.h
+++ msi/include/linux/pci_regs.h
@@ -480,6 +480,13 @@
#define HT_CAPTYPE_UNITID_CLUMP 0x90 /* Unit ID clumping */
#define HT_CAPTYPE_EXTCONF 0x98 /* Extended Configuration Space Access */
#define HT_CAPTYPE_MSI_MAPPING 0xA8 /* MSI Mapping Capability */
+#define HT_MSI_FLAGS 0x02 /* Offset to flags */
+#define HT_MSI_FLAGS_ENABLE 0x1 /* Mapping enable */
+#define HT_MSI_FLAGS_FIXED 0x2 /* Fixed mapping only */
+#define HT_MSI_FIXED_ADDR 0x0000000FEE0000ULL /* Fixed addr */
+#define HT_MSI_ADDR_LO 0x04 /* Offset to low addr bits */
+#define HT_MSI_ADDR_LO_MASK 0xFFF00000 /* Low address bit mask */
+#define HT_MSI_ADDR_HI 0x08 /* Offset to high addr bits */
#define HT_CAPTYPE_DIRECT_ROUTE 0xB0 /* Direct routing configuration */
#define HT_CAPTYPE_VCSET 0xB8 /* Virtual Channel configuration */
#define HT_CAPTYPE_ERROR_RETRY 0xC0 /* Retry on error configuration */
^ permalink raw reply
* [RFC/PATCH 0/7] Powerpc MSI Implementation
From: Michael Ellerman @ 2006-11-07 7:21 UTC (permalink / raw)
To: linuxppc-dev
Cc: Eric W. Biederman, Greg Kroah-Hartman, linux-pci, David S. Miller
Another cut at a Powerpc MSI implementation. This is working with the MPIC
backend, still haven't tested the RTAS backend.
Things I remember changing since last time:
* Fixed a few bugs pointed out by Jake.
* We discover the MPIC magic address via the PCI config space rather than
hard coding it.
* We reuse pdev->irq in the MPIC backend for now. This is nasty but works.
* Moved msi_info into the pci_dev. This is ugly, but not sure where else
we can put it, pci_dn is no longer an option.
* Added a place holder msi_info->msix_base pointer.
* Still haven't implement raw MSIX enabled, todo.
* Removed the msi_info->priv. No on was using it, can always put it back.
* Added lots of debugging in anticipation of getting some firware to test on.
...
cheers
^ permalink raw reply
* Re: [Fwd: [Bug 7431] New: ohci1394 Oops after a rmmod/modprobe cycle]
From: Benjamin Herrenschmidt @ 2006-11-07 7:17 UTC (permalink / raw)
To: Olaf Hering; +Cc: linuxppc-dev, Gioele Barabucci, Stefan Richter
In-Reply-To: <20061107070839.GA31460@aepfle.de>
On Tue, 2006-11-07 at 08:08 +0100, Olaf Hering wrote:
> On Tue, Nov 07, Benjamin Herrenschmidt wrote:
>
> > On Mon, 2006-11-06 at 17:58 +0100, Olaf Hering wrote:
> > > On Sun, Nov 05, Stefan Richter wrote:
> > >
> > > > Yes, PowerBook G3 Pismo seems affected.
> > > > https://bugzilla.novell.com/show_bug.cgi?id=115228
> > >
> > > This patch does not help. Doing an ip link set dev eth0 up makes no
> > > difference.
> >
> > What about trying to add a delay between the pmac clock code and the
> > init of the controller ? does that help ?
>
> no. and it is the rmmod, not the insmod, that hangs the pismo.
> Adding some mdelay to the remove function doesnt help either.
Tried not calling the pmac feature stuff at all in the rmmod ?
I reckon there might be a problem with the cable power control on some
old models like... the pismo :-)
Ben.
^ permalink raw reply
* Re: [Fwd: [Bug 7431] New: ohci1394 Oops after a rmmod/modprobe cycle]
From: Olaf Hering @ 2006-11-07 7:08 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev, Gioele Barabucci, Stefan Richter
In-Reply-To: <1162851570.28571.361.camel@localhost.localdomain>
On Tue, Nov 07, Benjamin Herrenschmidt wrote:
> On Mon, 2006-11-06 at 17:58 +0100, Olaf Hering wrote:
> > On Sun, Nov 05, Stefan Richter wrote:
> >
> > > Yes, PowerBook G3 Pismo seems affected.
> > > https://bugzilla.novell.com/show_bug.cgi?id=115228
> >
> > This patch does not help. Doing an ip link set dev eth0 up makes no
> > difference.
>
> What about trying to add a delay between the pmac clock code and the
> init of the controller ? does that help ?
no. and it is the rmmod, not the insmod, that hangs the pismo.
Adding some mdelay to the remove function doesnt help either.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox