linux/drivers/soc/qcom/smem.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2015, Sony Mobile Communications AB.
   3 * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License version 2 and
   7 * only version 2 as published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope that it will be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 * GNU General Public License for more details.
  13 */
  14
  15#include <linux/hwspinlock.h>
  16#include <linux/io.h>
  17#include <linux/module.h>
  18#include <linux/of.h>
  19#include <linux/of_address.h>
  20#include <linux/platform_device.h>
  21#include <linux/slab.h>
  22#include <linux/soc/qcom/smem.h>
  23
  24/*
  25 * The Qualcomm shared memory system is a allocate only heap structure that
  26 * consists of one of more memory areas that can be accessed by the processors
  27 * in the SoC.
  28 *
  29 * All systems contains a global heap, accessible by all processors in the SoC,
  30 * with a table of contents data structure (@smem_header) at the beginning of
  31 * the main shared memory block.
  32 *
  33 * The global header contains meta data for allocations as well as a fixed list
  34 * of 512 entries (@smem_global_entry) that can be initialized to reference
  35 * parts of the shared memory space.
  36 *
  37 *
  38 * In addition to this global heap a set of "private" heaps can be set up at
  39 * boot time with access restrictions so that only certain processor pairs can
  40 * access the data.
  41 *
  42 * These partitions are referenced from an optional partition table
  43 * (@smem_ptable), that is found 4kB from the end of the main smem region. The
  44 * partition table entries (@smem_ptable_entry) lists the involved processors
  45 * (or hosts) and their location in the main shared memory region.
  46 *
  47 * Each partition starts with a header (@smem_partition_header) that identifies
  48 * the partition and holds properties for the two internal memory regions. The
  49 * two regions are cached and non-cached memory respectively. Each region
  50 * contain a link list of allocation headers (@smem_private_entry) followed by
  51 * their data.
  52 *
  53 * Items in the non-cached region are allocated from the start of the partition
  54 * while items in the cached region are allocated from the end. The free area
  55 * is hence the region between the cached and non-cached offsets.
  56 *
  57 *
  58 * To synchronize allocations in the shared memory heaps a remote spinlock must
  59 * be held - currently lock number 3 of the sfpb or tcsr is used for this on all
  60 * platforms.
  61 *
  62 */
  63
  64/*
  65 * Item 3 of the global heap contains an array of versions for the various
  66 * software components in the SoC. We verify that the boot loader version is
  67 * what the expected version (SMEM_EXPECTED_VERSION) as a sanity check.
  68 */
  69#define SMEM_ITEM_VERSION       3
  70#define  SMEM_MASTER_SBL_VERSION_INDEX  7
  71#define  SMEM_EXPECTED_VERSION          11
  72
  73/*
  74 * The first 8 items are only to be allocated by the boot loader while
  75 * initializing the heap.
  76 */
  77#define SMEM_ITEM_LAST_FIXED    8
  78
  79/* Highest accepted item number, for both global and private heaps */
  80#define SMEM_ITEM_COUNT         512
  81
  82/* Processor/host identifier for the application processor */
  83#define SMEM_HOST_APPS          0
  84
  85/* Max number of processors/hosts in a system */
  86#define SMEM_HOST_COUNT         9
  87
  88/**
  89  * struct smem_proc_comm - proc_comm communication struct (legacy)
  90  * @command:   current command to be executed
  91  * @status:    status of the currently requested command
  92  * @params:    parameters to the command
  93  */
  94struct smem_proc_comm {
  95        __le32 command;
  96        __le32 status;
  97        __le32 params[2];
  98};
  99
 100/**
 101 * struct smem_global_entry - entry to reference smem items on the heap
 102 * @allocated:  boolean to indicate if this entry is used
 103 * @offset:     offset to the allocated space
 104 * @size:       size of the allocated space, 8 byte aligned
 105 * @aux_base:   base address for the memory region used by this unit, or 0 for
 106 *              the default region. bits 0,1 are reserved
 107 */
 108struct smem_global_entry {
 109        __le32 allocated;
 110        __le32 offset;
 111        __le32 size;
 112        __le32 aux_base; /* bits 1:0 reserved */
 113};
 114#define AUX_BASE_MASK           0xfffffffc
 115
 116/**
 117 * struct smem_header - header found in beginning of primary smem region
 118 * @proc_comm:          proc_comm communication interface (legacy)
 119 * @version:            array of versions for the various subsystems
 120 * @initialized:        boolean to indicate that smem is initialized
 121 * @free_offset:        index of the first unallocated byte in smem
 122 * @available:          number of bytes available for allocation
 123 * @reserved:           reserved field, must be 0
 124 * toc:                 array of references to items
 125 */
 126struct smem_header {
 127        struct smem_proc_comm proc_comm[4];
 128        __le32 version[32];
 129        __le32 initialized;
 130        __le32 free_offset;
 131        __le32 available;
 132        __le32 reserved;
 133        struct smem_global_entry toc[SMEM_ITEM_COUNT];
 134};
 135
 136/**
 137 * struct smem_ptable_entry - one entry in the @smem_ptable list
 138 * @offset:     offset, within the main shared memory region, of the partition
 139 * @size:       size of the partition
 140 * @flags:      flags for the partition (currently unused)
 141 * @host0:      first processor/host with access to this partition
 142 * @host1:      second processor/host with access to this partition
 143 * @reserved:   reserved entries for later use
 144 */
 145struct smem_ptable_entry {
 146        __le32 offset;
 147        __le32 size;
 148        __le32 flags;
 149        __le16 host0;
 150        __le16 host1;
 151        __le32 reserved[8];
 152};
 153
 154/**
 155 * struct smem_ptable - partition table for the private partitions
 156 * @magic:      magic number, must be SMEM_PTABLE_MAGIC
 157 * @version:    version of the partition table
 158 * @num_entries: number of partitions in the table
 159 * @reserved:   for now reserved entries
 160 * @entry:      list of @smem_ptable_entry for the @num_entries partitions
 161 */
 162struct smem_ptable {
 163        u8 magic[4];
 164        __le32 version;
 165        __le32 num_entries;
 166        __le32 reserved[5];
 167        struct smem_ptable_entry entry[];
 168};
 169
 170static const u8 SMEM_PTABLE_MAGIC[] = { 0x24, 0x54, 0x4f, 0x43 }; /* "$TOC" */
 171
 172/**
 173 * struct smem_partition_header - header of the partitions
 174 * @magic:      magic number, must be SMEM_PART_MAGIC
 175 * @host0:      first processor/host with access to this partition
 176 * @host1:      second processor/host with access to this partition
 177 * @size:       size of the partition
 178 * @offset_free_uncached: offset to the first free byte of uncached memory in
 179 *              this partition
 180 * @offset_free_cached: offset to the first free byte of cached memory in this
 181 *              partition
 182 * @reserved:   for now reserved entries
 183 */
 184struct smem_partition_header {
 185        u8 magic[4];
 186        __le16 host0;
 187        __le16 host1;
 188        __le32 size;
 189        __le32 offset_free_uncached;
 190        __le32 offset_free_cached;
 191        __le32 reserved[3];
 192};
 193
 194static const u8 SMEM_PART_MAGIC[] = { 0x24, 0x50, 0x52, 0x54 };
 195
 196/**
 197 * struct smem_private_entry - header of each item in the private partition
 198 * @canary:     magic number, must be SMEM_PRIVATE_CANARY
 199 * @item:       identifying number of the smem item
 200 * @size:       size of the data, including padding bytes
 201 * @padding_data: number of bytes of padding of data
 202 * @padding_hdr: number of bytes of padding between the header and the data
 203 * @reserved:   for now reserved entry
 204 */
 205struct smem_private_entry {
 206        u16 canary; /* bytes are the same so no swapping needed */
 207        __le16 item;
 208        __le32 size; /* includes padding bytes */
 209        __le16 padding_data;
 210        __le16 padding_hdr;
 211        __le32 reserved;
 212};
 213#define SMEM_PRIVATE_CANARY     0xa5a5
 214
 215/**
 216 * struct smem_region - representation of a chunk of memory used for smem
 217 * @aux_base:   identifier of aux_mem base
 218 * @virt_base:  virtual base address of memory with this aux_mem identifier
 219 * @size:       size of the memory region
 220 */
 221struct smem_region {
 222        u32 aux_base;
 223        void __iomem *virt_base;
 224        size_t size;
 225};
 226
 227/**
 228 * struct qcom_smem - device data for the smem device
 229 * @dev:        device pointer
 230 * @hwlock:     reference to a hwspinlock
 231 * @partitions: list of pointers to partitions affecting the current
 232 *              processor/host
 233 * @num_regions: number of @regions
 234 * @regions:    list of the memory regions defining the shared memory
 235 */
 236struct qcom_smem {
 237        struct device *dev;
 238
 239        struct hwspinlock *hwlock;
 240
 241        struct smem_partition_header *partitions[SMEM_HOST_COUNT];
 242
 243        unsigned num_regions;
 244        struct smem_region regions[0];
 245};
 246
 247static struct smem_private_entry *
 248phdr_to_last_private_entry(struct smem_partition_header *phdr)
 249{
 250        void *p = phdr;
 251
 252        return p + le32_to_cpu(phdr->offset_free_uncached);
 253}
 254
 255static void *phdr_to_first_cached_entry(struct smem_partition_header *phdr)
 256{
 257        void *p = phdr;
 258
 259        return p + le32_to_cpu(phdr->offset_free_cached);
 260}
 261
 262static struct smem_private_entry *
 263phdr_to_first_private_entry(struct smem_partition_header *phdr)
 264{
 265        void *p = phdr;
 266
 267        return p + sizeof(*phdr);
 268}
 269
 270static struct smem_private_entry *
 271private_entry_next(struct smem_private_entry *e)
 272{
 273        void *p = e;
 274
 275        return p + sizeof(*e) + le16_to_cpu(e->padding_hdr) +
 276               le32_to_cpu(e->size);
 277}
 278
 279static void *entry_to_item(struct smem_private_entry *e)
 280{
 281        void *p = e;
 282
 283        return p + sizeof(*e) + le16_to_cpu(e->padding_hdr);
 284}
 285
 286/* Pointer to the one and only smem handle */
 287static struct qcom_smem *__smem;
 288
 289/* Timeout (ms) for the trylock of remote spinlocks */
 290#define HWSPINLOCK_TIMEOUT      1000
 291
 292static int qcom_smem_alloc_private(struct qcom_smem *smem,
 293                                   unsigned host,
 294                                   unsigned item,
 295                                   size_t size)
 296{
 297        struct smem_partition_header *phdr;
 298        struct smem_private_entry *hdr, *end;
 299        size_t alloc_size;
 300        void *cached;
 301
 302        phdr = smem->partitions[host];
 303        hdr = phdr_to_first_private_entry(phdr);
 304        end = phdr_to_last_private_entry(phdr);
 305        cached = phdr_to_first_cached_entry(phdr);
 306
 307        while (hdr < end) {
 308                if (hdr->canary != SMEM_PRIVATE_CANARY) {
 309                        dev_err(smem->dev,
 310                                "Found invalid canary in host %d partition\n",
 311                                host);
 312                        return -EINVAL;
 313                }
 314
 315                if (le16_to_cpu(hdr->item) == item)
 316                        return -EEXIST;
 317
 318                hdr = private_entry_next(hdr);
 319        }
 320
 321        /* Check that we don't grow into the cached region */
 322        alloc_size = sizeof(*hdr) + ALIGN(size, 8);
 323        if ((void *)hdr + alloc_size >= cached) {
 324                dev_err(smem->dev, "Out of memory\n");
 325                return -ENOSPC;
 326        }
 327
 328        hdr->canary = SMEM_PRIVATE_CANARY;
 329        hdr->item = cpu_to_le16(item);
 330        hdr->size = cpu_to_le32(ALIGN(size, 8));
 331        hdr->padding_data = cpu_to_le16(le32_to_cpu(hdr->size) - size);
 332        hdr->padding_hdr = 0;
 333
 334        /*
 335         * Ensure the header is written before we advance the free offset, so
 336         * that remote processors that does not take the remote spinlock still
 337         * gets a consistent view of the linked list.
 338         */
 339        wmb();
 340        le32_add_cpu(&phdr->offset_free_uncached, alloc_size);
 341
 342        return 0;
 343}
 344
 345static int qcom_smem_alloc_global(struct qcom_smem *smem,
 346                                  unsigned item,
 347                                  size_t size)
 348{
 349        struct smem_header *header;
 350        struct smem_global_entry *entry;
 351
 352        if (WARN_ON(item >= SMEM_ITEM_COUNT))
 353                return -EINVAL;
 354
 355        header = smem->regions[0].virt_base;
 356        entry = &header->toc[item];
 357        if (entry->allocated)
 358                return -EEXIST;
 359
 360        size = ALIGN(size, 8);
 361        if (WARN_ON(size > le32_to_cpu(header->available)))
 362                return -ENOMEM;
 363
 364        entry->offset = header->free_offset;
 365        entry->size = cpu_to_le32(size);
 366
 367        /*
 368         * Ensure the header is consistent before we mark the item allocated,
 369         * so that remote processors will get a consistent view of the item
 370         * even though they do not take the spinlock on read.
 371         */
 372        wmb();
 373        entry->allocated = cpu_to_le32(1);
 374
 375        le32_add_cpu(&header->free_offset, size);
 376        le32_add_cpu(&header->available, -size);
 377
 378        return 0;
 379}
 380
 381/**
 382 * qcom_smem_alloc() - allocate space for a smem item
 383 * @host:       remote processor id, or -1
 384 * @item:       smem item handle
 385 * @size:       number of bytes to be allocated
 386 *
 387 * Allocate space for a given smem item of size @size, given that the item is
 388 * not yet allocated.
 389 */
 390int qcom_smem_alloc(unsigned host, unsigned item, size_t size)
 391{
 392        unsigned long flags;
 393        int ret;
 394
 395        if (!__smem)
 396                return -EPROBE_DEFER;
 397
 398        if (item < SMEM_ITEM_LAST_FIXED) {
 399                dev_err(__smem->dev,
 400                        "Rejecting allocation of static entry %d\n", item);
 401                return -EINVAL;
 402        }
 403
 404        ret = hwspin_lock_timeout_irqsave(__smem->hwlock,
 405                                          HWSPINLOCK_TIMEOUT,
 406                                          &flags);
 407        if (ret)
 408                return ret;
 409
 410        if (host < SMEM_HOST_COUNT && __smem->partitions[host])
 411                ret = qcom_smem_alloc_private(__smem, host, item, size);
 412        else
 413                ret = qcom_smem_alloc_global(__smem, item, size);
 414
 415        hwspin_unlock_irqrestore(__smem->hwlock, &flags);
 416
 417        return ret;
 418}
 419EXPORT_SYMBOL(qcom_smem_alloc);
 420
 421static void *qcom_smem_get_global(struct qcom_smem *smem,
 422                                  unsigned item,
 423                                  size_t *size)
 424{
 425        struct smem_header *header;
 426        struct smem_region *area;
 427        struct smem_global_entry *entry;
 428        u32 aux_base;
 429        unsigned i;
 430
 431        if (WARN_ON(item >= SMEM_ITEM_COUNT))
 432                return ERR_PTR(-EINVAL);
 433
 434        header = smem->regions[0].virt_base;
 435        entry = &header->toc[item];
 436        if (!entry->allocated)
 437                return ERR_PTR(-ENXIO);
 438
 439        aux_base = le32_to_cpu(entry->aux_base) & AUX_BASE_MASK;
 440
 441        for (i = 0; i < smem->num_regions; i++) {
 442                area = &smem->regions[i];
 443
 444                if (area->aux_base == aux_base || !aux_base) {
 445                        if (size != NULL)
 446                                *size = le32_to_cpu(entry->size);
 447                        return area->virt_base + le32_to_cpu(entry->offset);
 448                }
 449        }
 450
 451        return ERR_PTR(-ENOENT);
 452}
 453
 454static void *qcom_smem_get_private(struct qcom_smem *smem,
 455                                   unsigned host,
 456                                   unsigned item,
 457                                   size_t *size)
 458{
 459        struct smem_partition_header *phdr;
 460        struct smem_private_entry *e, *end;
 461
 462        phdr = smem->partitions[host];
 463        e = phdr_to_first_private_entry(phdr);
 464        end = phdr_to_last_private_entry(phdr);
 465
 466        while (e < end) {
 467                if (e->canary != SMEM_PRIVATE_CANARY) {
 468                        dev_err(smem->dev,
 469                                "Found invalid canary in host %d partition\n",
 470                                host);
 471                        return ERR_PTR(-EINVAL);
 472                }
 473
 474                if (le16_to_cpu(e->item) == item) {
 475                        if (size != NULL)
 476                                *size = le32_to_cpu(e->size) -
 477                                        le16_to_cpu(e->padding_data);
 478
 479                        return entry_to_item(e);
 480                }
 481
 482                e = private_entry_next(e);
 483        }
 484
 485        return ERR_PTR(-ENOENT);
 486}
 487
 488/**
 489 * qcom_smem_get() - resolve ptr of size of a smem item
 490 * @host:       the remote processor, or -1
 491 * @item:       smem item handle
 492 * @size:       pointer to be filled out with size of the item
 493 *
 494 * Looks up smem item and returns pointer to it. Size of smem
 495 * item is returned in @size.
 496 */
 497void *qcom_smem_get(unsigned host, unsigned item, size_t *size)
 498{
 499        unsigned long flags;
 500        int ret;
 501        void *ptr = ERR_PTR(-EPROBE_DEFER);
 502
 503        if (!__smem)
 504                return ptr;
 505
 506        ret = hwspin_lock_timeout_irqsave(__smem->hwlock,
 507                                          HWSPINLOCK_TIMEOUT,
 508                                          &flags);
 509        if (ret)
 510                return ERR_PTR(ret);
 511
 512        if (host < SMEM_HOST_COUNT && __smem->partitions[host])
 513                ptr = qcom_smem_get_private(__smem, host, item, size);
 514        else
 515                ptr = qcom_smem_get_global(__smem, item, size);
 516
 517        hwspin_unlock_irqrestore(__smem->hwlock, &flags);
 518
 519        return ptr;
 520
 521}
 522EXPORT_SYMBOL(qcom_smem_get);
 523
 524/**
 525 * qcom_smem_get_free_space() - retrieve amount of free space in a partition
 526 * @host:       the remote processor identifying a partition, or -1
 527 *
 528 * To be used by smem clients as a quick way to determine if any new
 529 * allocations has been made.
 530 */
 531int qcom_smem_get_free_space(unsigned host)
 532{
 533        struct smem_partition_header *phdr;
 534        struct smem_header *header;
 535        unsigned ret;
 536
 537        if (!__smem)
 538                return -EPROBE_DEFER;
 539
 540        if (host < SMEM_HOST_COUNT && __smem->partitions[host]) {
 541                phdr = __smem->partitions[host];
 542                ret = le32_to_cpu(phdr->offset_free_cached) -
 543                      le32_to_cpu(phdr->offset_free_uncached);
 544        } else {
 545                header = __smem->regions[0].virt_base;
 546                ret = le32_to_cpu(header->available);
 547        }
 548
 549        return ret;
 550}
 551EXPORT_SYMBOL(qcom_smem_get_free_space);
 552
 553static int qcom_smem_get_sbl_version(struct qcom_smem *smem)
 554{
 555        __le32 *versions;
 556        size_t size;
 557
 558        versions = qcom_smem_get_global(smem, SMEM_ITEM_VERSION, &size);
 559        if (IS_ERR(versions)) {
 560                dev_err(smem->dev, "Unable to read the version item\n");
 561                return -ENOENT;
 562        }
 563
 564        if (size < sizeof(unsigned) * SMEM_MASTER_SBL_VERSION_INDEX) {
 565                dev_err(smem->dev, "Version item is too small\n");
 566                return -EINVAL;
 567        }
 568
 569        return le32_to_cpu(versions[SMEM_MASTER_SBL_VERSION_INDEX]);
 570}
 571
 572static int qcom_smem_enumerate_partitions(struct qcom_smem *smem,
 573                                          unsigned local_host)
 574{
 575        struct smem_partition_header *header;
 576        struct smem_ptable_entry *entry;
 577        struct smem_ptable *ptable;
 578        unsigned remote_host;
 579        u32 version, host0, host1;
 580        int i;
 581
 582        ptable = smem->regions[0].virt_base + smem->regions[0].size - SZ_4K;
 583        if (memcmp(ptable->magic, SMEM_PTABLE_MAGIC, sizeof(ptable->magic)))
 584                return 0;
 585
 586        version = le32_to_cpu(ptable->version);
 587        if (version != 1) {
 588                dev_err(smem->dev,
 589                        "Unsupported partition header version %d\n", version);
 590                return -EINVAL;
 591        }
 592
 593        for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
 594                entry = &ptable->entry[i];
 595                host0 = le16_to_cpu(entry->host0);
 596                host1 = le16_to_cpu(entry->host1);
 597
 598                if (host0 != local_host && host1 != local_host)
 599                        continue;
 600
 601                if (!le32_to_cpu(entry->offset))
 602                        continue;
 603
 604                if (!le32_to_cpu(entry->size))
 605                        continue;
 606
 607                if (host0 == local_host)
 608                        remote_host = host1;
 609                else
 610                        remote_host = host0;
 611
 612                if (remote_host >= SMEM_HOST_COUNT) {
 613                        dev_err(smem->dev,
 614                                "Invalid remote host %d\n",
 615                                remote_host);
 616                        return -EINVAL;
 617                }
 618
 619                if (smem->partitions[remote_host]) {
 620                        dev_err(smem->dev,
 621                                "Already found a partition for host %d\n",
 622                                remote_host);
 623                        return -EINVAL;
 624                }
 625
 626                header = smem->regions[0].virt_base + le32_to_cpu(entry->offset);
 627                host0 = le16_to_cpu(header->host0);
 628                host1 = le16_to_cpu(header->host1);
 629
 630                if (memcmp(header->magic, SMEM_PART_MAGIC,
 631                            sizeof(header->magic))) {
 632                        dev_err(smem->dev,
 633                                "Partition %d has invalid magic\n", i);
 634                        return -EINVAL;
 635                }
 636
 637                if (host0 != local_host && host1 != local_host) {
 638                        dev_err(smem->dev,
 639                                "Partition %d hosts are invalid\n", i);
 640                        return -EINVAL;
 641                }
 642
 643                if (host0 != remote_host && host1 != remote_host) {
 644                        dev_err(smem->dev,
 645                                "Partition %d hosts are invalid\n", i);
 646                        return -EINVAL;
 647                }
 648
 649                if (header->size != entry->size) {
 650                        dev_err(smem->dev,
 651                                "Partition %d has invalid size\n", i);
 652                        return -EINVAL;
 653                }
 654
 655                if (le32_to_cpu(header->offset_free_uncached) > le32_to_cpu(header->size)) {
 656                        dev_err(smem->dev,
 657                                "Partition %d has invalid free pointer\n", i);
 658                        return -EINVAL;
 659                }
 660
 661                smem->partitions[remote_host] = header;
 662        }
 663
 664        return 0;
 665}
 666
 667static int qcom_smem_map_memory(struct qcom_smem *smem, struct device *dev,
 668                                const char *name, int i)
 669{
 670        struct device_node *np;
 671        struct resource r;
 672        int ret;
 673
 674        np = of_parse_phandle(dev->of_node, name, 0);
 675        if (!np) {
 676                dev_err(dev, "No %s specified\n", name);
 677                return -EINVAL;
 678        }
 679
 680        ret = of_address_to_resource(np, 0, &r);
 681        of_node_put(np);
 682        if (ret)
 683                return ret;
 684
 685        smem->regions[i].aux_base = (u32)r.start;
 686        smem->regions[i].size = resource_size(&r);
 687        smem->regions[i].virt_base = devm_ioremap_wc(dev, r.start, resource_size(&r));
 688        if (!smem->regions[i].virt_base)
 689                return -ENOMEM;
 690
 691        return 0;
 692}
 693
 694static int qcom_smem_probe(struct platform_device *pdev)
 695{
 696        struct smem_header *header;
 697        struct qcom_smem *smem;
 698        size_t array_size;
 699        int num_regions;
 700        int hwlock_id;
 701        u32 version;
 702        int ret;
 703
 704        num_regions = 1;
 705        if (of_find_property(pdev->dev.of_node, "qcom,rpm-msg-ram", NULL))
 706                num_regions++;
 707
 708        array_size = num_regions * sizeof(struct smem_region);
 709        smem = devm_kzalloc(&pdev->dev, sizeof(*smem) + array_size, GFP_KERNEL);
 710        if (!smem)
 711                return -ENOMEM;
 712
 713        smem->dev = &pdev->dev;
 714        smem->num_regions = num_regions;
 715
 716        ret = qcom_smem_map_memory(smem, &pdev->dev, "memory-region", 0);
 717        if (ret)
 718                return ret;
 719
 720        if (num_regions > 1 && (ret = qcom_smem_map_memory(smem, &pdev->dev,
 721                                        "qcom,rpm-msg-ram", 1)))
 722                return ret;
 723
 724        header = smem->regions[0].virt_base;
 725        if (le32_to_cpu(header->initialized) != 1 ||
 726            le32_to_cpu(header->reserved)) {
 727                dev_err(&pdev->dev, "SMEM is not initialized by SBL\n");
 728                return -EINVAL;
 729        }
 730
 731        version = qcom_smem_get_sbl_version(smem);
 732        if (version >> 16 != SMEM_EXPECTED_VERSION) {
 733                dev_err(&pdev->dev, "Unsupported SMEM version 0x%x\n", version);
 734                return -EINVAL;
 735        }
 736
 737        ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS);
 738        if (ret < 0)
 739                return ret;
 740
 741        hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0);
 742        if (hwlock_id < 0) {
 743                if (hwlock_id != -EPROBE_DEFER)
 744                        dev_err(&pdev->dev, "failed to retrieve hwlock\n");
 745                return hwlock_id;
 746        }
 747
 748        smem->hwlock = hwspin_lock_request_specific(hwlock_id);
 749        if (!smem->hwlock)
 750                return -ENXIO;
 751
 752        __smem = smem;
 753
 754        return 0;
 755}
 756
 757static int qcom_smem_remove(struct platform_device *pdev)
 758{
 759        hwspin_lock_free(__smem->hwlock);
 760        __smem = NULL;
 761
 762        return 0;
 763}
 764
 765static const struct of_device_id qcom_smem_of_match[] = {
 766        { .compatible = "qcom,smem" },
 767        {}
 768};
 769MODULE_DEVICE_TABLE(of, qcom_smem_of_match);
 770
 771static struct platform_driver qcom_smem_driver = {
 772        .probe = qcom_smem_probe,
 773        .remove = qcom_smem_remove,
 774        .driver  = {
 775                .name = "qcom-smem",
 776                .of_match_table = qcom_smem_of_match,
 777                .suppress_bind_attrs = true,
 778        },
 779};
 780
 781static int __init qcom_smem_init(void)
 782{
 783        return platform_driver_register(&qcom_smem_driver);
 784}
 785arch_initcall(qcom_smem_init);
 786
 787static void __exit qcom_smem_exit(void)
 788{
 789        platform_driver_unregister(&qcom_smem_driver);
 790}
 791module_exit(qcom_smem_exit)
 792
 793MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
 794MODULE_DESCRIPTION("Qualcomm Shared Memory Manager");
 795MODULE_LICENSE("GPL v2");
 796