diff options
author | Chris Metcalf <cmetcalf@tilera.com> | 2013-08-02 12:24:42 -0400 |
---|---|---|
committer | Chris Metcalf <cmetcalf@tilera.com> | 2013-08-06 12:52:33 -0400 |
commit | 803c874abe1358998ab65a8cca728684ebb50a13 (patch) | |
tree | 06f6ca14ed84458c0bd2b40e8b61a3cb4c0e8e1c | |
parent | 26cde05a2cb7d4c0f4cd1d4aeeadc2939c972786 (diff) |
tile: support LSI MEGARAID SAS HBA hybrid dma_ops
The LSI MEGARAID SAS HBA suffers from the problem where it can do
64-bit DMA to streaming buffers but not to consistent buffers.
In other words, 64-bit DMA is used for disk data transfers and 32-bit
DMA must be used for control message transfers. According to LSI,
the firmware is not fully functional yet. This change implements a
kind of hybrid dma_ops to support this.
Note that on most other platforms, the 64-bit DMA addressing space is the
same as the 32-bit DMA space and they overlap the physical memory space.
No special arrangement is needed to support this kind of mixed DMA
capability. On TILE-Gx, the 64-bit DMA space is completely separate
from the 32-bit DMA space. Due to the use of the IOMMU, the 64-bit DMA
space doesn't overlap the physical memory space. On the other hand,
the 32-bit DMA space overlaps the physical memory space under 4GB.
The separate address spaces make it necessary to have separate dma_ops.
Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
-rw-r--r-- | arch/tile/include/asm/dma-mapping.h | 10 | ||||
-rw-r--r-- | arch/tile/kernel/pci-dma.c | 40 |
2 files changed, 38 insertions, 12 deletions
diff --git a/arch/tile/include/asm/dma-mapping.h b/arch/tile/include/asm/dma-mapping.h index f2ff191376b4..4a60059876e6 100644 --- a/arch/tile/include/asm/dma-mapping.h +++ b/arch/tile/include/asm/dma-mapping.h | |||
@@ -23,6 +23,7 @@ | |||
23 | extern struct dma_map_ops *tile_dma_map_ops; | 23 | extern struct dma_map_ops *tile_dma_map_ops; |
24 | extern struct dma_map_ops *gx_pci_dma_map_ops; | 24 | extern struct dma_map_ops *gx_pci_dma_map_ops; |
25 | extern struct dma_map_ops *gx_legacy_pci_dma_map_ops; | 25 | extern struct dma_map_ops *gx_legacy_pci_dma_map_ops; |
26 | extern struct dma_map_ops *gx_hybrid_pci_dma_map_ops; | ||
26 | 27 | ||
27 | static inline struct dma_map_ops *get_dma_ops(struct device *dev) | 28 | static inline struct dma_map_ops *get_dma_ops(struct device *dev) |
28 | { | 29 | { |
@@ -44,12 +45,12 @@ static inline void set_dma_offset(struct device *dev, dma_addr_t off) | |||
44 | 45 | ||
45 | static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr) | 46 | static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr) |
46 | { | 47 | { |
47 | return paddr + get_dma_offset(dev); | 48 | return paddr; |
48 | } | 49 | } |
49 | 50 | ||
50 | static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr) | 51 | static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr) |
51 | { | 52 | { |
52 | return daddr - get_dma_offset(dev); | 53 | return daddr; |
53 | } | 54 | } |
54 | 55 | ||
55 | static inline void dma_mark_clean(void *addr, size_t size) {} | 56 | static inline void dma_mark_clean(void *addr, size_t size) {} |
@@ -88,7 +89,10 @@ dma_set_mask(struct device *dev, u64 mask) | |||
88 | struct dma_map_ops *dma_ops = get_dma_ops(dev); | 89 | struct dma_map_ops *dma_ops = get_dma_ops(dev); |
89 | 90 | ||
90 | /* Handle legacy PCI devices with limited memory addressability. */ | 91 | /* Handle legacy PCI devices with limited memory addressability. */ |
91 | if ((dma_ops == gx_pci_dma_map_ops) && (mask <= DMA_BIT_MASK(32))) { | 92 | if ((dma_ops == gx_pci_dma_map_ops || |
93 | dma_ops == gx_hybrid_pci_dma_map_ops || | ||
94 | dma_ops == gx_legacy_pci_dma_map_ops) && | ||
95 | (mask <= DMA_BIT_MASK(32))) { | ||
92 | set_dma_ops(dev, gx_legacy_pci_dma_map_ops); | 96 | set_dma_ops(dev, gx_legacy_pci_dma_map_ops); |
93 | set_dma_offset(dev, 0); | 97 | set_dma_offset(dev, 0); |
94 | if (mask > dev->archdata.max_direct_dma_addr) | 98 | if (mask > dev->archdata.max_direct_dma_addr) |
diff --git a/arch/tile/kernel/pci-dma.c b/arch/tile/kernel/pci-dma.c index b9fe80ec1089..7e22e73264a9 100644 --- a/arch/tile/kernel/pci-dma.c +++ b/arch/tile/kernel/pci-dma.c | |||
@@ -357,7 +357,7 @@ static void *tile_pci_dma_alloc_coherent(struct device *dev, size_t size, | |||
357 | 357 | ||
358 | addr = page_to_phys(pg); | 358 | addr = page_to_phys(pg); |
359 | 359 | ||
360 | *dma_handle = phys_to_dma(dev, addr); | 360 | *dma_handle = addr + get_dma_offset(dev); |
361 | 361 | ||
362 | return page_address(pg); | 362 | return page_address(pg); |
363 | } | 363 | } |
@@ -387,7 +387,7 @@ static int tile_pci_dma_map_sg(struct device *dev, struct scatterlist *sglist, | |||
387 | sg->dma_address = sg_phys(sg); | 387 | sg->dma_address = sg_phys(sg); |
388 | __dma_prep_pa_range(sg->dma_address, sg->length, direction); | 388 | __dma_prep_pa_range(sg->dma_address, sg->length, direction); |
389 | 389 | ||
390 | sg->dma_address = phys_to_dma(dev, sg->dma_address); | 390 | sg->dma_address = sg->dma_address + get_dma_offset(dev); |
391 | #ifdef CONFIG_NEED_SG_DMA_LENGTH | 391 | #ifdef CONFIG_NEED_SG_DMA_LENGTH |
392 | sg->dma_length = sg->length; | 392 | sg->dma_length = sg->length; |
393 | #endif | 393 | #endif |
@@ -422,7 +422,7 @@ static dma_addr_t tile_pci_dma_map_page(struct device *dev, struct page *page, | |||
422 | BUG_ON(offset + size > PAGE_SIZE); | 422 | BUG_ON(offset + size > PAGE_SIZE); |
423 | __dma_prep_page(page, offset, size, direction); | 423 | __dma_prep_page(page, offset, size, direction); |
424 | 424 | ||
425 | return phys_to_dma(dev, page_to_pa(page) + offset); | 425 | return page_to_pa(page) + offset + get_dma_offset(dev); |
426 | } | 426 | } |
427 | 427 | ||
428 | static void tile_pci_dma_unmap_page(struct device *dev, dma_addr_t dma_address, | 428 | static void tile_pci_dma_unmap_page(struct device *dev, dma_addr_t dma_address, |
@@ -432,7 +432,7 @@ static void tile_pci_dma_unmap_page(struct device *dev, dma_addr_t dma_address, | |||
432 | { | 432 | { |
433 | BUG_ON(!valid_dma_direction(direction)); | 433 | BUG_ON(!valid_dma_direction(direction)); |
434 | 434 | ||
435 | dma_address = dma_to_phys(dev, dma_address); | 435 | dma_address -= get_dma_offset(dev); |
436 | 436 | ||
437 | __dma_complete_page(pfn_to_page(PFN_DOWN(dma_address)), | 437 | __dma_complete_page(pfn_to_page(PFN_DOWN(dma_address)), |
438 | dma_address & PAGE_OFFSET, size, direction); | 438 | dma_address & PAGE_OFFSET, size, direction); |
@@ -445,7 +445,7 @@ static void tile_pci_dma_sync_single_for_cpu(struct device *dev, | |||
445 | { | 445 | { |
446 | BUG_ON(!valid_dma_direction(direction)); | 446 | BUG_ON(!valid_dma_direction(direction)); |
447 | 447 | ||
448 | dma_handle = dma_to_phys(dev, dma_handle); | 448 | dma_handle -= get_dma_offset(dev); |
449 | 449 | ||
450 | __dma_complete_pa_range(dma_handle, size, direction); | 450 | __dma_complete_pa_range(dma_handle, size, direction); |
451 | } | 451 | } |
@@ -456,7 +456,7 @@ static void tile_pci_dma_sync_single_for_device(struct device *dev, | |||
456 | enum dma_data_direction | 456 | enum dma_data_direction |
457 | direction) | 457 | direction) |
458 | { | 458 | { |
459 | dma_handle = dma_to_phys(dev, dma_handle); | 459 | dma_handle -= get_dma_offset(dev); |
460 | 460 | ||
461 | __dma_prep_pa_range(dma_handle, size, direction); | 461 | __dma_prep_pa_range(dma_handle, size, direction); |
462 | } | 462 | } |
@@ -558,21 +558,43 @@ static struct dma_map_ops pci_swiotlb_dma_ops = { | |||
558 | .mapping_error = swiotlb_dma_mapping_error, | 558 | .mapping_error = swiotlb_dma_mapping_error, |
559 | }; | 559 | }; |
560 | 560 | ||
561 | static struct dma_map_ops pci_hybrid_dma_ops = { | ||
562 | .alloc = tile_swiotlb_alloc_coherent, | ||
563 | .free = tile_swiotlb_free_coherent, | ||
564 | .map_page = tile_pci_dma_map_page, | ||
565 | .unmap_page = tile_pci_dma_unmap_page, | ||
566 | .map_sg = tile_pci_dma_map_sg, | ||
567 | .unmap_sg = tile_pci_dma_unmap_sg, | ||
568 | .sync_single_for_cpu = tile_pci_dma_sync_single_for_cpu, | ||
569 | .sync_single_for_device = tile_pci_dma_sync_single_for_device, | ||
570 | .sync_sg_for_cpu = tile_pci_dma_sync_sg_for_cpu, | ||
571 | .sync_sg_for_device = tile_pci_dma_sync_sg_for_device, | ||
572 | .mapping_error = tile_pci_dma_mapping_error, | ||
573 | .dma_supported = tile_pci_dma_supported | ||
574 | }; | ||
575 | |||
561 | struct dma_map_ops *gx_legacy_pci_dma_map_ops = &pci_swiotlb_dma_ops; | 576 | struct dma_map_ops *gx_legacy_pci_dma_map_ops = &pci_swiotlb_dma_ops; |
577 | struct dma_map_ops *gx_hybrid_pci_dma_map_ops = &pci_hybrid_dma_ops; | ||
562 | #else | 578 | #else |
563 | struct dma_map_ops *gx_legacy_pci_dma_map_ops; | 579 | struct dma_map_ops *gx_legacy_pci_dma_map_ops; |
580 | struct dma_map_ops *gx_hybrid_pci_dma_map_ops; | ||
564 | #endif | 581 | #endif |
565 | EXPORT_SYMBOL(gx_legacy_pci_dma_map_ops); | 582 | EXPORT_SYMBOL(gx_legacy_pci_dma_map_ops); |
583 | EXPORT_SYMBOL(gx_hybrid_pci_dma_map_ops); | ||
566 | 584 | ||
567 | #ifdef CONFIG_ARCH_HAS_DMA_SET_COHERENT_MASK | 585 | #ifdef CONFIG_ARCH_HAS_DMA_SET_COHERENT_MASK |
568 | int dma_set_coherent_mask(struct device *dev, u64 mask) | 586 | int dma_set_coherent_mask(struct device *dev, u64 mask) |
569 | { | 587 | { |
570 | struct dma_map_ops *dma_ops = get_dma_ops(dev); | 588 | struct dma_map_ops *dma_ops = get_dma_ops(dev); |
571 | 589 | ||
572 | /* Handle legacy PCI devices with limited memory addressability. */ | 590 | /* Handle hybrid PCI devices with limited memory addressability. */ |
573 | if (((dma_ops == gx_pci_dma_map_ops) || | 591 | if ((dma_ops == gx_pci_dma_map_ops || |
574 | (dma_ops == gx_legacy_pci_dma_map_ops)) && | 592 | dma_ops == gx_hybrid_pci_dma_map_ops || |
593 | dma_ops == gx_legacy_pci_dma_map_ops) && | ||
575 | (mask <= DMA_BIT_MASK(32))) { | 594 | (mask <= DMA_BIT_MASK(32))) { |
595 | if (dma_ops == gx_pci_dma_map_ops) | ||
596 | set_dma_ops(dev, gx_hybrid_pci_dma_map_ops); | ||
597 | |||
576 | if (mask > dev->archdata.max_direct_dma_addr) | 598 | if (mask > dev->archdata.max_direct_dma_addr) |
577 | mask = dev->archdata.max_direct_dma_addr; | 599 | mask = dev->archdata.max_direct_dma_addr; |
578 | } | 600 | } |