1/*
2 *  Port on Texas Instruments TMS320C6x architecture
3 *
4 *  Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated
5 *  Author: Aurelien Jacquiot <aurelien.jacquiot@ti.com>
6 *
7 *  This program is free software; you can redistribute it and/or modify
8 *  it under the terms of the GNU General Public License version 2 as
9 *  published by the Free Software Foundation.
10 *
11 *  DMA uncached mapping support.
12 *
13 *  Using code pulled from ARM
14 *  Copyright (C) 2000-2004 Russell King
15 *
16 */
17#include <linux/slab.h>
18#include <linux/bitmap.h>
19#include <linux/bitops.h>
20#include <linux/module.h>
21#include <linux/interrupt.h>
22#include <linux/dma-mapping.h>
23#include <linux/memblock.h>
24
25#include <asm/page.h>
26
27/*
28 * DMA coherent memory management, can be redefined using the memdma=
29 * kernel command line
30 */
31
32/* none by default */
33static phys_addr_t dma_base;
34static u32 dma_size;
35static u32 dma_pages;
36
37static unsigned long *dma_bitmap;
38
39/* bitmap lock */
40static DEFINE_SPINLOCK(dma_lock);
41
42/*
43 * Return a DMA coherent and contiguous memory chunk from the DMA memory
44 */
45static inline u32 __alloc_dma_pages(int order)
46{
47	unsigned long flags;
48	u32 pos;
49
50	spin_lock_irqsave(&dma_lock, flags);
51	pos = bitmap_find_free_region(dma_bitmap, dma_pages, order);
52	spin_unlock_irqrestore(&dma_lock, flags);
53
54	return dma_base + (pos << PAGE_SHIFT);
55}
56
57static void __free_dma_pages(u32 addr, int order)
58{
59	unsigned long flags;
60	u32 pos = (addr - dma_base) >> PAGE_SHIFT;
61
62	if (addr < dma_base || (pos + (1 << order)) >= dma_pages) {
63		printk(KERN_ERR "%s: freeing outside range.\n", __func__);
64		BUG();
65	}
66
67	spin_lock_irqsave(&dma_lock, flags);
68	bitmap_release_region(dma_bitmap, pos, order);
69	spin_unlock_irqrestore(&dma_lock, flags);
70}
71
72/*
73 * Allocate DMA coherent memory space and return both the kernel
74 * virtual and DMA address for that space.
75 */
76void *dma_alloc_coherent(struct device *dev, size_t size,
77			 dma_addr_t *handle, gfp_t gfp)
78{
79	u32 paddr;
80	int order;
81
82	if (!dma_size || !size)
83		return NULL;
84
85	order = get_count_order(((size - 1) >> PAGE_SHIFT) + 1);
86
87	paddr = __alloc_dma_pages(order);
88
89	if (handle)
90		*handle = paddr;
91
92	if (!paddr)
93		return NULL;
94
95	return phys_to_virt(paddr);
96}
97EXPORT_SYMBOL(dma_alloc_coherent);
98
99/*
100 * Free DMA coherent memory as defined by the above mapping.
101 */
102void dma_free_coherent(struct device *dev, size_t size, void *vaddr,
103		       dma_addr_t dma_handle)
104{
105	int order;
106
107	if (!dma_size || !size)
108		return;
109
110	order = get_count_order(((size - 1) >> PAGE_SHIFT) + 1);
111
112	__free_dma_pages(virt_to_phys(vaddr), order);
113}
114EXPORT_SYMBOL(dma_free_coherent);
115
116/*
117 * Initialise the coherent DMA memory allocator using the given uncached region.
118 */
119void __init coherent_mem_init(phys_addr_t start, u32 size)
120{
121	phys_addr_t bitmap_phys;
122
123	if (!size)
124		return;
125
126	printk(KERN_INFO
127	       "Coherent memory (DMA) region start=0x%x size=0x%x\n",
128	       start, size);
129
130	dma_base = start;
131	dma_size = size;
132
133	/* allocate bitmap */
134	dma_pages = dma_size >> PAGE_SHIFT;
135	if (dma_size & (PAGE_SIZE - 1))
136		++dma_pages;
137
138	bitmap_phys = memblock_alloc(BITS_TO_LONGS(dma_pages) * sizeof(long),
139				     sizeof(long));
140
141	dma_bitmap = phys_to_virt(bitmap_phys);
142	memset(dma_bitmap, 0, dma_pages * PAGE_SIZE);
143}
144