summaryrefslogtreecommitdiffstats
path: root/arch/sparc64/kernel/pci_iommu.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-02-24 00:12:35 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-02-24 00:12:35 +0000
commit482368b1a8e45430672c58c9a42e7d2004367126 (patch)
treece2a1a567d4d62dee7c2e71a46a99cf72cf1d606 /arch/sparc64/kernel/pci_iommu.c
parente4d0251c6f56ab2e191afb70f80f382793e23f74 (diff)
Merge with 2.3.47. Guys, this is buggy as shit. You've been warned.
Diffstat (limited to 'arch/sparc64/kernel/pci_iommu.c')
-rw-r--r--arch/sparc64/kernel/pci_iommu.c112
1 files changed, 63 insertions, 49 deletions
diff --git a/arch/sparc64/kernel/pci_iommu.c b/arch/sparc64/kernel/pci_iommu.c
index 409a44897..00f635ab3 100644
--- a/arch/sparc64/kernel/pci_iommu.c
+++ b/arch/sparc64/kernel/pci_iommu.c
@@ -1,4 +1,4 @@
-/* $Id: pci_iommu.c,v 1.8 2000/01/28 13:41:59 jj Exp $
+/* $Id: pci_iommu.c,v 1.10 2000/02/18 13:48:54 davem Exp $
* pci_iommu.c: UltraSparc PCI controller IOM/STC support.
*
* Copyright (C) 1999 David S. Miller (davem@redhat.com)
@@ -99,13 +99,12 @@ static iopte_t *alloc_consistent_cluster(struct pci_iommu *iommu, unsigned long
return NULL;
}
-#define IOPTE_CONSISTANT(CTX, PADDR) \
- (IOPTE_VALID | IOPTE_CACHE | IOPTE_WRITE | \
- (((CTX) << 47) & IOPTE_CONTEXT) | \
- ((PADDR) & IOPTE_PAGE))
+#define IOPTE_CONSISTENT(CTX) \
+ (IOPTE_VALID | IOPTE_CACHE | \
+ (((CTX) << 47) & IOPTE_CONTEXT))
-#define IOPTE_STREAMING(CTX, PADDR) \
- (IOPTE_CONSISTANT(CTX, PADDR) | IOPTE_STBUF)
+#define IOPTE_STREAMING(CTX) \
+ (IOPTE_CONSISTENT(CTX) | IOPTE_STBUF)
#define IOPTE_INVALID 0UL
@@ -123,18 +122,10 @@ void *pci_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_ad
int npages;
size = PAGE_ALIGN(size);
- for (order = 0; order < 10; order++) {
- if ((PAGE_SIZE << order) >= size)
- break;
- }
- if (order == 10)
+ order = get_order(size);
+ if (order >= 10)
return NULL;
- /* We still don't support devices which don't recognize at least 30 bits
- of bus address. Bug me to code it (is pretty easy actually). -jj */
- if ((pdev->dma_mask & 0x3fffffff) != 0x3fffffff)
- BUG();
-
first_page = __get_free_pages(GFP_ATOMIC, order);
if (first_page == 0UL)
return NULL;
@@ -160,7 +151,9 @@ void *pci_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_ad
ctx = iommu->iommu_cur_ctx++;
first_page = __pa(first_page);
while (npages--) {
- iopte_val(*iopte) = IOPTE_CONSISTANT(ctx, first_page);
+ iopte_val(*iopte) = (IOPTE_CONSISTENT(ctx) |
+ IOPTE_WRITE |
+ (first_page & IOPTE_PAGE));
iopte++;
first_page += PAGE_SIZE;
}
@@ -210,10 +203,7 @@ void pci_free_consistent(struct pci_dev *pdev, size_t size, void *cpu, dma_addr_
spin_unlock_irqrestore(&iommu->lock, flags);
- for (order = 0; order < 10; order++) {
- if ((PAGE_SIZE << order) >= size)
- break;
- }
+ order = get_order(size);
if (order < 10)
free_pages((unsigned long)cpu, order);
}
@@ -221,7 +211,7 @@ void pci_free_consistent(struct pci_dev *pdev, size_t size, void *cpu, dma_addr_
/* Map a single buffer at PTR of SZ bytes for PCI DMA
* in streaming mode.
*/
-dma_addr_t pci_map_single(struct pci_dev *pdev, void *ptr, size_t sz)
+dma_addr_t pci_map_single(struct pci_dev *pdev, void *ptr, size_t sz, int direction)
{
struct pcidev_cookie *pcp;
struct pci_iommu *iommu;
@@ -230,14 +220,13 @@ dma_addr_t pci_map_single(struct pci_dev *pdev, void *ptr, size_t sz)
unsigned long flags, npages, oaddr;
unsigned long i, base_paddr, ctx;
u32 bus_addr, ret;
+ unsigned long iopte_protection;
pcp = pdev->sysdata;
iommu = &pcp->pbm->parent->iommu;
strbuf = &pcp->pbm->stc;
- /* We still don't support devices which don't recognize at least 30 bits
- of bus address. Bug me to code it (is pretty easy actually). -jj */
- if ((pdev->dma_mask & 0x3fffffff) != 0x3fffffff)
+ if (direction == PCI_DMA_NONE)
BUG();
oaddr = (unsigned long)ptr;
@@ -254,13 +243,15 @@ dma_addr_t pci_map_single(struct pci_dev *pdev, void *ptr, size_t sz)
ctx = 0;
if (iommu->iommu_ctxflush)
ctx = iommu->iommu_cur_ctx++;
- if (strbuf->strbuf_enabled) {
- for (i = 0; i < npages; i++, base++, base_paddr += PAGE_SIZE)
- iopte_val(*base) = IOPTE_STREAMING(ctx, base_paddr);
- } else {
- for (i = 0; i < npages; i++, base++, base_paddr += PAGE_SIZE)
- iopte_val(*base) = IOPTE_CONSISTANT(ctx, base_paddr);
- }
+ if (strbuf->strbuf_enabled)
+ iopte_protection = IOPTE_STREAMING(ctx);
+ else
+ iopte_protection = IOPTE_CONSISTENT(ctx);
+ if (direction != PCI_DMA_TODEVICE)
+ iopte_protection |= IOPTE_WRITE;
+
+ for (i = 0; i < npages; i++, base++, base_paddr += PAGE_SIZE)
+ iopte_val(*base) = iopte_protection | base_paddr;
/* Flush the IOMMU TLB. */
if (iommu->iommu_ctxflush) {
@@ -276,7 +267,7 @@ dma_addr_t pci_map_single(struct pci_dev *pdev, void *ptr, size_t sz)
}
/* Unmap a single streaming mode DMA translation. */
-void pci_unmap_single(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz)
+void pci_unmap_single(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz, int direction)
{
struct pcidev_cookie *pcp;
struct pci_iommu *iommu;
@@ -284,6 +275,9 @@ void pci_unmap_single(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz)
iopte_t *base;
unsigned long flags, npages, i, ctx;
+ if (direction == PCI_DMA_NONE)
+ BUG();
+
pcp = pdev->sysdata;
iommu = &pcp->pbm->parent->iommu;
strbuf = &pcp->pbm->stc;
@@ -341,7 +335,7 @@ void pci_unmap_single(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz)
spin_unlock_irqrestore(&iommu->lock, flags);
}
-static inline void fill_sg(iopte_t *iopte, struct scatterlist *sg, int nused, unsigned long ctx, int streaming)
+static inline void fill_sg(iopte_t *iopte, struct scatterlist *sg, int nused, unsigned long iopte_protection)
{
struct scatterlist *dma_sg = sg;
int i;
@@ -381,10 +375,7 @@ static inline void fill_sg(iopte_t *iopte, struct scatterlist *sg, int nused, un
sg++;
}
- if (streaming)
- pteval = IOPTE_STREAMING(ctx, pteval);
- else
- pteval = IOPTE_CONSISTANT(ctx, pteval);
+ pteval = iopte_protection | (pteval & IOPTE_PAGE);
while (len > 0) {
*iopte++ = __iopte(pteval);
pteval += PAGE_SIZE;
@@ -419,12 +410,12 @@ static inline void fill_sg(iopte_t *iopte, struct scatterlist *sg, int nused, un
* When making changes here, inspect the assembly output. I was having
* hard time to kepp this routine out of using stack slots for holding variables.
*/
-int pci_map_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems)
+int pci_map_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction)
{
struct pcidev_cookie *pcp;
struct pci_iommu *iommu;
struct pci_strbuf *strbuf;
- unsigned long flags, ctx, i, npages;
+ unsigned long flags, ctx, i, npages, iopte_protection;
iopte_t *base;
u32 dma_base;
struct scatterlist *sgtmp;
@@ -432,7 +423,7 @@ int pci_map_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems)
/* Fast path single entry scatterlists. */
if (nelems == 1) {
- sglist->dvma_address = pci_map_single(pdev, sglist->address, sglist->length);
+ sglist->dvma_address = pci_map_single(pdev, sglist->address, sglist->length, direction);
sglist->dvma_length = sglist->length;
return 1;
}
@@ -441,9 +432,7 @@ int pci_map_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems)
iommu = &pcp->pbm->parent->iommu;
strbuf = &pcp->pbm->stc;
- /* We still don't support devices which don't recognize at least 30 bits
- of bus address. Bug me to code it (is pretty easy actually). -jj */
- if ((pdev->dma_mask & 0x3fffffff) != 0x3fffffff)
+ if (direction == PCI_DMA_NONE)
BUG();
/* Step 1: Prepare scatter list. */
@@ -474,7 +463,13 @@ int pci_map_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems)
ctx = iommu->iommu_cur_ctx++;
/* Step 5: Create the mappings. */
- fill_sg (base, sglist, used, ctx, strbuf->strbuf_enabled);
+ if (strbuf->strbuf_enabled)
+ iopte_protection = IOPTE_STREAMING(ctx);
+ else
+ iopte_protection = IOPTE_CONSISTENT(ctx);
+ if (direction != PCI_DMA_TODEVICE)
+ iopte_protection |= IOPTE_WRITE;
+ fill_sg (base, sglist, used, iopte_protection);
#ifdef VERIFY_SG
verify_sglist(sglist, nelems, base, npages);
#endif
@@ -493,7 +488,7 @@ int pci_map_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems)
}
/* Unmap a set of streaming mode DMA translations. */
-void pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems)
+void pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction)
{
struct pcidev_cookie *pcp;
struct pci_iommu *iommu;
@@ -502,6 +497,9 @@ void pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems)
unsigned long flags, ctx, i, npages;
u32 bus_addr;
+ if (direction == PCI_DMA_NONE)
+ BUG();
+
pcp = pdev->sysdata;
iommu = &pcp->pbm->parent->iommu;
strbuf = &pcp->pbm->stc;
@@ -568,7 +566,7 @@ void pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems)
/* Make physical memory consistent for a single
* streaming mode DMA translation after a transfer.
*/
-void pci_dma_sync_single(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz)
+void pci_dma_sync_single(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz, int direction)
{
struct pcidev_cookie *pcp;
struct pci_iommu *iommu;
@@ -629,7 +627,7 @@ void pci_dma_sync_single(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz)
/* Make physical memory consistent for a set of streaming
* mode DMA translations after a transfer.
*/
-void pci_dma_sync_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems)
+void pci_dma_sync_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction)
{
struct pcidev_cookie *pcp;
struct pci_iommu *iommu;
@@ -690,3 +688,19 @@ void pci_dma_sync_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelem
spin_unlock_irqrestore(&iommu->lock, flags);
}
+
+int pci_dma_supported(struct pci_dev *pdev, dma_addr_t device_mask)
+{
+ struct pcidev_cookie *pcp = pdev->sysdata;
+ u32 dma_addr_mask;
+
+ if (pdev == NULL) {
+ dma_addr_mask = 0xffffffff;
+ } else {
+ struct pci_iommu *iommu = &pcp->pbm->parent->iommu;
+
+ dma_addr_mask = iommu->dma_addr_mask;
+ }
+
+ return (device_mask & dma_addr_mask) == dma_addr_mask;
+}