xref: /kernel/linux/linux-6.6/drivers/soc/qcom/smem.c (revision 62306a36)
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