11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* pci-dma-nommu.c: Dynamic DMA mapping support for the FRV
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
444d1b980c72db0faf35adb082fb2208351803028David Woodhouse * Written by David Woodhouse (dwmw2@infradead.org)
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * modify it under the terms of the GNU General Public License
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * as published by the Free Software Foundation; either version
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 2 of the License, or (at your option) any later version.
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h>
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
14a5788caa269e446201018bb8879a1dd90f41d32bDavid Howells#include <linux/export.h>
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/dma-mapping.h>
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/list.h>
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/pci.h>
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 1
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DMA_SRAM_START	dma_coherent_mem_start
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DMA_SRAM_END	dma_coherent_mem_end
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else // Use video RAM on Matrox
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DMA_SRAM_START	0xe8900000
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DMA_SRAM_END	0xe8a00000
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct dma_alloc_record {
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct list_head	list;
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long		ofs;
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long		len;
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic DEFINE_SPINLOCK(dma_alloc_lock);
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic LIST_HEAD(dma_alloc_list);
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
37a5da7d3c6e8fcd7aaab6c4e1e9101ba333248ffbAl Virovoid *dma_alloc_coherent(struct device *hwdev, size_t size, dma_addr_t *dma_handle, gfp_t gfp)
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dma_alloc_record *new;
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct list_head *this = &dma_alloc_list;
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long start = DMA_SRAM_START;
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long end;
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!DMA_SRAM_START) {
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("%s called without any DMA area reserved!\n", __func__);
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return NULL;
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	new = kmalloc(sizeof (*new), GFP_ATOMIC);
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!new)
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return NULL;
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Round up to a reasonable alignment */
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	new->len = (size + 31) & ~31;
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&dma_alloc_lock, flags);
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_for_each (this, &dma_alloc_list) {
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct dma_alloc_record *this_r = list_entry(this, struct dma_alloc_record, list);
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		end = this_r->ofs;
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (end - start >= size)
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto gotone;
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		start = this_r->ofs + this_r->len;
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Reached end of list. */
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	end = DMA_SRAM_END;
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	this = &dma_alloc_list;
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (end - start >= size) {
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	gotone:
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		new->ofs = start;
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		list_add_tail(&new->list, this);
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_unlock_irqrestore(&dma_alloc_lock, flags);
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*dma_handle = start;
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return (void *)start;
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(new);
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&dma_alloc_lock, flags);
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return NULL;
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
87402344012ebe696d9353bbf056889ddaaec83079David HowellsEXPORT_SYMBOL(dma_alloc_coherent);
88402344012ebe696d9353bbf056889ddaaec83079David Howells
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid dma_free_coherent(struct device *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle)
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dma_alloc_record *rec;
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&dma_alloc_lock, flags);
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_for_each_entry(rec, &dma_alloc_list, list) {
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rec->ofs == dma_handle) {
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			list_del(&rec->list);
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kfree(rec);
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			spin_unlock_irqrestore(&dma_alloc_lock, flags);
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return;
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&dma_alloc_lock, flags);
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	BUG();
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
108402344012ebe696d9353bbf056889ddaaec83079David HowellsEXPORT_SYMBOL(dma_free_coherent);
109402344012ebe696d9353bbf056889ddaaec83079David Howells
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size,
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			  enum dma_data_direction direction)
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
113db5c444eeb781788a0db36a2682b2417cf71f764Stoyan Gaydarov	BUG_ON(direction == DMA_NONE);
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	frv_cache_wback_inv((unsigned long) ptr, (unsigned long) ptr + size);
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return virt_to_bus(ptr);
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
120402344012ebe696d9353bbf056889ddaaec83079David HowellsEXPORT_SYMBOL(dma_map_single);
121402344012ebe696d9353bbf056889ddaaec83079David Howells
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       enum dma_data_direction direction)
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i=0; i<nents; i++)
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		frv_cache_wback_inv(sg_dma_address(&sg[i]),
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				    sg_dma_address(&sg[i]) + sg_dma_len(&sg[i]));
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
131db5c444eeb781788a0db36a2682b2417cf71f764Stoyan Gaydarov	BUG_ON(direction == DMA_NONE);
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return nents;
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
135402344012ebe696d9353bbf056889ddaaec83079David Howells
136402344012ebe696d9353bbf056889ddaaec83079David HowellsEXPORT_SYMBOL(dma_map_sg);
137c9af956cf7548bc352557eb8356913e489fd8716David Howells
138c9af956cf7548bc352557eb8356913e489fd8716David Howellsdma_addr_t dma_map_page(struct device *dev, struct page *page, unsigned long offset,
139c9af956cf7548bc352557eb8356913e489fd8716David Howells			size_t size, enum dma_data_direction direction)
140c9af956cf7548bc352557eb8356913e489fd8716David Howells{
141c9af956cf7548bc352557eb8356913e489fd8716David Howells	BUG_ON(direction == DMA_NONE);
142c9af956cf7548bc352557eb8356913e489fd8716David Howells	flush_dcache_page(page);
143c9af956cf7548bc352557eb8356913e489fd8716David Howells	return (dma_addr_t) page_to_phys(page) + offset;
144c9af956cf7548bc352557eb8356913e489fd8716David Howells}
145c9af956cf7548bc352557eb8356913e489fd8716David Howells
146c9af956cf7548bc352557eb8356913e489fd8716David HowellsEXPORT_SYMBOL(dma_map_page);
147