linux/drivers/misc/sram.c
<<
>>
Prefs
   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/clk.h>
  22#include <linux/genalloc.h>
  23#include <linux/io.h>
  24#include <linux/list_sort.h>
  25#include <linux/of_address.h>
  26#include <linux/platform_device.h>
  27#include <linux/slab.h>
  28
  29#define SRAM_GRANULARITY        32
  30
  31struct sram_partition {
  32        void __iomem *base;
  33
  34        struct gen_pool *pool;
  35        struct bin_attribute battr;
  36        struct mutex lock;
  37};
  38
  39struct sram_dev {
  40        struct device *dev;
  41        void __iomem *virt_base;
  42
  43        struct gen_pool *pool;
  44        struct clk *clk;
  45
  46        struct sram_partition *partition;
  47        u32 partitions;
  48};
  49
  50struct sram_reserve {
  51        struct list_head list;
  52        u32 start;
  53        u32 size;
  54        bool export;
  55        bool pool;
  56        const char *label;
  57};
  58
  59static ssize_t sram_read(struct file *filp, struct kobject *kobj,
  60                         struct bin_attribute *attr,
  61                         char *buf, loff_t pos, size_t count)
  62{
  63        struct sram_partition *part;
  64
  65        part = container_of(attr, struct sram_partition, battr);
  66
  67        mutex_lock(&part->lock);
  68        memcpy_fromio(buf, part->base + pos, count);
  69        mutex_unlock(&part->lock);
  70
  71        return count;
  72}
  73
  74static ssize_t sram_write(struct file *filp, struct kobject *kobj,
  75                          struct bin_attribute *attr,
  76                          char *buf, loff_t pos, size_t count)
  77{
  78        struct sram_partition *part;
  79
  80        part = container_of(attr, struct sram_partition, battr);
  81
  82        mutex_lock(&part->lock);
  83        memcpy_toio(part->base + pos, buf, count);
  84        mutex_unlock(&part->lock);
  85
  86        return count;
  87}
  88
  89static int sram_add_pool(struct sram_dev *sram, struct sram_reserve *block,
  90                         phys_addr_t start, struct sram_partition *part)
  91{
  92        int ret;
  93
  94        part->pool = devm_gen_pool_create(sram->dev, ilog2(SRAM_GRANULARITY),
  95                                          NUMA_NO_NODE, block->label);
  96        if (IS_ERR(part->pool))
  97                return PTR_ERR(part->pool);
  98
  99        ret = gen_pool_add_virt(part->pool, (unsigned long)part->base, start,
 100                                block->size, NUMA_NO_NODE);
 101        if (ret < 0) {
 102                dev_err(sram->dev, "failed to register subpool: %d\n", ret);
 103                return ret;
 104        }
 105
 106        return 0;
 107}
 108
 109static int sram_add_export(struct sram_dev *sram, struct sram_reserve *block,
 110                           phys_addr_t start, struct sram_partition *part)
 111{
 112        sysfs_bin_attr_init(&part->battr);
 113        part->battr.attr.name = devm_kasprintf(sram->dev, GFP_KERNEL,
 114                                               "%llx.sram",
 115                                               (unsigned long long)start);
 116        if (!part->battr.attr.name)
 117                return -ENOMEM;
 118
 119        part->battr.attr.mode = S_IRUSR | S_IWUSR;
 120        part->battr.read = sram_read;
 121        part->battr.write = sram_write;
 122        part->battr.size = block->size;
 123
 124        return device_create_bin_file(sram->dev, &part->battr);
 125}
 126
 127static int sram_add_partition(struct sram_dev *sram, struct sram_reserve *block,
 128                              phys_addr_t start)
 129{
 130        int ret;
 131        struct sram_partition *part = &sram->partition[sram->partitions];
 132
 133        mutex_init(&part->lock);
 134        part->base = sram->virt_base + block->start;
 135
 136        if (block->pool) {
 137                ret = sram_add_pool(sram, block, start, part);
 138                if (ret)
 139                        return ret;
 140        }
 141        if (block->export) {
 142                ret = sram_add_export(sram, block, start, part);
 143                if (ret)
 144                        return ret;
 145        }
 146        sram->partitions++;
 147
 148        return 0;
 149}
 150
 151static void sram_free_partitions(struct sram_dev *sram)
 152{
 153        struct sram_partition *part;
 154
 155        if (!sram->partitions)
 156                return;
 157
 158        part = &sram->partition[sram->partitions - 1];
 159        for (; sram->partitions; sram->partitions--, part--) {
 160                if (part->battr.size)
 161                        device_remove_bin_file(sram->dev, &part->battr);
 162
 163                if (part->pool &&
 164                    gen_pool_avail(part->pool) < gen_pool_size(part->pool))
 165                        dev_err(sram->dev, "removed pool while SRAM allocated\n");
 166        }
 167}
 168
 169static int sram_reserve_cmp(void *priv, struct list_head *a,
 170                                        struct list_head *b)
 171{
 172        struct sram_reserve *ra = list_entry(a, struct sram_reserve, list);
 173        struct sram_reserve *rb = list_entry(b, struct sram_reserve, list);
 174
 175        return ra->start - rb->start;
 176}
 177
 178static int sram_reserve_regions(struct sram_dev *sram, struct resource *res)
 179{
 180        struct device_node *np = sram->dev->of_node, *child;
 181        unsigned long size, cur_start, cur_size;
 182        struct sram_reserve *rblocks, *block;
 183        struct list_head reserve_list;
 184        unsigned int nblocks, exports = 0;
 185        const char *label;
 186        int ret = 0;
 187
 188        INIT_LIST_HEAD(&reserve_list);
 189
 190        size = resource_size(res);
 191
 192        /*
 193         * We need an additional block to mark the end of the memory region
 194         * after the reserved blocks from the dt are processed.
 195         */
 196        nblocks = (np) ? of_get_available_child_count(np) + 1 : 1;
 197        rblocks = kzalloc((nblocks) * sizeof(*rblocks), GFP_KERNEL);
 198        if (!rblocks)
 199                return -ENOMEM;
 200
 201        block = &rblocks[0];
 202        for_each_available_child_of_node(np, child) {
 203                struct resource child_res;
 204
 205                ret = of_address_to_resource(child, 0, &child_res);
 206                if (ret < 0) {
 207                        dev_err(sram->dev,
 208                                "could not get address for node %s\n",
 209                                child->full_name);
 210                        goto err_chunks;
 211                }
 212
 213                if (child_res.start < res->start || child_res.end > res->end) {
 214                        dev_err(sram->dev,
 215                                "reserved block %s outside the sram area\n",
 216                                child->full_name);
 217                        ret = -EINVAL;
 218                        goto err_chunks;
 219                }
 220
 221                block->start = child_res.start - res->start;
 222                block->size = resource_size(&child_res);
 223                list_add_tail(&block->list, &reserve_list);
 224
 225                if (of_find_property(child, "export", NULL))
 226                        block->export = true;
 227
 228                if (of_find_property(child, "pool", NULL))
 229                        block->pool = true;
 230
 231                if ((block->export || block->pool) && block->size) {
 232                        exports++;
 233
 234                        label = NULL;
 235                        ret = of_property_read_string(child, "label", &label);
 236                        if (ret && ret != -EINVAL) {
 237                                dev_err(sram->dev,
 238                                        "%s has invalid label name\n",
 239                                        child->full_name);
 240                                goto err_chunks;
 241                        }
 242                        if (!label)
 243                                label = child->name;
 244
 245                        block->label = devm_kstrdup(sram->dev,
 246                                                    label, GFP_KERNEL);
 247                        if (!block->label)
 248                                goto err_chunks;
 249
 250                        dev_dbg(sram->dev, "found %sblock '%s' 0x%x-0x%x\n",
 251                                block->export ? "exported " : "", block->label,
 252                                block->start, block->start + block->size);
 253                } else {
 254                        dev_dbg(sram->dev, "found reserved block 0x%x-0x%x\n",
 255                                block->start, block->start + block->size);
 256                }
 257
 258                block++;
 259        }
 260        child = NULL;
 261
 262        /* the last chunk marks the end of the region */
 263        rblocks[nblocks - 1].start = size;
 264        rblocks[nblocks - 1].size = 0;
 265        list_add_tail(&rblocks[nblocks - 1].list, &reserve_list);
 266
 267        list_sort(NULL, &reserve_list, sram_reserve_cmp);
 268
 269        if (exports) {
 270                sram->partition = devm_kzalloc(sram->dev,
 271                                       exports * sizeof(*sram->partition),
 272                                       GFP_KERNEL);
 273                if (!sram->partition) {
 274                        ret = -ENOMEM;
 275                        goto err_chunks;
 276                }
 277        }
 278
 279        cur_start = 0;
 280        list_for_each_entry(block, &reserve_list, list) {
 281                /* can only happen if sections overlap */
 282                if (block->start < cur_start) {
 283                        dev_err(sram->dev,
 284                                "block at 0x%x starts after current offset 0x%lx\n",
 285                                block->start, cur_start);
 286                        ret = -EINVAL;
 287                        sram_free_partitions(sram);
 288                        goto err_chunks;
 289                }
 290
 291                if ((block->export || block->pool) && block->size) {
 292                        ret = sram_add_partition(sram, block,
 293                                                 res->start + block->start);
 294                        if (ret) {
 295                                sram_free_partitions(sram);
 296                                goto err_chunks;
 297                        }
 298                }
 299
 300                /* current start is in a reserved block, so continue after it */
 301                if (block->start == cur_start) {
 302                        cur_start = block->start + block->size;
 303                        continue;
 304                }
 305
 306                /*
 307                 * allocate the space between the current starting
 308                 * address and the following reserved block, or the
 309                 * end of the region.
 310                 */
 311                cur_size = block->start - cur_start;
 312
 313                dev_dbg(sram->dev, "adding chunk 0x%lx-0x%lx\n",
 314                        cur_start, cur_start + cur_size);
 315
 316                ret = gen_pool_add_virt(sram->pool,
 317                                (unsigned long)sram->virt_base + cur_start,
 318                                res->start + cur_start, cur_size, -1);
 319                if (ret < 0) {
 320                        sram_free_partitions(sram);
 321                        goto err_chunks;
 322                }
 323
 324                /* next allocation after this reserved block */
 325                cur_start = block->start + block->size;
 326        }
 327
 328 err_chunks:
 329        if (child)
 330                of_node_put(child);
 331
 332        kfree(rblocks);
 333
 334        return ret;
 335}
 336
 337static int sram_probe(struct platform_device *pdev)
 338{
 339        struct sram_dev *sram;
 340        struct resource *res;
 341        size_t size;
 342        int ret;
 343
 344        sram = devm_kzalloc(&pdev->dev, sizeof(*sram), GFP_KERNEL);
 345        if (!sram)
 346                return -ENOMEM;
 347
 348        sram->dev = &pdev->dev;
 349
 350        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 351        if (!res) {
 352                dev_err(sram->dev, "found no memory resource\n");
 353                return -EINVAL;
 354        }
 355
 356        size = resource_size(res);
 357
 358        if (!devm_request_mem_region(sram->dev, res->start, size, pdev->name)) {
 359                dev_err(sram->dev, "could not request region for resource\n");
 360                return -EBUSY;
 361        }
 362
 363        if (of_property_read_bool(pdev->dev.of_node, "no-memory-wc"))
 364                sram->virt_base = devm_ioremap(sram->dev, res->start, size);
 365        else
 366                sram->virt_base = devm_ioremap_wc(sram->dev, res->start, size);
 367        if (IS_ERR(sram->virt_base))
 368                return PTR_ERR(sram->virt_base);
 369
 370        sram->pool = devm_gen_pool_create(sram->dev, ilog2(SRAM_GRANULARITY),
 371                                          NUMA_NO_NODE, NULL);
 372        if (IS_ERR(sram->pool))
 373                return PTR_ERR(sram->pool);
 374
 375        ret = sram_reserve_regions(sram, res);
 376        if (ret)
 377                return ret;
 378
 379        sram->clk = devm_clk_get(sram->dev, NULL);
 380        if (IS_ERR(sram->clk))
 381                sram->clk = NULL;
 382        else
 383                clk_prepare_enable(sram->clk);
 384
 385        platform_set_drvdata(pdev, sram);
 386
 387        dev_dbg(sram->dev, "SRAM pool: %zu KiB @ 0x%p\n",
 388                gen_pool_size(sram->pool) / 1024, sram->virt_base);
 389
 390        return 0;
 391}
 392
 393static int sram_remove(struct platform_device *pdev)
 394{
 395        struct sram_dev *sram = platform_get_drvdata(pdev);
 396
 397        sram_free_partitions(sram);
 398
 399        if (gen_pool_avail(sram->pool) < gen_pool_size(sram->pool))
 400                dev_err(sram->dev, "removed while SRAM allocated\n");
 401
 402        if (sram->clk)
 403                clk_disable_unprepare(sram->clk);
 404
 405        return 0;
 406}
 407
 408#ifdef CONFIG_OF
 409static const struct of_device_id sram_dt_ids[] = {
 410        { .compatible = "mmio-sram" },
 411        {}
 412};
 413#endif
 414
 415static struct platform_driver sram_driver = {
 416        .driver = {
 417                .name = "sram",
 418                .of_match_table = of_match_ptr(sram_dt_ids),
 419        },
 420        .probe = sram_probe,
 421        .remove = sram_remove,
 422};
 423
 424static int __init sram_init(void)
 425{
 426        return platform_driver_register(&sram_driver);
 427}
 428
 429postcore_initcall(sram_init);
 430