Linux-2.6.12-rc2
[linux-flexiantxendom0-natty.git] / arch / sh / cchips / voyagergx / consistent.c
1 /*
2  * arch/sh/cchips/voyagergx/consistent.c
3  *
4  * Copyright (C) 2004  Paul Mundt
5  *
6  * This file is subject to the terms and conditions of the GNU General Public
7  * License.  See the file "COPYING" in the main directory of this archive
8  * for more details.
9  */
10 #include <linux/mm.h>
11 #include <linux/dma-mapping.h>
12 #include <linux/slab.h>
13 #include <linux/list.h>
14 #include <linux/types.h>
15 #include <linux/module.h>
16 #include <linux/device.h>
17 #include <asm/io.h>
18 #include <asm/bus-sh.h>
19
20 struct voya_alloc_entry {
21         struct list_head list;
22         unsigned long ofs;
23         unsigned long len;
24 };
25
26 static DEFINE_SPINLOCK(voya_list_lock);
27 static LIST_HEAD(voya_alloc_list);
28
29 #define OHCI_SRAM_START 0xb0000000
30 #define OHCI_HCCA_SIZE  0x100
31 #define OHCI_SRAM_SIZE  0x10000
32
33 void *voyagergx_consistent_alloc(struct device *dev, size_t size,
34                                  dma_addr_t *handle, int flag)
35 {
36         struct list_head *list = &voya_alloc_list;
37         struct voya_alloc_entry *entry;
38         struct sh_dev *shdev = to_sh_dev(dev);
39         unsigned long start, end;
40         unsigned long flags;
41
42         /*
43          * The SM501 contains an integrated 8051 with its own SRAM.
44          * Devices within the cchip can all hook into the 8051 SRAM.
45          * We presently use this for the OHCI.
46          *
47          * Everything else goes through consistent_alloc().
48          */
49         if (!dev || dev->bus != &sh_bus_types[SH_BUS_VIRT] ||
50                    (dev->bus == &sh_bus_types[SH_BUS_VIRT] &&
51                     shdev->dev_id != SH_DEV_ID_USB_OHCI))
52                 return NULL;
53
54         start = OHCI_SRAM_START + OHCI_HCCA_SIZE;
55
56         entry = kmalloc(sizeof(struct voya_alloc_entry), GFP_ATOMIC);
57         if (!entry)
58                 return ERR_PTR(-ENOMEM);
59
60         entry->len = (size + 15) & ~15;
61
62         /*
63          * The basis for this allocator is dwmw2's malloc.. the
64          * Matrox allocator :-)
65          */
66         spin_lock_irqsave(&voya_list_lock, flags);
67         list_for_each(list, &voya_alloc_list) {
68                 struct voya_alloc_entry *p;
69
70                 p = list_entry(list, struct voya_alloc_entry, list);
71
72                 if (p->ofs - start >= size)
73                         goto out;
74
75                 start = p->ofs + p->len;
76         }
77
78         end  = start + (OHCI_SRAM_SIZE  - OHCI_HCCA_SIZE);
79         list = &voya_alloc_list;
80
81         if (end - start >= size) {
82 out:
83                 entry->ofs = start;
84                 list_add_tail(&entry->list, list);
85                 spin_unlock_irqrestore(&voya_list_lock, flags);
86
87                 *handle = start;
88                 return (void *)start;
89         }
90
91         kfree(entry);
92         spin_unlock_irqrestore(&voya_list_lock, flags);
93
94         return ERR_PTR(-EINVAL);
95 }
96
97 int voyagergx_consistent_free(struct device *dev, size_t size,
98                               void *vaddr, dma_addr_t handle)
99 {
100         struct voya_alloc_entry *entry;
101         struct sh_dev *shdev = to_sh_dev(dev);
102         unsigned long flags;
103
104         if (!dev || dev->bus != &sh_bus_types[SH_BUS_VIRT] ||
105                    (dev->bus == &sh_bus_types[SH_BUS_VIRT] &&
106                     shdev->dev_id != SH_DEV_ID_USB_OHCI))
107                 return -EINVAL;
108
109         spin_lock_irqsave(&voya_list_lock, flags);
110         list_for_each_entry(entry, &voya_alloc_list, list) {
111                 if (entry->ofs != handle)
112                         continue;
113
114                 list_del(&entry->list);
115                 kfree(entry);
116
117                 break;
118         }
119         spin_unlock_irqrestore(&voya_list_lock, flags);
120
121         return 0;
122 }
123
124 EXPORT_SYMBOL(voyagergx_consistent_alloc);
125 EXPORT_SYMBOL(voyagergx_consistent_free);
126