summaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel
diff options
context:
space:
mode:
authorBecky Bruce <becky.bruce@freescale.com>2008-09-12 10:34:46 +0000
committerKumar Gala <galak@kernel.crashing.org>2008-09-24 16:26:45 -0500
commit4fc665b88a79a45bae8bbf3a05563c27c7337c3d (patch)
treeca668c2fab7c3a4d62b92174f4a5fcae2625cdd1 /arch/powerpc/kernel
parent8fae0353247530d2124b2419052fa6120462fa99 (diff)
downloadlinux-4fc665b88a79a45bae8bbf3a05563c27c7337c3d.tar.gz
linux-4fc665b88a79a45bae8bbf3a05563c27c7337c3d.tar.bz2
linux-4fc665b88a79a45bae8bbf3a05563c27c7337c3d.zip
powerpc: Merge 32 and 64-bit dma code
We essentially adopt the 64-bit dma code, with some changes to support 32-bit systems, including HIGHMEM. dma functions on 32-bit are now invoked via accessor functions which call the correct op for a device based on archdata dma_ops. If there is no archdata dma_ops, this defaults to dma_direct_ops. In addition, the dma_map/unmap_page functions are added to dma_ops because we can't just fall back on map/unmap_single when HIGHMEM is enabled. In the case of dma_direct_*, we stop using map/unmap_single and just use the page version - this saves a lot of ugly ifdeffing. We leave map/unmap_single in the dma_ops definition, though, because they are needed by the iommu code, which does not implement map/unmap_page. Ideally, going forward, we will completely eliminate map/unmap_single and just have map/unmap_page, if it's workable for 64-bit. Signed-off-by: Becky Bruce <becky.bruce@freescale.com> Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/kernel')
-rw-r--r--arch/powerpc/kernel/Makefile4
-rw-r--r--arch/powerpc/kernel/dma.c69
-rw-r--r--arch/powerpc/kernel/pci-common.c48
-rw-r--r--arch/powerpc/kernel/pci_32.c7
-rw-r--r--arch/powerpc/kernel/pci_64.c46
5 files changed, 103 insertions, 71 deletions
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index 09b3cabf2f91..fdb58253fa5b 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -70,10 +70,10 @@ extra-$(CONFIG_8xx) := head_8xx.o
extra-y += vmlinux.lds
obj-y += time.o prom.o traps.o setup-common.o \
- udbg.o misc.o io.o \
+ udbg.o misc.o io.o dma.o \
misc_$(CONFIG_WORD_SIZE).o
obj-$(CONFIG_PPC32) += entry_32.o setup_32.o
-obj-$(CONFIG_PPC64) += dma.o dma-iommu.o iommu.o
+obj-$(CONFIG_PPC64) += dma-iommu.o iommu.o
obj-$(CONFIG_KGDB) += kgdb.o
obj-$(CONFIG_PPC_MULTIPLATFORM) += prom_init.o
obj-$(CONFIG_MODULES) += ppc_ksyms.o
diff --git a/arch/powerpc/kernel/dma.c b/arch/powerpc/kernel/dma.c
index 124f86743bde..41fdd48bf433 100644
--- a/arch/powerpc/kernel/dma.c
+++ b/arch/powerpc/kernel/dma.c
@@ -16,21 +16,30 @@
* This implementation supports a per-device offset that can be applied if
* the address at which memory is visible to devices is not 0. Platform code
* can set archdata.dma_data to an unsigned long holding the offset. By
- * default the offset is zero.
+ * default the offset is PCI_DRAM_OFFSET.
*/
static unsigned long get_dma_direct_offset(struct device *dev)
{
- return (unsigned long)dev->archdata.dma_data;
+ if (dev)
+ return (unsigned long)dev->archdata.dma_data;
+
+ return PCI_DRAM_OFFSET;
}
-static void *dma_direct_alloc_coherent(struct device *dev, size_t size,
- dma_addr_t *dma_handle, gfp_t flag)
+void *dma_direct_alloc_coherent(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t flag)
{
+#ifdef CONFIG_NOT_COHERENT_CACHE
+ return __dma_alloc_coherent(size, dma_handle, flag);
+#else
struct page *page;
void *ret;
int node = dev_to_node(dev);
+ /* ignore region specifiers */
+ flag &= ~(__GFP_HIGHMEM);
+
page = alloc_pages_node(node, flag, get_order(size));
if (page == NULL)
return NULL;
@@ -39,27 +48,17 @@ static void *dma_direct_alloc_coherent(struct device *dev, size_t size,
*dma_handle = virt_to_abs(ret) + get_dma_direct_offset(dev);
return ret;
+#endif
}
-static void dma_direct_free_coherent(struct device *dev, size_t size,
- void *vaddr, dma_addr_t dma_handle)
+void dma_direct_free_coherent(struct device *dev, size_t size,
+ void *vaddr, dma_addr_t dma_handle)
{
+#ifdef CONFIG_NOT_COHERENT_CACHE
+ __dma_free_coherent(size, vaddr);
+#else
free_pages((unsigned long)vaddr, get_order(size));
-}
-
-static dma_addr_t dma_direct_map_single(struct device *dev, void *ptr,
- size_t size,
- enum dma_data_direction direction,
- struct dma_attrs *attrs)
-{
- return virt_to_abs(ptr) + get_dma_direct_offset(dev);
-}
-
-static void dma_direct_unmap_single(struct device *dev, dma_addr_t dma_addr,
- size_t size,
- enum dma_data_direction direction,
- struct dma_attrs *attrs)
-{
+#endif
}
static int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl,
@@ -85,20 +84,44 @@ static void dma_direct_unmap_sg(struct device *dev, struct scatterlist *sg,
static int dma_direct_dma_supported(struct device *dev, u64 mask)
{
+#ifdef CONFIG_PPC64
/* Could be improved to check for memory though it better be
* done via some global so platforms can set the limit in case
* they have limited DMA windows
*/
return mask >= DMA_32BIT_MASK;
+#else
+ return 1;
+#endif
+}
+
+static inline dma_addr_t dma_direct_map_page(struct device *dev,
+ struct page *page,
+ unsigned long offset,
+ size_t size,
+ enum dma_data_direction dir,
+ struct dma_attrs *attrs)
+{
+ BUG_ON(dir == DMA_NONE);
+ __dma_sync_page(page, offset, size, dir);
+ return page_to_phys(page) + offset + get_dma_direct_offset(dev);
+}
+
+static inline void dma_direct_unmap_page(struct device *dev,
+ dma_addr_t dma_address,
+ size_t size,
+ enum dma_data_direction direction,
+ struct dma_attrs *attrs)
+{
}
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,
+ .map_page = dma_direct_map_page,
+ .unmap_page = dma_direct_unmap_page,
};
EXPORT_SYMBOL(dma_direct_ops);
diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c
index ea0c61e09b76..52ccfed416ad 100644
--- a/arch/powerpc/kernel/pci-common.c
+++ b/arch/powerpc/kernel/pci-common.c
@@ -56,6 +56,34 @@ resource_size_t isa_mem_base;
/* Default PCI flags is 0 */
unsigned int ppc_pci_flags;
+static struct dma_mapping_ops *pci_dma_ops;
+
+void set_pci_dma_ops(struct dma_mapping_ops *dma_ops)
+{
+ pci_dma_ops = dma_ops;
+}
+
+struct dma_mapping_ops *get_pci_dma_ops(void)
+{
+ return pci_dma_ops;
+}
+EXPORT_SYMBOL(get_pci_dma_ops);
+
+int pci_set_dma_mask(struct pci_dev *dev, u64 mask)
+{
+ return dma_set_mask(&dev->dev, mask);
+}
+
+int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask)
+{
+ int rc;
+
+ rc = dma_set_mask(&dev->dev, mask);
+ dev->dev.coherent_dma_mask = dev->dma_mask;
+
+ return rc;
+}
+
struct pci_controller *pcibios_alloc_controller(struct device_node *dev)
{
struct pci_controller *phb;
@@ -180,6 +208,26 @@ char __devinit *pcibios_setup(char *str)
return str;
}
+void __devinit pcibios_setup_new_device(struct pci_dev *dev)
+{
+ struct dev_archdata *sd = &dev->dev.archdata;
+
+ sd->of_node = pci_device_to_OF_node(dev);
+
+ DBG("PCI: device %s OF node: %s\n", pci_name(dev),
+ sd->of_node ? sd->of_node->full_name : "<none>");
+
+ sd->dma_ops = pci_dma_ops;
+#ifdef CONFIG_PPC32
+ sd->dma_data = (void *)PCI_DRAM_OFFSET;
+#endif
+ set_dev_node(&dev->dev, pcibus_to_node(dev->bus));
+
+ if (ppc_md.pci_dma_dev_setup)
+ ppc_md.pci_dma_dev_setup(dev);
+}
+EXPORT_SYMBOL(pcibios_setup_new_device);
+
/*
* Reads the interrupt pin to determine if interrupt is use by card.
* If the interrupt is used, then gets the interrupt line from the
diff --git a/arch/powerpc/kernel/pci_32.c b/arch/powerpc/kernel/pci_32.c
index 88db4ffaf11c..174b77ee18ff 100644
--- a/arch/powerpc/kernel/pci_32.c
+++ b/arch/powerpc/kernel/pci_32.c
@@ -424,6 +424,7 @@ void __devinit pcibios_do_bus_setup(struct pci_bus *bus)
unsigned long io_offset;
struct resource *res;
int i;
+ struct pci_dev *dev;
/* Hookup PHB resources */
io_offset = (unsigned long)hose->io_base_virt - isa_io_base;
@@ -457,6 +458,12 @@ void __devinit pcibios_do_bus_setup(struct pci_bus *bus)
bus->resource[i+1] = res;
}
}
+
+ if (ppc_md.pci_dma_bus_setup)
+ ppc_md.pci_dma_bus_setup(bus);
+
+ list_for_each_entry(dev, &bus->devices, bus_list)
+ pcibios_setup_new_device(dev);
}
/* the next one is stolen from the alpha port... */
diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c
index 1f75bf03446f..8247cff1cb3e 100644
--- a/arch/powerpc/kernel/pci_64.c
+++ b/arch/powerpc/kernel/pci_64.c
@@ -52,35 +52,6 @@ EXPORT_SYMBOL(pci_io_base);
LIST_HEAD(hose_list);
-static struct dma_mapping_ops *pci_dma_ops;
-
-void set_pci_dma_ops(struct dma_mapping_ops *dma_ops)
-{
- pci_dma_ops = dma_ops;
-}
-
-struct dma_mapping_ops *get_pci_dma_ops(void)
-{
- return pci_dma_ops;
-}
-EXPORT_SYMBOL(get_pci_dma_ops);
-
-
-int pci_set_dma_mask(struct pci_dev *dev, u64 mask)
-{
- return dma_set_mask(&dev->dev, mask);
-}
-
-int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask)
-{
- int rc;
-
- rc = dma_set_mask(&dev->dev, mask);
- dev->dev.coherent_dma_mask = dev->dma_mask;
-
- return rc;
-}
-
static void fixup_broken_pcnet32(struct pci_dev* dev)
{
if ((dev->class>>8 == PCI_CLASS_NETWORK_ETHERNET)) {
@@ -548,23 +519,6 @@ int __devinit pcibios_map_io_space(struct pci_bus *bus)
}
EXPORT_SYMBOL_GPL(pcibios_map_io_space);
-void __devinit pcibios_setup_new_device(struct pci_dev *dev)
-{
- struct dev_archdata *sd = &dev->dev.archdata;
-
- sd->of_node = pci_device_to_OF_node(dev);
-
- DBG("PCI: device %s OF node: %s\n", pci_name(dev),
- sd->of_node ? sd->of_node->full_name : "<none>");
-
- sd->dma_ops = pci_dma_ops;
- set_dev_node(&dev->dev, pcibus_to_node(dev->bus));
-
- if (ppc_md.pci_dma_dev_setup)
- ppc_md.pci_dma_dev_setup(dev);
-}
-EXPORT_SYMBOL(pcibios_setup_new_device);
-
void __devinit pcibios_do_bus_setup(struct pci_bus *bus)
{
struct pci_dev *dev;