summaryrefslogtreecommitdiffstats
path: root/kernel/drivers/dma/bestcomm/sram.c
blob: 2074e0e3fa21f8a23dd2179394d2b677c17a9934 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/*
 * Simple memory allocator for on-board SRAM
 *
 *
 * Maintainer : Sylvain Munaut <tnt@246tNt.com>
 *
 * Copyright (C) 2005 Sylvain Munaut <tnt@246tNt.com>
 *
 * This file is licensed under the terms of the GNU General Public License
 * version 2. This program is licensed "as is" without any warranty of any
 * kind, whether express or implied.
 */

#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/of.h>
#include <linux/of_address.h>

#include <asm/io.h>
#include <asm/mmu.h>

#include <linux/fsl/bestcomm/sram.h>


/* Struct keeping our 'state' */
struct bcom_sram *bcom_sram = NULL;
EXPORT_SYMBOL_GPL(bcom_sram);	/* needed for inline functions */


/* ======================================================================== */
/* Public API                                                               */
/* ======================================================================== */
/* DO NOT USE in interrupts, if needed in irq handler, we should use the
   _irqsave version of the spin_locks */

int bcom_sram_init(struct device_node *sram_node, char *owner)
{
	int rv;
	const u32 *regaddr_p;
	u64 regaddr64, size64;
	unsigned int psize;

	/* Create our state struct */
	if (bcom_sram) {
		printk(KERN_ERR "%s: bcom_sram_init: "
			"Already initialized !\n", owner);
		return -EBUSY;
	}

	bcom_sram = kmalloc(sizeof(struct bcom_sram), GFP_KERNEL);
	if (!bcom_sram) {
		printk(KERN_ERR "%s: bcom_sram_init: "
			"Couldn't allocate internal state !\n", owner);
		return -ENOMEM;
	}

	/* Get address and size of the sram */
	regaddr_p = of_get_address(sram_node, 0, &size64, NULL);
	if (!regaddr_p) {
		printk(KERN_ERR "%s: bcom_sram_init: "
			"Invalid device node !\n", owner);
		rv = -EINVAL;
		goto error_free;
	}

	regaddr64 = of_translate_address(sram_node, regaddr_p);

	bcom_sram->base_phys = (phys_addr_t) regaddr64;
	bcom_sram->size = (unsigned int) size64;

	/* Request region */
	if (!request_mem_region(bcom_sram->base_phys, bcom_sram->size, owner)) {
		printk(KERN_ERR "%s: bcom_sram_init: "
			"Couldn't request region !\n", owner);
		rv = -EBUSY;
		goto error_free;
	}

	/* Map SRAM */
		/* sram is not really __iomem */
	bcom_sram->base_virt = (void*) ioremap(bcom_sram->base_phys, bcom_sram->size);

	if (!bcom_sram->base_virt) {
		printk(KERN_ERR "%s: bcom_sram_init: "
			"Map error SRAM zone 0x%08lx (0x%0x)!\n",
			owner, (long)bcom_sram->base_phys, bcom_sram->size );
		rv = -ENOMEM;
		goto error_release;
	}

	/* Create an rheap (defaults to 32 bits word alignment) */
	bcom_sram->rh = rh_create(4);

	/* Attach the free zones */
#if 0
	/* Currently disabled ... for future use only */
	reg_addr_p = of_get_property(sram_node, "available", &psize);
#else
	regaddr_p = NULL;
	psize = 0;
#endif

	if (!regaddr_p || !psize) {
		/* Attach the whole zone */
		rh_attach_region(bcom_sram->rh, 0, bcom_sram->size);
	} else {
		/* Attach each zone independently */
		while (psize >= 2 * sizeof(u32)) {
			phys_addr_t zbase = of_translate_address(sram_node, regaddr_p);
			rh_attach_region(bcom_sram->rh, zbase - bcom_sram->base_phys, regaddr_p[1]);
			regaddr_p += 2;
			psize -= 2 * sizeof(u32);
		}
	}

	/* Init our spinlock */
	spin_lock_init(&bcom_sram->lock);

	return 0;

error_release:
	release_mem_region(bcom_sram->base_phys, bcom_sram->size);
error_free:
	kfree(bcom_sram);
	bcom_sram = NULL;

	return rv;
}
EXPORT_SYMBOL_GPL(bcom_sram_init);

void bcom_sram_cleanup(void)
{
	/* Free resources */
	if (bcom_sram) {
		rh_destroy(bcom_sram->rh);
		iounmap((void __iomem *)bcom_sram->base_virt);
		release_mem_region(bcom_sram->base_phys, bcom_sram->size);
		kfree(bcom_sram);
		bcom_sram = NULL;
	}
}
EXPORT_SYMBOL_GPL(bcom_sram_cleanup);

void* bcom_sram_alloc(int size, int align, phys_addr_t *phys)
{
	unsigned long offset;

	spin_lock(&bcom_sram->lock);
	offset = rh_alloc_align(bcom_sram->rh, size, align, NULL);
	spin_unlock(&bcom_sram->lock);

	if (IS_ERR_VALUE(offset))
		return NULL;

	*phys = bcom_sram->base_phys + offset;
	return bcom_sram->base_virt + offset;
}
EXPORT_SYMBOL_GPL(bcom_sram_alloc);

void bcom_sram_free(void *ptr)
{
	unsigned long offset;

	if (!ptr)
		return;

	offset = ptr - bcom_sram->base_virt;

	spin_lock(&bcom_sram->lock);
	rh_free(bcom_sram->rh, offset);
	spin_unlock(&bcom_sram->lock);
}
EXPORT_SYMBOL_GPL(bcom_sram_free);