14b638df4SBjorn Andersson /* 24b638df4SBjorn Andersson * Copyright (c) 2015, Sony Mobile Communications AB. 34b638df4SBjorn Andersson * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. 44b638df4SBjorn Andersson * 54b638df4SBjorn Andersson * This program is free software; you can redistribute it and/or modify 64b638df4SBjorn Andersson * it under the terms of the GNU General Public License version 2 and 74b638df4SBjorn Andersson * only version 2 as published by the Free Software Foundation. 84b638df4SBjorn Andersson * 94b638df4SBjorn Andersson * This program is distributed in the hope that it will be useful, 104b638df4SBjorn Andersson * but WITHOUT ANY WARRANTY; without even the implied warranty of 114b638df4SBjorn Andersson * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 124b638df4SBjorn Andersson * GNU General Public License for more details. 134b638df4SBjorn Andersson */ 144b638df4SBjorn Andersson 154b638df4SBjorn Andersson #include <linux/hwspinlock.h> 164b638df4SBjorn Andersson #include <linux/io.h> 174b638df4SBjorn Andersson #include <linux/module.h> 184b638df4SBjorn Andersson #include <linux/of.h> 194b638df4SBjorn Andersson #include <linux/of_address.h> 204b638df4SBjorn Andersson #include <linux/platform_device.h> 214b638df4SBjorn Andersson #include <linux/slab.h> 224b638df4SBjorn Andersson #include <linux/soc/qcom/smem.h> 234b638df4SBjorn Andersson 244b638df4SBjorn Andersson /* 254b638df4SBjorn Andersson * The Qualcomm shared memory system is a allocate only heap structure that 264b638df4SBjorn Andersson * consists of one of more memory areas that can be accessed by the processors 274b638df4SBjorn Andersson * in the SoC. 284b638df4SBjorn Andersson * 294b638df4SBjorn Andersson * All systems contains a global heap, accessible by all processors in the SoC, 304b638df4SBjorn Andersson * with a table of contents data structure (@smem_header) at the beginning of 314b638df4SBjorn Andersson * the main shared memory block. 324b638df4SBjorn Andersson * 334b638df4SBjorn Andersson * The global header contains meta data for allocations as well as a fixed list 344b638df4SBjorn Andersson * of 512 entries (@smem_global_entry) that can be initialized to reference 354b638df4SBjorn Andersson * parts of the shared memory space. 364b638df4SBjorn Andersson * 374b638df4SBjorn Andersson * 384b638df4SBjorn Andersson * In addition to this global heap a set of "private" heaps can be set up at 394b638df4SBjorn Andersson * boot time with access restrictions so that only certain processor pairs can 404b638df4SBjorn Andersson * access the data. 414b638df4SBjorn Andersson * 424b638df4SBjorn Andersson * These partitions are referenced from an optional partition table 434b638df4SBjorn Andersson * (@smem_ptable), that is found 4kB from the end of the main smem region. The 444b638df4SBjorn Andersson * partition table entries (@smem_ptable_entry) lists the involved processors 454b638df4SBjorn Andersson * (or hosts) and their location in the main shared memory region. 464b638df4SBjorn Andersson * 474b638df4SBjorn Andersson * Each partition starts with a header (@smem_partition_header) that identifies 484b638df4SBjorn Andersson * the partition and holds properties for the two internal memory regions. The 494b638df4SBjorn Andersson * two regions are cached and non-cached memory respectively. Each region 504b638df4SBjorn Andersson * contain a link list of allocation headers (@smem_private_entry) followed by 514b638df4SBjorn Andersson * their data. 524b638df4SBjorn Andersson * 534b638df4SBjorn Andersson * Items in the non-cached region are allocated from the start of the partition 544b638df4SBjorn Andersson * while items in the cached region are allocated from the end. The free area 55c7c1dc35SBjorn Andersson * is hence the region between the cached and non-cached offsets. The header of 56c7c1dc35SBjorn Andersson * cached items comes after the data. 574b638df4SBjorn Andersson * 584b638df4SBjorn Andersson * 594b638df4SBjorn Andersson * To synchronize allocations in the shared memory heaps a remote spinlock must 604b638df4SBjorn Andersson * be held - currently lock number 3 of the sfpb or tcsr is used for this on all 614b638df4SBjorn Andersson * platforms. 624b638df4SBjorn Andersson * 634b638df4SBjorn Andersson */ 644b638df4SBjorn Andersson 654b638df4SBjorn Andersson /* 66*dcc0967dSChris Lew * The version member of the smem header contains an array of versions for the 67*dcc0967dSChris Lew * various software components in the SoC. We verify that the boot loader 68*dcc0967dSChris Lew * version is a valid version as a sanity check. 694b638df4SBjorn Andersson */ 704b638df4SBjorn Andersson #define SMEM_MASTER_SBL_VERSION_INDEX 7 714b638df4SBjorn Andersson #define SMEM_EXPECTED_VERSION 11 724b638df4SBjorn Andersson 734b638df4SBjorn Andersson /* 744b638df4SBjorn Andersson * The first 8 items are only to be allocated by the boot loader while 754b638df4SBjorn Andersson * initializing the heap. 764b638df4SBjorn Andersson */ 774b638df4SBjorn Andersson #define SMEM_ITEM_LAST_FIXED 8 784b638df4SBjorn Andersson 794b638df4SBjorn Andersson /* Highest accepted item number, for both global and private heaps */ 804b638df4SBjorn Andersson #define SMEM_ITEM_COUNT 512 814b638df4SBjorn Andersson 824b638df4SBjorn Andersson /* Processor/host identifier for the application processor */ 834b638df4SBjorn Andersson #define SMEM_HOST_APPS 0 844b638df4SBjorn Andersson 854b638df4SBjorn Andersson /* Max number of processors/hosts in a system */ 864b638df4SBjorn Andersson #define SMEM_HOST_COUNT 9 874b638df4SBjorn Andersson 884b638df4SBjorn Andersson /** 894b638df4SBjorn Andersson * struct smem_proc_comm - proc_comm communication struct (legacy) 904b638df4SBjorn Andersson * @command: current command to be executed 914b638df4SBjorn Andersson * @status: status of the currently requested command 924b638df4SBjorn Andersson * @params: parameters to the command 934b638df4SBjorn Andersson */ 944b638df4SBjorn Andersson struct smem_proc_comm { 959806884dSStephen Boyd __le32 command; 969806884dSStephen Boyd __le32 status; 979806884dSStephen Boyd __le32 params[2]; 984b638df4SBjorn Andersson }; 994b638df4SBjorn Andersson 1004b638df4SBjorn Andersson /** 1014b638df4SBjorn Andersson * struct smem_global_entry - entry to reference smem items on the heap 1024b638df4SBjorn Andersson * @allocated: boolean to indicate if this entry is used 1034b638df4SBjorn Andersson * @offset: offset to the allocated space 1044b638df4SBjorn Andersson * @size: size of the allocated space, 8 byte aligned 1054b638df4SBjorn Andersson * @aux_base: base address for the memory region used by this unit, or 0 for 1064b638df4SBjorn Andersson * the default region. bits 0,1 are reserved 1074b638df4SBjorn Andersson */ 1084b638df4SBjorn Andersson struct smem_global_entry { 1099806884dSStephen Boyd __le32 allocated; 1109806884dSStephen Boyd __le32 offset; 1119806884dSStephen Boyd __le32 size; 1129806884dSStephen Boyd __le32 aux_base; /* bits 1:0 reserved */ 1134b638df4SBjorn Andersson }; 1144b638df4SBjorn Andersson #define AUX_BASE_MASK 0xfffffffc 1154b638df4SBjorn Andersson 1164b638df4SBjorn Andersson /** 1174b638df4SBjorn Andersson * struct smem_header - header found in beginning of primary smem region 1184b638df4SBjorn Andersson * @proc_comm: proc_comm communication interface (legacy) 1194b638df4SBjorn Andersson * @version: array of versions for the various subsystems 1204b638df4SBjorn Andersson * @initialized: boolean to indicate that smem is initialized 1214b638df4SBjorn Andersson * @free_offset: index of the first unallocated byte in smem 1224b638df4SBjorn Andersson * @available: number of bytes available for allocation 1234b638df4SBjorn Andersson * @reserved: reserved field, must be 0 1244b638df4SBjorn Andersson * toc: array of references to items 1254b638df4SBjorn Andersson */ 1264b638df4SBjorn Andersson struct smem_header { 1274b638df4SBjorn Andersson struct smem_proc_comm proc_comm[4]; 1289806884dSStephen Boyd __le32 version[32]; 1299806884dSStephen Boyd __le32 initialized; 1309806884dSStephen Boyd __le32 free_offset; 1319806884dSStephen Boyd __le32 available; 1329806884dSStephen Boyd __le32 reserved; 1334b638df4SBjorn Andersson struct smem_global_entry toc[SMEM_ITEM_COUNT]; 1344b638df4SBjorn Andersson }; 1354b638df4SBjorn Andersson 1364b638df4SBjorn Andersson /** 1374b638df4SBjorn Andersson * struct smem_ptable_entry - one entry in the @smem_ptable list 1384b638df4SBjorn Andersson * @offset: offset, within the main shared memory region, of the partition 1394b638df4SBjorn Andersson * @size: size of the partition 1404b638df4SBjorn Andersson * @flags: flags for the partition (currently unused) 1414b638df4SBjorn Andersson * @host0: first processor/host with access to this partition 1424b638df4SBjorn Andersson * @host1: second processor/host with access to this partition 143c7c1dc35SBjorn Andersson * @cacheline: alignment for "cached" entries 1444b638df4SBjorn Andersson * @reserved: reserved entries for later use 1454b638df4SBjorn Andersson */ 1464b638df4SBjorn Andersson struct smem_ptable_entry { 1479806884dSStephen Boyd __le32 offset; 1489806884dSStephen Boyd __le32 size; 1499806884dSStephen Boyd __le32 flags; 1509806884dSStephen Boyd __le16 host0; 1519806884dSStephen Boyd __le16 host1; 152c7c1dc35SBjorn Andersson __le32 cacheline; 153c7c1dc35SBjorn Andersson __le32 reserved[7]; 1544b638df4SBjorn Andersson }; 1554b638df4SBjorn Andersson 1564b638df4SBjorn Andersson /** 1574b638df4SBjorn Andersson * struct smem_ptable - partition table for the private partitions 1584b638df4SBjorn Andersson * @magic: magic number, must be SMEM_PTABLE_MAGIC 1594b638df4SBjorn Andersson * @version: version of the partition table 1604b638df4SBjorn Andersson * @num_entries: number of partitions in the table 1614b638df4SBjorn Andersson * @reserved: for now reserved entries 1624b638df4SBjorn Andersson * @entry: list of @smem_ptable_entry for the @num_entries partitions 1634b638df4SBjorn Andersson */ 1644b638df4SBjorn Andersson struct smem_ptable { 1659806884dSStephen Boyd u8 magic[4]; 1669806884dSStephen Boyd __le32 version; 1679806884dSStephen Boyd __le32 num_entries; 1689806884dSStephen Boyd __le32 reserved[5]; 1694b638df4SBjorn Andersson struct smem_ptable_entry entry[]; 1704b638df4SBjorn Andersson }; 1719806884dSStephen Boyd 1729806884dSStephen Boyd static const u8 SMEM_PTABLE_MAGIC[] = { 0x24, 0x54, 0x4f, 0x43 }; /* "$TOC" */ 1734b638df4SBjorn Andersson 1744b638df4SBjorn Andersson /** 1754b638df4SBjorn Andersson * struct smem_partition_header - header of the partitions 1764b638df4SBjorn Andersson * @magic: magic number, must be SMEM_PART_MAGIC 1774b638df4SBjorn Andersson * @host0: first processor/host with access to this partition 1784b638df4SBjorn Andersson * @host1: second processor/host with access to this partition 1794b638df4SBjorn Andersson * @size: size of the partition 1804b638df4SBjorn Andersson * @offset_free_uncached: offset to the first free byte of uncached memory in 1814b638df4SBjorn Andersson * this partition 1824b638df4SBjorn Andersson * @offset_free_cached: offset to the first free byte of cached memory in this 1834b638df4SBjorn Andersson * partition 1844b638df4SBjorn Andersson * @reserved: for now reserved entries 1854b638df4SBjorn Andersson */ 1864b638df4SBjorn Andersson struct smem_partition_header { 1879806884dSStephen Boyd u8 magic[4]; 1889806884dSStephen Boyd __le16 host0; 1899806884dSStephen Boyd __le16 host1; 1909806884dSStephen Boyd __le32 size; 1919806884dSStephen Boyd __le32 offset_free_uncached; 1929806884dSStephen Boyd __le32 offset_free_cached; 1939806884dSStephen Boyd __le32 reserved[3]; 1944b638df4SBjorn Andersson }; 1959806884dSStephen Boyd 1969806884dSStephen Boyd static const u8 SMEM_PART_MAGIC[] = { 0x24, 0x50, 0x52, 0x54 }; 1974b638df4SBjorn Andersson 1984b638df4SBjorn Andersson /** 1994b638df4SBjorn Andersson * struct smem_private_entry - header of each item in the private partition 2004b638df4SBjorn Andersson * @canary: magic number, must be SMEM_PRIVATE_CANARY 2014b638df4SBjorn Andersson * @item: identifying number of the smem item 2024b638df4SBjorn Andersson * @size: size of the data, including padding bytes 2034b638df4SBjorn Andersson * @padding_data: number of bytes of padding of data 2044b638df4SBjorn Andersson * @padding_hdr: number of bytes of padding between the header and the data 2054b638df4SBjorn Andersson * @reserved: for now reserved entry 2064b638df4SBjorn Andersson */ 2074b638df4SBjorn Andersson struct smem_private_entry { 2089806884dSStephen Boyd u16 canary; /* bytes are the same so no swapping needed */ 2099806884dSStephen Boyd __le16 item; 2109806884dSStephen Boyd __le32 size; /* includes padding bytes */ 2119806884dSStephen Boyd __le16 padding_data; 2129806884dSStephen Boyd __le16 padding_hdr; 2139806884dSStephen Boyd __le32 reserved; 2144b638df4SBjorn Andersson }; 2154b638df4SBjorn Andersson #define SMEM_PRIVATE_CANARY 0xa5a5 2164b638df4SBjorn Andersson 2174b638df4SBjorn Andersson /** 2184b638df4SBjorn Andersson * struct smem_region - representation of a chunk of memory used for smem 2194b638df4SBjorn Andersson * @aux_base: identifier of aux_mem base 2204b638df4SBjorn Andersson * @virt_base: virtual base address of memory with this aux_mem identifier 2214b638df4SBjorn Andersson * @size: size of the memory region 2224b638df4SBjorn Andersson */ 2234b638df4SBjorn Andersson struct smem_region { 2244b638df4SBjorn Andersson u32 aux_base; 2254b638df4SBjorn Andersson void __iomem *virt_base; 2264b638df4SBjorn Andersson size_t size; 2274b638df4SBjorn Andersson }; 2284b638df4SBjorn Andersson 2294b638df4SBjorn Andersson /** 2304b638df4SBjorn Andersson * struct qcom_smem - device data for the smem device 2314b638df4SBjorn Andersson * @dev: device pointer 2324b638df4SBjorn Andersson * @hwlock: reference to a hwspinlock 2334b638df4SBjorn Andersson * @partitions: list of pointers to partitions affecting the current 2344b638df4SBjorn Andersson * processor/host 235c7c1dc35SBjorn Andersson * @cacheline: list of cacheline sizes for each host 2364b638df4SBjorn Andersson * @num_regions: number of @regions 2374b638df4SBjorn Andersson * @regions: list of the memory regions defining the shared memory 2384b638df4SBjorn Andersson */ 2394b638df4SBjorn Andersson struct qcom_smem { 2404b638df4SBjorn Andersson struct device *dev; 2414b638df4SBjorn Andersson 2424b638df4SBjorn Andersson struct hwspinlock *hwlock; 2434b638df4SBjorn Andersson 2444b638df4SBjorn Andersson struct smem_partition_header *partitions[SMEM_HOST_COUNT]; 245c7c1dc35SBjorn Andersson size_t cacheline[SMEM_HOST_COUNT]; 2464b638df4SBjorn Andersson 2474b638df4SBjorn Andersson unsigned num_regions; 2484b638df4SBjorn Andersson struct smem_region regions[0]; 2494b638df4SBjorn Andersson }; 2504b638df4SBjorn Andersson 2519806884dSStephen Boyd static struct smem_private_entry * 25201f14154SBjorn Andersson phdr_to_last_uncached_entry(struct smem_partition_header *phdr) 2539806884dSStephen Boyd { 2549806884dSStephen Boyd void *p = phdr; 2559806884dSStephen Boyd 2569806884dSStephen Boyd return p + le32_to_cpu(phdr->offset_free_uncached); 2579806884dSStephen Boyd } 2589806884dSStephen Boyd 259c7c1dc35SBjorn Andersson static void *phdr_to_first_cached_entry(struct smem_partition_header *phdr, 260c7c1dc35SBjorn Andersson size_t cacheline) 261c7c1dc35SBjorn Andersson { 262c7c1dc35SBjorn Andersson void *p = phdr; 263c7c1dc35SBjorn Andersson 264c7c1dc35SBjorn Andersson return p + le32_to_cpu(phdr->size) - ALIGN(sizeof(*phdr), cacheline); 265c7c1dc35SBjorn Andersson } 266c7c1dc35SBjorn Andersson 26701f14154SBjorn Andersson static void *phdr_to_last_cached_entry(struct smem_partition_header *phdr) 2689806884dSStephen Boyd { 2699806884dSStephen Boyd void *p = phdr; 2709806884dSStephen Boyd 2719806884dSStephen Boyd return p + le32_to_cpu(phdr->offset_free_cached); 2729806884dSStephen Boyd } 2739806884dSStephen Boyd 2749806884dSStephen Boyd static struct smem_private_entry * 27501f14154SBjorn Andersson phdr_to_first_uncached_entry(struct smem_partition_header *phdr) 2769806884dSStephen Boyd { 2779806884dSStephen Boyd void *p = phdr; 2789806884dSStephen Boyd 2799806884dSStephen Boyd return p + sizeof(*phdr); 2809806884dSStephen Boyd } 2819806884dSStephen Boyd 2829806884dSStephen Boyd static struct smem_private_entry * 28301f14154SBjorn Andersson uncached_entry_next(struct smem_private_entry *e) 2849806884dSStephen Boyd { 2859806884dSStephen Boyd void *p = e; 2869806884dSStephen Boyd 2879806884dSStephen Boyd return p + sizeof(*e) + le16_to_cpu(e->padding_hdr) + 2889806884dSStephen Boyd le32_to_cpu(e->size); 2899806884dSStephen Boyd } 2909806884dSStephen Boyd 291c7c1dc35SBjorn Andersson static struct smem_private_entry * 292c7c1dc35SBjorn Andersson cached_entry_next(struct smem_private_entry *e, size_t cacheline) 293c7c1dc35SBjorn Andersson { 294c7c1dc35SBjorn Andersson void *p = e; 295c7c1dc35SBjorn Andersson 296c7c1dc35SBjorn Andersson return p - le32_to_cpu(e->size) - ALIGN(sizeof(*e), cacheline); 297c7c1dc35SBjorn Andersson } 298c7c1dc35SBjorn Andersson 29901f14154SBjorn Andersson static void *uncached_entry_to_item(struct smem_private_entry *e) 3009806884dSStephen Boyd { 3019806884dSStephen Boyd void *p = e; 3029806884dSStephen Boyd 3039806884dSStephen Boyd return p + sizeof(*e) + le16_to_cpu(e->padding_hdr); 3049806884dSStephen Boyd } 3059806884dSStephen Boyd 306c7c1dc35SBjorn Andersson static void *cached_entry_to_item(struct smem_private_entry *e) 307c7c1dc35SBjorn Andersson { 308c7c1dc35SBjorn Andersson void *p = e; 309c7c1dc35SBjorn Andersson 310c7c1dc35SBjorn Andersson return p - le32_to_cpu(e->size); 311c7c1dc35SBjorn Andersson } 312c7c1dc35SBjorn Andersson 3134b638df4SBjorn Andersson /* Pointer to the one and only smem handle */ 3144b638df4SBjorn Andersson static struct qcom_smem *__smem; 3154b638df4SBjorn Andersson 3164b638df4SBjorn Andersson /* Timeout (ms) for the trylock of remote spinlocks */ 3174b638df4SBjorn Andersson #define HWSPINLOCK_TIMEOUT 1000 3184b638df4SBjorn Andersson 3194b638df4SBjorn Andersson static int qcom_smem_alloc_private(struct qcom_smem *smem, 3204b638df4SBjorn Andersson unsigned host, 3214b638df4SBjorn Andersson unsigned item, 3224b638df4SBjorn Andersson size_t size) 3234b638df4SBjorn Andersson { 3244b638df4SBjorn Andersson struct smem_partition_header *phdr; 3259806884dSStephen Boyd struct smem_private_entry *hdr, *end; 3264b638df4SBjorn Andersson size_t alloc_size; 3279806884dSStephen Boyd void *cached; 3284b638df4SBjorn Andersson 3294b638df4SBjorn Andersson phdr = smem->partitions[host]; 33001f14154SBjorn Andersson hdr = phdr_to_first_uncached_entry(phdr); 33101f14154SBjorn Andersson end = phdr_to_last_uncached_entry(phdr); 33201f14154SBjorn Andersson cached = phdr_to_last_cached_entry(phdr); 3334b638df4SBjorn Andersson 3349806884dSStephen Boyd while (hdr < end) { 3354b638df4SBjorn Andersson if (hdr->canary != SMEM_PRIVATE_CANARY) { 3364b638df4SBjorn Andersson dev_err(smem->dev, 3374b638df4SBjorn Andersson "Found invalid canary in host %d partition\n", 3384b638df4SBjorn Andersson host); 3394b638df4SBjorn Andersson return -EINVAL; 3404b638df4SBjorn Andersson } 3414b638df4SBjorn Andersson 3429806884dSStephen Boyd if (le16_to_cpu(hdr->item) == item) 3434b638df4SBjorn Andersson return -EEXIST; 3444b638df4SBjorn Andersson 34501f14154SBjorn Andersson hdr = uncached_entry_next(hdr); 3464b638df4SBjorn Andersson } 3474b638df4SBjorn Andersson 3484b638df4SBjorn Andersson /* Check that we don't grow into the cached region */ 3494b638df4SBjorn Andersson alloc_size = sizeof(*hdr) + ALIGN(size, 8); 3509806884dSStephen Boyd if ((void *)hdr + alloc_size >= cached) { 3514b638df4SBjorn Andersson dev_err(smem->dev, "Out of memory\n"); 3524b638df4SBjorn Andersson return -ENOSPC; 3534b638df4SBjorn Andersson } 3544b638df4SBjorn Andersson 3554b638df4SBjorn Andersson hdr->canary = SMEM_PRIVATE_CANARY; 3569806884dSStephen Boyd hdr->item = cpu_to_le16(item); 3579806884dSStephen Boyd hdr->size = cpu_to_le32(ALIGN(size, 8)); 3589806884dSStephen Boyd hdr->padding_data = cpu_to_le16(le32_to_cpu(hdr->size) - size); 3594b638df4SBjorn Andersson hdr->padding_hdr = 0; 3604b638df4SBjorn Andersson 3614b638df4SBjorn Andersson /* 3624b638df4SBjorn Andersson * Ensure the header is written before we advance the free offset, so 3634b638df4SBjorn Andersson * that remote processors that does not take the remote spinlock still 3644b638df4SBjorn Andersson * gets a consistent view of the linked list. 3654b638df4SBjorn Andersson */ 3664b638df4SBjorn Andersson wmb(); 3679806884dSStephen Boyd le32_add_cpu(&phdr->offset_free_uncached, alloc_size); 3684b638df4SBjorn Andersson 3694b638df4SBjorn Andersson return 0; 3704b638df4SBjorn Andersson } 3714b638df4SBjorn Andersson 3724b638df4SBjorn Andersson static int qcom_smem_alloc_global(struct qcom_smem *smem, 3734b638df4SBjorn Andersson unsigned item, 3744b638df4SBjorn Andersson size_t size) 3754b638df4SBjorn Andersson { 3764b638df4SBjorn Andersson struct smem_header *header; 3774b638df4SBjorn Andersson struct smem_global_entry *entry; 3784b638df4SBjorn Andersson 3794b638df4SBjorn Andersson if (WARN_ON(item >= SMEM_ITEM_COUNT)) 3804b638df4SBjorn Andersson return -EINVAL; 3814b638df4SBjorn Andersson 3824b638df4SBjorn Andersson header = smem->regions[0].virt_base; 3834b638df4SBjorn Andersson entry = &header->toc[item]; 3844b638df4SBjorn Andersson if (entry->allocated) 3854b638df4SBjorn Andersson return -EEXIST; 3864b638df4SBjorn Andersson 3874b638df4SBjorn Andersson size = ALIGN(size, 8); 3889806884dSStephen Boyd if (WARN_ON(size > le32_to_cpu(header->available))) 3894b638df4SBjorn Andersson return -ENOMEM; 3904b638df4SBjorn Andersson 3914b638df4SBjorn Andersson entry->offset = header->free_offset; 3929806884dSStephen Boyd entry->size = cpu_to_le32(size); 3934b638df4SBjorn Andersson 3944b638df4SBjorn Andersson /* 3954b638df4SBjorn Andersson * Ensure the header is consistent before we mark the item allocated, 3964b638df4SBjorn Andersson * so that remote processors will get a consistent view of the item 3974b638df4SBjorn Andersson * even though they do not take the spinlock on read. 3984b638df4SBjorn Andersson */ 3994b638df4SBjorn Andersson wmb(); 4009806884dSStephen Boyd entry->allocated = cpu_to_le32(1); 4014b638df4SBjorn Andersson 4029806884dSStephen Boyd le32_add_cpu(&header->free_offset, size); 4039806884dSStephen Boyd le32_add_cpu(&header->available, -size); 4044b638df4SBjorn Andersson 4054b638df4SBjorn Andersson return 0; 4064b638df4SBjorn Andersson } 4074b638df4SBjorn Andersson 4084b638df4SBjorn Andersson /** 4094b638df4SBjorn Andersson * qcom_smem_alloc() - allocate space for a smem item 4104b638df4SBjorn Andersson * @host: remote processor id, or -1 4114b638df4SBjorn Andersson * @item: smem item handle 4124b638df4SBjorn Andersson * @size: number of bytes to be allocated 4134b638df4SBjorn Andersson * 4144b638df4SBjorn Andersson * Allocate space for a given smem item of size @size, given that the item is 4154b638df4SBjorn Andersson * not yet allocated. 4164b638df4SBjorn Andersson */ 4174b638df4SBjorn Andersson int qcom_smem_alloc(unsigned host, unsigned item, size_t size) 4184b638df4SBjorn Andersson { 4194b638df4SBjorn Andersson unsigned long flags; 4204b638df4SBjorn Andersson int ret; 4214b638df4SBjorn Andersson 4224b638df4SBjorn Andersson if (!__smem) 4234b638df4SBjorn Andersson return -EPROBE_DEFER; 4244b638df4SBjorn Andersson 4254b638df4SBjorn Andersson if (item < SMEM_ITEM_LAST_FIXED) { 4264b638df4SBjorn Andersson dev_err(__smem->dev, 4274b638df4SBjorn Andersson "Rejecting allocation of static entry %d\n", item); 4284b638df4SBjorn Andersson return -EINVAL; 4294b638df4SBjorn Andersson } 4304b638df4SBjorn Andersson 4314b638df4SBjorn Andersson ret = hwspin_lock_timeout_irqsave(__smem->hwlock, 4324b638df4SBjorn Andersson HWSPINLOCK_TIMEOUT, 4334b638df4SBjorn Andersson &flags); 4344b638df4SBjorn Andersson if (ret) 4354b638df4SBjorn Andersson return ret; 4364b638df4SBjorn Andersson 43718912806SAndy Gross if (host < SMEM_HOST_COUNT && __smem->partitions[host]) 4384b638df4SBjorn Andersson ret = qcom_smem_alloc_private(__smem, host, item, size); 43918912806SAndy Gross else 4404b638df4SBjorn Andersson ret = qcom_smem_alloc_global(__smem, item, size); 4414b638df4SBjorn Andersson 4424b638df4SBjorn Andersson hwspin_unlock_irqrestore(__smem->hwlock, &flags); 4434b638df4SBjorn Andersson 4444b638df4SBjorn Andersson return ret; 4454b638df4SBjorn Andersson } 4464b638df4SBjorn Andersson EXPORT_SYMBOL(qcom_smem_alloc); 4474b638df4SBjorn Andersson 4481a03964dSStephen Boyd static void *qcom_smem_get_global(struct qcom_smem *smem, 4494b638df4SBjorn Andersson unsigned item, 4504b638df4SBjorn Andersson size_t *size) 4514b638df4SBjorn Andersson { 4524b638df4SBjorn Andersson struct smem_header *header; 4534b638df4SBjorn Andersson struct smem_region *area; 4544b638df4SBjorn Andersson struct smem_global_entry *entry; 4554b638df4SBjorn Andersson u32 aux_base; 4564b638df4SBjorn Andersson unsigned i; 4574b638df4SBjorn Andersson 4584b638df4SBjorn Andersson if (WARN_ON(item >= SMEM_ITEM_COUNT)) 4591a03964dSStephen Boyd return ERR_PTR(-EINVAL); 4604b638df4SBjorn Andersson 4614b638df4SBjorn Andersson header = smem->regions[0].virt_base; 4624b638df4SBjorn Andersson entry = &header->toc[item]; 4634b638df4SBjorn Andersson if (!entry->allocated) 4641a03964dSStephen Boyd return ERR_PTR(-ENXIO); 4654b638df4SBjorn Andersson 4669806884dSStephen Boyd aux_base = le32_to_cpu(entry->aux_base) & AUX_BASE_MASK; 4674b638df4SBjorn Andersson 4684b638df4SBjorn Andersson for (i = 0; i < smem->num_regions; i++) { 4694b638df4SBjorn Andersson area = &smem->regions[i]; 4704b638df4SBjorn Andersson 4714b638df4SBjorn Andersson if (area->aux_base == aux_base || !aux_base) { 4724b638df4SBjorn Andersson if (size != NULL) 4739806884dSStephen Boyd *size = le32_to_cpu(entry->size); 4749806884dSStephen Boyd return area->virt_base + le32_to_cpu(entry->offset); 4751a03964dSStephen Boyd } 4764b638df4SBjorn Andersson } 4774b638df4SBjorn Andersson 4781a03964dSStephen Boyd return ERR_PTR(-ENOENT); 4791a03964dSStephen Boyd } 4801a03964dSStephen Boyd 4811a03964dSStephen Boyd static void *qcom_smem_get_private(struct qcom_smem *smem, 4824b638df4SBjorn Andersson unsigned host, 4834b638df4SBjorn Andersson unsigned item, 4844b638df4SBjorn Andersson size_t *size) 4854b638df4SBjorn Andersson { 4864b638df4SBjorn Andersson struct smem_partition_header *phdr; 4879806884dSStephen Boyd struct smem_private_entry *e, *end; 488c7c1dc35SBjorn Andersson size_t cacheline; 4894b638df4SBjorn Andersson 4904b638df4SBjorn Andersson phdr = smem->partitions[host]; 491c7c1dc35SBjorn Andersson cacheline = smem->cacheline[host]; 492c7c1dc35SBjorn Andersson 49301f14154SBjorn Andersson e = phdr_to_first_uncached_entry(phdr); 49401f14154SBjorn Andersson end = phdr_to_last_uncached_entry(phdr); 4954b638df4SBjorn Andersson 4969806884dSStephen Boyd while (e < end) { 497c7c1dc35SBjorn Andersson if (e->canary != SMEM_PRIVATE_CANARY) 498c7c1dc35SBjorn Andersson goto invalid_canary; 4994b638df4SBjorn Andersson 5009806884dSStephen Boyd if (le16_to_cpu(e->item) == item) { 5014b638df4SBjorn Andersson if (size != NULL) 5029806884dSStephen Boyd *size = le32_to_cpu(e->size) - 5039806884dSStephen Boyd le16_to_cpu(e->padding_data); 5044b638df4SBjorn Andersson 50501f14154SBjorn Andersson return uncached_entry_to_item(e); 5064b638df4SBjorn Andersson } 5074b638df4SBjorn Andersson 50801f14154SBjorn Andersson e = uncached_entry_next(e); 5094b638df4SBjorn Andersson } 5104b638df4SBjorn Andersson 511c7c1dc35SBjorn Andersson /* Item was not found in the uncached list, search the cached list */ 512c7c1dc35SBjorn Andersson 513c7c1dc35SBjorn Andersson e = phdr_to_first_cached_entry(phdr, cacheline); 514c7c1dc35SBjorn Andersson end = phdr_to_last_cached_entry(phdr); 515c7c1dc35SBjorn Andersson 516c7c1dc35SBjorn Andersson while (e > end) { 517c7c1dc35SBjorn Andersson if (e->canary != SMEM_PRIVATE_CANARY) 518c7c1dc35SBjorn Andersson goto invalid_canary; 519c7c1dc35SBjorn Andersson 520c7c1dc35SBjorn Andersson if (le16_to_cpu(e->item) == item) { 521c7c1dc35SBjorn Andersson if (size != NULL) 522c7c1dc35SBjorn Andersson *size = le32_to_cpu(e->size) - 523c7c1dc35SBjorn Andersson le16_to_cpu(e->padding_data); 524c7c1dc35SBjorn Andersson 525c7c1dc35SBjorn Andersson return cached_entry_to_item(e); 526c7c1dc35SBjorn Andersson } 527c7c1dc35SBjorn Andersson 528c7c1dc35SBjorn Andersson e = cached_entry_next(e, cacheline); 529c7c1dc35SBjorn Andersson } 530c7c1dc35SBjorn Andersson 5311a03964dSStephen Boyd return ERR_PTR(-ENOENT); 532c7c1dc35SBjorn Andersson 533c7c1dc35SBjorn Andersson invalid_canary: 534c7c1dc35SBjorn Andersson dev_err(smem->dev, "Found invalid canary in host %d partition\n", host); 535c7c1dc35SBjorn Andersson 536c7c1dc35SBjorn Andersson return ERR_PTR(-EINVAL); 5374b638df4SBjorn Andersson } 5384b638df4SBjorn Andersson 5394b638df4SBjorn Andersson /** 5404b638df4SBjorn Andersson * qcom_smem_get() - resolve ptr of size of a smem item 5414b638df4SBjorn Andersson * @host: the remote processor, or -1 5424b638df4SBjorn Andersson * @item: smem item handle 5434b638df4SBjorn Andersson * @size: pointer to be filled out with size of the item 5444b638df4SBjorn Andersson * 5451a03964dSStephen Boyd * Looks up smem item and returns pointer to it. Size of smem 5461a03964dSStephen Boyd * item is returned in @size. 5474b638df4SBjorn Andersson */ 5481a03964dSStephen Boyd void *qcom_smem_get(unsigned host, unsigned item, size_t *size) 5494b638df4SBjorn Andersson { 5504b638df4SBjorn Andersson unsigned long flags; 5514b638df4SBjorn Andersson int ret; 5521a03964dSStephen Boyd void *ptr = ERR_PTR(-EPROBE_DEFER); 5534b638df4SBjorn Andersson 5544b638df4SBjorn Andersson if (!__smem) 5551a03964dSStephen Boyd return ptr; 5564b638df4SBjorn Andersson 5574b638df4SBjorn Andersson ret = hwspin_lock_timeout_irqsave(__smem->hwlock, 5584b638df4SBjorn Andersson HWSPINLOCK_TIMEOUT, 5594b638df4SBjorn Andersson &flags); 5604b638df4SBjorn Andersson if (ret) 5611a03964dSStephen Boyd return ERR_PTR(ret); 5624b638df4SBjorn Andersson 56318912806SAndy Gross if (host < SMEM_HOST_COUNT && __smem->partitions[host]) 5641a03964dSStephen Boyd ptr = qcom_smem_get_private(__smem, host, item, size); 56518912806SAndy Gross else 5661a03964dSStephen Boyd ptr = qcom_smem_get_global(__smem, item, size); 5674b638df4SBjorn Andersson 5684b638df4SBjorn Andersson hwspin_unlock_irqrestore(__smem->hwlock, &flags); 5691a03964dSStephen Boyd 5701a03964dSStephen Boyd return ptr; 5714b638df4SBjorn Andersson 5724b638df4SBjorn Andersson } 5734b638df4SBjorn Andersson EXPORT_SYMBOL(qcom_smem_get); 5744b638df4SBjorn Andersson 5754b638df4SBjorn Andersson /** 5764b638df4SBjorn Andersson * qcom_smem_get_free_space() - retrieve amount of free space in a partition 5774b638df4SBjorn Andersson * @host: the remote processor identifying a partition, or -1 5784b638df4SBjorn Andersson * 5794b638df4SBjorn Andersson * To be used by smem clients as a quick way to determine if any new 5804b638df4SBjorn Andersson * allocations has been made. 5814b638df4SBjorn Andersson */ 5824b638df4SBjorn Andersson int qcom_smem_get_free_space(unsigned host) 5834b638df4SBjorn Andersson { 5844b638df4SBjorn Andersson struct smem_partition_header *phdr; 5854b638df4SBjorn Andersson struct smem_header *header; 5864b638df4SBjorn Andersson unsigned ret; 5874b638df4SBjorn Andersson 5884b638df4SBjorn Andersson if (!__smem) 5894b638df4SBjorn Andersson return -EPROBE_DEFER; 5904b638df4SBjorn Andersson 5914b638df4SBjorn Andersson if (host < SMEM_HOST_COUNT && __smem->partitions[host]) { 5924b638df4SBjorn Andersson phdr = __smem->partitions[host]; 5939806884dSStephen Boyd ret = le32_to_cpu(phdr->offset_free_cached) - 5949806884dSStephen Boyd le32_to_cpu(phdr->offset_free_uncached); 5954b638df4SBjorn Andersson } else { 5964b638df4SBjorn Andersson header = __smem->regions[0].virt_base; 5979806884dSStephen Boyd ret = le32_to_cpu(header->available); 5984b638df4SBjorn Andersson } 5994b638df4SBjorn Andersson 6004b638df4SBjorn Andersson return ret; 6014b638df4SBjorn Andersson } 6024b638df4SBjorn Andersson EXPORT_SYMBOL(qcom_smem_get_free_space); 6034b638df4SBjorn Andersson 6044b638df4SBjorn Andersson static int qcom_smem_get_sbl_version(struct qcom_smem *smem) 6054b638df4SBjorn Andersson { 606*dcc0967dSChris Lew struct smem_header *header; 6079806884dSStephen Boyd __le32 *versions; 6084b638df4SBjorn Andersson 609*dcc0967dSChris Lew header = smem->regions[0].virt_base; 610*dcc0967dSChris Lew versions = header->version; 6114b638df4SBjorn Andersson 6129806884dSStephen Boyd return le32_to_cpu(versions[SMEM_MASTER_SBL_VERSION_INDEX]); 6134b638df4SBjorn Andersson } 6144b638df4SBjorn Andersson 6154b638df4SBjorn Andersson static int qcom_smem_enumerate_partitions(struct qcom_smem *smem, 6164b638df4SBjorn Andersson unsigned local_host) 6174b638df4SBjorn Andersson { 6184b638df4SBjorn Andersson struct smem_partition_header *header; 6194b638df4SBjorn Andersson struct smem_ptable_entry *entry; 6204b638df4SBjorn Andersson struct smem_ptable *ptable; 6214b638df4SBjorn Andersson unsigned remote_host; 6229806884dSStephen Boyd u32 version, host0, host1; 6234b638df4SBjorn Andersson int i; 6244b638df4SBjorn Andersson 6254b638df4SBjorn Andersson ptable = smem->regions[0].virt_base + smem->regions[0].size - SZ_4K; 6269806884dSStephen Boyd if (memcmp(ptable->magic, SMEM_PTABLE_MAGIC, sizeof(ptable->magic))) 6274b638df4SBjorn Andersson return 0; 6284b638df4SBjorn Andersson 6299806884dSStephen Boyd version = le32_to_cpu(ptable->version); 6309806884dSStephen Boyd if (version != 1) { 6314b638df4SBjorn Andersson dev_err(smem->dev, 6329806884dSStephen Boyd "Unsupported partition header version %d\n", version); 6334b638df4SBjorn Andersson return -EINVAL; 6344b638df4SBjorn Andersson } 6354b638df4SBjorn Andersson 6369806884dSStephen Boyd for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) { 6374b638df4SBjorn Andersson entry = &ptable->entry[i]; 6389806884dSStephen Boyd host0 = le16_to_cpu(entry->host0); 6399806884dSStephen Boyd host1 = le16_to_cpu(entry->host1); 6404b638df4SBjorn Andersson 6419806884dSStephen Boyd if (host0 != local_host && host1 != local_host) 6424b638df4SBjorn Andersson continue; 6434b638df4SBjorn Andersson 6449806884dSStephen Boyd if (!le32_to_cpu(entry->offset)) 6454b638df4SBjorn Andersson continue; 6464b638df4SBjorn Andersson 6479806884dSStephen Boyd if (!le32_to_cpu(entry->size)) 6484b638df4SBjorn Andersson continue; 6494b638df4SBjorn Andersson 6509806884dSStephen Boyd if (host0 == local_host) 6519806884dSStephen Boyd remote_host = host1; 6524b638df4SBjorn Andersson else 6539806884dSStephen Boyd remote_host = host0; 6544b638df4SBjorn Andersson 6554b638df4SBjorn Andersson if (remote_host >= SMEM_HOST_COUNT) { 6564b638df4SBjorn Andersson dev_err(smem->dev, 6574b638df4SBjorn Andersson "Invalid remote host %d\n", 6584b638df4SBjorn Andersson remote_host); 6594b638df4SBjorn Andersson return -EINVAL; 6604b638df4SBjorn Andersson } 6614b638df4SBjorn Andersson 6624b638df4SBjorn Andersson if (smem->partitions[remote_host]) { 6634b638df4SBjorn Andersson dev_err(smem->dev, 6644b638df4SBjorn Andersson "Already found a partition for host %d\n", 6654b638df4SBjorn Andersson remote_host); 6664b638df4SBjorn Andersson return -EINVAL; 6674b638df4SBjorn Andersson } 6684b638df4SBjorn Andersson 6699806884dSStephen Boyd header = smem->regions[0].virt_base + le32_to_cpu(entry->offset); 6709806884dSStephen Boyd host0 = le16_to_cpu(header->host0); 6719806884dSStephen Boyd host1 = le16_to_cpu(header->host1); 6724b638df4SBjorn Andersson 6739806884dSStephen Boyd if (memcmp(header->magic, SMEM_PART_MAGIC, 6749806884dSStephen Boyd sizeof(header->magic))) { 6754b638df4SBjorn Andersson dev_err(smem->dev, 6764b638df4SBjorn Andersson "Partition %d has invalid magic\n", i); 6774b638df4SBjorn Andersson return -EINVAL; 6784b638df4SBjorn Andersson } 6794b638df4SBjorn Andersson 6809806884dSStephen Boyd if (host0 != local_host && host1 != local_host) { 6814b638df4SBjorn Andersson dev_err(smem->dev, 6824b638df4SBjorn Andersson "Partition %d hosts are invalid\n", i); 6834b638df4SBjorn Andersson return -EINVAL; 6844b638df4SBjorn Andersson } 6854b638df4SBjorn Andersson 6869806884dSStephen Boyd if (host0 != remote_host && host1 != remote_host) { 6874b638df4SBjorn Andersson dev_err(smem->dev, 6884b638df4SBjorn Andersson "Partition %d hosts are invalid\n", i); 6894b638df4SBjorn Andersson return -EINVAL; 6904b638df4SBjorn Andersson } 6914b638df4SBjorn Andersson 692a216000fSChris Lew if (le32_to_cpu(header->size) != le32_to_cpu(entry->size)) { 6934b638df4SBjorn Andersson dev_err(smem->dev, 6944b638df4SBjorn Andersson "Partition %d has invalid size\n", i); 6954b638df4SBjorn Andersson return -EINVAL; 6964b638df4SBjorn Andersson } 6974b638df4SBjorn Andersson 6989806884dSStephen Boyd if (le32_to_cpu(header->offset_free_uncached) > le32_to_cpu(header->size)) { 6994b638df4SBjorn Andersson dev_err(smem->dev, 7004b638df4SBjorn Andersson "Partition %d has invalid free pointer\n", i); 7014b638df4SBjorn Andersson return -EINVAL; 7024b638df4SBjorn Andersson } 7034b638df4SBjorn Andersson 7044b638df4SBjorn Andersson smem->partitions[remote_host] = header; 705c7c1dc35SBjorn Andersson smem->cacheline[remote_host] = le32_to_cpu(entry->cacheline); 7064b638df4SBjorn Andersson } 7074b638df4SBjorn Andersson 7084b638df4SBjorn Andersson return 0; 7094b638df4SBjorn Andersson } 7104b638df4SBjorn Andersson 711d0bfd7c9SStephen Boyd static int qcom_smem_map_memory(struct qcom_smem *smem, struct device *dev, 712d0bfd7c9SStephen Boyd const char *name, int i) 7134b638df4SBjorn Andersson { 714d0bfd7c9SStephen Boyd struct device_node *np; 715d0bfd7c9SStephen Boyd struct resource r; 716d0bfd7c9SStephen Boyd int ret; 7174b638df4SBjorn Andersson 718d0bfd7c9SStephen Boyd np = of_parse_phandle(dev->of_node, name, 0); 719d0bfd7c9SStephen Boyd if (!np) { 720d0bfd7c9SStephen Boyd dev_err(dev, "No %s specified\n", name); 721d0bfd7c9SStephen Boyd return -EINVAL; 7224b638df4SBjorn Andersson } 7234b638df4SBjorn Andersson 724d0bfd7c9SStephen Boyd ret = of_address_to_resource(np, 0, &r); 725d0bfd7c9SStephen Boyd of_node_put(np); 726d0bfd7c9SStephen Boyd if (ret) 727d0bfd7c9SStephen Boyd return ret; 728d0bfd7c9SStephen Boyd 729d0bfd7c9SStephen Boyd smem->regions[i].aux_base = (u32)r.start; 730d0bfd7c9SStephen Boyd smem->regions[i].size = resource_size(&r); 731afd356dfSBjorn Andersson smem->regions[i].virt_base = devm_ioremap_wc(dev, r.start, resource_size(&r)); 732d0bfd7c9SStephen Boyd if (!smem->regions[i].virt_base) 733d0bfd7c9SStephen Boyd return -ENOMEM; 734d0bfd7c9SStephen Boyd 735d0bfd7c9SStephen Boyd return 0; 7364b638df4SBjorn Andersson } 7374b638df4SBjorn Andersson 7384b638df4SBjorn Andersson static int qcom_smem_probe(struct platform_device *pdev) 7394b638df4SBjorn Andersson { 7404b638df4SBjorn Andersson struct smem_header *header; 7414b638df4SBjorn Andersson struct qcom_smem *smem; 7424b638df4SBjorn Andersson size_t array_size; 743d0bfd7c9SStephen Boyd int num_regions; 7444b638df4SBjorn Andersson int hwlock_id; 7454b638df4SBjorn Andersson u32 version; 7464b638df4SBjorn Andersson int ret; 7474b638df4SBjorn Andersson 748d0bfd7c9SStephen Boyd num_regions = 1; 749d0bfd7c9SStephen Boyd if (of_find_property(pdev->dev.of_node, "qcom,rpm-msg-ram", NULL)) 750d0bfd7c9SStephen Boyd num_regions++; 7514b638df4SBjorn Andersson 7524b638df4SBjorn Andersson array_size = num_regions * sizeof(struct smem_region); 7534b638df4SBjorn Andersson smem = devm_kzalloc(&pdev->dev, sizeof(*smem) + array_size, GFP_KERNEL); 7544b638df4SBjorn Andersson if (!smem) 7554b638df4SBjorn Andersson return -ENOMEM; 7564b638df4SBjorn Andersson 7574b638df4SBjorn Andersson smem->dev = &pdev->dev; 7584b638df4SBjorn Andersson smem->num_regions = num_regions; 7594b638df4SBjorn Andersson 760d0bfd7c9SStephen Boyd ret = qcom_smem_map_memory(smem, &pdev->dev, "memory-region", 0); 7614b638df4SBjorn Andersson if (ret) 7624b638df4SBjorn Andersson return ret; 7634b638df4SBjorn Andersson 764d0bfd7c9SStephen Boyd if (num_regions > 1 && (ret = qcom_smem_map_memory(smem, &pdev->dev, 765d0bfd7c9SStephen Boyd "qcom,rpm-msg-ram", 1))) 766d0bfd7c9SStephen Boyd return ret; 7674b638df4SBjorn Andersson 7684b638df4SBjorn Andersson header = smem->regions[0].virt_base; 7699806884dSStephen Boyd if (le32_to_cpu(header->initialized) != 1 || 7709806884dSStephen Boyd le32_to_cpu(header->reserved)) { 7714b638df4SBjorn Andersson dev_err(&pdev->dev, "SMEM is not initialized by SBL\n"); 7724b638df4SBjorn Andersson return -EINVAL; 7734b638df4SBjorn Andersson } 7744b638df4SBjorn Andersson 7754b638df4SBjorn Andersson version = qcom_smem_get_sbl_version(smem); 7764b638df4SBjorn Andersson if (version >> 16 != SMEM_EXPECTED_VERSION) { 7774b638df4SBjorn Andersson dev_err(&pdev->dev, "Unsupported SMEM version 0x%x\n", version); 7784b638df4SBjorn Andersson return -EINVAL; 7794b638df4SBjorn Andersson } 7804b638df4SBjorn Andersson 7814b638df4SBjorn Andersson ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS); 7824b638df4SBjorn Andersson if (ret < 0) 7834b638df4SBjorn Andersson return ret; 7844b638df4SBjorn Andersson 7854b638df4SBjorn Andersson hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0); 7864b638df4SBjorn Andersson if (hwlock_id < 0) { 787951a5af9SStephen Boyd if (hwlock_id != -EPROBE_DEFER) 7884b638df4SBjorn Andersson dev_err(&pdev->dev, "failed to retrieve hwlock\n"); 7894b638df4SBjorn Andersson return hwlock_id; 7904b638df4SBjorn Andersson } 7914b638df4SBjorn Andersson 7924b638df4SBjorn Andersson smem->hwlock = hwspin_lock_request_specific(hwlock_id); 7934b638df4SBjorn Andersson if (!smem->hwlock) 7944b638df4SBjorn Andersson return -ENXIO; 7954b638df4SBjorn Andersson 7964b638df4SBjorn Andersson __smem = smem; 7974b638df4SBjorn Andersson 7984b638df4SBjorn Andersson return 0; 7994b638df4SBjorn Andersson } 8004b638df4SBjorn Andersson 8014b638df4SBjorn Andersson static int qcom_smem_remove(struct platform_device *pdev) 8024b638df4SBjorn Andersson { 8034b638df4SBjorn Andersson hwspin_lock_free(__smem->hwlock); 804f8c67df7SStephen Boyd __smem = NULL; 8054b638df4SBjorn Andersson 8064b638df4SBjorn Andersson return 0; 8074b638df4SBjorn Andersson } 8084b638df4SBjorn Andersson 8094b638df4SBjorn Andersson static const struct of_device_id qcom_smem_of_match[] = { 8104b638df4SBjorn Andersson { .compatible = "qcom,smem" }, 8114b638df4SBjorn Andersson {} 8124b638df4SBjorn Andersson }; 8134b638df4SBjorn Andersson MODULE_DEVICE_TABLE(of, qcom_smem_of_match); 8144b638df4SBjorn Andersson 8154b638df4SBjorn Andersson static struct platform_driver qcom_smem_driver = { 8164b638df4SBjorn Andersson .probe = qcom_smem_probe, 8174b638df4SBjorn Andersson .remove = qcom_smem_remove, 8184b638df4SBjorn Andersson .driver = { 8194b638df4SBjorn Andersson .name = "qcom-smem", 8204b638df4SBjorn Andersson .of_match_table = qcom_smem_of_match, 8214b638df4SBjorn Andersson .suppress_bind_attrs = true, 8224b638df4SBjorn Andersson }, 8234b638df4SBjorn Andersson }; 8244b638df4SBjorn Andersson 8254b638df4SBjorn Andersson static int __init qcom_smem_init(void) 8264b638df4SBjorn Andersson { 8274b638df4SBjorn Andersson return platform_driver_register(&qcom_smem_driver); 8284b638df4SBjorn Andersson } 8294b638df4SBjorn Andersson arch_initcall(qcom_smem_init); 8304b638df4SBjorn Andersson 8314b638df4SBjorn Andersson static void __exit qcom_smem_exit(void) 8324b638df4SBjorn Andersson { 8334b638df4SBjorn Andersson platform_driver_unregister(&qcom_smem_driver); 8344b638df4SBjorn Andersson } 8354b638df4SBjorn Andersson module_exit(qcom_smem_exit) 8364b638df4SBjorn Andersson 8374b638df4SBjorn Andersson MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>"); 8384b638df4SBjorn Andersson MODULE_DESCRIPTION("Qualcomm Shared Memory Manager"); 8394b638df4SBjorn Andersson MODULE_LICENSE("GPL v2"); 840