| /* | 
 |  * arch/sh/cchips/voyagergx/consistent.c | 
 |  * | 
 |  * Copyright (C) 2004  Paul Mundt | 
 |  * | 
 |  * This file is subject to the terms and conditions of the GNU General Public | 
 |  * License.  See the file "COPYING" in the main directory of this archive | 
 |  * for more details. | 
 |  */ | 
 | #include <linux/mm.h> | 
 | #include <linux/dma-mapping.h> | 
 | #include <linux/slab.h> | 
 | #include <linux/list.h> | 
 | #include <linux/types.h> | 
 | #include <linux/module.h> | 
 | #include <linux/device.h> | 
 | #include <asm/io.h> | 
 |  | 
 |  | 
 | struct voya_alloc_entry { | 
 | 	struct list_head list; | 
 | 	unsigned long ofs; | 
 | 	unsigned long len; | 
 | }; | 
 |  | 
 | static DEFINE_SPINLOCK(voya_list_lock); | 
 | static LIST_HEAD(voya_alloc_list); | 
 |  | 
 | #define OHCI_SRAM_START	0xb0000000 | 
 | #define OHCI_HCCA_SIZE	0x100 | 
 | #define OHCI_SRAM_SIZE	0x10000 | 
 |  | 
 | #define VOYAGER_OHCI_NAME	"voyager-ohci" | 
 |  | 
 | void *voyagergx_consistent_alloc(struct device *dev, size_t size, | 
 | 				 dma_addr_t *handle, gfp_t flag) | 
 | { | 
 | 	struct list_head *list = &voya_alloc_list; | 
 | 	struct voya_alloc_entry *entry; | 
 | 	unsigned long start, end; | 
 | 	unsigned long flags; | 
 |  | 
 | 	/* | 
 | 	 * The SM501 contains an integrated 8051 with its own SRAM. | 
 | 	 * Devices within the cchip can all hook into the 8051 SRAM. | 
 | 	 * We presently use this for the OHCI. | 
 | 	 * | 
 | 	 * Everything else goes through consistent_alloc(). | 
 | 	 */ | 
 | 	if (!dev || strcmp(dev->driver->name, VOYAGER_OHCI_NAME)) | 
 | 		return NULL; | 
 |  | 
 | 	start = OHCI_SRAM_START + OHCI_HCCA_SIZE; | 
 |  | 
 | 	entry = kmalloc(sizeof(struct voya_alloc_entry), GFP_ATOMIC); | 
 | 	if (!entry) | 
 | 		return ERR_PTR(-ENOMEM); | 
 |  | 
 | 	entry->len = (size + 15) & ~15; | 
 |  | 
 | 	/* | 
 | 	 * The basis for this allocator is dwmw2's malloc.. the | 
 | 	 * Matrox allocator :-) | 
 | 	 */ | 
 | 	spin_lock_irqsave(&voya_list_lock, flags); | 
 | 	list_for_each(list, &voya_alloc_list) { | 
 | 		struct voya_alloc_entry *p; | 
 |  | 
 | 		p = list_entry(list, struct voya_alloc_entry, list); | 
 |  | 
 | 		if (p->ofs - start >= size) | 
 | 			goto out; | 
 |  | 
 | 		start = p->ofs + p->len; | 
 | 	} | 
 |  | 
 | 	end  = start + (OHCI_SRAM_SIZE  - OHCI_HCCA_SIZE); | 
 | 	list = &voya_alloc_list; | 
 |  | 
 | 	if (end - start >= size) { | 
 | out: | 
 | 		entry->ofs = start; | 
 | 		list_add_tail(&entry->list, list); | 
 | 		spin_unlock_irqrestore(&voya_list_lock, flags); | 
 |  | 
 | 		*handle = start; | 
 | 		return (void *)start; | 
 | 	} | 
 |  | 
 | 	kfree(entry); | 
 | 	spin_unlock_irqrestore(&voya_list_lock, flags); | 
 |  | 
 | 	return ERR_PTR(-EINVAL); | 
 | } | 
 |  | 
 | int voyagergx_consistent_free(struct device *dev, size_t size, | 
 | 			      void *vaddr, dma_addr_t handle) | 
 | { | 
 | 	struct voya_alloc_entry *entry; | 
 | 	unsigned long flags; | 
 |  | 
 | 	if (!dev || strcmp(dev->driver->name, VOYAGER_OHCI_NAME)) | 
 | 		return -EINVAL; | 
 |  | 
 | 	spin_lock_irqsave(&voya_list_lock, flags); | 
 | 	list_for_each_entry(entry, &voya_alloc_list, list) { | 
 | 		if (entry->ofs != handle) | 
 | 			continue; | 
 |  | 
 | 		list_del(&entry->list); | 
 | 		kfree(entry); | 
 |  | 
 | 		break; | 
 | 	} | 
 | 	spin_unlock_irqrestore(&voya_list_lock, flags); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | EXPORT_SYMBOL(voyagergx_consistent_alloc); | 
 | EXPORT_SYMBOL(voyagergx_consistent_free); |