diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2006-11-11 06:25:02 (GMT) |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2006-12-04 09:38:40 (GMT) |
commit | 12d04eef927bf61328af2c7cbe756c96f98ac3bf (patch) | |
tree | 18865369100e9059c7e883dec93ea67f7b52a287 /arch/powerpc/kernel | |
parent | 7c719871ff4d5f15b71f0138d08b758281b58631 (diff) | |
download | linux-fsl-qoriq-12d04eef927bf61328af2c7cbe756c96f98ac3bf.tar.xz |
[POWERPC] Refactor 64 bits DMA operations
This patch completely refactors DMA operations for 64 bits powerpc. 32 bits
is untouched for now.
We use the new dev_archdata structure to add the dma operations pointer
and associated data to struct device. While at it, we also add the OF node
pointer and numa node. In the future, we might want to look into merging
that with pci_dn as well.
The old vio, pci-iommu and pci-direct DMA ops are gone. They are now replaced
by a set of generic iommu and direct DMA ops (non PCI specific) that can be
used by bus types. The toplevel implementation is now inline.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/kernel')
-rw-r--r-- | arch/powerpc/kernel/Makefile | 3 | ||||
-rw-r--r-- | arch/powerpc/kernel/dma_64.c | 240 | ||||
-rw-r--r-- | arch/powerpc/kernel/ibmebus.c | 6 | ||||
-rw-r--r-- | arch/powerpc/kernel/iommu.c | 6 | ||||
-rw-r--r-- | arch/powerpc/kernel/of_platform.c | 9 | ||||
-rw-r--r-- | arch/powerpc/kernel/pci_64.c | 26 | ||||
-rw-r--r-- | arch/powerpc/kernel/pci_direct_iommu.c | 98 | ||||
-rw-r--r-- | arch/powerpc/kernel/pci_iommu.c | 164 | ||||
-rw-r--r-- | arch/powerpc/kernel/setup_64.c | 1 | ||||
-rw-r--r-- | arch/powerpc/kernel/vio.c | 94 |
10 files changed, 199 insertions, 448 deletions
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 04fdbe5..eba8d11 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -62,8 +62,7 @@ obj-$(CONFIG_PPC_UDBG_16550) += legacy_serial.o udbg_16550.o 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 diff --git a/arch/powerpc/kernel/dma_64.c b/arch/powerpc/kernel/dma_64.c index 6c168f6..4e65511 100644 --- a/arch/powerpc/kernel/dma_64.c +++ b/arch/powerpc/kernel/dma_64.c @@ -1,151 +1,185 @@ /* - * 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) -{ -#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; -} +/* + * Generic iommu implementation + */ -int dma_supported(struct device *dev, u64 mask) +static inline unsigned long device_to_mask(struct device *dev) { - struct dma_mapping_ops *dma_ops = get_dma_ops(dev); + if (dev->dma_mask && *dev->dma_mask) + return *dev->dma_mask; + /* Assume devices without mask can take 32 bit addresses */ + return 0xfffffffful; +} - BUG_ON(!dma_ops); - return dma_ops->dma_supported(dev, mask); +/* 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) +{ + return iommu_alloc_coherent(dev->archdata.dma_data, size, dma_handle, + device_to_mask(dev), flag, + dev->archdata.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; + iommu_free_coherent(dev->archdata.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); - - BUG_ON(!dma_ops); - - return dma_ops->alloc_coherent(dev, size, dma_handle, flag); + return iommu_map_single(dev->archdata.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) + +static void dma_iommu_unmap_single(struct device *dev, dma_addr_t dma_handle, + size_t size, + enum dma_data_direction direction) { - struct dma_mapping_ops *dma_ops = get_dma_ops(dev); + iommu_unmap_single(dev->archdata.dma_data, dma_handle, size, direction); +} - BUG_ON(!dma_ops); - dma_ops->free_coherent(dev, size, cpu_addr, dma_handle); +static int dma_iommu_map_sg(struct device *dev, struct scatterlist *sglist, + int nelems, enum dma_data_direction direction) +{ + return iommu_map_sg(dev->archdata.dma_data, sglist, nelems, + device_to_mask(dev), 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) +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); - - return dma_ops->map_single(dev, cpu_addr, size, direction); + iommu_unmap_sg(dev->archdata.dma_data, sglist, nelems, 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) +/* 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); - - BUG_ON(!dma_ops); - - dma_ops->unmap_single(dev, dma_addr, size, direction); + struct iommu_table *tbl = dev->archdata.dma_data; + + if (!tbl || tbl->it_offset > mask) { + printk(KERN_INFO + "Warning: IOMMU 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; } -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) -{ - struct dma_mapping_ops *dma_ops = get_dma_ops(dev); +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); - BUG_ON(!dma_ops); +/* + * Generic direct DMA implementation + */ - return dma_ops->map_single(dev, page_address(page) + offset, size, - direction); +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) +{ + /* 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; } -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); diff --git a/arch/powerpc/kernel/ibmebus.c b/arch/powerpc/kernel/ibmebus.c index 39db7a3..8e51579 100644 --- a/arch/powerpc/kernel/ibmebus.c +++ b/arch/powerpc/kernel/ibmebus.c @@ -112,7 +112,7 @@ static int ibmebus_dma_supported(struct device *dev, u64 mask) 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,10 @@ static struct ibmebus_dev* __devinit ibmebus_register_device_common( dev->ofdev.dev.bus = &ibmebus_bus_type; dev->ofdev.dev.release = ibmebus_dev_release; + dev->ofdev.dev.archdata.of_node = dev->ofdev.node; + dev->ofdev.dev.archdata.dma_ops = &ibmebus_dma_ops; + dev->ofdev.dev.archdata.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. */ diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c index ba6b725..95edad4 100644 --- a/arch/powerpc/kernel/iommu.c +++ b/arch/powerpc/kernel/iommu.c @@ -258,9 +258,9 @@ static void iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr, 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; diff --git a/arch/powerpc/kernel/of_platform.c b/arch/powerpc/kernel/of_platform.c index 25850ad..7a0e77a 100644 --- a/arch/powerpc/kernel/of_platform.c +++ b/arch/powerpc/kernel/of_platform.c @@ -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,13 @@ struct of_device* of_platform_device_create(struct device_node *np, dev->dev.parent = parent; dev->dev.bus = &of_platform_bus_type; dev->dev.release = of_release_dev; + dev->dev.archdata.of_node = np; + dev->dev.archdata.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); diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c index 9a6bb80..88b7848 100644 --- a/arch/powerpc/kernel/pci_64.c +++ b/arch/powerpc/kernel/pci_64.c @@ -61,7 +61,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 */ @@ -1205,15 +1205,35 @@ void __devinit pcibios_fixup_device_resources(struct pci_dev *dev, } EXPORT_SYMBOL(pcibios_fixup_device_resources); +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_NUMA + sd->numa_node = pcibus_to_node(dev->bus); +#else + sd->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) { diff --git a/arch/powerpc/kernel/pci_direct_iommu.c b/arch/powerpc/kernel/pci_direct_iommu.c deleted file mode 100644 index 72ce082..0000000 --- a/arch/powerpc/kernel/pci_direct_iommu.c +++ /dev/null @@ -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; -} diff --git a/arch/powerpc/kernel/pci_iommu.c b/arch/powerpc/kernel/pci_iommu.c deleted file mode 100644 index 0688b25..0000000 --- a/arch/powerpc/kernel/pci_iommu.c +++ /dev/null @@ -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; -} diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index b0f1c82..f7ad64a 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -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> diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c index ed00787..a80f8f1 100644 --- a/arch/powerpc/kernel/vio.c +++ b/arch/powerpc/kernel/vio.c @@ -81,15 +81,15 @@ static struct iommu_table *vio_build_iommu_table(struct vio_dev *dev) 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->dev.archdata.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->dev.archdata.of_node, dma_window, + &tbl->it_index, &offset, &size); /* TCE table size - measured in tce entries */ tbl->it_size = size >> IOMMU_PAGE_SHIFT; @@ -117,7 +117,8 @@ static const struct vio_device_id *vio_match_device( { 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(dev->dev.archdata.of_node, + ids->compat)) return ids; ids++; } @@ -198,9 +199,9 @@ EXPORT_SYMBOL(vio_unregister_driver); /* vio_dev refcount hit 0 */ static void __devinit vio_dev_release(struct device *dev) { - if (dev->platform_data) { - /* XXX free TCE table */ - of_node_put(dev->platform_data); + if (dev->archdata.of_node) { + /* XXX should free TCE table */ + of_node_put(dev->archdata.of_node); } kfree(to_vio_dev(dev)); } @@ -210,7 +211,7 @@ static void __devinit vio_dev_release(struct device *dev) * @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 +241,6 @@ struct vio_dev * __devinit vio_register_device_node(struct device_node *of_node) 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 +253,10 @@ struct vio_dev * __devinit vio_register_device_node(struct device_node *of_node) if (unit_address != NULL) viodev->unit_address = *unit_address; } - viodev->iommu_table = vio_build_iommu_table(viodev); + viodev->dev.archdata.of_node = of_node_get(of_node); + viodev->dev.archdata.dma_ops = &dma_iommu_ops; + viodev->dev.archdata.dma_data = vio_build_iommu_table(viodev); + viodev->dev.archdata.numa_node = of_node_to_nid(of_node); /* init generic 'struct device' fields: */ viodev->dev.parent = &vio_bus_device.dev; @@ -285,10 +287,11 @@ static int __init vio_bus_init(void) #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.dev.archdata.dma_ops = &dma_iommu_ops; + vio_bus_device.dev.archdata.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 +339,7 @@ static ssize_t name_show(struct device *dev, static ssize_t devspec_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct device_node *of_node = dev->platform_data; + struct device_node *of_node = dev->archdata.of_node; return sprintf(buf, "%s\n", of_node ? of_node->full_name : "none"); } @@ -353,62 +356,6 @@ void __devinit vio_unregister_device(struct vio_dev *viodev) } 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 +369,14 @@ static int vio_hotplug(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size) { const struct vio_dev *vio_dev = to_vio_dev(dev); - struct device_node *dn = dev->platform_data; + struct device_node *dn; const char *cp; int length; if (!num_envp) return -ENOMEM; + dn = dev->archdata.of_node; if (!dn) return -ENODEV; cp = get_property(dn, "compatible", &length); @@ -465,7 +413,7 @@ 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); + return get_property(vdev->dev.archdata.of_node, which, length); } EXPORT_SYMBOL(vio_get_attribute); |