Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * Generic on-chip SRAM allocation driver
  3 *
  4 * Copyright (C) 2012 Philipp Zabel, Pengutronix
  5 *
  6 * This program is free software; you can redistribute it and/or
  7 * modify it under the terms of the GNU General Public License
  8 * as published by the Free Software Foundation; either version 2
  9 * of the License, or (at your option) any later version.
 10 * This program is distributed in the hope that it will be useful,
 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13 * GNU General Public License for more details.
 14 *
 15 * You should have received a copy of the GNU General Public License
 16 * along with this program; if not, write to the Free Software
 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 18 * MA 02110-1301, USA.
 19 */
 20
 21#include <linux/kernel.h>
 22#include <linux/init.h>
 23#include <linux/clk.h>
 24#include <linux/err.h>
 25#include <linux/io.h>
 26#include <linux/of.h>
 27#include <linux/of_address.h>
 28#include <linux/list.h>
 29#include <linux/list_sort.h>
 30#include <linux/platform_device.h>
 31#include <linux/slab.h>
 32#include <linux/spinlock.h>
 33#include <linux/genalloc.h>
 34
 35#define SRAM_GRANULARITY	32
 36
 37struct sram_dev {
 38	struct gen_pool *pool;
 39	struct clk *clk;
 40};
 41
 42struct sram_reserve {
 43	struct list_head list;
 44	u32 start;
 45	u32 size;
 46};
 47
 48static int sram_reserve_cmp(void *priv, struct list_head *a,
 49					struct list_head *b)
 50{
 51	struct sram_reserve *ra = list_entry(a, struct sram_reserve, list);
 52	struct sram_reserve *rb = list_entry(b, struct sram_reserve, list);
 53
 54	return ra->start - rb->start;
 55}
 56
 57static int sram_probe(struct platform_device *pdev)
 58{
 59	void __iomem *virt_base;
 60	struct sram_dev *sram;
 61	struct resource *res;
 62	struct device_node *np = pdev->dev.of_node, *child;
 63	unsigned long size, cur_start, cur_size;
 64	struct sram_reserve *rblocks, *block;
 65	struct list_head reserve_list;
 66	unsigned int nblocks;
 67	int ret;
 68
 69	INIT_LIST_HEAD(&reserve_list);
 70
 71	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 72	virt_base = devm_ioremap_resource(&pdev->dev, res);
 73	if (IS_ERR(virt_base))
 74		return PTR_ERR(virt_base);
 75
 76	size = resource_size(res);
 77
 78	sram = devm_kzalloc(&pdev->dev, sizeof(*sram), GFP_KERNEL);
 79	if (!sram)
 80		return -ENOMEM;
 81
 82	sram->clk = devm_clk_get(&pdev->dev, NULL);
 83	if (IS_ERR(sram->clk))
 84		sram->clk = NULL;
 85	else
 86		clk_prepare_enable(sram->clk);
 87
 88	sram->pool = devm_gen_pool_create(&pdev->dev, ilog2(SRAM_GRANULARITY), -1);
 89	if (!sram->pool)
 90		return -ENOMEM;
 91
 92	/*
 93	 * We need an additional block to mark the end of the memory region
 94	 * after the reserved blocks from the dt are processed.
 95	 */
 96	nblocks = (np) ? of_get_available_child_count(np) + 1 : 1;
 97	rblocks = kmalloc((nblocks) * sizeof(*rblocks), GFP_KERNEL);
 98	if (!rblocks) {
 99		ret = -ENOMEM;
100		goto err_alloc;
101	}
102
103	block = &rblocks[0];
104	for_each_available_child_of_node(np, child) {
105		struct resource child_res;
106
107		ret = of_address_to_resource(child, 0, &child_res);
108		if (ret < 0) {
109			dev_err(&pdev->dev,
110				"could not get address for node %s\n",
111				child->full_name);
112			goto err_chunks;
113		}
114
115		if (child_res.start < res->start || child_res.end > res->end) {
116			dev_err(&pdev->dev,
117				"reserved block %s outside the sram area\n",
118				child->full_name);
119			ret = -EINVAL;
120			goto err_chunks;
121		}
122
123		block->start = child_res.start - res->start;
124		block->size = resource_size(&child_res);
125		list_add_tail(&block->list, &reserve_list);
126
127		dev_dbg(&pdev->dev, "found reserved block 0x%x-0x%x\n",
128			block->start,
129			block->start + block->size);
130
131		block++;
132	}
133
134	/* the last chunk marks the end of the region */
135	rblocks[nblocks - 1].start = size;
136	rblocks[nblocks - 1].size = 0;
137	list_add_tail(&rblocks[nblocks - 1].list, &reserve_list);
138
139	list_sort(NULL, &reserve_list, sram_reserve_cmp);
140
141	cur_start = 0;
142
143	list_for_each_entry(block, &reserve_list, list) {
144		/* can only happen if sections overlap */
145		if (block->start < cur_start) {
146			dev_err(&pdev->dev,
147				"block at 0x%x starts after current offset 0x%lx\n",
148				block->start, cur_start);
149			ret = -EINVAL;
150			goto err_chunks;
151		}
152
153		/* current start is in a reserved block, so continue after it */
154		if (block->start == cur_start) {
155			cur_start = block->start + block->size;
156			continue;
157		}
158
159		/*
160		 * allocate the space between the current starting
161		 * address and the following reserved block, or the
162		 * end of the region.
163		 */
164		cur_size = block->start - cur_start;
165
166		dev_dbg(&pdev->dev, "adding chunk 0x%lx-0x%lx\n",
167			cur_start, cur_start + cur_size);
168		ret = gen_pool_add_virt(sram->pool,
169				(unsigned long)virt_base + cur_start,
170				res->start + cur_start, cur_size, -1);
171		if (ret < 0)
172			goto err_chunks;
173
174		/* next allocation after this reserved block */
175		cur_start = block->start + block->size;
176	}
177
178	kfree(rblocks);
179
180	platform_set_drvdata(pdev, sram);
181
182	dev_dbg(&pdev->dev, "SRAM pool: %ld KiB @ 0x%p\n", size / 1024, virt_base);
183
184	return 0;
185
186err_chunks:
187	kfree(rblocks);
188err_alloc:
189	if (sram->clk)
190		clk_disable_unprepare(sram->clk);
191	return ret;
192}
193
194static int sram_remove(struct platform_device *pdev)
195{
196	struct sram_dev *sram = platform_get_drvdata(pdev);
197
198	if (gen_pool_avail(sram->pool) < gen_pool_size(sram->pool))
199		dev_dbg(&pdev->dev, "removed while SRAM allocated\n");
200
201	if (sram->clk)
202		clk_disable_unprepare(sram->clk);
203
204	return 0;
205}
206
207#ifdef CONFIG_OF
208static struct of_device_id sram_dt_ids[] = {
209	{ .compatible = "mmio-sram" },
210	{}
211};
212#endif
213
214static struct platform_driver sram_driver = {
215	.driver = {
216		.name = "sram",
217		.of_match_table = of_match_ptr(sram_dt_ids),
218	},
219	.probe = sram_probe,
220	.remove = sram_remove,
221};
222
223static int __init sram_init(void)
224{
225	return platform_driver_register(&sram_driver);
226}
227
228postcore_initcall(sram_init);