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 if (!res) { 73 dev_err(&pdev->dev, "found no memory resource\n"); 74 return -EINVAL; 75 } 76 77 size = resource_size(res); 78 79 if (!devm_request_mem_region(&pdev->dev, 80 res->start, size, pdev->name)) { 81 dev_err(&pdev->dev, "could not request region for resource\n"); 82 return -EBUSY; 83 } 84 85 virt_base = devm_ioremap_wc(&pdev->dev, res->start, size); 86 if (IS_ERR(virt_base)) 87 return PTR_ERR(virt_base); 88 89 sram = devm_kzalloc(&pdev->dev, sizeof(*sram), GFP_KERNEL); 90 if (!sram) 91 return -ENOMEM; 92 93 sram->clk = devm_clk_get(&pdev->dev, NULL); 94 if (IS_ERR(sram->clk)) 95 sram->clk = NULL; 96 else 97 clk_prepare_enable(sram->clk); 98 99 sram->pool = devm_gen_pool_create(&pdev->dev, ilog2(SRAM_GRANULARITY), -1); 100 if (!sram->pool) 101 return -ENOMEM; 102 103 /* 104 * We need an additional block to mark the end of the memory region 105 * after the reserved blocks from the dt are processed. 106 */ 107 nblocks = (np) ? of_get_available_child_count(np) + 1 : 1; 108 rblocks = kmalloc((nblocks) * sizeof(*rblocks), GFP_KERNEL); 109 if (!rblocks) { 110 ret = -ENOMEM; 111 goto err_alloc; 112 } 113 114 block = &rblocks[0]; 115 for_each_available_child_of_node(np, child) { 116 struct resource child_res; 117 118 ret = of_address_to_resource(child, 0, &child_res); 119 if (ret < 0) { 120 dev_err(&pdev->dev, 121 "could not get address for node %s\n", 122 child->full_name); 123 goto err_chunks; 124 } 125 126 if (child_res.start < res->start || child_res.end > res->end) { 127 dev_err(&pdev->dev, 128 "reserved block %s outside the sram area\n", 129 child->full_name); 130 ret = -EINVAL; 131 goto err_chunks; 132 } 133 134 block->start = child_res.start - res->start; 135 block->size = resource_size(&child_res); 136 list_add_tail(&block->list, &reserve_list); 137 138 dev_dbg(&pdev->dev, "found reserved block 0x%x-0x%x\n", 139 block->start, 140 block->start + block->size); 141 142 block++; 143 } 144 145 /* the last chunk marks the end of the region */ 146 rblocks[nblocks - 1].start = size; 147 rblocks[nblocks - 1].size = 0; 148 list_add_tail(&rblocks[nblocks - 1].list, &reserve_list); 149 150 list_sort(NULL, &reserve_list, sram_reserve_cmp); 151 152 cur_start = 0; 153 154 list_for_each_entry(block, &reserve_list, list) { 155 /* can only happen if sections overlap */ 156 if (block->start < cur_start) { 157 dev_err(&pdev->dev, 158 "block at 0x%x starts after current offset 0x%lx\n", 159 block->start, cur_start); 160 ret = -EINVAL; 161 goto err_chunks; 162 } 163 164 /* current start is in a reserved block, so continue after it */ 165 if (block->start == cur_start) { 166 cur_start = block->start + block->size; 167 continue; 168 } 169 170 /* 171 * allocate the space between the current starting 172 * address and the following reserved block, or the 173 * end of the region. 174 */ 175 cur_size = block->start - cur_start; 176 177 dev_dbg(&pdev->dev, "adding chunk 0x%lx-0x%lx\n", 178 cur_start, cur_start + cur_size); 179 ret = gen_pool_add_virt(sram->pool, 180 (unsigned long)virt_base + cur_start, 181 res->start + cur_start, cur_size, -1); 182 if (ret < 0) 183 goto err_chunks; 184 185 /* next allocation after this reserved block */ 186 cur_start = block->start + block->size; 187 } 188 189 kfree(rblocks); 190 191 platform_set_drvdata(pdev, sram); 192 193 dev_dbg(&pdev->dev, "SRAM pool: %ld KiB @ 0x%p\n", size / 1024, virt_base); 194 195 return 0; 196 197 err_chunks: 198 kfree(rblocks); 199 err_alloc: 200 if (sram->clk) 201 clk_disable_unprepare(sram->clk); 202 return ret; 203 } 204 205 static int sram_remove(struct platform_device *pdev) 206 { 207 struct sram_dev *sram = platform_get_drvdata(pdev); 208 209 if (gen_pool_avail(sram->pool) < gen_pool_size(sram->pool)) 210 dev_dbg(&pdev->dev, "removed while SRAM allocated\n"); 211 212 if (sram->clk) 213 clk_disable_unprepare(sram->clk); 214 215 return 0; 216 } 217 218 #ifdef CONFIG_OF 219 static const struct of_device_id sram_dt_ids[] = { 220 { .compatible = "mmio-sram" }, 221 {} 222 }; 223 #endif 224 225 static struct platform_driver sram_driver = { 226 .driver = { 227 .name = "sram", 228 .of_match_table = of_match_ptr(sram_dt_ids), 229 }, 230 .probe = sram_probe, 231 .remove = sram_remove, 232 }; 233 234 static int __init sram_init(void) 235 { 236 return platform_driver_register(&sram_driver); 237 } 238 239 postcore_initcall(sram_init); 240