From c6d8f92cfd7f4f19eb3b16545b3b68c561978fe8 Mon Sep 17 00:00:00 2001 From: Kristoffer Glembo <kristoffer@gaisler.com> Date: Mon, 7 Jun 2010 14:00:30 +0200 Subject: [PATCH] sparc32: Added LEON dma_ops. Added leon3_dma_ops and mmu_inval_dma_area. --- arch/sparc/kernel/ioport.c | 139 +++++++++++++++++++++++++++++++------------ 1 files changed, 100 insertions(+), 39 deletions(-) --- a/arch/sparc/kernel/ioport.c +++ b/arch/sparc/kernel/ioport.c @@ -50,10 +50,15 @@ #include <asm/io-unit.h> #include <asm/leon.h> -#ifdef CONFIG_SPARC_LEON -#define mmu_inval_dma_area(p, l) leon_flush_dcache_all() -#else +#ifndef CONFIG_SPARC_LEON #define mmu_inval_dma_area(p, l) /* Anton pulled it out for 2.4.0-xx */ +#else +static inline void mmu_inval_dma_area(unsigned long va, unsigned long len) +{ + if (!sparc_leon3_snooping_enabled()) { + leon_flush_dcache_all(); + } +} #endif static struct resource *_sparc_find_resource(struct resource *r, @@ -254,7 +259,7 @@ static void *sbus_alloc_coherent(struct dma_addr_t *dma_addrp, gfp_t gfp) { struct platform_device *op = to_platform_device(dev); - unsigned long len_total = (len + PAGE_SIZE-1) & PAGE_MASK; + unsigned long len_total = PAGE_ALIGN(len); unsigned long va; struct resource *res; int order; @@ -287,15 +292,19 @@ static void *sbus_alloc_coherent(struct * XXX That's where sdev would be used. Currently we load * all iommu tables with the same translations. */ - if (mmu_map_dma_area(dev, dma_addrp, va, res->start, len_total) != 0) - goto err_noiommu; - - res->name = op->dev.of_node->name; +#ifdef CONFIG_SPARC_LEON + sparc_mapiorange(0, virt_to_phys(va), res->start, len_total); + *dma_addrp = virt_to_phys(va); +#else + if (mmu_map_dma_area(dev, dma_addrp, va, res->start, len_total) != 0) { + release_resource(res); + goto err_nova; + } +#endif + res->name = op->node->name; return (void *)(unsigned long)res->start; -err_noiommu: - release_resource(res); err_nova: free_pages(va, order); err_nomem: @@ -321,7 +330,7 @@ static void sbus_free_coherent(struct de return; } - n = (n + PAGE_SIZE-1) & PAGE_MASK; + n = PAGE_ALIGN(n); if ((res->end-res->start)+1 != n) { printk("sbus_free_consistent: region 0x%lx asked 0x%zx\n", (long)((res->end-res->start)+1), n); @@ -333,7 +342,12 @@ static void sbus_free_coherent(struct de /* mmu_inval_dma_area(va, n); */ /* it's consistent, isn't it */ pgv = virt_to_page(p); - mmu_unmap_dma_area(dev, ba, n); + +#ifdef CONFIG_SPARC_LEON + sparc_unmapiorange((unsigned long)p, n); +#else + mmu_unmap_dma_area(dev, ba, n); +#endif __free_pages(pgv, get_order(n)); } @@ -408,9 +422,6 @@ struct dma_map_ops sbus_dma_ops = { .sync_sg_for_device = sbus_sync_sg_for_device, }; -struct dma_map_ops *dma_ops = &sbus_dma_ops; -EXPORT_SYMBOL(dma_ops); - static int __init sparc_register_ioport(void) { register_proc_sparc_ioport(); @@ -422,7 +433,7 @@ arch_initcall(sparc_register_ioport); #endif /* CONFIG_SBUS */ -#ifdef CONFIG_PCI +#if defined(CONFIG_PCI) || defined(CONFIG_SPARC_LEON) /* Allocate and map kernel buffer using consistent mode DMA for a device. * hwdev should be valid struct pci_dev pointer for PCI devices. @@ -430,7 +441,7 @@ arch_initcall(sparc_register_ioport); static void *pci32_alloc_coherent(struct device *dev, size_t len, dma_addr_t *pba, gfp_t gfp) { - unsigned long len_total = (len + PAGE_SIZE-1) & PAGE_MASK; + unsigned long len_total = PAGE_ALIGN(len); unsigned long va; struct resource *res; int order; @@ -463,10 +474,6 @@ static void *pci32_alloc_coherent(struct return NULL; } mmu_inval_dma_area(va, len_total); -#if 0 -/* P3 */ printk("pci_alloc_consistent: kva %lx uncva %lx phys %lx size %lx\n", - (long)va, (long)res->start, (long)virt_to_phys(va), len_total); -#endif sparc_mapiorange(0, virt_to_phys(va), res->start, len_total); *pba = virt_to_phys(va); /* equals virt_to_bus (R.I.P.) for us. */ @@ -498,7 +505,7 @@ static void pci32_free_coherent(struct d return; } - n = (n + PAGE_SIZE-1) & PAGE_MASK; + n = PAGE_ALIGN(n); if ((res->end-res->start)+1 != n) { printk("pci_free_consistent: region 0x%lx asked 0x%lx\n", (long)((res->end-res->start)+1), (long)n); @@ -515,6 +522,14 @@ static void pci32_free_coherent(struct d free_pages(pgp, get_order(n)); } +static void pci32_unmap_page(struct device *dev, dma_addr_t ba, size_t size, + enum dma_data_direction dir, struct dma_attrs *attrs) +{ + if (dir != PCI_DMA_TODEVICE) { + mmu_inval_dma_area((unsigned long)phys_to_virt(ba), PAGE_ALIGN(size)); + } +} + /* * Same as pci_map_single, but with pages. */ @@ -551,8 +566,7 @@ static int pci32_map_sg(struct device *d /* IIep is write-through, not flushing. */ for_each_sg(sgl, sg, nents, n) { - BUG_ON(page_address(sg_page(sg)) == NULL); - sg->dma_address = virt_to_phys(sg_virt(sg)); + sg->dma_address = sg_phys(sg); sg->dma_length = sg->length; } return nents; @@ -571,10 +585,7 @@ static void pci32_unmap_sg(struct device if (dir != PCI_DMA_TODEVICE) { for_each_sg(sgl, sg, nents, n) { - BUG_ON(page_address(sg_page(sg)) == NULL); - mmu_inval_dma_area( - (unsigned long) page_address(sg_page(sg)), - (sg->length + PAGE_SIZE-1) & PAGE_MASK); + mmu_inval_dma_area((unsigned long)sg_virt(sg), PAGE_ALIGN(sg->length)); } } } @@ -594,7 +605,7 @@ static void pci32_sync_single_for_cpu(st { if (dir != PCI_DMA_TODEVICE) { mmu_inval_dma_area((unsigned long)phys_to_virt(ba), - (size + PAGE_SIZE-1) & PAGE_MASK); + PAGE_ALIGN(size)); } } @@ -621,10 +632,7 @@ static void pci32_sync_sg_for_cpu(struct if (dir != PCI_DMA_TODEVICE) { for_each_sg(sgl, sg, nents, n) { - BUG_ON(page_address(sg_page(sg)) == NULL); - mmu_inval_dma_area( - (unsigned long) page_address(sg_page(sg)), - (sg->length + PAGE_SIZE-1) & PAGE_MASK); + mmu_inval_dma_area((unsigned long)sg_virt(sg), PAGE_ALIGN(sg->length)); } } } @@ -637,18 +645,38 @@ static void pci32_sync_sg_for_device(str if (dir != PCI_DMA_TODEVICE) { for_each_sg(sgl, sg, nents, n) { - BUG_ON(page_address(sg_page(sg)) == NULL); - mmu_inval_dma_area( - (unsigned long) page_address(sg_page(sg)), - (sg->length + PAGE_SIZE-1) & PAGE_MASK); + mmu_inval_dma_area((unsigned long)sg_virt(sg), PAGE_ALIGN(sg->length)); } } } +/* LEON3 unmapping functions + * + * We can only invalidate the whole cache so unmap_page and unmap_sg do the same thing + */ +static void leon3_unmap_page(struct device *dev, dma_addr_t ba, size_t size, + enum dma_data_direction dir, struct dma_attrs *attrs) +{ + if (dir != PCI_DMA_TODEVICE) { + mmu_inval_dma_area(0, 0); + } +} + +static void leon3_unmap_sg(struct device *dev, struct scatterlist *sgl, + int nents, enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + + if (dir != PCI_DMA_TODEVICE) { + mmu_inval_dma_area(0, 0); + } +} + struct dma_map_ops pci32_dma_ops = { .alloc_coherent = pci32_alloc_coherent, .free_coherent = pci32_free_coherent, .map_page = pci32_map_page, + .unmap_page = pci32_unmap_page, .map_sg = pci32_map_sg, .unmap_sg = pci32_unmap_sg, .sync_single_for_cpu = pci32_sync_single_for_cpu, @@ -658,7 +686,30 @@ struct dma_map_ops pci32_dma_ops = { }; EXPORT_SYMBOL(pci32_dma_ops); -#endif /* CONFIG_PCI */ +struct dma_map_ops leon3_dma_ops = { + .alloc_coherent = sbus_alloc_coherent, + .free_coherent = sbus_free_coherent, + .map_page = pci32_map_page, + .unmap_page = leon3_unmap_page, + .map_sg = pci32_map_sg, + .unmap_sg = leon3_unmap_sg, + .sync_single_for_cpu = pci32_sync_single_for_cpu, + .sync_single_for_device = pci32_sync_single_for_device, + .sync_sg_for_cpu = pci32_sync_sg_for_cpu, + .sync_sg_for_device = pci32_sync_sg_for_device, +}; + +#endif /* CONFIG_PCI || CONFIG_SPARC_LEON */ + +#ifdef CONFIG_SPARC_LEON +struct dma_map_ops *dma_ops = &leon3_dma_ops; +#else +struct dma_map_ops *dma_ops = &sbus_dma_ops; +#endif + +#ifdef CONFIG_SBUS +EXPORT_SYMBOL(dma_ops); +#endif /* * Return whether the given PCI device DMA address mask can be @@ -676,6 +727,16 @@ int dma_supported(struct device *dev, u6 } EXPORT_SYMBOL(dma_supported); +int dma_set_mask(struct device *dev, u64 dma_mask) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) + return pci_set_dma_mask(to_pci_dev(dev), dma_mask); +#endif + return -EOPNOTSUPP; +} +EXPORT_SYMBOL(dma_set_mask); + #ifdef CONFIG_PROC_FS static int sparc_io_proc_show(struct seq_file *m, void *v) @@ -717,7 +778,7 @@ static const struct file_operations spar static struct resource *_sparc_find_resource(struct resource *root, unsigned long hit) { - struct resource *tmp; + struct resource *tmp; for (tmp = root->child; tmp != 0; tmp = tmp->sibling) { if (tmp->start <= hit && tmp->end >= hit)