Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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