===== Documentation/kernel-parameters.txt 1.68 vs edited ===== --- 1.68/Documentation/kernel-parameters.txt 2005-01-10 13:55:30 -08:00 +++ edited/Documentation/kernel-parameters.txt 2005-01-20 20:50:08 -08:00 @@ -1165,6 +1165,9 @@ smart2= [HW] Format: [,[,...,]] + sn_bounce_force [IA-64, sn2] + Force bounce buffering on sn2 platforms. + snd-ad1816a= [HW,ALSA] snd-ad1848= [HW,ALSA] ===== arch/ia64/lib/swiotlb.c 1.25 vs edited ===== --- 1.25/arch/ia64/lib/swiotlb.c 2005-01-18 11:53:23 -08:00 +++ edited/arch/ia64/lib/swiotlb.c 2005-01-20 20:01:50 -08:00 @@ -162,7 +162,7 @@ /* If the device has a mask, use it, otherwise default to 32 bits */ if (hwdev && hwdev->dma_mask) mask = *hwdev->dma_mask; - return (addr & ~mask) != 0; + return !(__pa(MIN_DMA_ADDRESS) <= addr && addr < __pa(MAX_DMA_ADDRESS)); } /* ===== arch/ia64/mm/init.c 1.76 vs edited ===== --- 1.76/arch/ia64/mm/init.c 2005-01-18 12:06:18 -08:00 +++ edited/arch/ia64/mm/init.c 2005-01-20 10:20:18 -08:00 @@ -43,6 +43,7 @@ extern void efi_get_pal_addr (void); unsigned long MAX_DMA_ADDRESS = PAGE_OFFSET + 0x100000000UL; +unsigned long MIN_DMA_ADDRESS; #ifdef CONFIG_VIRTUAL_MEM_MAP unsigned long vmalloc_end = VMALLOC_END_INIT; ===== arch/ia64/sn/kernel/setup.c 1.47 vs edited ===== --- 1.47/arch/ia64/sn/kernel/setup.c 2005-01-18 12:27:58 -08:00 +++ edited/arch/ia64/sn/kernel/setup.c 2005-01-20 17:26:41 -08:00 @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -237,6 +238,22 @@ } /** + * sn_direct_map_base - return direct map base address + * + * Returns the physical address that the direct map registers point to. + * Code assumes that all the direct map registers are pointing at the same + * window. + */ +unsigned long sn_direct_map_base(void) +{ + /* + * FIXME: Should be the value of the direct map register(s) in + * cacheable space. + */ + return __pa(GLOBAL_CAC_ADDR(cnodeid_to_nasid(0), 0)); +} + +/** * sn_setup - SN platform setup routine * @cmdline_p: kernel command line * @@ -268,7 +285,14 @@ } #endif /* def(CONFIG_VT) && def(CONFIG_VGA_CONSOLE) */ - MAX_DMA_ADDRESS = PAGE_OFFSET + MAX_PHYS_MEMORY; + /* + * 32 bit PCI-X devices need bounce buffering since SGI PCI-X bridges + * don't have IOMMUs. Use ZONE_DMA for bounce buffering with swiotlb. + * The window is 2GB in size. + */ + MAX_DMA_ADDRESS = (unsigned long)__va(sn_direct_map_base() + + (1UL << 31)); + MIN_DMA_ADDRESS = (unsigned long)__va(sn_direct_map_base()); memset(physical_node_map, -1, sizeof(physical_node_map)); for (pxm = 0; pxm < MAX_PXM_DOMAINS; pxm++) ===== arch/ia64/sn/pci/pci_dma.c 1.4 vs edited ===== --- 1.4/arch/ia64/sn/pci/pci_dma.c 2005-01-20 10:49:10 -08:00 +++ edited/arch/ia64/sn/pci/pci_dma.c 2005-01-21 07:59:41 -08:00 @@ -19,6 +19,164 @@ #define SG_ENT_VIRT_ADDRESS(sg) (page_address((sg)->page) + (sg)->offset) #define SG_ENT_PHYS_ADDRESS(SG) virt_to_phys(SG_ENT_VIRT_ADDRESS(SG)) +/* + * sn2 bounce buffering routines + * + * We use the swiotlb here since it's convenient and nearly exactly what + * we need. The swiotlb will allocate DMA memory for a bounce buffer, + * return it to the device, and then copy the new data back to the original + * buffer at unmap time. sn2 deals with remapped physical addresses, however, + * so some translation must be done before and after the calls to the swiotlb + * routines, which deal only with CPU physical and virtual addresses. + */ + +static int sn_bounce_force; + +static int __init sn_bounce_force_setup(char *str) +{ + sn_bounce_force = 1; + printk(KERN_WARNING "forcing sn2 bounce buffering\n"); + return 0; +} +__setup("sn_bounce_force", sn_bounce_force_setup); + +/** + * sn_bounce_dev - use bounce buffering for this device? + * @dev: device pointer + * + * Returns true or false depending on whether this device should be bounce + * buffered. Should return true for 32 bit devices running in PCI-X mode on + * PIC chips. + */ +static int sn_bounce_dev(struct device *dev) +{ + return sn_bounce_force; +} + +/** + * sn_phys_to_dma32 - convert a CPU physical address to a direct32 one + * @dev: device we're mapping for + * @addr: DMA address to map + * + * Move an address into the direct32 space of a device. Right now it + * just subtracts MIN_DMA_ADDRESS from @addr. That will have to change + * if we ever want to support multiple direct32 address windows. The + * platform init code is responsible for setting MIN_DMA_ADDRESS correctly + * (i.e. it should match the contents of all the direct32 map registers). + */ +static dma_addr_t sn_phys_to_dma32(struct device *dev, dma_addr_t addr) +{ + /* @addr must be in the CPU's view of the direct32 window */ + BUG_ON(addr < __pa(MIN_DMA_ADDRESS) || __pa(MAX_DMA_ADDRESS) <= addr); + + /* + * Move the addr into the direct32 window and turn on the + * direct map bit in the new DMA addr + */ + return (addr - __pa(MIN_DMA_ADDRESS)) | 0x80000000; +} + +/** + * sn_dma32_to_phys - relocate an address back into CPU physical space + * @dev: device to unmap for + * @addr: addr to unmap + * + * Turn @addr back into a CPU physical address based on the direct32 map + * window for @dev. Intended to be used with DMA routines so it returns + * dma_addr_t. + */ +static dma_addr_t sn_dma32_to_phys(struct device *dev, dma_addr_t addr) +{ + BUG_ON(addr > 0xffffffff); + return (addr & 0x7fffffff) | __pa(MIN_DMA_ADDRESS); +} + +/** + * sn_bounce_map_single - map a single page in bounce buffer space + * @dev: device we're mapping for + * @cpu_addr: CPU virtual address of page + * @size: size of mapping + * @dir: direction of DMA + * + * Allocate memory from bounce buffer space and map it into the direct32 + * window space for @dev. The data will be copied back into @cpu_addr when + * the unmap occurs. + */ +static dma_addr_t sn_bounce_map_single(struct device *dev, void *cpu_addr, + size_t size, int dir) +{ + dma_addr_t phys_addr; /* swiotlb returns CPU phys addrs */ + + phys_addr = swiotlb_map_single(dev, cpu_addr, size, dir); + if (!phys_addr) + return 0; + return sn_phys_to_dma32(dev, phys_addr); +} + +/** + * sn_bounce_unmap_single - 'unbounce' a buffer mapped with the above fn + * @dev: device we're unmapping + * @dma_addr: DMA address for this bounce buffer + * @size: size of buffer + * @dir: DMA direction + * + * Remap @dma_addr back into CPU physical space and let the swiotlb copy + * the data from the bounce buffer into the target. + */ +static void sn_bounce_unmap_single(struct device *dev, dma_addr_t dma_addr, + size_t size, int dir) +{ + unsigned long phys_addr = sn_dma32_to_phys(dev, dma_addr); + + swiotlb_unmap_single(dev, phys_addr, size, dir); +} + +/** + * sn_bounce_unmap_sg - unmap a bounce sg list + * @dev: device we're unmapping + * @sg: scatterlist to unmap + * @num: number of elements in @sg + * @direction: DMA direction + * + * Remap the addresses back into CPU physical space and then let the swiotlb + * do its thing. + */ +void sn_bounce_unmap_sg(struct device *dev, struct scatterlist *sg, int num, + int direction) +{ + struct scatterlist *sg_index = sg; + int i; + + for (i = 0; i < num; i++, sg_index++) + sg_index->dma_address = sn_dma32_to_phys(dev, sg_index->dma_address); + + return swiotlb_unmap_sg(dev, sg, num, direction); +} + +/** + * sn_bounce_map_sg - map a bounce sg list + * @dev: device we're mapping + * @sg: scatterlist to map + * @num: number of elements in @sg + * @direction: DMA direction + * + * Call the swiotlb to allocate bounce buffers and remap into physical space, + * then remap again into direct32 space. + */ +int sn_bounce_map_sg(struct device *dev, struct scatterlist *sg, int num, + int direction) +{ + int i; + + if (swiotlb_map_sg(dev, sg, num, direction) != num) + return 0; + + for (i = 0; i < num; i++, sg++) + sg->dma_address = sn_phys_to_dma32(dev, sg->dma_address); + + return num; +} + /** * sn_dma_supported - test a DMA mask * @dev: device to test @@ -81,15 +239,19 @@ void *cpuaddr; unsigned long phys_addr; struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(to_pci_dev(dev)); + int gfp_mask = GFP_ATOMIC; BUG_ON(dev->bus != &pci_bus_type); + if (sn_bounce_dev(dev)) + gfp_mask |= GFP_DMA; /* device needs ZONE_DMA memory */ + /* * Allocate the memory. * FIXME: We should be doing alloc_pages_node for the node closest * to the PCI device. */ - if (!(cpuaddr = (void *)__get_free_pages(GFP_ATOMIC, get_order(size)))) + if (!(cpuaddr = (void *)__get_free_pages(gfp_mask, get_order(size)))) return NULL; memset(cpuaddr, 0x0, size); @@ -164,7 +326,11 @@ BUG_ON(dev->bus != &pci_bus_type); + if (sn_bounce_dev(dev)) + return sn_bounce_map_single(dev, cpu_addr, size, direction); + phys_addr = __pa(cpu_addr); + dma_addr = pcibr_dma_map(pcidev_info, phys_addr, size, 0); if (!dma_addr) { printk(KERN_ERR "%s: out of ATEs\n", __FUNCTION__); @@ -191,6 +357,10 @@ struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(to_pci_dev(dev)); BUG_ON(dev->bus != &pci_bus_type); + + if (sn_bounce_dev(dev)) + return sn_bounce_unmap_single(dev, dma_addr, size, direction); + pcibr_dma_unmap(pcidev_info, dma_addr, direction); } @@ -213,6 +383,9 @@ BUG_ON(dev->bus != &pci_bus_type); + if (sn_bounce_dev(dev)) + return sn_bounce_unmap_sg(dev, sg, nhwentries, direction); + for (i = 0; i < nhwentries; i++, sg++) { pcibr_dma_unmap(pcidev_info, sg->dma_address, direction); sg->dma_address = (dma_addr_t) NULL; @@ -240,6 +413,9 @@ BUG_ON(dev->bus != &pci_bus_type); + if (sn_bounce_dev(dev)) + return sn_bounce_map_sg(dev, sg, nhwentries, direction); + /* * Setup a DMA address for each entry in the scatterlist. */ @@ -270,6 +446,11 @@ size_t size, int direction) { BUG_ON(dev->bus != &pci_bus_type); + + if (sn_bounce_dev(dev)) { + dma_addr_t phys_addr = sn_dma32_to_phys(dev, dma_handle); + swiotlb_sync_single_for_cpu(dev, phys_addr, size, direction); + } } EXPORT_SYMBOL(sn_dma_sync_single_for_cpu); @@ -278,14 +459,37 @@ size_t size, int direction) { BUG_ON(dev->bus != &pci_bus_type); + + if (sn_bounce_dev(dev)) { + dma_addr_t phys_addr = sn_dma32_to_phys(dev, dma_handle); + swiotlb_sync_single_for_device(dev, phys_addr, size, + direction); + } } EXPORT_SYMBOL(sn_dma_sync_single_for_device); +/* + * For sgs that need to be bounced, we convert all the addresses to physical + * first, then call the swiotlb, then convert them back. + */ + void sn_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, int direction) { BUG_ON(dev->bus != &pci_bus_type); + + if (sn_bounce_dev(dev)) { + struct scatterlist *sg_index = sg; + int i; + + for (i = 0; i < nelems; i++, sg_index++) + sg->dma_address = sn_dma32_to_phys(dev, sg->dma_address); + swiotlb_sync_sg_for_cpu(dev, sg, nelems, direction); + sg_index = sg; + for (i = 0; i < nelems; i++, sg_index++) + sg->dma_address = sn_phys_to_dma32(dev, sg->dma_address); + } } EXPORT_SYMBOL(sn_dma_sync_sg_for_cpu); @@ -294,6 +498,18 @@ int nelems, int direction) { BUG_ON(dev->bus != &pci_bus_type); + + if (sn_bounce_dev(dev)) { + struct scatterlist *sg_index = sg; + int i; + + for (i = 0; i < nelems; i++, sg_index++) + sg->dma_address = sn_dma32_to_phys(dev, sg->dma_address); + swiotlb_sync_sg_for_device(dev, sg, nelems, direction); + sg_index = sg; + for (i = 0; i < nelems; i++, sg_index++) + sg->dma_address = sn_phys_to_dma32(dev, sg->dma_address); + } } EXPORT_SYMBOL(sn_dma_sync_sg_for_device); ===== include/asm-ia64/dma.h 1.3 vs edited ===== --- 1.3/include/asm-ia64/dma.h 2002-08-12 20:47:44 -07:00 +++ edited/include/asm-ia64/dma.h 2005-01-20 17:14:44 -08:00 @@ -10,7 +10,9 @@ #include /* need byte IO */ +/* CPU virtual addresses in identity mapped space */ extern unsigned long MAX_DMA_ADDRESS; +extern unsigned long MIN_DMA_ADDRESS; #ifdef CONFIG_PCI extern int isa_dma_bridge_buggy; ===== include/asm-ia64/machvec_sn2.h 1.17 vs edited ===== --- 1.17/include/asm-ia64/machvec_sn2.h 2005-01-10 14:04:23 -08:00 +++ edited/include/asm-ia64/machvec_sn2.h 2005-01-20 10:20:45 -08:00 @@ -111,7 +111,6 @@ #define platform_pci_get_legacy_mem sn_pci_get_legacy_mem #define platform_pci_legacy_read sn_pci_legacy_read #define platform_pci_legacy_write sn_pci_legacy_write -#define platform_dma_init machvec_noop #define platform_dma_alloc_coherent sn_dma_alloc_coherent #define platform_dma_free_coherent sn_dma_free_coherent #define platform_dma_map_single sn_dma_map_single