8d8bb39b9e
Add per-device dma_mapping_ops support for CONFIG_X86_64 as POWER architecture does: This enables us to cleanly fix the Calgary IOMMU issue that some devices are not behind the IOMMU (http://lkml.org/lkml/2008/5/8/423). I think that per-device dma_mapping_ops support would be also helpful for KVM people to support PCI passthrough but Andi thinks that this makes it difficult to support the PCI passthrough (see the above thread). So I CC'ed this to KVM camp. Comments are appreciated. A pointer to dma_mapping_ops to struct dev_archdata is added. If the pointer is non NULL, DMA operations in asm/dma-mapping.h use it. If it's NULL, the system-wide dma_ops pointer is used as before. If it's useful for KVM people, I plan to implement a mechanism to register a hook called when a new pci (or dma capable) device is created (it works with hot plugging). It enables IOMMUs to set up an appropriate dma_mapping_ops per device. The major obstacle is that dma_mapping_error doesn't take a pointer to the device unlike other DMA operations. So x86 can't have dma_mapping_ops per device. Note all the POWER IOMMUs use the same dma_mapping_error function so this is not a problem for POWER but x86 IOMMUs use different dma_mapping_error functions. The first patch adds the device argument to dma_mapping_error. The patch is trivial but large since it touches lots of drivers and dma-mapping.h in all the architecture. This patch: dma_mapping_error() doesn't take a pointer to the device unlike other DMA operations. So we can't have dma_mapping_ops per device. Note that POWER already has dma_mapping_ops per device but all the POWER IOMMUs use the same dma_mapping_error function. x86 IOMMUs use device argument. [akpm@linux-foundation.org: fix sge] [akpm@linux-foundation.org: fix svc_rdma] [akpm@linux-foundation.org: build fix] [akpm@linux-foundation.org: fix bnx2x] [akpm@linux-foundation.org: fix s2io] [akpm@linux-foundation.org: fix pasemi_mac] [akpm@linux-foundation.org: fix sdhci] [akpm@linux-foundation.org: build fix] [akpm@linux-foundation.org: fix sparc] [akpm@linux-foundation.org: fix ibmvscsi] Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> Cc: Muli Ben-Yehuda <muli@il.ibm.com> Cc: Andi Kleen <andi@firstfloor.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Ingo Molnar <mingo@elte.hu> Cc: Avi Kivity <avi@qumranet.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
185 lines
4.8 KiB
C
185 lines
4.8 KiB
C
/*
|
|
* IO workarounds for PCI on Celleb/Cell platform
|
|
*
|
|
* (C) Copyright 2006-2007 TOSHIBA 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.
|
|
*
|
|
* 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.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#undef DEBUG
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/io.h>
|
|
|
|
#include <asm/ppc-pci.h>
|
|
#include <asm/pci-bridge.h>
|
|
|
|
#include "io-workarounds.h"
|
|
|
|
#define SPIDER_PCI_DISABLE_PREFETCH
|
|
|
|
struct spiderpci_iowa_private {
|
|
void __iomem *regs;
|
|
};
|
|
|
|
static void spiderpci_io_flush(struct iowa_bus *bus)
|
|
{
|
|
struct spiderpci_iowa_private *priv;
|
|
u32 val;
|
|
|
|
priv = bus->private;
|
|
val = in_be32(priv->regs + SPIDER_PCI_DUMMY_READ);
|
|
iosync();
|
|
}
|
|
|
|
#define SPIDER_PCI_MMIO_READ(name, ret) \
|
|
static ret spiderpci_##name(const PCI_IO_ADDR addr) \
|
|
{ \
|
|
ret val = __do_##name(addr); \
|
|
spiderpci_io_flush(iowa_mem_find_bus(addr)); \
|
|
return val; \
|
|
}
|
|
|
|
#define SPIDER_PCI_MMIO_READ_STR(name) \
|
|
static void spiderpci_##name(const PCI_IO_ADDR addr, void *buf, \
|
|
unsigned long count) \
|
|
{ \
|
|
__do_##name(addr, buf, count); \
|
|
spiderpci_io_flush(iowa_mem_find_bus(addr)); \
|
|
}
|
|
|
|
SPIDER_PCI_MMIO_READ(readb, u8)
|
|
SPIDER_PCI_MMIO_READ(readw, u16)
|
|
SPIDER_PCI_MMIO_READ(readl, u32)
|
|
SPIDER_PCI_MMIO_READ(readq, u64)
|
|
SPIDER_PCI_MMIO_READ(readw_be, u16)
|
|
SPIDER_PCI_MMIO_READ(readl_be, u32)
|
|
SPIDER_PCI_MMIO_READ(readq_be, u64)
|
|
SPIDER_PCI_MMIO_READ_STR(readsb)
|
|
SPIDER_PCI_MMIO_READ_STR(readsw)
|
|
SPIDER_PCI_MMIO_READ_STR(readsl)
|
|
|
|
static void spiderpci_memcpy_fromio(void *dest, const PCI_IO_ADDR src,
|
|
unsigned long n)
|
|
{
|
|
__do_memcpy_fromio(dest, src, n);
|
|
spiderpci_io_flush(iowa_mem_find_bus(src));
|
|
}
|
|
|
|
static int __init spiderpci_pci_setup_chip(struct pci_controller *phb,
|
|
void __iomem *regs)
|
|
{
|
|
void *dummy_page_va;
|
|
dma_addr_t dummy_page_da;
|
|
|
|
#ifdef SPIDER_PCI_DISABLE_PREFETCH
|
|
u32 val = in_be32(regs + SPIDER_PCI_VCI_CNTL_STAT);
|
|
pr_debug("SPIDER_IOWA:PVCI_Control_Status was 0x%08x\n", val);
|
|
out_be32(regs + SPIDER_PCI_VCI_CNTL_STAT, val | 0x8);
|
|
#endif /* SPIDER_PCI_DISABLE_PREFETCH */
|
|
|
|
/* setup dummy read */
|
|
/*
|
|
* On CellBlade, we can't know that which XDR memory is used by
|
|
* kmalloc() to allocate dummy_page_va.
|
|
* In order to imporve the performance, the XDR which is used to
|
|
* allocate dummy_page_va is the nearest the spider-pci.
|
|
* We have to select the CBE which is the nearest the spider-pci
|
|
* to allocate memory from the best XDR, but I don't know that
|
|
* how to do.
|
|
*
|
|
* Celleb does not have this problem, because it has only one XDR.
|
|
*/
|
|
dummy_page_va = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
|
if (!dummy_page_va) {
|
|
pr_err("SPIDERPCI-IOWA:Alloc dummy_page_va failed.\n");
|
|
return -1;
|
|
}
|
|
|
|
dummy_page_da = dma_map_single(phb->parent, dummy_page_va,
|
|
PAGE_SIZE, DMA_FROM_DEVICE);
|
|
if (dma_mapping_error(phb->parent, dummy_page_da)) {
|
|
pr_err("SPIDER-IOWA:Map dummy page filed.\n");
|
|
kfree(dummy_page_va);
|
|
return -1;
|
|
}
|
|
|
|
out_be32(regs + SPIDER_PCI_DUMMY_READ_BASE, dummy_page_da);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int __init spiderpci_iowa_init(struct iowa_bus *bus, void *data)
|
|
{
|
|
void __iomem *regs = NULL;
|
|
struct spiderpci_iowa_private *priv;
|
|
struct device_node *np = bus->phb->dn;
|
|
struct resource r;
|
|
unsigned long offset = (unsigned long)data;
|
|
|
|
pr_debug("SPIDERPCI-IOWA:Bus initialize for spider(%s)\n",
|
|
np->full_name);
|
|
|
|
priv = kzalloc(sizeof(struct spiderpci_iowa_private), GFP_KERNEL);
|
|
if (!priv) {
|
|
pr_err("SPIDERPCI-IOWA:"
|
|
"Can't allocate struct spiderpci_iowa_private");
|
|
return -1;
|
|
}
|
|
|
|
if (of_address_to_resource(np, 0, &r)) {
|
|
pr_err("SPIDERPCI-IOWA:Can't get resource.\n");
|
|
goto error;
|
|
}
|
|
|
|
regs = ioremap(r.start + offset, SPIDER_PCI_REG_SIZE);
|
|
if (!regs) {
|
|
pr_err("SPIDERPCI-IOWA:ioremap failed.\n");
|
|
goto error;
|
|
}
|
|
priv->regs = regs;
|
|
bus->private = priv;
|
|
|
|
if (spiderpci_pci_setup_chip(bus->phb, regs))
|
|
goto error;
|
|
|
|
return 0;
|
|
|
|
error:
|
|
kfree(priv);
|
|
bus->private = NULL;
|
|
|
|
if (regs)
|
|
iounmap(regs);
|
|
|
|
return -1;
|
|
}
|
|
|
|
struct ppc_pci_io spiderpci_ops = {
|
|
.readb = spiderpci_readb,
|
|
.readw = spiderpci_readw,
|
|
.readl = spiderpci_readl,
|
|
.readq = spiderpci_readq,
|
|
.readw_be = spiderpci_readw_be,
|
|
.readl_be = spiderpci_readl_be,
|
|
.readq_be = spiderpci_readq_be,
|
|
.readsb = spiderpci_readsb,
|
|
.readsw = spiderpci_readsw,
|
|
.readsl = spiderpci_readsl,
|
|
.memcpy_fromio = spiderpci_memcpy_fromio,
|
|
};
|
|
|