uboot/drivers/smem/msm_smem.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2015, Sony Mobile Communications AB.
   4 * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
   5 * Copyright (c) 2018, Ramon Fried <ramon.fried@gmail.com>
   6 */
   7
   8#include <common.h>
   9#include <errno.h>
  10#include <dm.h>
  11#include <asm/global_data.h>
  12#include <dm/device_compat.h>
  13#include <dm/devres.h>
  14#include <dm/of_access.h>
  15#include <dm/of_addr.h>
  16#include <asm/io.h>
  17#include <linux/bug.h>
  18#include <linux/err.h>
  19#include <linux/ioport.h>
  20#include <linux/io.h>
  21#include <smem.h>
  22
  23DECLARE_GLOBAL_DATA_PTR;
  24
  25/*
  26 * The Qualcomm shared memory system is an allocate-only heap structure that
  27 * consists of one of more memory areas that can be accessed by the processors
  28 * in the SoC.
  29 *
  30 * All systems contains a global heap, accessible by all processors in the SoC,
  31 * with a table of contents data structure (@smem_header) at the beginning of
  32 * the main shared memory block.
  33 *
  34 * The global header contains meta data for allocations as well as a fixed list
  35 * of 512 entries (@smem_global_entry) that can be initialized to reference
  36 * parts of the shared memory space.
  37 *
  38 *
  39 * In addition to this global heap, a set of "private" heaps can be set up at
  40 * boot time with access restrictions so that only certain processor pairs can
  41 * access the data.
  42 *
  43 * These partitions are referenced from an optional partition table
  44 * (@smem_ptable), that is found 4kB from the end of the main smem region. The
  45 * partition table entries (@smem_ptable_entry) lists the involved processors
  46 * (or hosts) and their location in the main shared memory region.
  47 *
  48 * Each partition starts with a header (@smem_partition_header) that identifies
  49 * the partition and holds properties for the two internal memory regions. The
  50 * two regions are cached and non-cached memory respectively. Each region
  51 * contain a link list of allocation headers (@smem_private_entry) followed by
  52 * their data.
  53 *
  54 * Items in the non-cached region are allocated from the start of the partition
  55 * while items in the cached region are allocated from the end. The free area
  56 * is hence the region between the cached and non-cached offsets. The header of
  57 * cached items comes after the data.
  58 *
  59 * Version 12 (SMEM_GLOBAL_PART_VERSION) changes the item alloc/get procedure
  60 * for the global heap. A new global partition is created from the global heap
  61 * region with partition type (SMEM_GLOBAL_HOST) and the max smem item count is
  62 * set by the bootloader.
  63 *
  64 */
  65
  66/*
  67 * The version member of the smem header contains an array of versions for the
  68 * various software components in the SoC. We verify that the boot loader
  69 * version is a valid version as a sanity check.
  70 */
  71#define SMEM_MASTER_SBL_VERSION_INDEX   7
  72#define SMEM_GLOBAL_HEAP_VERSION        11
  73#define SMEM_GLOBAL_PART_VERSION        12
  74
  75/*
  76 * The first 8 items are only to be allocated by the boot loader while
  77 * initializing the heap.
  78 */
  79#define SMEM_ITEM_LAST_FIXED    8
  80
  81/* Highest accepted item number, for both global and private heaps */
  82#define SMEM_ITEM_COUNT         512
  83
  84/* Processor/host identifier for the application processor */
  85#define SMEM_HOST_APPS          0
  86
  87/* Processor/host identifier for the global partition */
  88#define SMEM_GLOBAL_HOST        0xfffe
  89
  90/* Max number of processors/hosts in a system */
  91#define SMEM_HOST_COUNT         10
  92
  93/**
  94 * struct smem_proc_comm - proc_comm communication struct (legacy)
  95 * @command:    current command to be executed
  96 * @status:     status of the currently requested command
  97 * @params:     parameters to the command
  98 */
  99struct smem_proc_comm {
 100        __le32 command;
 101        __le32 status;
 102        __le32 params[2];
 103};
 104
 105/**
 106 * struct smem_global_entry - entry to reference smem items on the heap
 107 * @allocated:  boolean to indicate if this entry is used
 108 * @offset:     offset to the allocated space
 109 * @size:       size of the allocated space, 8 byte aligned
 110 * @aux_base:   base address for the memory region used by this unit, or 0 for
 111 *              the default region. bits 0,1 are reserved
 112 */
 113struct smem_global_entry {
 114        __le32 allocated;
 115        __le32 offset;
 116        __le32 size;
 117        __le32 aux_base; /* bits 1:0 reserved */
 118};
 119#define AUX_BASE_MASK           0xfffffffc
 120
 121/**
 122 * struct smem_header - header found in beginning of primary smem region
 123 * @proc_comm:          proc_comm communication interface (legacy)
 124 * @version:            array of versions for the various subsystems
 125 * @initialized:        boolean to indicate that smem is initialized
 126 * @free_offset:        index of the first unallocated byte in smem
 127 * @available:          number of bytes available for allocation
 128 * @reserved:           reserved field, must be 0
 129 * toc:                 array of references to items
 130 */
 131struct smem_header {
 132        struct smem_proc_comm proc_comm[4];
 133        __le32 version[32];
 134        __le32 initialized;
 135        __le32 free_offset;
 136        __le32 available;
 137        __le32 reserved;
 138        struct smem_global_entry toc[SMEM_ITEM_COUNT];
 139};
 140
 141/**
 142 * struct smem_ptable_entry - one entry in the @smem_ptable list
 143 * @offset:     offset, within the main shared memory region, of the partition
 144 * @size:       size of the partition
 145 * @flags:      flags for the partition (currently unused)
 146 * @host0:      first processor/host with access to this partition
 147 * @host1:      second processor/host with access to this partition
 148 * @cacheline:  alignment for "cached" entries
 149 * @reserved:   reserved entries for later use
 150 */
 151struct smem_ptable_entry {
 152        __le32 offset;
 153        __le32 size;
 154        __le32 flags;
 155        __le16 host0;
 156        __le16 host1;
 157        __le32 cacheline;
 158        __le32 reserved[7];
 159};
 160
 161/**
 162 * struct smem_ptable - partition table for the private partitions
 163 * @magic:      magic number, must be SMEM_PTABLE_MAGIC
 164 * @version:    version of the partition table
 165 * @num_entries: number of partitions in the table
 166 * @reserved:   for now reserved entries
 167 * @entry:      list of @smem_ptable_entry for the @num_entries partitions
 168 */
 169struct smem_ptable {
 170        u8 magic[4];
 171        __le32 version;
 172        __le32 num_entries;
 173        __le32 reserved[5];
 174        struct smem_ptable_entry entry[];
 175};
 176
 177static const u8 SMEM_PTABLE_MAGIC[] = { 0x24, 0x54, 0x4f, 0x43 }; /* "$TOC" */
 178
 179/**
 180 * struct smem_partition_header - header of the partitions
 181 * @magic:      magic number, must be SMEM_PART_MAGIC
 182 * @host0:      first processor/host with access to this partition
 183 * @host1:      second processor/host with access to this partition
 184 * @size:       size of the partition
 185 * @offset_free_uncached: offset to the first free byte of uncached memory in
 186 *              this partition
 187 * @offset_free_cached: offset to the first free byte of cached memory in this
 188 *              partition
 189 * @reserved:   for now reserved entries
 190 */
 191struct smem_partition_header {
 192        u8 magic[4];
 193        __le16 host0;
 194        __le16 host1;
 195        __le32 size;
 196        __le32 offset_free_uncached;
 197        __le32 offset_free_cached;
 198        __le32 reserved[3];
 199};
 200
 201static const u8 SMEM_PART_MAGIC[] = { 0x24, 0x50, 0x52, 0x54 };
 202
 203/**
 204 * struct smem_private_entry - header of each item in the private partition
 205 * @canary:     magic number, must be SMEM_PRIVATE_CANARY
 206 * @item:       identifying number of the smem item
 207 * @size:       size of the data, including padding bytes
 208 * @padding_data: number of bytes of padding of data
 209 * @padding_hdr: number of bytes of padding between the header and the data
 210 * @reserved:   for now reserved entry
 211 */
 212struct smem_private_entry {
 213        u16 canary; /* bytes are the same so no swapping needed */
 214        __le16 item;
 215        __le32 size; /* includes padding bytes */
 216        __le16 padding_data;
 217        __le16 padding_hdr;
 218        __le32 reserved;
 219};
 220#define SMEM_PRIVATE_CANARY     0xa5a5
 221
 222/**
 223 * struct smem_info - smem region info located after the table of contents
 224 * @magic:      magic number, must be SMEM_INFO_MAGIC
 225 * @size:       size of the smem region
 226 * @base_addr:  base address of the smem region
 227 * @reserved:   for now reserved entry
 228 * @num_items:  highest accepted item number
 229 */
 230struct smem_info {
 231        u8 magic[4];
 232        __le32 size;
 233        __le32 base_addr;
 234        __le32 reserved;
 235        __le16 num_items;
 236};
 237
 238static const u8 SMEM_INFO_MAGIC[] = { 0x53, 0x49, 0x49, 0x49 }; /* SIII */
 239
 240/**
 241 * struct smem_region - representation of a chunk of memory used for smem
 242 * @aux_base:   identifier of aux_mem base
 243 * @virt_base:  virtual base address of memory with this aux_mem identifier
 244 * @size:       size of the memory region
 245 */
 246struct smem_region {
 247        u32 aux_base;
 248        void __iomem *virt_base;
 249        size_t size;
 250};
 251
 252/**
 253 * struct qcom_smem - device data for the smem device
 254 * @dev:        device pointer
 255 * @global_partition:   pointer to global partition when in use
 256 * @global_cacheline:   cacheline size for global partition
 257 * @partitions: list of pointers to partitions affecting the current
 258 *              processor/host
 259 * @cacheline:  list of cacheline sizes for each host
 260 * @item_count: max accepted item number
 261 * @num_regions: number of @regions
 262 * @regions:    list of the memory regions defining the shared memory
 263 */
 264struct qcom_smem {
 265        struct udevice *dev;
 266
 267        struct smem_partition_header *global_partition;
 268        size_t global_cacheline;
 269        struct smem_partition_header *partitions[SMEM_HOST_COUNT];
 270        size_t cacheline[SMEM_HOST_COUNT];
 271        u32 item_count;
 272
 273        unsigned int num_regions;
 274        struct smem_region regions[0];
 275};
 276
 277static struct smem_private_entry *
 278phdr_to_last_uncached_entry(struct smem_partition_header *phdr)
 279{
 280        void *p = phdr;
 281
 282        return p + le32_to_cpu(phdr->offset_free_uncached);
 283}
 284
 285static void *phdr_to_first_cached_entry(struct smem_partition_header *phdr,
 286                                        size_t cacheline)
 287{
 288        void *p = phdr;
 289
 290        return p + le32_to_cpu(phdr->size) - ALIGN(sizeof(*phdr), cacheline);
 291}
 292
 293static void *phdr_to_last_cached_entry(struct smem_partition_header *phdr)
 294{
 295        void *p = phdr;
 296
 297        return p + le32_to_cpu(phdr->offset_free_cached);
 298}
 299
 300static struct smem_private_entry *
 301phdr_to_first_uncached_entry(struct smem_partition_header *phdr)
 302{
 303        void *p = phdr;
 304
 305        return p + sizeof(*phdr);
 306}
 307
 308static struct smem_private_entry *
 309uncached_entry_next(struct smem_private_entry *e)
 310{
 311        void *p = e;
 312
 313        return p + sizeof(*e) + le16_to_cpu(e->padding_hdr) +
 314               le32_to_cpu(e->size);
 315}
 316
 317static struct smem_private_entry *
 318cached_entry_next(struct smem_private_entry *e, size_t cacheline)
 319{
 320        void *p = e;
 321
 322        return p - le32_to_cpu(e->size) - ALIGN(sizeof(*e), cacheline);
 323}
 324
 325static void *uncached_entry_to_item(struct smem_private_entry *e)
 326{
 327        void *p = e;
 328
 329        return p + sizeof(*e) + le16_to_cpu(e->padding_hdr);
 330}
 331
 332static void *cached_entry_to_item(struct smem_private_entry *e)
 333{
 334        void *p = e;
 335
 336        return p - le32_to_cpu(e->size);
 337}
 338
 339/* Pointer to the one and only smem handle */
 340static struct qcom_smem *__smem;
 341
 342static int qcom_smem_alloc_private(struct qcom_smem *smem,
 343                                   struct smem_partition_header *phdr,
 344                                   unsigned int item,
 345                                   size_t size)
 346{
 347        struct smem_private_entry *hdr, *end;
 348        size_t alloc_size;
 349        void *cached;
 350
 351        hdr = phdr_to_first_uncached_entry(phdr);
 352        end = phdr_to_last_uncached_entry(phdr);
 353        cached = phdr_to_last_cached_entry(phdr);
 354
 355        while (hdr < end) {
 356                if (hdr->canary != SMEM_PRIVATE_CANARY) {
 357                        dev_err(smem->dev,
 358                                "Found invalid canary in hosts %d:%d partition\n",
 359                                phdr->host0, phdr->host1);
 360                        return -EINVAL;
 361                }
 362
 363                if (le16_to_cpu(hdr->item) == item)
 364                        return -EEXIST;
 365
 366                hdr = uncached_entry_next(hdr);
 367        }
 368
 369        /* Check that we don't grow into the cached region */
 370        alloc_size = sizeof(*hdr) + ALIGN(size, 8);
 371        if ((void *)hdr + alloc_size >= cached) {
 372                dev_err(smem->dev, "Out of memory\n");
 373                return -ENOSPC;
 374        }
 375
 376        hdr->canary = SMEM_PRIVATE_CANARY;
 377        hdr->item = cpu_to_le16(item);
 378        hdr->size = cpu_to_le32(ALIGN(size, 8));
 379        hdr->padding_data = cpu_to_le16(le32_to_cpu(hdr->size) - size);
 380        hdr->padding_hdr = 0;
 381
 382        /*
 383         * Ensure the header is written before we advance the free offset, so
 384         * that remote processors that does not take the remote spinlock still
 385         * gets a consistent view of the linked list.
 386         */
 387        dmb();
 388        le32_add_cpu(&phdr->offset_free_uncached, alloc_size);
 389
 390        return 0;
 391}
 392
 393static int qcom_smem_alloc_global(struct qcom_smem *smem,
 394                                  unsigned int item,
 395                                  size_t size)
 396{
 397        struct smem_global_entry *entry;
 398        struct smem_header *header;
 399
 400        header = smem->regions[0].virt_base;
 401        entry = &header->toc[item];
 402        if (entry->allocated)
 403                return -EEXIST;
 404
 405        size = ALIGN(size, 8);
 406        if (WARN_ON(size > le32_to_cpu(header->available)))
 407                return -ENOMEM;
 408
 409        entry->offset = header->free_offset;
 410        entry->size = cpu_to_le32(size);
 411
 412        /*
 413         * Ensure the header is consistent before we mark the item allocated,
 414         * so that remote processors will get a consistent view of the item
 415         * even though they do not take the spinlock on read.
 416         */
 417        dmb();
 418        entry->allocated = cpu_to_le32(1);
 419
 420        le32_add_cpu(&header->free_offset, size);
 421        le32_add_cpu(&header->available, -size);
 422
 423        return 0;
 424}
 425
 426/**
 427 * qcom_smem_alloc() - allocate space for a smem item
 428 * @host:       remote processor id, or -1
 429 * @item:       smem item handle
 430 * @size:       number of bytes to be allocated
 431 *
 432 * Allocate space for a given smem item of size @size, given that the item is
 433 * not yet allocated.
 434 */
 435static int qcom_smem_alloc(unsigned int host, unsigned int item, size_t size)
 436{
 437        struct smem_partition_header *phdr;
 438        int ret;
 439
 440        if (!__smem)
 441                return -ENOMEM;
 442
 443        if (item < SMEM_ITEM_LAST_FIXED) {
 444                dev_err(__smem->dev,
 445                        "Rejecting allocation of static entry %d\n", item);
 446                return -EINVAL;
 447        }
 448
 449        if (WARN_ON(item >= __smem->item_count))
 450                return -EINVAL;
 451
 452        if (host < SMEM_HOST_COUNT && __smem->partitions[host]) {
 453                phdr = __smem->partitions[host];
 454                ret = qcom_smem_alloc_private(__smem, phdr, item, size);
 455        } else if (__smem->global_partition) {
 456                phdr = __smem->global_partition;
 457                ret = qcom_smem_alloc_private(__smem, phdr, item, size);
 458        } else {
 459                ret = qcom_smem_alloc_global(__smem, item, size);
 460        }
 461
 462        return ret;
 463}
 464
 465static void *qcom_smem_get_global(struct qcom_smem *smem,
 466                                  unsigned int item,
 467                                  size_t *size)
 468{
 469        struct smem_header *header;
 470        struct smem_region *area;
 471        struct smem_global_entry *entry;
 472        u32 aux_base;
 473        unsigned int i;
 474
 475        header = smem->regions[0].virt_base;
 476        entry = &header->toc[item];
 477        if (!entry->allocated)
 478                return ERR_PTR(-ENXIO);
 479
 480        aux_base = le32_to_cpu(entry->aux_base) & AUX_BASE_MASK;
 481
 482        for (i = 0; i < smem->num_regions; i++) {
 483                area = &smem->regions[i];
 484
 485                if (area->aux_base == aux_base || !aux_base) {
 486                        if (size != NULL)
 487                                *size = le32_to_cpu(entry->size);
 488                        return area->virt_base + le32_to_cpu(entry->offset);
 489                }
 490        }
 491
 492        return ERR_PTR(-ENOENT);
 493}
 494
 495static void *qcom_smem_get_private(struct qcom_smem *smem,
 496                                   struct smem_partition_header *phdr,
 497                                   size_t cacheline,
 498                                   unsigned int item,
 499                                   size_t *size)
 500{
 501        struct smem_private_entry *e, *end;
 502
 503        e = phdr_to_first_uncached_entry(phdr);
 504        end = phdr_to_last_uncached_entry(phdr);
 505
 506        while (e < end) {
 507                if (e->canary != SMEM_PRIVATE_CANARY)
 508                        goto invalid_canary;
 509
 510                if (le16_to_cpu(e->item) == item) {
 511                        if (size != NULL)
 512                                *size = le32_to_cpu(e->size) -
 513                                        le16_to_cpu(e->padding_data);
 514
 515                        return uncached_entry_to_item(e);
 516                }
 517
 518                e = uncached_entry_next(e);
 519        }
 520
 521        /* Item was not found in the uncached list, search the cached list */
 522
 523        e = phdr_to_first_cached_entry(phdr, cacheline);
 524        end = phdr_to_last_cached_entry(phdr);
 525
 526        while (e > end) {
 527                if (e->canary != SMEM_PRIVATE_CANARY)
 528                        goto invalid_canary;
 529
 530                if (le16_to_cpu(e->item) == item) {
 531                        if (size != NULL)
 532                                *size = le32_to_cpu(e->size) -
 533                                        le16_to_cpu(e->padding_data);
 534
 535                        return cached_entry_to_item(e);
 536                }
 537
 538                e = cached_entry_next(e, cacheline);
 539        }
 540
 541        return ERR_PTR(-ENOENT);
 542
 543invalid_canary:
 544        dev_err(smem->dev, "Found invalid canary in hosts %d:%d partition\n",
 545                        phdr->host0, phdr->host1);
 546
 547        return ERR_PTR(-EINVAL);
 548}
 549
 550/**
 551 * qcom_smem_get() - resolve ptr of size of a smem item
 552 * @host:       the remote processor, or -1
 553 * @item:       smem item handle
 554 * @size:       pointer to be filled out with size of the item
 555 *
 556 * Looks up smem item and returns pointer to it. Size of smem
 557 * item is returned in @size.
 558 */
 559static void *qcom_smem_get(unsigned int host, unsigned int item, size_t *size)
 560{
 561        struct smem_partition_header *phdr;
 562        size_t cacheln;
 563        void *ptr = ERR_PTR(-ENOMEM);
 564
 565        if (!__smem)
 566                return ptr;
 567
 568        if (WARN_ON(item >= __smem->item_count))
 569                return ERR_PTR(-EINVAL);
 570
 571        if (host < SMEM_HOST_COUNT && __smem->partitions[host]) {
 572                phdr = __smem->partitions[host];
 573                cacheln = __smem->cacheline[host];
 574                ptr = qcom_smem_get_private(__smem, phdr, cacheln, item, size);
 575        } else if (__smem->global_partition) {
 576                phdr = __smem->global_partition;
 577                cacheln = __smem->global_cacheline;
 578                ptr = qcom_smem_get_private(__smem, phdr, cacheln, item, size);
 579        } else {
 580                ptr = qcom_smem_get_global(__smem, item, size);
 581        }
 582
 583        return ptr;
 584
 585}
 586
 587/**
 588 * qcom_smem_get_free_space() - retrieve amount of free space in a partition
 589 * @host:       the remote processor identifying a partition, or -1
 590 *
 591 * To be used by smem clients as a quick way to determine if any new
 592 * allocations has been made.
 593 */
 594static int qcom_smem_get_free_space(unsigned int host)
 595{
 596        struct smem_partition_header *phdr;
 597        struct smem_header *header;
 598        unsigned int ret;
 599
 600        if (!__smem)
 601                return -ENOMEM;
 602
 603        if (host < SMEM_HOST_COUNT && __smem->partitions[host]) {
 604                phdr = __smem->partitions[host];
 605                ret = le32_to_cpu(phdr->offset_free_cached) -
 606                      le32_to_cpu(phdr->offset_free_uncached);
 607        } else if (__smem->global_partition) {
 608                phdr = __smem->global_partition;
 609                ret = le32_to_cpu(phdr->offset_free_cached) -
 610                      le32_to_cpu(phdr->offset_free_uncached);
 611        } else {
 612                header = __smem->regions[0].virt_base;
 613                ret = le32_to_cpu(header->available);
 614        }
 615
 616        return ret;
 617}
 618
 619static int qcom_smem_get_sbl_version(struct qcom_smem *smem)
 620{
 621        struct smem_header *header;
 622        __le32 *versions;
 623
 624        header = smem->regions[0].virt_base;
 625        versions = header->version;
 626
 627        return le32_to_cpu(versions[SMEM_MASTER_SBL_VERSION_INDEX]);
 628}
 629
 630static struct smem_ptable *qcom_smem_get_ptable(struct qcom_smem *smem)
 631{
 632        struct smem_ptable *ptable;
 633        u32 version;
 634
 635        ptable = smem->regions[0].virt_base + smem->regions[0].size - SZ_4K;
 636        if (memcmp(ptable->magic, SMEM_PTABLE_MAGIC, sizeof(ptable->magic)))
 637                return ERR_PTR(-ENOENT);
 638
 639        version = le32_to_cpu(ptable->version);
 640        if (version != 1) {
 641                dev_err(smem->dev,
 642                        "Unsupported partition header version %d\n", version);
 643                return ERR_PTR(-EINVAL);
 644        }
 645        return ptable;
 646}
 647
 648static u32 qcom_smem_get_item_count(struct qcom_smem *smem)
 649{
 650        struct smem_ptable *ptable;
 651        struct smem_info *info;
 652
 653        ptable = qcom_smem_get_ptable(smem);
 654        if (IS_ERR_OR_NULL(ptable))
 655                return SMEM_ITEM_COUNT;
 656
 657        info = (struct smem_info *)&ptable->entry[ptable->num_entries];
 658        if (memcmp(info->magic, SMEM_INFO_MAGIC, sizeof(info->magic)))
 659                return SMEM_ITEM_COUNT;
 660
 661        return le16_to_cpu(info->num_items);
 662}
 663
 664static int qcom_smem_set_global_partition(struct qcom_smem *smem)
 665{
 666        struct smem_partition_header *header;
 667        struct smem_ptable_entry *entry = NULL;
 668        struct smem_ptable *ptable;
 669        u32 host0, host1, size;
 670        int i;
 671
 672        ptable = qcom_smem_get_ptable(smem);
 673        if (IS_ERR(ptable))
 674                return PTR_ERR(ptable);
 675
 676        for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
 677                entry = &ptable->entry[i];
 678                host0 = le16_to_cpu(entry->host0);
 679                host1 = le16_to_cpu(entry->host1);
 680
 681                if (host0 == SMEM_GLOBAL_HOST && host0 == host1)
 682                        break;
 683        }
 684
 685        if (!entry) {
 686                dev_err(smem->dev, "Missing entry for global partition\n");
 687                return -EINVAL;
 688        }
 689
 690        if (!le32_to_cpu(entry->offset) || !le32_to_cpu(entry->size)) {
 691                dev_err(smem->dev, "Invalid entry for global partition\n");
 692                return -EINVAL;
 693        }
 694
 695        if (smem->global_partition) {
 696                dev_err(smem->dev, "Already found the global partition\n");
 697                return -EINVAL;
 698        }
 699
 700        header = smem->regions[0].virt_base + le32_to_cpu(entry->offset);
 701        host0 = le16_to_cpu(header->host0);
 702        host1 = le16_to_cpu(header->host1);
 703
 704        if (memcmp(header->magic, SMEM_PART_MAGIC, sizeof(header->magic))) {
 705                dev_err(smem->dev, "Global partition has invalid magic\n");
 706                return -EINVAL;
 707        }
 708
 709        if (host0 != SMEM_GLOBAL_HOST && host1 != SMEM_GLOBAL_HOST) {
 710                dev_err(smem->dev, "Global partition hosts are invalid\n");
 711                return -EINVAL;
 712        }
 713
 714        if (le32_to_cpu(header->size) != le32_to_cpu(entry->size)) {
 715                dev_err(smem->dev, "Global partition has invalid size\n");
 716                return -EINVAL;
 717        }
 718
 719        size = le32_to_cpu(header->offset_free_uncached);
 720        if (size > le32_to_cpu(header->size)) {
 721                dev_err(smem->dev,
 722                        "Global partition has invalid free pointer\n");
 723                return -EINVAL;
 724        }
 725
 726        smem->global_partition = header;
 727        smem->global_cacheline = le32_to_cpu(entry->cacheline);
 728
 729        return 0;
 730}
 731
 732static int qcom_smem_enumerate_partitions(struct qcom_smem *smem,
 733                                          unsigned int local_host)
 734{
 735        struct smem_partition_header *header;
 736        struct smem_ptable_entry *entry;
 737        struct smem_ptable *ptable;
 738        unsigned int remote_host;
 739        u32 host0, host1;
 740        int i;
 741
 742        ptable = qcom_smem_get_ptable(smem);
 743        if (IS_ERR(ptable))
 744                return PTR_ERR(ptable);
 745
 746        for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
 747                entry = &ptable->entry[i];
 748                host0 = le16_to_cpu(entry->host0);
 749                host1 = le16_to_cpu(entry->host1);
 750
 751                if (host0 != local_host && host1 != local_host)
 752                        continue;
 753
 754                if (!le32_to_cpu(entry->offset))
 755                        continue;
 756
 757                if (!le32_to_cpu(entry->size))
 758                        continue;
 759
 760                if (host0 == local_host)
 761                        remote_host = host1;
 762                else
 763                        remote_host = host0;
 764
 765                if (remote_host >= SMEM_HOST_COUNT) {
 766                        dev_err(smem->dev,
 767                                "Invalid remote host %d\n",
 768                                remote_host);
 769                        return -EINVAL;
 770                }
 771
 772                if (smem->partitions[remote_host]) {
 773                        dev_err(smem->dev,
 774                                "Already found a partition for host %d\n",
 775                                remote_host);
 776                        return -EINVAL;
 777                }
 778
 779                header = smem->regions[0].virt_base + le32_to_cpu(entry->offset);
 780                host0 = le16_to_cpu(header->host0);
 781                host1 = le16_to_cpu(header->host1);
 782
 783                if (memcmp(header->magic, SMEM_PART_MAGIC,
 784                            sizeof(header->magic))) {
 785                        dev_err(smem->dev,
 786                                "Partition %d has invalid magic\n", i);
 787                        return -EINVAL;
 788                }
 789
 790                if (host0 != local_host && host1 != local_host) {
 791                        dev_err(smem->dev,
 792                                "Partition %d hosts are invalid\n", i);
 793                        return -EINVAL;
 794                }
 795
 796                if (host0 != remote_host && host1 != remote_host) {
 797                        dev_err(smem->dev,
 798                                "Partition %d hosts are invalid\n", i);
 799                        return -EINVAL;
 800                }
 801
 802                if (le32_to_cpu(header->size) != le32_to_cpu(entry->size)) {
 803                        dev_err(smem->dev,
 804                                "Partition %d has invalid size\n", i);
 805                        return -EINVAL;
 806                }
 807
 808                if (le32_to_cpu(header->offset_free_uncached) > le32_to_cpu(header->size)) {
 809                        dev_err(smem->dev,
 810                                "Partition %d has invalid free pointer\n", i);
 811                        return -EINVAL;
 812                }
 813
 814                smem->partitions[remote_host] = header;
 815                smem->cacheline[remote_host] = le32_to_cpu(entry->cacheline);
 816        }
 817
 818        return 0;
 819}
 820
 821static int qcom_smem_map_memory(struct qcom_smem *smem, struct udevice *dev,
 822                                const char *name, int i)
 823{
 824        struct fdt_resource r;
 825        int ret;
 826        int node = dev_of_offset(dev);
 827
 828        ret = fdtdec_lookup_phandle(gd->fdt_blob, node, name);
 829        if (ret < 0) {
 830                dev_err(dev, "No %s specified\n", name);
 831                return -EINVAL;
 832        }
 833
 834        ret = fdt_get_resource(gd->fdt_blob, ret, "reg", 0, &r);
 835        if (ret)
 836                return ret;
 837
 838        smem->regions[i].aux_base = (u32)r.start;
 839        smem->regions[i].size = fdt_resource_size(&r);
 840        smem->regions[i].virt_base = devm_ioremap(dev, r.start, fdt_resource_size(&r));
 841        if (!smem->regions[i].virt_base)
 842                return -ENOMEM;
 843
 844        return 0;
 845}
 846
 847static int qcom_smem_probe(struct udevice *dev)
 848{
 849        struct smem_header *header;
 850        struct qcom_smem *smem;
 851        size_t array_size;
 852        int num_regions;
 853        u32 version;
 854        int ret;
 855        int node = dev_of_offset(dev);
 856
 857        num_regions = 1;
 858        if (fdtdec_lookup_phandle(gd->fdt_blob, node, "qcomrpm-msg-ram") >= 0)
 859                num_regions++;
 860
 861        array_size = num_regions * sizeof(struct smem_region);
 862        smem = devm_kzalloc(dev, sizeof(*smem) + array_size, GFP_KERNEL);
 863        if (!smem)
 864                return -ENOMEM;
 865
 866        smem->dev = dev;
 867        smem->num_regions = num_regions;
 868
 869        ret = qcom_smem_map_memory(smem, dev, "memory-region", 0);
 870        if (ret)
 871                return ret;
 872
 873        if (num_regions > 1) {
 874                ret = qcom_smem_map_memory(smem, dev,
 875                                        "qcom,rpm-msg-ram", 1);
 876                if (ret)
 877                        return ret;
 878        }
 879
 880        header = smem->regions[0].virt_base;
 881        if (le32_to_cpu(header->initialized) != 1 ||
 882            le32_to_cpu(header->reserved)) {
 883                dev_err(dev, "SMEM is not initialized by SBL\n");
 884                return -EINVAL;
 885        }
 886
 887        version = qcom_smem_get_sbl_version(smem);
 888        switch (version >> 16) {
 889        case SMEM_GLOBAL_PART_VERSION:
 890                ret = qcom_smem_set_global_partition(smem);
 891                if (ret < 0)
 892                        return ret;
 893                smem->item_count = qcom_smem_get_item_count(smem);
 894                break;
 895        case SMEM_GLOBAL_HEAP_VERSION:
 896                smem->item_count = SMEM_ITEM_COUNT;
 897                break;
 898        default:
 899                dev_err(dev, "Unsupported SMEM version 0x%x\n", version);
 900                return -EINVAL;
 901        }
 902
 903        ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS);
 904        if (ret < 0 && ret != -ENOENT)
 905                return ret;
 906
 907        __smem = smem;
 908
 909        return 0;
 910}
 911
 912static int qcom_smem_remove(struct udevice *dev)
 913{
 914        __smem = NULL;
 915
 916        return 0;
 917}
 918
 919const struct udevice_id qcom_smem_of_match[] = {
 920        { .compatible = "qcom,smem" },
 921        { }
 922};
 923
 924static const struct smem_ops msm_smem_ops = {
 925        .alloc = qcom_smem_alloc,
 926        .get = qcom_smem_get,
 927        .get_free_space = qcom_smem_get_free_space,
 928};
 929
 930U_BOOT_DRIVER(qcom_smem) = {
 931        .name   = "qcom_smem",
 932        .id     = UCLASS_SMEM,
 933        .of_match = qcom_smem_of_match,
 934        .ops = &msm_smem_ops,
 935        .probe = qcom_smem_probe,
 936        .remove = qcom_smem_remove,
 937};
 938