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 554b638df4SBjorn Andersson * is hence the region between the cached and non-cached offsets. 564b638df4SBjorn Andersson * 574b638df4SBjorn Andersson * 584b638df4SBjorn Andersson * To synchronize allocations in the shared memory heaps a remote spinlock must 594b638df4SBjorn Andersson * be held - currently lock number 3 of the sfpb or tcsr is used for this on all 604b638df4SBjorn Andersson * platforms. 614b638df4SBjorn Andersson * 624b638df4SBjorn Andersson */ 634b638df4SBjorn Andersson 644b638df4SBjorn Andersson /* 654b638df4SBjorn Andersson * Item 3 of the global heap contains an array of versions for the various 664b638df4SBjorn Andersson * software components in the SoC. We verify that the boot loader version is 674b638df4SBjorn Andersson * what the expected version (SMEM_EXPECTED_VERSION) as a sanity check. 684b638df4SBjorn Andersson */ 694b638df4SBjorn Andersson #define SMEM_ITEM_VERSION 3 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 { 95*9806884dSStephen Boyd __le32 command; 96*9806884dSStephen Boyd __le32 status; 97*9806884dSStephen 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 { 109*9806884dSStephen Boyd __le32 allocated; 110*9806884dSStephen Boyd __le32 offset; 111*9806884dSStephen Boyd __le32 size; 112*9806884dSStephen 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]; 128*9806884dSStephen Boyd __le32 version[32]; 129*9806884dSStephen Boyd __le32 initialized; 130*9806884dSStephen Boyd __le32 free_offset; 131*9806884dSStephen Boyd __le32 available; 132*9806884dSStephen 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 1434b638df4SBjorn Andersson * @reserved: reserved entries for later use 1444b638df4SBjorn Andersson */ 1454b638df4SBjorn Andersson struct smem_ptable_entry { 146*9806884dSStephen Boyd __le32 offset; 147*9806884dSStephen Boyd __le32 size; 148*9806884dSStephen Boyd __le32 flags; 149*9806884dSStephen Boyd __le16 host0; 150*9806884dSStephen Boyd __le16 host1; 151*9806884dSStephen Boyd __le32 reserved[8]; 1524b638df4SBjorn Andersson }; 1534b638df4SBjorn Andersson 1544b638df4SBjorn Andersson /** 1554b638df4SBjorn Andersson * struct smem_ptable - partition table for the private partitions 1564b638df4SBjorn Andersson * @magic: magic number, must be SMEM_PTABLE_MAGIC 1574b638df4SBjorn Andersson * @version: version of the partition table 1584b638df4SBjorn Andersson * @num_entries: number of partitions in the table 1594b638df4SBjorn Andersson * @reserved: for now reserved entries 1604b638df4SBjorn Andersson * @entry: list of @smem_ptable_entry for the @num_entries partitions 1614b638df4SBjorn Andersson */ 1624b638df4SBjorn Andersson struct smem_ptable { 163*9806884dSStephen Boyd u8 magic[4]; 164*9806884dSStephen Boyd __le32 version; 165*9806884dSStephen Boyd __le32 num_entries; 166*9806884dSStephen Boyd __le32 reserved[5]; 1674b638df4SBjorn Andersson struct smem_ptable_entry entry[]; 1684b638df4SBjorn Andersson }; 169*9806884dSStephen Boyd 170*9806884dSStephen Boyd static const u8 SMEM_PTABLE_MAGIC[] = { 0x24, 0x54, 0x4f, 0x43 }; /* "$TOC" */ 1714b638df4SBjorn Andersson 1724b638df4SBjorn Andersson /** 1734b638df4SBjorn Andersson * struct smem_partition_header - header of the partitions 1744b638df4SBjorn Andersson * @magic: magic number, must be SMEM_PART_MAGIC 1754b638df4SBjorn Andersson * @host0: first processor/host with access to this partition 1764b638df4SBjorn Andersson * @host1: second processor/host with access to this partition 1774b638df4SBjorn Andersson * @size: size of the partition 1784b638df4SBjorn Andersson * @offset_free_uncached: offset to the first free byte of uncached memory in 1794b638df4SBjorn Andersson * this partition 1804b638df4SBjorn Andersson * @offset_free_cached: offset to the first free byte of cached memory in this 1814b638df4SBjorn Andersson * partition 1824b638df4SBjorn Andersson * @reserved: for now reserved entries 1834b638df4SBjorn Andersson */ 1844b638df4SBjorn Andersson struct smem_partition_header { 185*9806884dSStephen Boyd u8 magic[4]; 186*9806884dSStephen Boyd __le16 host0; 187*9806884dSStephen Boyd __le16 host1; 188*9806884dSStephen Boyd __le32 size; 189*9806884dSStephen Boyd __le32 offset_free_uncached; 190*9806884dSStephen Boyd __le32 offset_free_cached; 191*9806884dSStephen Boyd __le32 reserved[3]; 1924b638df4SBjorn Andersson }; 193*9806884dSStephen Boyd 194*9806884dSStephen Boyd static const u8 SMEM_PART_MAGIC[] = { 0x24, 0x50, 0x52, 0x54 }; 1954b638df4SBjorn Andersson 1964b638df4SBjorn Andersson /** 1974b638df4SBjorn Andersson * struct smem_private_entry - header of each item in the private partition 1984b638df4SBjorn Andersson * @canary: magic number, must be SMEM_PRIVATE_CANARY 1994b638df4SBjorn Andersson * @item: identifying number of the smem item 2004b638df4SBjorn Andersson * @size: size of the data, including padding bytes 2014b638df4SBjorn Andersson * @padding_data: number of bytes of padding of data 2024b638df4SBjorn Andersson * @padding_hdr: number of bytes of padding between the header and the data 2034b638df4SBjorn Andersson * @reserved: for now reserved entry 2044b638df4SBjorn Andersson */ 2054b638df4SBjorn Andersson struct smem_private_entry { 206*9806884dSStephen Boyd u16 canary; /* bytes are the same so no swapping needed */ 207*9806884dSStephen Boyd __le16 item; 208*9806884dSStephen Boyd __le32 size; /* includes padding bytes */ 209*9806884dSStephen Boyd __le16 padding_data; 210*9806884dSStephen Boyd __le16 padding_hdr; 211*9806884dSStephen Boyd __le32 reserved; 2124b638df4SBjorn Andersson }; 2134b638df4SBjorn Andersson #define SMEM_PRIVATE_CANARY 0xa5a5 2144b638df4SBjorn Andersson 2154b638df4SBjorn Andersson /** 2164b638df4SBjorn Andersson * struct smem_region - representation of a chunk of memory used for smem 2174b638df4SBjorn Andersson * @aux_base: identifier of aux_mem base 2184b638df4SBjorn Andersson * @virt_base: virtual base address of memory with this aux_mem identifier 2194b638df4SBjorn Andersson * @size: size of the memory region 2204b638df4SBjorn Andersson */ 2214b638df4SBjorn Andersson struct smem_region { 2224b638df4SBjorn Andersson u32 aux_base; 2234b638df4SBjorn Andersson void __iomem *virt_base; 2244b638df4SBjorn Andersson size_t size; 2254b638df4SBjorn Andersson }; 2264b638df4SBjorn Andersson 2274b638df4SBjorn Andersson /** 2284b638df4SBjorn Andersson * struct qcom_smem - device data for the smem device 2294b638df4SBjorn Andersson * @dev: device pointer 2304b638df4SBjorn Andersson * @hwlock: reference to a hwspinlock 2314b638df4SBjorn Andersson * @partitions: list of pointers to partitions affecting the current 2324b638df4SBjorn Andersson * processor/host 2334b638df4SBjorn Andersson * @num_regions: number of @regions 2344b638df4SBjorn Andersson * @regions: list of the memory regions defining the shared memory 2354b638df4SBjorn Andersson */ 2364b638df4SBjorn Andersson struct qcom_smem { 2374b638df4SBjorn Andersson struct device *dev; 2384b638df4SBjorn Andersson 2394b638df4SBjorn Andersson struct hwspinlock *hwlock; 2404b638df4SBjorn Andersson 2414b638df4SBjorn Andersson struct smem_partition_header *partitions[SMEM_HOST_COUNT]; 2424b638df4SBjorn Andersson 2434b638df4SBjorn Andersson unsigned num_regions; 2444b638df4SBjorn Andersson struct smem_region regions[0]; 2454b638df4SBjorn Andersson }; 2464b638df4SBjorn Andersson 247*9806884dSStephen Boyd static struct smem_private_entry * 248*9806884dSStephen Boyd phdr_to_last_private_entry(struct smem_partition_header *phdr) 249*9806884dSStephen Boyd { 250*9806884dSStephen Boyd void *p = phdr; 251*9806884dSStephen Boyd 252*9806884dSStephen Boyd return p + le32_to_cpu(phdr->offset_free_uncached); 253*9806884dSStephen Boyd } 254*9806884dSStephen Boyd 255*9806884dSStephen Boyd static void *phdr_to_first_cached_entry(struct smem_partition_header *phdr) 256*9806884dSStephen Boyd { 257*9806884dSStephen Boyd void *p = phdr; 258*9806884dSStephen Boyd 259*9806884dSStephen Boyd return p + le32_to_cpu(phdr->offset_free_cached); 260*9806884dSStephen Boyd } 261*9806884dSStephen Boyd 262*9806884dSStephen Boyd static struct smem_private_entry * 263*9806884dSStephen Boyd phdr_to_first_private_entry(struct smem_partition_header *phdr) 264*9806884dSStephen Boyd { 265*9806884dSStephen Boyd void *p = phdr; 266*9806884dSStephen Boyd 267*9806884dSStephen Boyd return p + sizeof(*phdr); 268*9806884dSStephen Boyd } 269*9806884dSStephen Boyd 270*9806884dSStephen Boyd static struct smem_private_entry * 271*9806884dSStephen Boyd private_entry_next(struct smem_private_entry *e) 272*9806884dSStephen Boyd { 273*9806884dSStephen Boyd void *p = e; 274*9806884dSStephen Boyd 275*9806884dSStephen Boyd return p + sizeof(*e) + le16_to_cpu(e->padding_hdr) + 276*9806884dSStephen Boyd le32_to_cpu(e->size); 277*9806884dSStephen Boyd } 278*9806884dSStephen Boyd 279*9806884dSStephen Boyd static void *entry_to_item(struct smem_private_entry *e) 280*9806884dSStephen Boyd { 281*9806884dSStephen Boyd void *p = e; 282*9806884dSStephen Boyd 283*9806884dSStephen Boyd return p + sizeof(*e) + le16_to_cpu(e->padding_hdr); 284*9806884dSStephen Boyd } 285*9806884dSStephen Boyd 2864b638df4SBjorn Andersson /* Pointer to the one and only smem handle */ 2874b638df4SBjorn Andersson static struct qcom_smem *__smem; 2884b638df4SBjorn Andersson 2894b638df4SBjorn Andersson /* Timeout (ms) for the trylock of remote spinlocks */ 2904b638df4SBjorn Andersson #define HWSPINLOCK_TIMEOUT 1000 2914b638df4SBjorn Andersson 2924b638df4SBjorn Andersson static int qcom_smem_alloc_private(struct qcom_smem *smem, 2934b638df4SBjorn Andersson unsigned host, 2944b638df4SBjorn Andersson unsigned item, 2954b638df4SBjorn Andersson size_t size) 2964b638df4SBjorn Andersson { 2974b638df4SBjorn Andersson struct smem_partition_header *phdr; 298*9806884dSStephen Boyd struct smem_private_entry *hdr, *end; 2994b638df4SBjorn Andersson size_t alloc_size; 300*9806884dSStephen Boyd void *cached; 3014b638df4SBjorn Andersson 3024b638df4SBjorn Andersson phdr = smem->partitions[host]; 303*9806884dSStephen Boyd hdr = phdr_to_first_private_entry(phdr); 304*9806884dSStephen Boyd end = phdr_to_last_private_entry(phdr); 305*9806884dSStephen Boyd cached = phdr_to_first_cached_entry(phdr); 3064b638df4SBjorn Andersson 307*9806884dSStephen Boyd while (hdr < end) { 3084b638df4SBjorn Andersson if (hdr->canary != SMEM_PRIVATE_CANARY) { 3094b638df4SBjorn Andersson dev_err(smem->dev, 3104b638df4SBjorn Andersson "Found invalid canary in host %d partition\n", 3114b638df4SBjorn Andersson host); 3124b638df4SBjorn Andersson return -EINVAL; 3134b638df4SBjorn Andersson } 3144b638df4SBjorn Andersson 315*9806884dSStephen Boyd if (le16_to_cpu(hdr->item) == item) 3164b638df4SBjorn Andersson return -EEXIST; 3174b638df4SBjorn Andersson 318*9806884dSStephen Boyd hdr = private_entry_next(hdr); 3194b638df4SBjorn Andersson } 3204b638df4SBjorn Andersson 3214b638df4SBjorn Andersson /* Check that we don't grow into the cached region */ 3224b638df4SBjorn Andersson alloc_size = sizeof(*hdr) + ALIGN(size, 8); 323*9806884dSStephen Boyd if ((void *)hdr + alloc_size >= cached) { 3244b638df4SBjorn Andersson dev_err(smem->dev, "Out of memory\n"); 3254b638df4SBjorn Andersson return -ENOSPC; 3264b638df4SBjorn Andersson } 3274b638df4SBjorn Andersson 3284b638df4SBjorn Andersson hdr->canary = SMEM_PRIVATE_CANARY; 329*9806884dSStephen Boyd hdr->item = cpu_to_le16(item); 330*9806884dSStephen Boyd hdr->size = cpu_to_le32(ALIGN(size, 8)); 331*9806884dSStephen Boyd hdr->padding_data = cpu_to_le16(le32_to_cpu(hdr->size) - size); 3324b638df4SBjorn Andersson hdr->padding_hdr = 0; 3334b638df4SBjorn Andersson 3344b638df4SBjorn Andersson /* 3354b638df4SBjorn Andersson * Ensure the header is written before we advance the free offset, so 3364b638df4SBjorn Andersson * that remote processors that does not take the remote spinlock still 3374b638df4SBjorn Andersson * gets a consistent view of the linked list. 3384b638df4SBjorn Andersson */ 3394b638df4SBjorn Andersson wmb(); 340*9806884dSStephen Boyd le32_add_cpu(&phdr->offset_free_uncached, alloc_size); 3414b638df4SBjorn Andersson 3424b638df4SBjorn Andersson return 0; 3434b638df4SBjorn Andersson } 3444b638df4SBjorn Andersson 3454b638df4SBjorn Andersson static int qcom_smem_alloc_global(struct qcom_smem *smem, 3464b638df4SBjorn Andersson unsigned item, 3474b638df4SBjorn Andersson size_t size) 3484b638df4SBjorn Andersson { 3494b638df4SBjorn Andersson struct smem_header *header; 3504b638df4SBjorn Andersson struct smem_global_entry *entry; 3514b638df4SBjorn Andersson 3524b638df4SBjorn Andersson if (WARN_ON(item >= SMEM_ITEM_COUNT)) 3534b638df4SBjorn Andersson return -EINVAL; 3544b638df4SBjorn Andersson 3554b638df4SBjorn Andersson header = smem->regions[0].virt_base; 3564b638df4SBjorn Andersson entry = &header->toc[item]; 3574b638df4SBjorn Andersson if (entry->allocated) 3584b638df4SBjorn Andersson return -EEXIST; 3594b638df4SBjorn Andersson 3604b638df4SBjorn Andersson size = ALIGN(size, 8); 361*9806884dSStephen Boyd if (WARN_ON(size > le32_to_cpu(header->available))) 3624b638df4SBjorn Andersson return -ENOMEM; 3634b638df4SBjorn Andersson 3644b638df4SBjorn Andersson entry->offset = header->free_offset; 365*9806884dSStephen Boyd entry->size = cpu_to_le32(size); 3664b638df4SBjorn Andersson 3674b638df4SBjorn Andersson /* 3684b638df4SBjorn Andersson * Ensure the header is consistent before we mark the item allocated, 3694b638df4SBjorn Andersson * so that remote processors will get a consistent view of the item 3704b638df4SBjorn Andersson * even though they do not take the spinlock on read. 3714b638df4SBjorn Andersson */ 3724b638df4SBjorn Andersson wmb(); 373*9806884dSStephen Boyd entry->allocated = cpu_to_le32(1); 3744b638df4SBjorn Andersson 375*9806884dSStephen Boyd le32_add_cpu(&header->free_offset, size); 376*9806884dSStephen Boyd le32_add_cpu(&header->available, -size); 3774b638df4SBjorn Andersson 3784b638df4SBjorn Andersson return 0; 3794b638df4SBjorn Andersson } 3804b638df4SBjorn Andersson 3814b638df4SBjorn Andersson /** 3824b638df4SBjorn Andersson * qcom_smem_alloc() - allocate space for a smem item 3834b638df4SBjorn Andersson * @host: remote processor id, or -1 3844b638df4SBjorn Andersson * @item: smem item handle 3854b638df4SBjorn Andersson * @size: number of bytes to be allocated 3864b638df4SBjorn Andersson * 3874b638df4SBjorn Andersson * Allocate space for a given smem item of size @size, given that the item is 3884b638df4SBjorn Andersson * not yet allocated. 3894b638df4SBjorn Andersson */ 3904b638df4SBjorn Andersson int qcom_smem_alloc(unsigned host, unsigned item, size_t size) 3914b638df4SBjorn Andersson { 3924b638df4SBjorn Andersson unsigned long flags; 3934b638df4SBjorn Andersson int ret; 3944b638df4SBjorn Andersson 3954b638df4SBjorn Andersson if (!__smem) 3964b638df4SBjorn Andersson return -EPROBE_DEFER; 3974b638df4SBjorn Andersson 3984b638df4SBjorn Andersson if (item < SMEM_ITEM_LAST_FIXED) { 3994b638df4SBjorn Andersson dev_err(__smem->dev, 4004b638df4SBjorn Andersson "Rejecting allocation of static entry %d\n", item); 4014b638df4SBjorn Andersson return -EINVAL; 4024b638df4SBjorn Andersson } 4034b638df4SBjorn Andersson 4044b638df4SBjorn Andersson ret = hwspin_lock_timeout_irqsave(__smem->hwlock, 4054b638df4SBjorn Andersson HWSPINLOCK_TIMEOUT, 4064b638df4SBjorn Andersson &flags); 4074b638df4SBjorn Andersson if (ret) 4084b638df4SBjorn Andersson return ret; 4094b638df4SBjorn Andersson 41018912806SAndy Gross if (host < SMEM_HOST_COUNT && __smem->partitions[host]) 4114b638df4SBjorn Andersson ret = qcom_smem_alloc_private(__smem, host, item, size); 41218912806SAndy Gross else 4134b638df4SBjorn Andersson ret = qcom_smem_alloc_global(__smem, item, size); 4144b638df4SBjorn Andersson 4154b638df4SBjorn Andersson hwspin_unlock_irqrestore(__smem->hwlock, &flags); 4164b638df4SBjorn Andersson 4174b638df4SBjorn Andersson return ret; 4184b638df4SBjorn Andersson } 4194b638df4SBjorn Andersson EXPORT_SYMBOL(qcom_smem_alloc); 4204b638df4SBjorn Andersson 4211a03964dSStephen Boyd static void *qcom_smem_get_global(struct qcom_smem *smem, 4224b638df4SBjorn Andersson unsigned item, 4234b638df4SBjorn Andersson size_t *size) 4244b638df4SBjorn Andersson { 4254b638df4SBjorn Andersson struct smem_header *header; 4264b638df4SBjorn Andersson struct smem_region *area; 4274b638df4SBjorn Andersson struct smem_global_entry *entry; 4284b638df4SBjorn Andersson u32 aux_base; 4294b638df4SBjorn Andersson unsigned i; 4304b638df4SBjorn Andersson 4314b638df4SBjorn Andersson if (WARN_ON(item >= SMEM_ITEM_COUNT)) 4321a03964dSStephen Boyd return ERR_PTR(-EINVAL); 4334b638df4SBjorn Andersson 4344b638df4SBjorn Andersson header = smem->regions[0].virt_base; 4354b638df4SBjorn Andersson entry = &header->toc[item]; 4364b638df4SBjorn Andersson if (!entry->allocated) 4371a03964dSStephen Boyd return ERR_PTR(-ENXIO); 4384b638df4SBjorn Andersson 439*9806884dSStephen Boyd aux_base = le32_to_cpu(entry->aux_base) & AUX_BASE_MASK; 4404b638df4SBjorn Andersson 4414b638df4SBjorn Andersson for (i = 0; i < smem->num_regions; i++) { 4424b638df4SBjorn Andersson area = &smem->regions[i]; 4434b638df4SBjorn Andersson 4444b638df4SBjorn Andersson if (area->aux_base == aux_base || !aux_base) { 4454b638df4SBjorn Andersson if (size != NULL) 446*9806884dSStephen Boyd *size = le32_to_cpu(entry->size); 447*9806884dSStephen Boyd return area->virt_base + le32_to_cpu(entry->offset); 4481a03964dSStephen Boyd } 4494b638df4SBjorn Andersson } 4504b638df4SBjorn Andersson 4511a03964dSStephen Boyd return ERR_PTR(-ENOENT); 4521a03964dSStephen Boyd } 4531a03964dSStephen Boyd 4541a03964dSStephen Boyd static void *qcom_smem_get_private(struct qcom_smem *smem, 4554b638df4SBjorn Andersson unsigned host, 4564b638df4SBjorn Andersson unsigned item, 4574b638df4SBjorn Andersson size_t *size) 4584b638df4SBjorn Andersson { 4594b638df4SBjorn Andersson struct smem_partition_header *phdr; 460*9806884dSStephen Boyd struct smem_private_entry *e, *end; 4614b638df4SBjorn Andersson 4624b638df4SBjorn Andersson phdr = smem->partitions[host]; 463*9806884dSStephen Boyd e = phdr_to_first_private_entry(phdr); 464*9806884dSStephen Boyd end = phdr_to_last_private_entry(phdr); 4654b638df4SBjorn Andersson 466*9806884dSStephen Boyd while (e < end) { 467*9806884dSStephen Boyd if (e->canary != SMEM_PRIVATE_CANARY) { 4684b638df4SBjorn Andersson dev_err(smem->dev, 4694b638df4SBjorn Andersson "Found invalid canary in host %d partition\n", 4704b638df4SBjorn Andersson host); 4711a03964dSStephen Boyd return ERR_PTR(-EINVAL); 4724b638df4SBjorn Andersson } 4734b638df4SBjorn Andersson 474*9806884dSStephen Boyd if (le16_to_cpu(e->item) == item) { 4754b638df4SBjorn Andersson if (size != NULL) 476*9806884dSStephen Boyd *size = le32_to_cpu(e->size) - 477*9806884dSStephen Boyd le16_to_cpu(e->padding_data); 4784b638df4SBjorn Andersson 479*9806884dSStephen Boyd return entry_to_item(e); 4804b638df4SBjorn Andersson } 4814b638df4SBjorn Andersson 482*9806884dSStephen Boyd e = private_entry_next(e); 4834b638df4SBjorn Andersson } 4844b638df4SBjorn Andersson 4851a03964dSStephen Boyd return ERR_PTR(-ENOENT); 4864b638df4SBjorn Andersson } 4874b638df4SBjorn Andersson 4884b638df4SBjorn Andersson /** 4894b638df4SBjorn Andersson * qcom_smem_get() - resolve ptr of size of a smem item 4904b638df4SBjorn Andersson * @host: the remote processor, or -1 4914b638df4SBjorn Andersson * @item: smem item handle 4924b638df4SBjorn Andersson * @size: pointer to be filled out with size of the item 4934b638df4SBjorn Andersson * 4941a03964dSStephen Boyd * Looks up smem item and returns pointer to it. Size of smem 4951a03964dSStephen Boyd * item is returned in @size. 4964b638df4SBjorn Andersson */ 4971a03964dSStephen Boyd void *qcom_smem_get(unsigned host, unsigned item, size_t *size) 4984b638df4SBjorn Andersson { 4994b638df4SBjorn Andersson unsigned long flags; 5004b638df4SBjorn Andersson int ret; 5011a03964dSStephen Boyd void *ptr = ERR_PTR(-EPROBE_DEFER); 5024b638df4SBjorn Andersson 5034b638df4SBjorn Andersson if (!__smem) 5041a03964dSStephen Boyd return ptr; 5054b638df4SBjorn Andersson 5064b638df4SBjorn Andersson ret = hwspin_lock_timeout_irqsave(__smem->hwlock, 5074b638df4SBjorn Andersson HWSPINLOCK_TIMEOUT, 5084b638df4SBjorn Andersson &flags); 5094b638df4SBjorn Andersson if (ret) 5101a03964dSStephen Boyd return ERR_PTR(ret); 5114b638df4SBjorn Andersson 51218912806SAndy Gross if (host < SMEM_HOST_COUNT && __smem->partitions[host]) 5131a03964dSStephen Boyd ptr = qcom_smem_get_private(__smem, host, item, size); 51418912806SAndy Gross else 5151a03964dSStephen Boyd ptr = qcom_smem_get_global(__smem, item, size); 5164b638df4SBjorn Andersson 5174b638df4SBjorn Andersson hwspin_unlock_irqrestore(__smem->hwlock, &flags); 5181a03964dSStephen Boyd 5191a03964dSStephen Boyd return ptr; 5204b638df4SBjorn Andersson 5214b638df4SBjorn Andersson } 5224b638df4SBjorn Andersson EXPORT_SYMBOL(qcom_smem_get); 5234b638df4SBjorn Andersson 5244b638df4SBjorn Andersson /** 5254b638df4SBjorn Andersson * qcom_smem_get_free_space() - retrieve amount of free space in a partition 5264b638df4SBjorn Andersson * @host: the remote processor identifying a partition, or -1 5274b638df4SBjorn Andersson * 5284b638df4SBjorn Andersson * To be used by smem clients as a quick way to determine if any new 5294b638df4SBjorn Andersson * allocations has been made. 5304b638df4SBjorn Andersson */ 5314b638df4SBjorn Andersson int qcom_smem_get_free_space(unsigned host) 5324b638df4SBjorn Andersson { 5334b638df4SBjorn Andersson struct smem_partition_header *phdr; 5344b638df4SBjorn Andersson struct smem_header *header; 5354b638df4SBjorn Andersson unsigned ret; 5364b638df4SBjorn Andersson 5374b638df4SBjorn Andersson if (!__smem) 5384b638df4SBjorn Andersson return -EPROBE_DEFER; 5394b638df4SBjorn Andersson 5404b638df4SBjorn Andersson if (host < SMEM_HOST_COUNT && __smem->partitions[host]) { 5414b638df4SBjorn Andersson phdr = __smem->partitions[host]; 542*9806884dSStephen Boyd ret = le32_to_cpu(phdr->offset_free_cached) - 543*9806884dSStephen Boyd le32_to_cpu(phdr->offset_free_uncached); 5444b638df4SBjorn Andersson } else { 5454b638df4SBjorn Andersson header = __smem->regions[0].virt_base; 546*9806884dSStephen Boyd ret = le32_to_cpu(header->available); 5474b638df4SBjorn Andersson } 5484b638df4SBjorn Andersson 5494b638df4SBjorn Andersson return ret; 5504b638df4SBjorn Andersson } 5514b638df4SBjorn Andersson EXPORT_SYMBOL(qcom_smem_get_free_space); 5524b638df4SBjorn Andersson 5534b638df4SBjorn Andersson static int qcom_smem_get_sbl_version(struct qcom_smem *smem) 5544b638df4SBjorn Andersson { 555*9806884dSStephen Boyd __le32 *versions; 5564b638df4SBjorn Andersson size_t size; 5574b638df4SBjorn Andersson 5581a03964dSStephen Boyd versions = qcom_smem_get_global(smem, SMEM_ITEM_VERSION, &size); 5591a03964dSStephen Boyd if (IS_ERR(versions)) { 5604b638df4SBjorn Andersson dev_err(smem->dev, "Unable to read the version item\n"); 5614b638df4SBjorn Andersson return -ENOENT; 5624b638df4SBjorn Andersson } 5634b638df4SBjorn Andersson 5644b638df4SBjorn Andersson if (size < sizeof(unsigned) * SMEM_MASTER_SBL_VERSION_INDEX) { 5654b638df4SBjorn Andersson dev_err(smem->dev, "Version item is too small\n"); 5664b638df4SBjorn Andersson return -EINVAL; 5674b638df4SBjorn Andersson } 5684b638df4SBjorn Andersson 569*9806884dSStephen Boyd return le32_to_cpu(versions[SMEM_MASTER_SBL_VERSION_INDEX]); 5704b638df4SBjorn Andersson } 5714b638df4SBjorn Andersson 5724b638df4SBjorn Andersson static int qcom_smem_enumerate_partitions(struct qcom_smem *smem, 5734b638df4SBjorn Andersson unsigned local_host) 5744b638df4SBjorn Andersson { 5754b638df4SBjorn Andersson struct smem_partition_header *header; 5764b638df4SBjorn Andersson struct smem_ptable_entry *entry; 5774b638df4SBjorn Andersson struct smem_ptable *ptable; 5784b638df4SBjorn Andersson unsigned remote_host; 579*9806884dSStephen Boyd u32 version, host0, host1; 5804b638df4SBjorn Andersson int i; 5814b638df4SBjorn Andersson 5824b638df4SBjorn Andersson ptable = smem->regions[0].virt_base + smem->regions[0].size - SZ_4K; 583*9806884dSStephen Boyd if (memcmp(ptable->magic, SMEM_PTABLE_MAGIC, sizeof(ptable->magic))) 5844b638df4SBjorn Andersson return 0; 5854b638df4SBjorn Andersson 586*9806884dSStephen Boyd version = le32_to_cpu(ptable->version); 587*9806884dSStephen Boyd if (version != 1) { 5884b638df4SBjorn Andersson dev_err(smem->dev, 589*9806884dSStephen Boyd "Unsupported partition header version %d\n", version); 5904b638df4SBjorn Andersson return -EINVAL; 5914b638df4SBjorn Andersson } 5924b638df4SBjorn Andersson 593*9806884dSStephen Boyd for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) { 5944b638df4SBjorn Andersson entry = &ptable->entry[i]; 595*9806884dSStephen Boyd host0 = le16_to_cpu(entry->host0); 596*9806884dSStephen Boyd host1 = le16_to_cpu(entry->host1); 5974b638df4SBjorn Andersson 598*9806884dSStephen Boyd if (host0 != local_host && host1 != local_host) 5994b638df4SBjorn Andersson continue; 6004b638df4SBjorn Andersson 601*9806884dSStephen Boyd if (!le32_to_cpu(entry->offset)) 6024b638df4SBjorn Andersson continue; 6034b638df4SBjorn Andersson 604*9806884dSStephen Boyd if (!le32_to_cpu(entry->size)) 6054b638df4SBjorn Andersson continue; 6064b638df4SBjorn Andersson 607*9806884dSStephen Boyd if (host0 == local_host) 608*9806884dSStephen Boyd remote_host = host1; 6094b638df4SBjorn Andersson else 610*9806884dSStephen Boyd remote_host = host0; 6114b638df4SBjorn Andersson 6124b638df4SBjorn Andersson if (remote_host >= SMEM_HOST_COUNT) { 6134b638df4SBjorn Andersson dev_err(smem->dev, 6144b638df4SBjorn Andersson "Invalid remote host %d\n", 6154b638df4SBjorn Andersson remote_host); 6164b638df4SBjorn Andersson return -EINVAL; 6174b638df4SBjorn Andersson } 6184b638df4SBjorn Andersson 6194b638df4SBjorn Andersson if (smem->partitions[remote_host]) { 6204b638df4SBjorn Andersson dev_err(smem->dev, 6214b638df4SBjorn Andersson "Already found a partition for host %d\n", 6224b638df4SBjorn Andersson remote_host); 6234b638df4SBjorn Andersson return -EINVAL; 6244b638df4SBjorn Andersson } 6254b638df4SBjorn Andersson 626*9806884dSStephen Boyd header = smem->regions[0].virt_base + le32_to_cpu(entry->offset); 627*9806884dSStephen Boyd host0 = le16_to_cpu(header->host0); 628*9806884dSStephen Boyd host1 = le16_to_cpu(header->host1); 6294b638df4SBjorn Andersson 630*9806884dSStephen Boyd if (memcmp(header->magic, SMEM_PART_MAGIC, 631*9806884dSStephen Boyd sizeof(header->magic))) { 6324b638df4SBjorn Andersson dev_err(smem->dev, 6334b638df4SBjorn Andersson "Partition %d has invalid magic\n", i); 6344b638df4SBjorn Andersson return -EINVAL; 6354b638df4SBjorn Andersson } 6364b638df4SBjorn Andersson 637*9806884dSStephen Boyd if (host0 != local_host && host1 != local_host) { 6384b638df4SBjorn Andersson dev_err(smem->dev, 6394b638df4SBjorn Andersson "Partition %d hosts are invalid\n", i); 6404b638df4SBjorn Andersson return -EINVAL; 6414b638df4SBjorn Andersson } 6424b638df4SBjorn Andersson 643*9806884dSStephen Boyd if (host0 != remote_host && host1 != remote_host) { 6444b638df4SBjorn Andersson dev_err(smem->dev, 6454b638df4SBjorn Andersson "Partition %d hosts are invalid\n", i); 6464b638df4SBjorn Andersson return -EINVAL; 6474b638df4SBjorn Andersson } 6484b638df4SBjorn Andersson 6494b638df4SBjorn Andersson if (header->size != entry->size) { 6504b638df4SBjorn Andersson dev_err(smem->dev, 6514b638df4SBjorn Andersson "Partition %d has invalid size\n", i); 6524b638df4SBjorn Andersson return -EINVAL; 6534b638df4SBjorn Andersson } 6544b638df4SBjorn Andersson 655*9806884dSStephen Boyd if (le32_to_cpu(header->offset_free_uncached) > le32_to_cpu(header->size)) { 6564b638df4SBjorn Andersson dev_err(smem->dev, 6574b638df4SBjorn Andersson "Partition %d has invalid free pointer\n", i); 6584b638df4SBjorn Andersson return -EINVAL; 6594b638df4SBjorn Andersson } 6604b638df4SBjorn Andersson 6614b638df4SBjorn Andersson smem->partitions[remote_host] = header; 6624b638df4SBjorn Andersson } 6634b638df4SBjorn Andersson 6644b638df4SBjorn Andersson return 0; 6654b638df4SBjorn Andersson } 6664b638df4SBjorn Andersson 6674b638df4SBjorn Andersson static int qcom_smem_count_mem_regions(struct platform_device *pdev) 6684b638df4SBjorn Andersson { 6694b638df4SBjorn Andersson struct resource *res; 6704b638df4SBjorn Andersson int num_regions = 0; 6714b638df4SBjorn Andersson int i; 6724b638df4SBjorn Andersson 6734b638df4SBjorn Andersson for (i = 0; i < pdev->num_resources; i++) { 6744b638df4SBjorn Andersson res = &pdev->resource[i]; 6754b638df4SBjorn Andersson 6764b638df4SBjorn Andersson if (resource_type(res) == IORESOURCE_MEM) 6774b638df4SBjorn Andersson num_regions++; 6784b638df4SBjorn Andersson } 6794b638df4SBjorn Andersson 6804b638df4SBjorn Andersson return num_regions; 6814b638df4SBjorn Andersson } 6824b638df4SBjorn Andersson 6834b638df4SBjorn Andersson static int qcom_smem_probe(struct platform_device *pdev) 6844b638df4SBjorn Andersson { 6854b638df4SBjorn Andersson struct smem_header *header; 6864b638df4SBjorn Andersson struct device_node *np; 6874b638df4SBjorn Andersson struct qcom_smem *smem; 6884b638df4SBjorn Andersson struct resource *res; 6894b638df4SBjorn Andersson struct resource r; 6904b638df4SBjorn Andersson size_t array_size; 6914b638df4SBjorn Andersson int num_regions = 0; 6924b638df4SBjorn Andersson int hwlock_id; 6934b638df4SBjorn Andersson u32 version; 6944b638df4SBjorn Andersson int ret; 6954b638df4SBjorn Andersson int i; 6964b638df4SBjorn Andersson 6974b638df4SBjorn Andersson num_regions = qcom_smem_count_mem_regions(pdev) + 1; 6984b638df4SBjorn Andersson 6994b638df4SBjorn Andersson array_size = num_regions * sizeof(struct smem_region); 7004b638df4SBjorn Andersson smem = devm_kzalloc(&pdev->dev, sizeof(*smem) + array_size, GFP_KERNEL); 7014b638df4SBjorn Andersson if (!smem) 7024b638df4SBjorn Andersson return -ENOMEM; 7034b638df4SBjorn Andersson 7044b638df4SBjorn Andersson smem->dev = &pdev->dev; 7054b638df4SBjorn Andersson smem->num_regions = num_regions; 7064b638df4SBjorn Andersson 7074b638df4SBjorn Andersson np = of_parse_phandle(pdev->dev.of_node, "memory-region", 0); 7084b638df4SBjorn Andersson if (!np) { 7094b638df4SBjorn Andersson dev_err(&pdev->dev, "No memory-region specified\n"); 7104b638df4SBjorn Andersson return -EINVAL; 7114b638df4SBjorn Andersson } 7124b638df4SBjorn Andersson 7134b638df4SBjorn Andersson ret = of_address_to_resource(np, 0, &r); 7144b638df4SBjorn Andersson of_node_put(np); 7154b638df4SBjorn Andersson if (ret) 7164b638df4SBjorn Andersson return ret; 7174b638df4SBjorn Andersson 7184b638df4SBjorn Andersson smem->regions[0].aux_base = (u32)r.start; 7194b638df4SBjorn Andersson smem->regions[0].size = resource_size(&r); 7204b638df4SBjorn Andersson smem->regions[0].virt_base = devm_ioremap_nocache(&pdev->dev, 7214b638df4SBjorn Andersson r.start, 7224b638df4SBjorn Andersson resource_size(&r)); 7234b638df4SBjorn Andersson if (!smem->regions[0].virt_base) 7244b638df4SBjorn Andersson return -ENOMEM; 7254b638df4SBjorn Andersson 7264b638df4SBjorn Andersson for (i = 1; i < num_regions; i++) { 7274b638df4SBjorn Andersson res = platform_get_resource(pdev, IORESOURCE_MEM, i - 1); 7284b638df4SBjorn Andersson 7294b638df4SBjorn Andersson smem->regions[i].aux_base = (u32)res->start; 7304b638df4SBjorn Andersson smem->regions[i].size = resource_size(res); 7314b638df4SBjorn Andersson smem->regions[i].virt_base = devm_ioremap_nocache(&pdev->dev, 7324b638df4SBjorn Andersson res->start, 7334b638df4SBjorn Andersson resource_size(res)); 7344b638df4SBjorn Andersson if (!smem->regions[i].virt_base) 7354b638df4SBjorn Andersson return -ENOMEM; 7364b638df4SBjorn Andersson } 7374b638df4SBjorn Andersson 7384b638df4SBjorn Andersson header = smem->regions[0].virt_base; 739*9806884dSStephen Boyd if (le32_to_cpu(header->initialized) != 1 || 740*9806884dSStephen Boyd le32_to_cpu(header->reserved)) { 7414b638df4SBjorn Andersson dev_err(&pdev->dev, "SMEM is not initialized by SBL\n"); 7424b638df4SBjorn Andersson return -EINVAL; 7434b638df4SBjorn Andersson } 7444b638df4SBjorn Andersson 7454b638df4SBjorn Andersson version = qcom_smem_get_sbl_version(smem); 7464b638df4SBjorn Andersson if (version >> 16 != SMEM_EXPECTED_VERSION) { 7474b638df4SBjorn Andersson dev_err(&pdev->dev, "Unsupported SMEM version 0x%x\n", version); 7484b638df4SBjorn Andersson return -EINVAL; 7494b638df4SBjorn Andersson } 7504b638df4SBjorn Andersson 7514b638df4SBjorn Andersson ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS); 7524b638df4SBjorn Andersson if (ret < 0) 7534b638df4SBjorn Andersson return ret; 7544b638df4SBjorn Andersson 7554b638df4SBjorn Andersson hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0); 7564b638df4SBjorn Andersson if (hwlock_id < 0) { 7574b638df4SBjorn Andersson dev_err(&pdev->dev, "failed to retrieve hwlock\n"); 7584b638df4SBjorn Andersson return hwlock_id; 7594b638df4SBjorn Andersson } 7604b638df4SBjorn Andersson 7614b638df4SBjorn Andersson smem->hwlock = hwspin_lock_request_specific(hwlock_id); 7624b638df4SBjorn Andersson if (!smem->hwlock) 7634b638df4SBjorn Andersson return -ENXIO; 7644b638df4SBjorn Andersson 7654b638df4SBjorn Andersson __smem = smem; 7664b638df4SBjorn Andersson 7674b638df4SBjorn Andersson return 0; 7684b638df4SBjorn Andersson } 7694b638df4SBjorn Andersson 7704b638df4SBjorn Andersson static int qcom_smem_remove(struct platform_device *pdev) 7714b638df4SBjorn Andersson { 7724b638df4SBjorn Andersson hwspin_lock_free(__smem->hwlock); 773f8c67df7SStephen Boyd __smem = NULL; 7744b638df4SBjorn Andersson 7754b638df4SBjorn Andersson return 0; 7764b638df4SBjorn Andersson } 7774b638df4SBjorn Andersson 7784b638df4SBjorn Andersson static const struct of_device_id qcom_smem_of_match[] = { 7794b638df4SBjorn Andersson { .compatible = "qcom,smem" }, 7804b638df4SBjorn Andersson {} 7814b638df4SBjorn Andersson }; 7824b638df4SBjorn Andersson MODULE_DEVICE_TABLE(of, qcom_smem_of_match); 7834b638df4SBjorn Andersson 7844b638df4SBjorn Andersson static struct platform_driver qcom_smem_driver = { 7854b638df4SBjorn Andersson .probe = qcom_smem_probe, 7864b638df4SBjorn Andersson .remove = qcom_smem_remove, 7874b638df4SBjorn Andersson .driver = { 7884b638df4SBjorn Andersson .name = "qcom-smem", 7894b638df4SBjorn Andersson .of_match_table = qcom_smem_of_match, 7904b638df4SBjorn Andersson .suppress_bind_attrs = true, 7914b638df4SBjorn Andersson }, 7924b638df4SBjorn Andersson }; 7934b638df4SBjorn Andersson 7944b638df4SBjorn Andersson static int __init qcom_smem_init(void) 7954b638df4SBjorn Andersson { 7964b638df4SBjorn Andersson return platform_driver_register(&qcom_smem_driver); 7974b638df4SBjorn Andersson } 7984b638df4SBjorn Andersson arch_initcall(qcom_smem_init); 7994b638df4SBjorn Andersson 8004b638df4SBjorn Andersson static void __exit qcom_smem_exit(void) 8014b638df4SBjorn Andersson { 8024b638df4SBjorn Andersson platform_driver_unregister(&qcom_smem_driver); 8034b638df4SBjorn Andersson } 8044b638df4SBjorn Andersson module_exit(qcom_smem_exit) 8054b638df4SBjorn Andersson 8064b638df4SBjorn Andersson MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>"); 8074b638df4SBjorn Andersson MODULE_DESCRIPTION("Qualcomm Shared Memory Manager"); 8084b638df4SBjorn Andersson MODULE_LICENSE("GPL v2"); 809