162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2015, Sony Mobile Communications AB. 462306a36Sopenharmony_ci * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/hwspinlock.h> 862306a36Sopenharmony_ci#include <linux/io.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/of.h> 1162306a36Sopenharmony_ci#include <linux/of_address.h> 1262306a36Sopenharmony_ci#include <linux/of_reserved_mem.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/sizes.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/soc/qcom/smem.h> 1762306a36Sopenharmony_ci#include <linux/soc/qcom/socinfo.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * The Qualcomm shared memory system is a allocate only heap structure that 2162306a36Sopenharmony_ci * consists of one of more memory areas that can be accessed by the processors 2262306a36Sopenharmony_ci * in the SoC. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * All systems contains a global heap, accessible by all processors in the SoC, 2562306a36Sopenharmony_ci * with a table of contents data structure (@smem_header) at the beginning of 2662306a36Sopenharmony_ci * the main shared memory block. 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * The global header contains meta data for allocations as well as a fixed list 2962306a36Sopenharmony_ci * of 512 entries (@smem_global_entry) that can be initialized to reference 3062306a36Sopenharmony_ci * parts of the shared memory space. 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * In addition to this global heap a set of "private" heaps can be set up at 3462306a36Sopenharmony_ci * boot time with access restrictions so that only certain processor pairs can 3562306a36Sopenharmony_ci * access the data. 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * These partitions are referenced from an optional partition table 3862306a36Sopenharmony_ci * (@smem_ptable), that is found 4kB from the end of the main smem region. The 3962306a36Sopenharmony_ci * partition table entries (@smem_ptable_entry) lists the involved processors 4062306a36Sopenharmony_ci * (or hosts) and their location in the main shared memory region. 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * Each partition starts with a header (@smem_partition_header) that identifies 4362306a36Sopenharmony_ci * the partition and holds properties for the two internal memory regions. The 4462306a36Sopenharmony_ci * two regions are cached and non-cached memory respectively. Each region 4562306a36Sopenharmony_ci * contain a link list of allocation headers (@smem_private_entry) followed by 4662306a36Sopenharmony_ci * their data. 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * Items in the non-cached region are allocated from the start of the partition 4962306a36Sopenharmony_ci * while items in the cached region are allocated from the end. The free area 5062306a36Sopenharmony_ci * is hence the region between the cached and non-cached offsets. The header of 5162306a36Sopenharmony_ci * cached items comes after the data. 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * Version 12 (SMEM_GLOBAL_PART_VERSION) changes the item alloc/get procedure 5462306a36Sopenharmony_ci * for the global heap. A new global partition is created from the global heap 5562306a36Sopenharmony_ci * region with partition type (SMEM_GLOBAL_HOST) and the max smem item count is 5662306a36Sopenharmony_ci * set by the bootloader. 5762306a36Sopenharmony_ci * 5862306a36Sopenharmony_ci * To synchronize allocations in the shared memory heaps a remote spinlock must 5962306a36Sopenharmony_ci * be held - currently lock number 3 of the sfpb or tcsr is used for this on all 6062306a36Sopenharmony_ci * platforms. 6162306a36Sopenharmony_ci * 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* 6562306a36Sopenharmony_ci * The version member of the smem header contains an array of versions for the 6662306a36Sopenharmony_ci * various software components in the SoC. We verify that the boot loader 6762306a36Sopenharmony_ci * version is a valid version as a sanity check. 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_ci#define SMEM_MASTER_SBL_VERSION_INDEX 7 7062306a36Sopenharmony_ci#define SMEM_GLOBAL_HEAP_VERSION 11 7162306a36Sopenharmony_ci#define SMEM_GLOBAL_PART_VERSION 12 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* 7462306a36Sopenharmony_ci * The first 8 items are only to be allocated by the boot loader while 7562306a36Sopenharmony_ci * initializing the heap. 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_ci#define SMEM_ITEM_LAST_FIXED 8 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* Highest accepted item number, for both global and private heaps */ 8062306a36Sopenharmony_ci#define SMEM_ITEM_COUNT 512 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* Processor/host identifier for the application processor */ 8362306a36Sopenharmony_ci#define SMEM_HOST_APPS 0 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* Processor/host identifier for the global partition */ 8662306a36Sopenharmony_ci#define SMEM_GLOBAL_HOST 0xfffe 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* Max number of processors/hosts in a system */ 8962306a36Sopenharmony_ci#define SMEM_HOST_COUNT 20 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/** 9262306a36Sopenharmony_ci * struct smem_proc_comm - proc_comm communication struct (legacy) 9362306a36Sopenharmony_ci * @command: current command to be executed 9462306a36Sopenharmony_ci * @status: status of the currently requested command 9562306a36Sopenharmony_ci * @params: parameters to the command 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_cistruct smem_proc_comm { 9862306a36Sopenharmony_ci __le32 command; 9962306a36Sopenharmony_ci __le32 status; 10062306a36Sopenharmony_ci __le32 params[2]; 10162306a36Sopenharmony_ci}; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/** 10462306a36Sopenharmony_ci * struct smem_global_entry - entry to reference smem items on the heap 10562306a36Sopenharmony_ci * @allocated: boolean to indicate if this entry is used 10662306a36Sopenharmony_ci * @offset: offset to the allocated space 10762306a36Sopenharmony_ci * @size: size of the allocated space, 8 byte aligned 10862306a36Sopenharmony_ci * @aux_base: base address for the memory region used by this unit, or 0 for 10962306a36Sopenharmony_ci * the default region. bits 0,1 are reserved 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_cistruct smem_global_entry { 11262306a36Sopenharmony_ci __le32 allocated; 11362306a36Sopenharmony_ci __le32 offset; 11462306a36Sopenharmony_ci __le32 size; 11562306a36Sopenharmony_ci __le32 aux_base; /* bits 1:0 reserved */ 11662306a36Sopenharmony_ci}; 11762306a36Sopenharmony_ci#define AUX_BASE_MASK 0xfffffffc 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/** 12062306a36Sopenharmony_ci * struct smem_header - header found in beginning of primary smem region 12162306a36Sopenharmony_ci * @proc_comm: proc_comm communication interface (legacy) 12262306a36Sopenharmony_ci * @version: array of versions for the various subsystems 12362306a36Sopenharmony_ci * @initialized: boolean to indicate that smem is initialized 12462306a36Sopenharmony_ci * @free_offset: index of the first unallocated byte in smem 12562306a36Sopenharmony_ci * @available: number of bytes available for allocation 12662306a36Sopenharmony_ci * @reserved: reserved field, must be 0 12762306a36Sopenharmony_ci * @toc: array of references to items 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_cistruct smem_header { 13062306a36Sopenharmony_ci struct smem_proc_comm proc_comm[4]; 13162306a36Sopenharmony_ci __le32 version[32]; 13262306a36Sopenharmony_ci __le32 initialized; 13362306a36Sopenharmony_ci __le32 free_offset; 13462306a36Sopenharmony_ci __le32 available; 13562306a36Sopenharmony_ci __le32 reserved; 13662306a36Sopenharmony_ci struct smem_global_entry toc[SMEM_ITEM_COUNT]; 13762306a36Sopenharmony_ci}; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/** 14062306a36Sopenharmony_ci * struct smem_ptable_entry - one entry in the @smem_ptable list 14162306a36Sopenharmony_ci * @offset: offset, within the main shared memory region, of the partition 14262306a36Sopenharmony_ci * @size: size of the partition 14362306a36Sopenharmony_ci * @flags: flags for the partition (currently unused) 14462306a36Sopenharmony_ci * @host0: first processor/host with access to this partition 14562306a36Sopenharmony_ci * @host1: second processor/host with access to this partition 14662306a36Sopenharmony_ci * @cacheline: alignment for "cached" entries 14762306a36Sopenharmony_ci * @reserved: reserved entries for later use 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_cistruct smem_ptable_entry { 15062306a36Sopenharmony_ci __le32 offset; 15162306a36Sopenharmony_ci __le32 size; 15262306a36Sopenharmony_ci __le32 flags; 15362306a36Sopenharmony_ci __le16 host0; 15462306a36Sopenharmony_ci __le16 host1; 15562306a36Sopenharmony_ci __le32 cacheline; 15662306a36Sopenharmony_ci __le32 reserved[7]; 15762306a36Sopenharmony_ci}; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/** 16062306a36Sopenharmony_ci * struct smem_ptable - partition table for the private partitions 16162306a36Sopenharmony_ci * @magic: magic number, must be SMEM_PTABLE_MAGIC 16262306a36Sopenharmony_ci * @version: version of the partition table 16362306a36Sopenharmony_ci * @num_entries: number of partitions in the table 16462306a36Sopenharmony_ci * @reserved: for now reserved entries 16562306a36Sopenharmony_ci * @entry: list of @smem_ptable_entry for the @num_entries partitions 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_cistruct smem_ptable { 16862306a36Sopenharmony_ci u8 magic[4]; 16962306a36Sopenharmony_ci __le32 version; 17062306a36Sopenharmony_ci __le32 num_entries; 17162306a36Sopenharmony_ci __le32 reserved[5]; 17262306a36Sopenharmony_ci struct smem_ptable_entry entry[]; 17362306a36Sopenharmony_ci}; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic const u8 SMEM_PTABLE_MAGIC[] = { 0x24, 0x54, 0x4f, 0x43 }; /* "$TOC" */ 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/** 17862306a36Sopenharmony_ci * struct smem_partition_header - header of the partitions 17962306a36Sopenharmony_ci * @magic: magic number, must be SMEM_PART_MAGIC 18062306a36Sopenharmony_ci * @host0: first processor/host with access to this partition 18162306a36Sopenharmony_ci * @host1: second processor/host with access to this partition 18262306a36Sopenharmony_ci * @size: size of the partition 18362306a36Sopenharmony_ci * @offset_free_uncached: offset to the first free byte of uncached memory in 18462306a36Sopenharmony_ci * this partition 18562306a36Sopenharmony_ci * @offset_free_cached: offset to the first free byte of cached memory in this 18662306a36Sopenharmony_ci * partition 18762306a36Sopenharmony_ci * @reserved: for now reserved entries 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_cistruct smem_partition_header { 19062306a36Sopenharmony_ci u8 magic[4]; 19162306a36Sopenharmony_ci __le16 host0; 19262306a36Sopenharmony_ci __le16 host1; 19362306a36Sopenharmony_ci __le32 size; 19462306a36Sopenharmony_ci __le32 offset_free_uncached; 19562306a36Sopenharmony_ci __le32 offset_free_cached; 19662306a36Sopenharmony_ci __le32 reserved[3]; 19762306a36Sopenharmony_ci}; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci/** 20062306a36Sopenharmony_ci * struct smem_partition - describes smem partition 20162306a36Sopenharmony_ci * @virt_base: starting virtual address of partition 20262306a36Sopenharmony_ci * @phys_base: starting physical address of partition 20362306a36Sopenharmony_ci * @cacheline: alignment for "cached" entries 20462306a36Sopenharmony_ci * @size: size of partition 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_cistruct smem_partition { 20762306a36Sopenharmony_ci void __iomem *virt_base; 20862306a36Sopenharmony_ci phys_addr_t phys_base; 20962306a36Sopenharmony_ci size_t cacheline; 21062306a36Sopenharmony_ci size_t size; 21162306a36Sopenharmony_ci}; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic const u8 SMEM_PART_MAGIC[] = { 0x24, 0x50, 0x52, 0x54 }; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci/** 21662306a36Sopenharmony_ci * struct smem_private_entry - header of each item in the private partition 21762306a36Sopenharmony_ci * @canary: magic number, must be SMEM_PRIVATE_CANARY 21862306a36Sopenharmony_ci * @item: identifying number of the smem item 21962306a36Sopenharmony_ci * @size: size of the data, including padding bytes 22062306a36Sopenharmony_ci * @padding_data: number of bytes of padding of data 22162306a36Sopenharmony_ci * @padding_hdr: number of bytes of padding between the header and the data 22262306a36Sopenharmony_ci * @reserved: for now reserved entry 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_cistruct smem_private_entry { 22562306a36Sopenharmony_ci u16 canary; /* bytes are the same so no swapping needed */ 22662306a36Sopenharmony_ci __le16 item; 22762306a36Sopenharmony_ci __le32 size; /* includes padding bytes */ 22862306a36Sopenharmony_ci __le16 padding_data; 22962306a36Sopenharmony_ci __le16 padding_hdr; 23062306a36Sopenharmony_ci __le32 reserved; 23162306a36Sopenharmony_ci}; 23262306a36Sopenharmony_ci#define SMEM_PRIVATE_CANARY 0xa5a5 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci/** 23562306a36Sopenharmony_ci * struct smem_info - smem region info located after the table of contents 23662306a36Sopenharmony_ci * @magic: magic number, must be SMEM_INFO_MAGIC 23762306a36Sopenharmony_ci * @size: size of the smem region 23862306a36Sopenharmony_ci * @base_addr: base address of the smem region 23962306a36Sopenharmony_ci * @reserved: for now reserved entry 24062306a36Sopenharmony_ci * @num_items: highest accepted item number 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_cistruct smem_info { 24362306a36Sopenharmony_ci u8 magic[4]; 24462306a36Sopenharmony_ci __le32 size; 24562306a36Sopenharmony_ci __le32 base_addr; 24662306a36Sopenharmony_ci __le32 reserved; 24762306a36Sopenharmony_ci __le16 num_items; 24862306a36Sopenharmony_ci}; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic const u8 SMEM_INFO_MAGIC[] = { 0x53, 0x49, 0x49, 0x49 }; /* SIII */ 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci/** 25362306a36Sopenharmony_ci * struct smem_region - representation of a chunk of memory used for smem 25462306a36Sopenharmony_ci * @aux_base: identifier of aux_mem base 25562306a36Sopenharmony_ci * @virt_base: virtual base address of memory with this aux_mem identifier 25662306a36Sopenharmony_ci * @size: size of the memory region 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_cistruct smem_region { 25962306a36Sopenharmony_ci phys_addr_t aux_base; 26062306a36Sopenharmony_ci void __iomem *virt_base; 26162306a36Sopenharmony_ci size_t size; 26262306a36Sopenharmony_ci}; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci/** 26562306a36Sopenharmony_ci * struct qcom_smem - device data for the smem device 26662306a36Sopenharmony_ci * @dev: device pointer 26762306a36Sopenharmony_ci * @hwlock: reference to a hwspinlock 26862306a36Sopenharmony_ci * @ptable: virtual base of partition table 26962306a36Sopenharmony_ci * @global_partition: describes for global partition when in use 27062306a36Sopenharmony_ci * @partitions: list of partitions of current processor/host 27162306a36Sopenharmony_ci * @item_count: max accepted item number 27262306a36Sopenharmony_ci * @socinfo: platform device pointer 27362306a36Sopenharmony_ci * @num_regions: number of @regions 27462306a36Sopenharmony_ci * @regions: list of the memory regions defining the shared memory 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_cistruct qcom_smem { 27762306a36Sopenharmony_ci struct device *dev; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci struct hwspinlock *hwlock; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci u32 item_count; 28262306a36Sopenharmony_ci struct platform_device *socinfo; 28362306a36Sopenharmony_ci struct smem_ptable *ptable; 28462306a36Sopenharmony_ci struct smem_partition global_partition; 28562306a36Sopenharmony_ci struct smem_partition partitions[SMEM_HOST_COUNT]; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci unsigned num_regions; 28862306a36Sopenharmony_ci struct smem_region regions[]; 28962306a36Sopenharmony_ci}; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic void * 29262306a36Sopenharmony_ciphdr_to_last_uncached_entry(struct smem_partition_header *phdr) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci void *p = phdr; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci return p + le32_to_cpu(phdr->offset_free_uncached); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic struct smem_private_entry * 30062306a36Sopenharmony_ciphdr_to_first_cached_entry(struct smem_partition_header *phdr, 30162306a36Sopenharmony_ci size_t cacheline) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci void *p = phdr; 30462306a36Sopenharmony_ci struct smem_private_entry *e; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return p + le32_to_cpu(phdr->size) - ALIGN(sizeof(*e), cacheline); 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic void * 31062306a36Sopenharmony_ciphdr_to_last_cached_entry(struct smem_partition_header *phdr) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci void *p = phdr; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return p + le32_to_cpu(phdr->offset_free_cached); 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic struct smem_private_entry * 31862306a36Sopenharmony_ciphdr_to_first_uncached_entry(struct smem_partition_header *phdr) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci void *p = phdr; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return p + sizeof(*phdr); 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic struct smem_private_entry * 32662306a36Sopenharmony_ciuncached_entry_next(struct smem_private_entry *e) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci void *p = e; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci return p + sizeof(*e) + le16_to_cpu(e->padding_hdr) + 33162306a36Sopenharmony_ci le32_to_cpu(e->size); 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic struct smem_private_entry * 33562306a36Sopenharmony_cicached_entry_next(struct smem_private_entry *e, size_t cacheline) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci void *p = e; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci return p - le32_to_cpu(e->size) - ALIGN(sizeof(*e), cacheline); 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic void *uncached_entry_to_item(struct smem_private_entry *e) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci void *p = e; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci return p + sizeof(*e) + le16_to_cpu(e->padding_hdr); 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic void *cached_entry_to_item(struct smem_private_entry *e) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci void *p = e; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return p - le32_to_cpu(e->size); 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci/* Pointer to the one and only smem handle */ 35762306a36Sopenharmony_cistatic struct qcom_smem *__smem; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci/* Timeout (ms) for the trylock of remote spinlocks */ 36062306a36Sopenharmony_ci#define HWSPINLOCK_TIMEOUT 1000 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci/** 36362306a36Sopenharmony_ci * qcom_smem_is_available() - Check if SMEM is available 36462306a36Sopenharmony_ci * 36562306a36Sopenharmony_ci * Return: true if SMEM is available, false otherwise. 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_cibool qcom_smem_is_available(void) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci return !!__smem; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ciEXPORT_SYMBOL(qcom_smem_is_available); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic int qcom_smem_alloc_private(struct qcom_smem *smem, 37462306a36Sopenharmony_ci struct smem_partition *part, 37562306a36Sopenharmony_ci unsigned item, 37662306a36Sopenharmony_ci size_t size) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci struct smem_private_entry *hdr, *end; 37962306a36Sopenharmony_ci struct smem_partition_header *phdr; 38062306a36Sopenharmony_ci size_t alloc_size; 38162306a36Sopenharmony_ci void *cached; 38262306a36Sopenharmony_ci void *p_end; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci phdr = (struct smem_partition_header __force *)part->virt_base; 38562306a36Sopenharmony_ci p_end = (void *)phdr + part->size; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci hdr = phdr_to_first_uncached_entry(phdr); 38862306a36Sopenharmony_ci end = phdr_to_last_uncached_entry(phdr); 38962306a36Sopenharmony_ci cached = phdr_to_last_cached_entry(phdr); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (WARN_ON((void *)end > p_end || cached > p_end)) 39262306a36Sopenharmony_ci return -EINVAL; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci while (hdr < end) { 39562306a36Sopenharmony_ci if (hdr->canary != SMEM_PRIVATE_CANARY) 39662306a36Sopenharmony_ci goto bad_canary; 39762306a36Sopenharmony_ci if (le16_to_cpu(hdr->item) == item) 39862306a36Sopenharmony_ci return -EEXIST; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci hdr = uncached_entry_next(hdr); 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (WARN_ON((void *)hdr > p_end)) 40462306a36Sopenharmony_ci return -EINVAL; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* Check that we don't grow into the cached region */ 40762306a36Sopenharmony_ci alloc_size = sizeof(*hdr) + ALIGN(size, 8); 40862306a36Sopenharmony_ci if ((void *)hdr + alloc_size > cached) { 40962306a36Sopenharmony_ci dev_err(smem->dev, "Out of memory\n"); 41062306a36Sopenharmony_ci return -ENOSPC; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci hdr->canary = SMEM_PRIVATE_CANARY; 41462306a36Sopenharmony_ci hdr->item = cpu_to_le16(item); 41562306a36Sopenharmony_ci hdr->size = cpu_to_le32(ALIGN(size, 8)); 41662306a36Sopenharmony_ci hdr->padding_data = cpu_to_le16(le32_to_cpu(hdr->size) - size); 41762306a36Sopenharmony_ci hdr->padding_hdr = 0; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* 42062306a36Sopenharmony_ci * Ensure the header is written before we advance the free offset, so 42162306a36Sopenharmony_ci * that remote processors that does not take the remote spinlock still 42262306a36Sopenharmony_ci * gets a consistent view of the linked list. 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_ci wmb(); 42562306a36Sopenharmony_ci le32_add_cpu(&phdr->offset_free_uncached, alloc_size); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci return 0; 42862306a36Sopenharmony_cibad_canary: 42962306a36Sopenharmony_ci dev_err(smem->dev, "Found invalid canary in hosts %hu:%hu partition\n", 43062306a36Sopenharmony_ci le16_to_cpu(phdr->host0), le16_to_cpu(phdr->host1)); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return -EINVAL; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic int qcom_smem_alloc_global(struct qcom_smem *smem, 43662306a36Sopenharmony_ci unsigned item, 43762306a36Sopenharmony_ci size_t size) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci struct smem_global_entry *entry; 44062306a36Sopenharmony_ci struct smem_header *header; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci header = smem->regions[0].virt_base; 44362306a36Sopenharmony_ci entry = &header->toc[item]; 44462306a36Sopenharmony_ci if (entry->allocated) 44562306a36Sopenharmony_ci return -EEXIST; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci size = ALIGN(size, 8); 44862306a36Sopenharmony_ci if (WARN_ON(size > le32_to_cpu(header->available))) 44962306a36Sopenharmony_ci return -ENOMEM; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci entry->offset = header->free_offset; 45262306a36Sopenharmony_ci entry->size = cpu_to_le32(size); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci /* 45562306a36Sopenharmony_ci * Ensure the header is consistent before we mark the item allocated, 45662306a36Sopenharmony_ci * so that remote processors will get a consistent view of the item 45762306a36Sopenharmony_ci * even though they do not take the spinlock on read. 45862306a36Sopenharmony_ci */ 45962306a36Sopenharmony_ci wmb(); 46062306a36Sopenharmony_ci entry->allocated = cpu_to_le32(1); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci le32_add_cpu(&header->free_offset, size); 46362306a36Sopenharmony_ci le32_add_cpu(&header->available, -size); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci return 0; 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci/** 46962306a36Sopenharmony_ci * qcom_smem_alloc() - allocate space for a smem item 47062306a36Sopenharmony_ci * @host: remote processor id, or -1 47162306a36Sopenharmony_ci * @item: smem item handle 47262306a36Sopenharmony_ci * @size: number of bytes to be allocated 47362306a36Sopenharmony_ci * 47462306a36Sopenharmony_ci * Allocate space for a given smem item of size @size, given that the item is 47562306a36Sopenharmony_ci * not yet allocated. 47662306a36Sopenharmony_ci */ 47762306a36Sopenharmony_ciint qcom_smem_alloc(unsigned host, unsigned item, size_t size) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct smem_partition *part; 48062306a36Sopenharmony_ci unsigned long flags; 48162306a36Sopenharmony_ci int ret; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (!__smem) 48462306a36Sopenharmony_ci return -EPROBE_DEFER; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (item < SMEM_ITEM_LAST_FIXED) { 48762306a36Sopenharmony_ci dev_err(__smem->dev, 48862306a36Sopenharmony_ci "Rejecting allocation of static entry %d\n", item); 48962306a36Sopenharmony_ci return -EINVAL; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if (WARN_ON(item >= __smem->item_count)) 49362306a36Sopenharmony_ci return -EINVAL; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci ret = hwspin_lock_timeout_irqsave(__smem->hwlock, 49662306a36Sopenharmony_ci HWSPINLOCK_TIMEOUT, 49762306a36Sopenharmony_ci &flags); 49862306a36Sopenharmony_ci if (ret) 49962306a36Sopenharmony_ci return ret; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) { 50262306a36Sopenharmony_ci part = &__smem->partitions[host]; 50362306a36Sopenharmony_ci ret = qcom_smem_alloc_private(__smem, part, item, size); 50462306a36Sopenharmony_ci } else if (__smem->global_partition.virt_base) { 50562306a36Sopenharmony_ci part = &__smem->global_partition; 50662306a36Sopenharmony_ci ret = qcom_smem_alloc_private(__smem, part, item, size); 50762306a36Sopenharmony_ci } else { 50862306a36Sopenharmony_ci ret = qcom_smem_alloc_global(__smem, item, size); 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci hwspin_unlock_irqrestore(__smem->hwlock, &flags); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci return ret; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_smem_alloc); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic void *qcom_smem_get_global(struct qcom_smem *smem, 51862306a36Sopenharmony_ci unsigned item, 51962306a36Sopenharmony_ci size_t *size) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct smem_header *header; 52262306a36Sopenharmony_ci struct smem_region *region; 52362306a36Sopenharmony_ci struct smem_global_entry *entry; 52462306a36Sopenharmony_ci u64 entry_offset; 52562306a36Sopenharmony_ci u32 e_size; 52662306a36Sopenharmony_ci u32 aux_base; 52762306a36Sopenharmony_ci unsigned i; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci header = smem->regions[0].virt_base; 53062306a36Sopenharmony_ci entry = &header->toc[item]; 53162306a36Sopenharmony_ci if (!entry->allocated) 53262306a36Sopenharmony_ci return ERR_PTR(-ENXIO); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci aux_base = le32_to_cpu(entry->aux_base) & AUX_BASE_MASK; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci for (i = 0; i < smem->num_regions; i++) { 53762306a36Sopenharmony_ci region = &smem->regions[i]; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if ((u32)region->aux_base == aux_base || !aux_base) { 54062306a36Sopenharmony_ci e_size = le32_to_cpu(entry->size); 54162306a36Sopenharmony_ci entry_offset = le32_to_cpu(entry->offset); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (WARN_ON(e_size + entry_offset > region->size)) 54462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (size != NULL) 54762306a36Sopenharmony_ci *size = e_size; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci return region->virt_base + entry_offset; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic void *qcom_smem_get_private(struct qcom_smem *smem, 55762306a36Sopenharmony_ci struct smem_partition *part, 55862306a36Sopenharmony_ci unsigned item, 55962306a36Sopenharmony_ci size_t *size) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci struct smem_private_entry *e, *end; 56262306a36Sopenharmony_ci struct smem_partition_header *phdr; 56362306a36Sopenharmony_ci void *item_ptr, *p_end; 56462306a36Sopenharmony_ci u32 padding_data; 56562306a36Sopenharmony_ci u32 e_size; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci phdr = (struct smem_partition_header __force *)part->virt_base; 56862306a36Sopenharmony_ci p_end = (void *)phdr + part->size; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci e = phdr_to_first_uncached_entry(phdr); 57162306a36Sopenharmony_ci end = phdr_to_last_uncached_entry(phdr); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci while (e < end) { 57462306a36Sopenharmony_ci if (e->canary != SMEM_PRIVATE_CANARY) 57562306a36Sopenharmony_ci goto invalid_canary; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (le16_to_cpu(e->item) == item) { 57862306a36Sopenharmony_ci if (size != NULL) { 57962306a36Sopenharmony_ci e_size = le32_to_cpu(e->size); 58062306a36Sopenharmony_ci padding_data = le16_to_cpu(e->padding_data); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (WARN_ON(e_size > part->size || padding_data > e_size)) 58362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci *size = e_size - padding_data; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci item_ptr = uncached_entry_to_item(e); 58962306a36Sopenharmony_ci if (WARN_ON(item_ptr > p_end)) 59062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci return item_ptr; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci e = uncached_entry_next(e); 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci if (WARN_ON((void *)e > p_end)) 59962306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci /* Item was not found in the uncached list, search the cached list */ 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci e = phdr_to_first_cached_entry(phdr, part->cacheline); 60462306a36Sopenharmony_ci end = phdr_to_last_cached_entry(phdr); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci if (WARN_ON((void *)e < (void *)phdr || (void *)end > p_end)) 60762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci while (e > end) { 61062306a36Sopenharmony_ci if (e->canary != SMEM_PRIVATE_CANARY) 61162306a36Sopenharmony_ci goto invalid_canary; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (le16_to_cpu(e->item) == item) { 61462306a36Sopenharmony_ci if (size != NULL) { 61562306a36Sopenharmony_ci e_size = le32_to_cpu(e->size); 61662306a36Sopenharmony_ci padding_data = le16_to_cpu(e->padding_data); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (WARN_ON(e_size > part->size || padding_data > e_size)) 61962306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci *size = e_size - padding_data; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci item_ptr = cached_entry_to_item(e); 62562306a36Sopenharmony_ci if (WARN_ON(item_ptr < (void *)phdr)) 62662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci return item_ptr; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci e = cached_entry_next(e, part->cacheline); 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (WARN_ON((void *)e < (void *)phdr)) 63562306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ciinvalid_canary: 64062306a36Sopenharmony_ci dev_err(smem->dev, "Found invalid canary in hosts %hu:%hu partition\n", 64162306a36Sopenharmony_ci le16_to_cpu(phdr->host0), le16_to_cpu(phdr->host1)); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci/** 64762306a36Sopenharmony_ci * qcom_smem_get() - resolve ptr of size of a smem item 64862306a36Sopenharmony_ci * @host: the remote processor, or -1 64962306a36Sopenharmony_ci * @item: smem item handle 65062306a36Sopenharmony_ci * @size: pointer to be filled out with size of the item 65162306a36Sopenharmony_ci * 65262306a36Sopenharmony_ci * Looks up smem item and returns pointer to it. Size of smem 65362306a36Sopenharmony_ci * item is returned in @size. 65462306a36Sopenharmony_ci */ 65562306a36Sopenharmony_civoid *qcom_smem_get(unsigned host, unsigned item, size_t *size) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci struct smem_partition *part; 65862306a36Sopenharmony_ci unsigned long flags; 65962306a36Sopenharmony_ci int ret; 66062306a36Sopenharmony_ci void *ptr = ERR_PTR(-EPROBE_DEFER); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (!__smem) 66362306a36Sopenharmony_ci return ptr; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if (WARN_ON(item >= __smem->item_count)) 66662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci ret = hwspin_lock_timeout_irqsave(__smem->hwlock, 66962306a36Sopenharmony_ci HWSPINLOCK_TIMEOUT, 67062306a36Sopenharmony_ci &flags); 67162306a36Sopenharmony_ci if (ret) 67262306a36Sopenharmony_ci return ERR_PTR(ret); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) { 67562306a36Sopenharmony_ci part = &__smem->partitions[host]; 67662306a36Sopenharmony_ci ptr = qcom_smem_get_private(__smem, part, item, size); 67762306a36Sopenharmony_ci } else if (__smem->global_partition.virt_base) { 67862306a36Sopenharmony_ci part = &__smem->global_partition; 67962306a36Sopenharmony_ci ptr = qcom_smem_get_private(__smem, part, item, size); 68062306a36Sopenharmony_ci } else { 68162306a36Sopenharmony_ci ptr = qcom_smem_get_global(__smem, item, size); 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci hwspin_unlock_irqrestore(__smem->hwlock, &flags); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci return ptr; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_smem_get); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci/** 69262306a36Sopenharmony_ci * qcom_smem_get_free_space() - retrieve amount of free space in a partition 69362306a36Sopenharmony_ci * @host: the remote processor identifying a partition, or -1 69462306a36Sopenharmony_ci * 69562306a36Sopenharmony_ci * To be used by smem clients as a quick way to determine if any new 69662306a36Sopenharmony_ci * allocations has been made. 69762306a36Sopenharmony_ci */ 69862306a36Sopenharmony_ciint qcom_smem_get_free_space(unsigned host) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci struct smem_partition *part; 70162306a36Sopenharmony_ci struct smem_partition_header *phdr; 70262306a36Sopenharmony_ci struct smem_header *header; 70362306a36Sopenharmony_ci unsigned ret; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci if (!__smem) 70662306a36Sopenharmony_ci return -EPROBE_DEFER; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) { 70962306a36Sopenharmony_ci part = &__smem->partitions[host]; 71062306a36Sopenharmony_ci phdr = part->virt_base; 71162306a36Sopenharmony_ci ret = le32_to_cpu(phdr->offset_free_cached) - 71262306a36Sopenharmony_ci le32_to_cpu(phdr->offset_free_uncached); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci if (ret > le32_to_cpu(part->size)) 71562306a36Sopenharmony_ci return -EINVAL; 71662306a36Sopenharmony_ci } else if (__smem->global_partition.virt_base) { 71762306a36Sopenharmony_ci part = &__smem->global_partition; 71862306a36Sopenharmony_ci phdr = part->virt_base; 71962306a36Sopenharmony_ci ret = le32_to_cpu(phdr->offset_free_cached) - 72062306a36Sopenharmony_ci le32_to_cpu(phdr->offset_free_uncached); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci if (ret > le32_to_cpu(part->size)) 72362306a36Sopenharmony_ci return -EINVAL; 72462306a36Sopenharmony_ci } else { 72562306a36Sopenharmony_ci header = __smem->regions[0].virt_base; 72662306a36Sopenharmony_ci ret = le32_to_cpu(header->available); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci if (ret > __smem->regions[0].size) 72962306a36Sopenharmony_ci return -EINVAL; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci return ret; 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_smem_get_free_space); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic bool addr_in_range(void __iomem *base, size_t size, void *addr) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci return base && ((void __iomem *)addr >= base && (void __iomem *)addr < base + size); 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci/** 74262306a36Sopenharmony_ci * qcom_smem_virt_to_phys() - return the physical address associated 74362306a36Sopenharmony_ci * with an smem item pointer (previously returned by qcom_smem_get() 74462306a36Sopenharmony_ci * @p: the virtual address to convert 74562306a36Sopenharmony_ci * 74662306a36Sopenharmony_ci * Returns 0 if the pointer provided is not within any smem region. 74762306a36Sopenharmony_ci */ 74862306a36Sopenharmony_ciphys_addr_t qcom_smem_virt_to_phys(void *p) 74962306a36Sopenharmony_ci{ 75062306a36Sopenharmony_ci struct smem_partition *part; 75162306a36Sopenharmony_ci struct smem_region *area; 75262306a36Sopenharmony_ci u64 offset; 75362306a36Sopenharmony_ci u32 i; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci for (i = 0; i < SMEM_HOST_COUNT; i++) { 75662306a36Sopenharmony_ci part = &__smem->partitions[i]; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci if (addr_in_range(part->virt_base, part->size, p)) { 75962306a36Sopenharmony_ci offset = p - part->virt_base; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci return (phys_addr_t)part->phys_base + offset; 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci part = &__smem->global_partition; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (addr_in_range(part->virt_base, part->size, p)) { 76862306a36Sopenharmony_ci offset = p - part->virt_base; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci return (phys_addr_t)part->phys_base + offset; 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci for (i = 0; i < __smem->num_regions; i++) { 77462306a36Sopenharmony_ci area = &__smem->regions[i]; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (addr_in_range(area->virt_base, area->size, p)) { 77762306a36Sopenharmony_ci offset = p - area->virt_base; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci return (phys_addr_t)area->aux_base + offset; 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci return 0; 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_smem_virt_to_phys); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci/** 78862306a36Sopenharmony_ci * qcom_smem_get_soc_id() - return the SoC ID 78962306a36Sopenharmony_ci * @id: On success, we return the SoC ID here. 79062306a36Sopenharmony_ci * 79162306a36Sopenharmony_ci * Look up SoC ID from HW/SW build ID and return it. 79262306a36Sopenharmony_ci * 79362306a36Sopenharmony_ci * Return: 0 on success, negative errno on failure. 79462306a36Sopenharmony_ci */ 79562306a36Sopenharmony_ciint qcom_smem_get_soc_id(u32 *id) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci struct socinfo *info; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, NULL); 80062306a36Sopenharmony_ci if (IS_ERR(info)) 80162306a36Sopenharmony_ci return PTR_ERR(info); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci *id = __le32_to_cpu(info->id); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci return 0; 80662306a36Sopenharmony_ci} 80762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_smem_get_soc_id); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_cistatic int qcom_smem_get_sbl_version(struct qcom_smem *smem) 81062306a36Sopenharmony_ci{ 81162306a36Sopenharmony_ci struct smem_header *header; 81262306a36Sopenharmony_ci __le32 *versions; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci header = smem->regions[0].virt_base; 81562306a36Sopenharmony_ci versions = header->version; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci return le32_to_cpu(versions[SMEM_MASTER_SBL_VERSION_INDEX]); 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_cistatic struct smem_ptable *qcom_smem_get_ptable(struct qcom_smem *smem) 82162306a36Sopenharmony_ci{ 82262306a36Sopenharmony_ci struct smem_ptable *ptable; 82362306a36Sopenharmony_ci u32 version; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci ptable = smem->ptable; 82662306a36Sopenharmony_ci if (memcmp(ptable->magic, SMEM_PTABLE_MAGIC, sizeof(ptable->magic))) 82762306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci version = le32_to_cpu(ptable->version); 83062306a36Sopenharmony_ci if (version != 1) { 83162306a36Sopenharmony_ci dev_err(smem->dev, 83262306a36Sopenharmony_ci "Unsupported partition header version %d\n", version); 83362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci return ptable; 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_cistatic u32 qcom_smem_get_item_count(struct qcom_smem *smem) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci struct smem_ptable *ptable; 84162306a36Sopenharmony_ci struct smem_info *info; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci ptable = qcom_smem_get_ptable(smem); 84462306a36Sopenharmony_ci if (IS_ERR_OR_NULL(ptable)) 84562306a36Sopenharmony_ci return SMEM_ITEM_COUNT; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci info = (struct smem_info *)&ptable->entry[ptable->num_entries]; 84862306a36Sopenharmony_ci if (memcmp(info->magic, SMEM_INFO_MAGIC, sizeof(info->magic))) 84962306a36Sopenharmony_ci return SMEM_ITEM_COUNT; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci return le16_to_cpu(info->num_items); 85262306a36Sopenharmony_ci} 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci/* 85562306a36Sopenharmony_ci * Validate the partition header for a partition whose partition 85662306a36Sopenharmony_ci * table entry is supplied. Returns a pointer to its header if 85762306a36Sopenharmony_ci * valid, or a null pointer otherwise. 85862306a36Sopenharmony_ci */ 85962306a36Sopenharmony_cistatic struct smem_partition_header * 86062306a36Sopenharmony_ciqcom_smem_partition_header(struct qcom_smem *smem, 86162306a36Sopenharmony_ci struct smem_ptable_entry *entry, u16 host0, u16 host1) 86262306a36Sopenharmony_ci{ 86362306a36Sopenharmony_ci struct smem_partition_header *header; 86462306a36Sopenharmony_ci u32 phys_addr; 86562306a36Sopenharmony_ci u32 size; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci phys_addr = smem->regions[0].aux_base + le32_to_cpu(entry->offset); 86862306a36Sopenharmony_ci header = devm_ioremap_wc(smem->dev, phys_addr, le32_to_cpu(entry->size)); 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci if (!header) 87162306a36Sopenharmony_ci return NULL; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci if (memcmp(header->magic, SMEM_PART_MAGIC, sizeof(header->magic))) { 87462306a36Sopenharmony_ci dev_err(smem->dev, "bad partition magic %4ph\n", header->magic); 87562306a36Sopenharmony_ci return NULL; 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci if (host0 != le16_to_cpu(header->host0)) { 87962306a36Sopenharmony_ci dev_err(smem->dev, "bad host0 (%hu != %hu)\n", 88062306a36Sopenharmony_ci host0, le16_to_cpu(header->host0)); 88162306a36Sopenharmony_ci return NULL; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci if (host1 != le16_to_cpu(header->host1)) { 88462306a36Sopenharmony_ci dev_err(smem->dev, "bad host1 (%hu != %hu)\n", 88562306a36Sopenharmony_ci host1, le16_to_cpu(header->host1)); 88662306a36Sopenharmony_ci return NULL; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci size = le32_to_cpu(header->size); 89062306a36Sopenharmony_ci if (size != le32_to_cpu(entry->size)) { 89162306a36Sopenharmony_ci dev_err(smem->dev, "bad partition size (%u != %u)\n", 89262306a36Sopenharmony_ci size, le32_to_cpu(entry->size)); 89362306a36Sopenharmony_ci return NULL; 89462306a36Sopenharmony_ci } 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci if (le32_to_cpu(header->offset_free_uncached) > size) { 89762306a36Sopenharmony_ci dev_err(smem->dev, "bad partition free uncached (%u > %u)\n", 89862306a36Sopenharmony_ci le32_to_cpu(header->offset_free_uncached), size); 89962306a36Sopenharmony_ci return NULL; 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci return header; 90362306a36Sopenharmony_ci} 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_cistatic int qcom_smem_set_global_partition(struct qcom_smem *smem) 90662306a36Sopenharmony_ci{ 90762306a36Sopenharmony_ci struct smem_partition_header *header; 90862306a36Sopenharmony_ci struct smem_ptable_entry *entry; 90962306a36Sopenharmony_ci struct smem_ptable *ptable; 91062306a36Sopenharmony_ci bool found = false; 91162306a36Sopenharmony_ci int i; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci if (smem->global_partition.virt_base) { 91462306a36Sopenharmony_ci dev_err(smem->dev, "Already found the global partition\n"); 91562306a36Sopenharmony_ci return -EINVAL; 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci ptable = qcom_smem_get_ptable(smem); 91962306a36Sopenharmony_ci if (IS_ERR(ptable)) 92062306a36Sopenharmony_ci return PTR_ERR(ptable); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) { 92362306a36Sopenharmony_ci entry = &ptable->entry[i]; 92462306a36Sopenharmony_ci if (!le32_to_cpu(entry->offset)) 92562306a36Sopenharmony_ci continue; 92662306a36Sopenharmony_ci if (!le32_to_cpu(entry->size)) 92762306a36Sopenharmony_ci continue; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci if (le16_to_cpu(entry->host0) != SMEM_GLOBAL_HOST) 93062306a36Sopenharmony_ci continue; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci if (le16_to_cpu(entry->host1) == SMEM_GLOBAL_HOST) { 93362306a36Sopenharmony_ci found = true; 93462306a36Sopenharmony_ci break; 93562306a36Sopenharmony_ci } 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci if (!found) { 93962306a36Sopenharmony_ci dev_err(smem->dev, "Missing entry for global partition\n"); 94062306a36Sopenharmony_ci return -EINVAL; 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci header = qcom_smem_partition_header(smem, entry, 94462306a36Sopenharmony_ci SMEM_GLOBAL_HOST, SMEM_GLOBAL_HOST); 94562306a36Sopenharmony_ci if (!header) 94662306a36Sopenharmony_ci return -EINVAL; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci smem->global_partition.virt_base = (void __iomem *)header; 94962306a36Sopenharmony_ci smem->global_partition.phys_base = smem->regions[0].aux_base + 95062306a36Sopenharmony_ci le32_to_cpu(entry->offset); 95162306a36Sopenharmony_ci smem->global_partition.size = le32_to_cpu(entry->size); 95262306a36Sopenharmony_ci smem->global_partition.cacheline = le32_to_cpu(entry->cacheline); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci return 0; 95562306a36Sopenharmony_ci} 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_cistatic int 95862306a36Sopenharmony_ciqcom_smem_enumerate_partitions(struct qcom_smem *smem, u16 local_host) 95962306a36Sopenharmony_ci{ 96062306a36Sopenharmony_ci struct smem_partition_header *header; 96162306a36Sopenharmony_ci struct smem_ptable_entry *entry; 96262306a36Sopenharmony_ci struct smem_ptable *ptable; 96362306a36Sopenharmony_ci u16 remote_host; 96462306a36Sopenharmony_ci u16 host0, host1; 96562306a36Sopenharmony_ci int i; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci ptable = qcom_smem_get_ptable(smem); 96862306a36Sopenharmony_ci if (IS_ERR(ptable)) 96962306a36Sopenharmony_ci return PTR_ERR(ptable); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) { 97262306a36Sopenharmony_ci entry = &ptable->entry[i]; 97362306a36Sopenharmony_ci if (!le32_to_cpu(entry->offset)) 97462306a36Sopenharmony_ci continue; 97562306a36Sopenharmony_ci if (!le32_to_cpu(entry->size)) 97662306a36Sopenharmony_ci continue; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci host0 = le16_to_cpu(entry->host0); 97962306a36Sopenharmony_ci host1 = le16_to_cpu(entry->host1); 98062306a36Sopenharmony_ci if (host0 == local_host) 98162306a36Sopenharmony_ci remote_host = host1; 98262306a36Sopenharmony_ci else if (host1 == local_host) 98362306a36Sopenharmony_ci remote_host = host0; 98462306a36Sopenharmony_ci else 98562306a36Sopenharmony_ci continue; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci if (remote_host >= SMEM_HOST_COUNT) { 98862306a36Sopenharmony_ci dev_err(smem->dev, "bad host %u\n", remote_host); 98962306a36Sopenharmony_ci return -EINVAL; 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci if (smem->partitions[remote_host].virt_base) { 99362306a36Sopenharmony_ci dev_err(smem->dev, "duplicate host %u\n", remote_host); 99462306a36Sopenharmony_ci return -EINVAL; 99562306a36Sopenharmony_ci } 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci header = qcom_smem_partition_header(smem, entry, host0, host1); 99862306a36Sopenharmony_ci if (!header) 99962306a36Sopenharmony_ci return -EINVAL; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci smem->partitions[remote_host].virt_base = (void __iomem *)header; 100262306a36Sopenharmony_ci smem->partitions[remote_host].phys_base = smem->regions[0].aux_base + 100362306a36Sopenharmony_ci le32_to_cpu(entry->offset); 100462306a36Sopenharmony_ci smem->partitions[remote_host].size = le32_to_cpu(entry->size); 100562306a36Sopenharmony_ci smem->partitions[remote_host].cacheline = le32_to_cpu(entry->cacheline); 100662306a36Sopenharmony_ci } 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci return 0; 100962306a36Sopenharmony_ci} 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_cistatic int qcom_smem_map_toc(struct qcom_smem *smem, struct smem_region *region) 101262306a36Sopenharmony_ci{ 101362306a36Sopenharmony_ci u32 ptable_start; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci /* map starting 4K for smem header */ 101662306a36Sopenharmony_ci region->virt_base = devm_ioremap_wc(smem->dev, region->aux_base, SZ_4K); 101762306a36Sopenharmony_ci ptable_start = region->aux_base + region->size - SZ_4K; 101862306a36Sopenharmony_ci /* map last 4k for toc */ 101962306a36Sopenharmony_ci smem->ptable = devm_ioremap_wc(smem->dev, ptable_start, SZ_4K); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci if (!region->virt_base || !smem->ptable) 102262306a36Sopenharmony_ci return -ENOMEM; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci return 0; 102562306a36Sopenharmony_ci} 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_cistatic int qcom_smem_map_global(struct qcom_smem *smem, u32 size) 102862306a36Sopenharmony_ci{ 102962306a36Sopenharmony_ci u32 phys_addr; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci phys_addr = smem->regions[0].aux_base; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci smem->regions[0].size = size; 103462306a36Sopenharmony_ci smem->regions[0].virt_base = devm_ioremap_wc(smem->dev, phys_addr, size); 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci if (!smem->regions[0].virt_base) 103762306a36Sopenharmony_ci return -ENOMEM; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci return 0; 104062306a36Sopenharmony_ci} 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_cistatic int qcom_smem_resolve_mem(struct qcom_smem *smem, const char *name, 104362306a36Sopenharmony_ci struct smem_region *region) 104462306a36Sopenharmony_ci{ 104562306a36Sopenharmony_ci struct device *dev = smem->dev; 104662306a36Sopenharmony_ci struct device_node *np; 104762306a36Sopenharmony_ci struct resource r; 104862306a36Sopenharmony_ci int ret; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci np = of_parse_phandle(dev->of_node, name, 0); 105162306a36Sopenharmony_ci if (!np) { 105262306a36Sopenharmony_ci dev_err(dev, "No %s specified\n", name); 105362306a36Sopenharmony_ci return -EINVAL; 105462306a36Sopenharmony_ci } 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci ret = of_address_to_resource(np, 0, &r); 105762306a36Sopenharmony_ci of_node_put(np); 105862306a36Sopenharmony_ci if (ret) 105962306a36Sopenharmony_ci return ret; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci region->aux_base = r.start; 106262306a36Sopenharmony_ci region->size = resource_size(&r); 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci return 0; 106562306a36Sopenharmony_ci} 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_cistatic int qcom_smem_probe(struct platform_device *pdev) 106862306a36Sopenharmony_ci{ 106962306a36Sopenharmony_ci struct smem_header *header; 107062306a36Sopenharmony_ci struct reserved_mem *rmem; 107162306a36Sopenharmony_ci struct qcom_smem *smem; 107262306a36Sopenharmony_ci unsigned long flags; 107362306a36Sopenharmony_ci int num_regions; 107462306a36Sopenharmony_ci int hwlock_id; 107562306a36Sopenharmony_ci u32 version; 107662306a36Sopenharmony_ci u32 size; 107762306a36Sopenharmony_ci int ret; 107862306a36Sopenharmony_ci int i; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci num_regions = 1; 108162306a36Sopenharmony_ci if (of_property_present(pdev->dev.of_node, "qcom,rpm-msg-ram")) 108262306a36Sopenharmony_ci num_regions++; 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci smem = devm_kzalloc(&pdev->dev, struct_size(smem, regions, num_regions), 108562306a36Sopenharmony_ci GFP_KERNEL); 108662306a36Sopenharmony_ci if (!smem) 108762306a36Sopenharmony_ci return -ENOMEM; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci smem->dev = &pdev->dev; 109062306a36Sopenharmony_ci smem->num_regions = num_regions; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci rmem = of_reserved_mem_lookup(pdev->dev.of_node); 109362306a36Sopenharmony_ci if (rmem) { 109462306a36Sopenharmony_ci smem->regions[0].aux_base = rmem->base; 109562306a36Sopenharmony_ci smem->regions[0].size = rmem->size; 109662306a36Sopenharmony_ci } else { 109762306a36Sopenharmony_ci /* 109862306a36Sopenharmony_ci * Fall back to the memory-region reference, if we're not a 109962306a36Sopenharmony_ci * reserved-memory node. 110062306a36Sopenharmony_ci */ 110162306a36Sopenharmony_ci ret = qcom_smem_resolve_mem(smem, "memory-region", &smem->regions[0]); 110262306a36Sopenharmony_ci if (ret) 110362306a36Sopenharmony_ci return ret; 110462306a36Sopenharmony_ci } 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci if (num_regions > 1) { 110762306a36Sopenharmony_ci ret = qcom_smem_resolve_mem(smem, "qcom,rpm-msg-ram", &smem->regions[1]); 110862306a36Sopenharmony_ci if (ret) 110962306a36Sopenharmony_ci return ret; 111062306a36Sopenharmony_ci } 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci ret = qcom_smem_map_toc(smem, &smem->regions[0]); 111462306a36Sopenharmony_ci if (ret) 111562306a36Sopenharmony_ci return ret; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci for (i = 1; i < num_regions; i++) { 111862306a36Sopenharmony_ci smem->regions[i].virt_base = devm_ioremap_wc(&pdev->dev, 111962306a36Sopenharmony_ci smem->regions[i].aux_base, 112062306a36Sopenharmony_ci smem->regions[i].size); 112162306a36Sopenharmony_ci if (!smem->regions[i].virt_base) { 112262306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to remap %pa\n", &smem->regions[i].aux_base); 112362306a36Sopenharmony_ci return -ENOMEM; 112462306a36Sopenharmony_ci } 112562306a36Sopenharmony_ci } 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci header = smem->regions[0].virt_base; 112862306a36Sopenharmony_ci if (le32_to_cpu(header->initialized) != 1 || 112962306a36Sopenharmony_ci le32_to_cpu(header->reserved)) { 113062306a36Sopenharmony_ci dev_err(&pdev->dev, "SMEM is not initialized by SBL\n"); 113162306a36Sopenharmony_ci return -EINVAL; 113262306a36Sopenharmony_ci } 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0); 113562306a36Sopenharmony_ci if (hwlock_id < 0) { 113662306a36Sopenharmony_ci if (hwlock_id != -EPROBE_DEFER) 113762306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to retrieve hwlock\n"); 113862306a36Sopenharmony_ci return hwlock_id; 113962306a36Sopenharmony_ci } 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci smem->hwlock = hwspin_lock_request_specific(hwlock_id); 114262306a36Sopenharmony_ci if (!smem->hwlock) 114362306a36Sopenharmony_ci return -ENXIO; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci ret = hwspin_lock_timeout_irqsave(smem->hwlock, HWSPINLOCK_TIMEOUT, &flags); 114662306a36Sopenharmony_ci if (ret) 114762306a36Sopenharmony_ci return ret; 114862306a36Sopenharmony_ci size = readl_relaxed(&header->available) + readl_relaxed(&header->free_offset); 114962306a36Sopenharmony_ci hwspin_unlock_irqrestore(smem->hwlock, &flags); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci version = qcom_smem_get_sbl_version(smem); 115262306a36Sopenharmony_ci /* 115362306a36Sopenharmony_ci * smem header mapping is required only in heap version scheme, so unmap 115462306a36Sopenharmony_ci * it here. It will be remapped in qcom_smem_map_global() when whole 115562306a36Sopenharmony_ci * partition is mapped again. 115662306a36Sopenharmony_ci */ 115762306a36Sopenharmony_ci devm_iounmap(smem->dev, smem->regions[0].virt_base); 115862306a36Sopenharmony_ci switch (version >> 16) { 115962306a36Sopenharmony_ci case SMEM_GLOBAL_PART_VERSION: 116062306a36Sopenharmony_ci ret = qcom_smem_set_global_partition(smem); 116162306a36Sopenharmony_ci if (ret < 0) 116262306a36Sopenharmony_ci return ret; 116362306a36Sopenharmony_ci smem->item_count = qcom_smem_get_item_count(smem); 116462306a36Sopenharmony_ci break; 116562306a36Sopenharmony_ci case SMEM_GLOBAL_HEAP_VERSION: 116662306a36Sopenharmony_ci qcom_smem_map_global(smem, size); 116762306a36Sopenharmony_ci smem->item_count = SMEM_ITEM_COUNT; 116862306a36Sopenharmony_ci break; 116962306a36Sopenharmony_ci default: 117062306a36Sopenharmony_ci dev_err(&pdev->dev, "Unsupported SMEM version 0x%x\n", version); 117162306a36Sopenharmony_ci return -EINVAL; 117262306a36Sopenharmony_ci } 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci BUILD_BUG_ON(SMEM_HOST_APPS >= SMEM_HOST_COUNT); 117562306a36Sopenharmony_ci ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS); 117662306a36Sopenharmony_ci if (ret < 0 && ret != -ENOENT) 117762306a36Sopenharmony_ci return ret; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci __smem = smem; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci smem->socinfo = platform_device_register_data(&pdev->dev, "qcom-socinfo", 118262306a36Sopenharmony_ci PLATFORM_DEVID_NONE, NULL, 118362306a36Sopenharmony_ci 0); 118462306a36Sopenharmony_ci if (IS_ERR(smem->socinfo)) 118562306a36Sopenharmony_ci dev_dbg(&pdev->dev, "failed to register socinfo device\n"); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci return 0; 118862306a36Sopenharmony_ci} 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_cistatic int qcom_smem_remove(struct platform_device *pdev) 119162306a36Sopenharmony_ci{ 119262306a36Sopenharmony_ci platform_device_unregister(__smem->socinfo); 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci hwspin_lock_free(__smem->hwlock); 119562306a36Sopenharmony_ci __smem = NULL; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci return 0; 119862306a36Sopenharmony_ci} 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_cistatic const struct of_device_id qcom_smem_of_match[] = { 120162306a36Sopenharmony_ci { .compatible = "qcom,smem" }, 120262306a36Sopenharmony_ci {} 120362306a36Sopenharmony_ci}; 120462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, qcom_smem_of_match); 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_cistatic struct platform_driver qcom_smem_driver = { 120762306a36Sopenharmony_ci .probe = qcom_smem_probe, 120862306a36Sopenharmony_ci .remove = qcom_smem_remove, 120962306a36Sopenharmony_ci .driver = { 121062306a36Sopenharmony_ci .name = "qcom-smem", 121162306a36Sopenharmony_ci .of_match_table = qcom_smem_of_match, 121262306a36Sopenharmony_ci .suppress_bind_attrs = true, 121362306a36Sopenharmony_ci }, 121462306a36Sopenharmony_ci}; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_cistatic int __init qcom_smem_init(void) 121762306a36Sopenharmony_ci{ 121862306a36Sopenharmony_ci return platform_driver_register(&qcom_smem_driver); 121962306a36Sopenharmony_ci} 122062306a36Sopenharmony_ciarch_initcall(qcom_smem_init); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_cistatic void __exit qcom_smem_exit(void) 122362306a36Sopenharmony_ci{ 122462306a36Sopenharmony_ci platform_driver_unregister(&qcom_smem_driver); 122562306a36Sopenharmony_ci} 122662306a36Sopenharmony_cimodule_exit(qcom_smem_exit) 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ciMODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>"); 122962306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm Shared Memory Manager"); 123062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1231