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 37 struct sram_dev { 38 struct gen_pool *pool; 39 struct clk *clk; 40 }; 41 42 struct sram_reserve { 43 struct list_head list; 44 u32 start; 45 u32 size; 46 }; 47 48 static 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 57 static 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 186 err_chunks: 187 kfree(rblocks); 188 err_alloc: 189 if (sram->clk) 190 clk_disable_unprepare(sram->clk); 191 return ret; 192 } 193 194 static 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 208 static const struct of_device_id sram_dt_ids[] = { 209 { .compatible = "mmio-sram" }, 210 {} 211 }; 212 #endif 213 214 static 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 223 static int __init sram_init(void) 224 { 225 return platform_driver_register(&sram_driver); 226 } 227 228 postcore_initcall(sram_init); 229