* [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot
@ 2026-05-04 18:57 Casey Connolly
2026-05-04 18:57 ` [PATCH v2 01/15] Revert "dm: SMEM (Shared memory) uclass" Casey Connolly
` (15 more replies)
0 siblings, 16 replies; 51+ messages in thread
From: Casey Connolly @ 2026-05-04 18:57 UTC (permalink / raw)
To: u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Casey Connolly, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
Smem is currently implemented a uclass in U-Boot. This carries with it
some implicit limitations about how and when we can actually talk with
it, since its modelled as a device.
The SMEM uclass is qualcomm specific and has no other users, smem is
also largely useful for parsing the memory map and other tasks which we
have to do before DM is alive anyway.
Let's drop the old smem code and replace it with a straight port from
Linux, providing a familiar interface and enabling us to access it much
earlier in the boot process than would be possible with a standard
udevice.
Additionally, this will be easier to maintain going forwards since any
patches from Linux can be more easily ported over.
With this new smem framework in place, we can now use SMEM to parse the
memory map when it's not provided via some other mechanism and to read
the serial number of the board.
Implement this functionality and split the snapdragon memory parsing off
to its own file to try and organise things a bit better.
Since the memory map stored in SMEM doesn't already have some carveouts
let's also be correct about unmapping reserved memory regions, we
should eventually do this on all Qualcomm boards but it's particularly
important when we aren't chainloading from ABL.
This depends on a fix for unmapping memory regions: https://lore.kernel.org/u-boot/20260504175511.585797-1-casey.connolly@linaro.org/
---
Changes in v2:
- Ensure reserved regions are unmapped appropriately when using SMEM as
the source of truth for the memory map.
- Support SMEM ram layout v0/v1 for older platforms using a header from
Qualcomm.
- Set an environment variable (memmap_source) providing info about
where U-Boots memory map is from.
- Correctly handle serial_num missing on msm8916
- Link to v1: https://lore.kernel.org/r/20241124-b4-modernise-smem-v1-0-b7852c11b67c@linaro.org
To: u-boot@lists.denx.de
To: Sumit Garg <sumit.garg@kernel.org>
To: u-boot-qcom@groups.io
Cc: Tom Rini <trini@konsulko.com>
Cc: Simon Glass <sjg@chromium.org>
Cc: Casey Connolly <casey.connolly@linaro.org>
Cc: Peng Fan <peng.fan@nxp.com>
Cc: Marek Vasut <marek.vasut+renesas@mailbox.org>
Cc: Alice Guo <alice.guo@nxp.com>
Cc: Quentin Schulz <quentin.schulz@cherry.de>
Cc: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Cc: Neil Armstrong <neil.armstrong@linaro.org>
Cc: Mattijs Korpershoek <mkorpershoek@kernel.org>
Cc: Kuan-Wei Chiu <visitorckw@gmail.com>
Cc: Raymond Mao <raymond.mao@riscstar.com>
Cc: Stefan Roese <stefan.roese@mailbox.org>
Cc: Philip Molloy <philip.molloy@analog.com>
Cc: Jerome Forissier <jerome.forissier@arm.com>
Cc: Marek Vasut <marek.vasut@mailbox.org>
Cc: Varadarajan Narayanan <varadarajan.narayanan@oss.qualcomm.com>
Cc: Patrice Chotard <patrice.chotard@foss.st.com>
Cc: Aswin Murugan <aswin.murugan@oss.qualcomm.com>
Cc: Rasmus Villemoes <ravi@prevas.dk>
Cc: Heiko Schocher <hs@nabladev.com>
Cc: Michal Simek <michal.simek@amd.com>
Cc: Sughosh Ganu <sughosh.ganu@arm.com>
Cc: Antony Kurniawan Soemardi <linux@smankusors.com>
Cc: Luca Weiss <luca.weiss@fairphone.com>
Cc: Balaji Selvanathan <balaji.selvanathan@oss.qualcomm.com>
---
Casey Connolly (15):
Revert "dm: SMEM (Shared memory) uclass"
smem: drop drivers/smem
Revert "test: smem: add basic smem test"
Revert "drivers: smem: sandbox"
mach-snapdragon: fix reserved memory carveout
soc: qcom: import smem from Linux 6.11-rc2
soc: qcom: smem: adjust headers for U-Boot
soc: qcom: smem: adjust for U-Boot
soc: qcom: smem: get serial number from socinfo
soc: qcom: smem: stub functions
soc: qcom: smem: add build infra
mach-snapdragon: move memory parsing to its own file
mach-snapdragon: support parsing memory map from SMEM
mach-snapdragon: fetch serial# from SMEM
configs: add qcom_sm8650_defconfig and debug fragment
MAINTAINERS | 1 -
arch/arm/Kconfig | 3 +-
arch/arm/mach-snapdragon/Makefile | 2 +-
arch/arm/mach-snapdragon/board.c | 253 ++++-----
arch/arm/mach-snapdragon/dram.c | 239 ++++++++
arch/arm/mach-snapdragon/qcom-priv.h | 17 +
arch/arm/mach-snapdragon/rampart.h | 194 +++++++
arch/sandbox/dts/test.dts | 4 -
board/qualcomm/debug-sm8650.config | 5 +
configs/qcom_sm8650_defconfig | 16 +
configs/sandbox64_defconfig | 2 -
configs/sandbox_defconfig | 2 -
drivers/Kconfig | 2 -
drivers/Makefile | 1 -
drivers/smem/Kconfig | 25 -
drivers/smem/Makefile | 7 -
drivers/smem/sandbox_smem.c | 44 --
drivers/smem/smem-uclass.c | 46 --
drivers/soc/qcom/Kconfig | 8 +
drivers/soc/qcom/Makefile | 1 +
drivers/{smem/msm_smem.c => soc/qcom/smem.c} | 777 ++++++++++++++++-----------
include/dm/uclass-id.h | 1 -
include/smem.h | 90 ----
include/soc/qcom/smem.h | 36 ++
include/soc/qcom/socinfo.h | 111 ++++
test/dm/Makefile | 1 -
test/dm/smem.c | 26 -
27 files changed, 1186 insertions(+), 728 deletions(-)
---
base-commit: 2920b49fb117dd70b6219804bf0d298c96f184f2
// Casey (she/they)
^ permalink raw reply [flat|nested] 51+ messages in thread
* [PATCH v2 01/15] Revert "dm: SMEM (Shared memory) uclass"
2026-05-04 18:57 [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot Casey Connolly
@ 2026-05-04 18:57 ` Casey Connolly
2026-05-05 10:15 ` Sumit Garg
` (2 more replies)
2026-05-04 18:57 ` [PATCH v2 02/15] smem: drop drivers/smem Casey Connolly
` (14 subsequent siblings)
15 siblings, 3 replies; 51+ messages in thread
From: Casey Connolly @ 2026-05-04 18:57 UTC (permalink / raw)
To: u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Casey Connolly, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
SMEM is a highly Qualcomm specific interface, while having a dedicated
UCLASS for it offers a nice abstraction, for things like memory layout
parsing we need to use it before the driver model is available.
Therefore, it doesn't make sense to fit SMEM into the driver model.
Instead let's adopt a model closer to Linux, and parse SMEM really early
during boot (as soon as we have the FDT).
This reverts commit 7b384eccc785b596f68448b155cbda26df57fb23.
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
---
arch/arm/Kconfig | 1 -
drivers/Kconfig | 2 --
drivers/Makefile | 1 -
drivers/smem/Kconfig | 25 -------------
drivers/smem/Makefile | 7 ----
drivers/smem/smem-uclass.c | 46 ------------------------
include/dm/uclass-id.h | 1 -
include/smem.h | 90 ----------------------------------------------
8 files changed, 173 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index f624675eadf8..dadb18e0b8cd 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1141,9 +1141,8 @@ config ARCH_SNAPDRAGON
select POWER_DOMAIN
select GPIO_EXTRA_HEADER
select MSM_SMEM
select OF_CONTROL
- select SMEM
select SPMI
select BOARD_LATE_INIT
select OF_BOARD
select SAVE_PREV_BL_FDT_ADDR if !ENABLE_ARM_SOC_BOOT0_HOOK
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 47606ddc6c8b..aa293df37c57 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -123,10 +123,8 @@ source "drivers/scsi/Kconfig"
source "drivers/serial/Kconfig"
source "drivers/sm/Kconfig"
-source "drivers/smem/Kconfig"
-
source "drivers/sound/Kconfig"
source "drivers/soc/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 43d0ba332818..ca3e87ffb102 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -118,9 +118,8 @@ obj-y += pwm/
obj-y += reset/
obj-y += input/
obj-y += iommu/
# SOC specific infrastructure drivers.
-obj-y += smem/
obj-y += thermal/
obj-$(CONFIG_TEE) += tee/
obj-$(CONFIG_ARM_FFA_TRANSPORT) += firmware/arm-ffa/
obj-y += axi/
diff --git a/drivers/smem/Kconfig b/drivers/smem/Kconfig
deleted file mode 100644
index e5d7dcc81b17..000000000000
--- a/drivers/smem/Kconfig
+++ /dev/null
@@ -1,25 +0,0 @@
-menuconfig SMEM
- bool "SMEM (Shared Memory mamanger) support"
-
-if SMEM
-
-config SANDBOX_SMEM
- bool "Sandbox Shared Memory Manager (SMEM)"
- depends on SANDBOX && DM
- help
- enable SMEM support for sandbox. This is an emulation of a real SMEM
- manager.
- The sandbox driver allocates a shared memory from the heap and
- initialzies it on start.
-
-config MSM_SMEM
- bool "Qualcomm Shared Memory Manager (SMEM)"
- depends on DM
- depends on ARCH_SNAPDRAGON || ARCH_IPQ40XX
- select DEVRES
- help
- Enable support for the Qualcomm Shared Memory Manager.
- The driver provides an interface to items in a heap shared among all
- processors in a Qualcomm platform.
-
-endif # menu "SMEM Support"
diff --git a/drivers/smem/Makefile b/drivers/smem/Makefile
deleted file mode 100644
index af3e9b50883c..000000000000
--- a/drivers/smem/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0+
-#
-# Makefile for the U-Boot SMEM interface drivers
-
-obj-$(CONFIG_SANDBOX_SMEM) += sandbox_smem.o
-obj-$(CONFIG_SMEM) += smem-uclass.o
-obj-$(CONFIG_MSM_SMEM) += msm_smem.o
diff --git a/drivers/smem/smem-uclass.c b/drivers/smem/smem-uclass.c
deleted file mode 100644
index 4dea5cc4bf1c..000000000000
--- a/drivers/smem/smem-uclass.c
+++ /dev/null
@@ -1,46 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (c) 2018 Ramon Fried <ramon.fried@gmail.com>
- */
-
-#define LOG_CATEGORY UCLASS_SMEM
-
-#include <dm.h>
-#include <smem.h>
-
-int smem_alloc(struct udevice *dev, unsigned int host,
- unsigned int item, size_t size)
-{
- struct smem_ops *ops = smem_get_ops(dev);
-
- if (!ops->alloc)
- return -ENOSYS;
-
- return ops->alloc(host, item, size);
-}
-
-void *smem_get(struct udevice *dev, unsigned int host,
- unsigned int item, size_t *size)
-{
- struct smem_ops *ops = smem_get_ops(dev);
-
- if (!ops->get)
- return NULL;
-
- return ops->get(host, item, size);
-}
-
-int smem_get_free_space(struct udevice *dev, unsigned int host)
-{
- struct smem_ops *ops = smem_get_ops(dev);
-
- if (!ops->get_free_space)
- return -ENOSYS;
-
- return ops->get_free_space(host);
-}
-
-UCLASS_DRIVER(smem) = {
- .id = UCLASS_SMEM,
- .name = "smem",
-};
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 36b5d87c304f..fe0aae2720ca 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -133,9 +133,8 @@ enum uclass_id {
UCLASS_SCMI_BASE, /* Interface for SCMI Base protocol */
UCLASS_SCSI, /* SCSI device */
UCLASS_SERIAL, /* Serial UART */
UCLASS_SIMPLE_BUS, /* Bus with child devices */
- UCLASS_SMEM, /* Shared memory interface */
UCLASS_SOC, /* SOC Device */
UCLASS_SOUND, /* Playing simple sounds */
UCLASS_SPI, /* SPI bus */
UCLASS_SPI_FLASH, /* SPI flash */
diff --git a/include/smem.h b/include/smem.h
deleted file mode 100644
index b19c534ebc43..000000000000
--- a/include/smem.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * The shared memory system is an allocate-only heap structure that
- * consists of one of more memory areas that can be accessed by the processors
- * in the SoC.
- *
- * Allocation can be done globally for all processors or to an individual processor.
- * This is controlled by the @host parameter.
- *
- * Allocation and management of heap can be implemented in various ways,
- * The @item parameter should be used as an index/hash to the memory region.
- *
- * Copyright (c) 2018 Ramon Fried <ramon.fried@gmail.com>
- */
-
-#ifndef _smemh_
-#define _smemh_
-
-/* struct smem_ops: Operations for the SMEM uclass */
-struct smem_ops {
- /**
- * alloc() - allocate space for a smem item
- *
- * @host: remote processor id, or -1 for all processors.
- * @item: smem item handle
- * @size: number of bytes to be allocated
- * @return 0 if OK, -ve on error
- */
- int (*alloc)(unsigned int host,
- unsigned int item, size_t size);
-
- /**
- * get() - Resolve ptr of size of a smem item
- *
- * @host: the remote processor, of -1 for all processors.
- * @item: smem item handle
- * @size: pointer to be filled out with the size of the item
- * @return pointer on success, NULL on error
- */
- void *(*get)(unsigned int host,
- unsigned int item, size_t *size);
-
- /**
- * get_free_space() - Get free space in smem in bytes
- *
- * @host: the remote processor identifying a partition, or -1
- * for all processors.
- * @return free space, -ve on error
- */
- int (*get_free_space)(unsigned int host);
-};
-
-#define smem_get_ops(dev) ((struct smem_ops *)(dev)->driver->ops)
-
-/**
- * smem_alloc() - allocate space for a smem item
- * @host: remote processor id, or -1
- * @item: smem item handle
- * @size: number of bytes to be allocated
- * Return: 0 if OK, -ve on error
- *
- * Allocate space for a given smem item of size @size, given that the item is
- * not yet allocated.
- */
-int smem_alloc(struct udevice *dev, unsigned int host, unsigned int item, size_t size);
-
-/**
- * smem_get() - resolve ptr of size of a smem item
- * @host: the remote processor, or -1 for all processors.
- * @item: smem item handle
- * @size: pointer to be filled out with size of the item
- * Return: pointer on success, NULL on error
- *
- * Looks up smem item and returns pointer to it. Size of smem
- * item is returned in @size.
- */
-void *smem_get(struct udevice *dev, unsigned int host, unsigned int item, size_t *size);
-
-/**
- * smem_get_free_space() - retrieve amount of free space in a partition
- * @host: the remote processor identifying a partition, or -1
- * for all processors.
- * Return: size in bytes, -ve on error
- *
- * To be used by smem clients as a quick way to determine if any new
- * allocations has been made.
- */
-int smem_get_free_space(struct udevice *dev, unsigned int host);
-
-#endif /* _smem_h_ */
--
2.53.0
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [PATCH v2 02/15] smem: drop drivers/smem
2026-05-04 18:57 [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot Casey Connolly
2026-05-04 18:57 ` [PATCH v2 01/15] Revert "dm: SMEM (Shared memory) uclass" Casey Connolly
@ 2026-05-04 18:57 ` Casey Connolly
2026-05-18 14:34 ` Neil Armstrong
2026-05-04 18:57 ` [PATCH v2 03/15] Revert "test: smem: add basic smem test" Casey Connolly
` (13 subsequent siblings)
15 siblings, 1 reply; 51+ messages in thread
From: Casey Connolly @ 2026-05-04 18:57 UTC (permalink / raw)
To: u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Casey Connolly, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
Remove the old qcom SMEM driver port and the sandbox stub
implementation.
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
---
MAINTAINERS | 1 -
arch/arm/Kconfig | 1 -
drivers/smem/msm_smem.c | 958 --------------------------------------------
drivers/smem/sandbox_smem.c | 44 --
4 files changed, 1004 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index 0dcc7243124e..4d54a61327c7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -687,9 +687,8 @@ F: drivers/mmc/msm_sdhci.c
F: drivers/phy/msm8916-usbh-phy.c
F: drivers/phy/qcom/
F: drivers/serial/serial_msm.c
F: drivers/serial/serial_msm_geni.c
-F: drivers/smem/msm_smem.c
F: drivers/spmi/spmi-msm.c
F: drivers/usb/host/ehci-msm.c
N: qcom
N: snapdragon
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index dadb18e0b8cd..a8fcfe76296c 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1139,9 +1139,8 @@ config ARCH_SNAPDRAGON
select DM_SERIAL
select DM_RESET
select POWER_DOMAIN
select GPIO_EXTRA_HEADER
- select MSM_SMEM
select OF_CONTROL
select SPMI
select BOARD_LATE_INIT
select OF_BOARD
diff --git a/drivers/smem/msm_smem.c b/drivers/smem/msm_smem.c
deleted file mode 100644
index 7a50d5a5792c..000000000000
--- a/drivers/smem/msm_smem.c
+++ /dev/null
@@ -1,958 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (c) 2015, Sony Mobile Communications AB.
- * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
- * Copyright (c) 2018, Ramon Fried <ramon.fried@gmail.com>
- */
-
-#include <errno.h>
-#include <dm.h>
-#include <dm/device_compat.h>
-#include <dm/devres.h>
-#include <dm/of_access.h>
-#include <dm/of_addr.h>
-#include <asm/io.h>
-#include <linux/bug.h>
-#include <linux/err.h>
-#include <linux/ioport.h>
-#include <linux/io.h>
-#include <linux/sizes.h>
-#include <smem.h>
-
-/*
- * The Qualcomm shared memory system is an allocate-only heap structure that
- * consists of one of more memory areas that can be accessed by the processors
- * in the SoC.
- *
- * All systems contains a global heap, accessible by all processors in the SoC,
- * with a table of contents data structure (@smem_header) at the beginning of
- * the main shared memory block.
- *
- * The global header contains meta data for allocations as well as a fixed list
- * of 512 entries (@smem_global_entry) that can be initialized to reference
- * parts of the shared memory space.
- *
- *
- * In addition to this global heap, a set of "private" heaps can be set up at
- * boot time with access restrictions so that only certain processor pairs can
- * access the data.
- *
- * These partitions are referenced from an optional partition table
- * (@smem_ptable), that is found 4kB from the end of the main smem region. The
- * partition table entries (@smem_ptable_entry) lists the involved processors
- * (or hosts) and their location in the main shared memory region.
- *
- * Each partition starts with a header (@smem_partition_header) that identifies
- * the partition and holds properties for the two internal memory regions. The
- * two regions are cached and non-cached memory respectively. Each region
- * contain a link list of allocation headers (@smem_private_entry) followed by
- * their data.
- *
- * Items in the non-cached region are allocated from the start of the partition
- * while items in the cached region are allocated from the end. The free area
- * is hence the region between the cached and non-cached offsets. The header of
- * cached items comes after the data.
- *
- * Version 12 (SMEM_GLOBAL_PART_VERSION) changes the item alloc/get procedure
- * for the global heap. A new global partition is created from the global heap
- * region with partition type (SMEM_GLOBAL_HOST) and the max smem item count is
- * set by the bootloader.
- *
- */
-
-/*
- * The version member of the smem header contains an array of versions for the
- * various software components in the SoC. We verify that the boot loader
- * version is a valid version as a sanity check.
- */
-#define SMEM_MASTER_SBL_VERSION_INDEX 7
-#define SMEM_GLOBAL_HEAP_VERSION 11
-#define SMEM_GLOBAL_PART_VERSION 12
-
-/*
- * The first 8 items are only to be allocated by the boot loader while
- * initializing the heap.
- */
-#define SMEM_ITEM_LAST_FIXED 8
-
-/* Highest accepted item number, for both global and private heaps */
-#define SMEM_ITEM_COUNT 512
-
-/* Processor/host identifier for the application processor */
-#define SMEM_HOST_APPS 0
-
-/* Processor/host identifier for the global partition */
-#define SMEM_GLOBAL_HOST 0xfffe
-
-/* Max number of processors/hosts in a system */
-#define SMEM_HOST_COUNT 25
-
-/**
- * struct smem_proc_comm - proc_comm communication struct (legacy)
- * @command: current command to be executed
- * @status: status of the currently requested command
- * @params: parameters to the command
- */
-struct smem_proc_comm {
- __le32 command;
- __le32 status;
- __le32 params[2];
-};
-
-/**
- * struct smem_global_entry - entry to reference smem items on the heap
- * @allocated: boolean to indicate if this entry is used
- * @offset: offset to the allocated space
- * @size: size of the allocated space, 8 byte aligned
- * @aux_base: base address for the memory region used by this unit, or 0 for
- * the default region. bits 0,1 are reserved
- */
-struct smem_global_entry {
- __le32 allocated;
- __le32 offset;
- __le32 size;
- __le32 aux_base; /* bits 1:0 reserved */
-};
-#define AUX_BASE_MASK 0xfffffffc
-
-/**
- * struct smem_header - header found in beginning of primary smem region
- * @proc_comm: proc_comm communication interface (legacy)
- * @version: array of versions for the various subsystems
- * @initialized: boolean to indicate that smem is initialized
- * @free_offset: index of the first unallocated byte in smem
- * @available: number of bytes available for allocation
- * @reserved: reserved field, must be 0
- * toc: array of references to items
- */
-struct smem_header {
- struct smem_proc_comm proc_comm[4];
- __le32 version[32];
- __le32 initialized;
- __le32 free_offset;
- __le32 available;
- __le32 reserved;
- struct smem_global_entry toc[SMEM_ITEM_COUNT];
-};
-
-/**
- * struct smem_ptable_entry - one entry in the @smem_ptable list
- * @offset: offset, within the main shared memory region, of the partition
- * @size: size of the partition
- * @flags: flags for the partition (currently unused)
- * @host0: first processor/host with access to this partition
- * @host1: second processor/host with access to this partition
- * @cacheline: alignment for "cached" entries
- * @reserved: reserved entries for later use
- */
-struct smem_ptable_entry {
- __le32 offset;
- __le32 size;
- __le32 flags;
- __le16 host0;
- __le16 host1;
- __le32 cacheline;
- __le32 reserved[7];
-};
-
-/**
- * struct smem_ptable - partition table for the private partitions
- * @magic: magic number, must be SMEM_PTABLE_MAGIC
- * @version: version of the partition table
- * @num_entries: number of partitions in the table
- * @reserved: for now reserved entries
- * @entry: list of @smem_ptable_entry for the @num_entries partitions
- */
-struct smem_ptable {
- u8 magic[4];
- __le32 version;
- __le32 num_entries;
- __le32 reserved[5];
- struct smem_ptable_entry entry[];
-};
-
-static const u8 SMEM_PTABLE_MAGIC[] = { 0x24, 0x54, 0x4f, 0x43 }; /* "$TOC" */
-
-/**
- * struct smem_partition_header - header of the partitions
- * @magic: magic number, must be SMEM_PART_MAGIC
- * @host0: first processor/host with access to this partition
- * @host1: second processor/host with access to this partition
- * @size: size of the partition
- * @offset_free_uncached: offset to the first free byte of uncached memory in
- * this partition
- * @offset_free_cached: offset to the first free byte of cached memory in this
- * partition
- * @reserved: for now reserved entries
- */
-struct smem_partition_header {
- u8 magic[4];
- __le16 host0;
- __le16 host1;
- __le32 size;
- __le32 offset_free_uncached;
- __le32 offset_free_cached;
- __le32 reserved[3];
-};
-
-static const u8 SMEM_PART_MAGIC[] = { 0x24, 0x50, 0x52, 0x54 };
-
-/**
- * struct smem_private_entry - header of each item in the private partition
- * @canary: magic number, must be SMEM_PRIVATE_CANARY
- * @item: identifying number of the smem item
- * @size: size of the data, including padding bytes
- * @padding_data: number of bytes of padding of data
- * @padding_hdr: number of bytes of padding between the header and the data
- * @reserved: for now reserved entry
- */
-struct smem_private_entry {
- u16 canary; /* bytes are the same so no swapping needed */
- __le16 item;
- __le32 size; /* includes padding bytes */
- __le16 padding_data;
- __le16 padding_hdr;
- __le32 reserved;
-};
-#define SMEM_PRIVATE_CANARY 0xa5a5
-
-/**
- * struct smem_info - smem region info located after the table of contents
- * @magic: magic number, must be SMEM_INFO_MAGIC
- * @size: size of the smem region
- * @base_addr: base address of the smem region
- * @reserved: for now reserved entry
- * @num_items: highest accepted item number
- */
-struct smem_info {
- u8 magic[4];
- __le32 size;
- __le32 base_addr;
- __le32 reserved;
- __le16 num_items;
-};
-
-static const u8 SMEM_INFO_MAGIC[] = { 0x53, 0x49, 0x49, 0x49 }; /* SIII */
-
-/**
- * struct smem_region - representation of a chunk of memory used for smem
- * @aux_base: identifier of aux_mem base
- * @virt_base: virtual base address of memory with this aux_mem identifier
- * @size: size of the memory region
- */
-struct smem_region {
- u32 aux_base;
- void __iomem *virt_base;
- size_t size;
-};
-
-/**
- * struct qcom_smem - device data for the smem device
- * @dev: device pointer
- * @global_partition: pointer to global partition when in use
- * @global_cacheline: cacheline size for global partition
- * @partitions: list of pointers to partitions affecting the current
- * processor/host
- * @cacheline: list of cacheline sizes for each host
- * @item_count: max accepted item number
- * @num_regions: number of @regions
- * @regions: list of the memory regions defining the shared memory
- */
-struct qcom_smem {
- struct udevice *dev;
-
- struct smem_partition_header *global_partition;
- size_t global_cacheline;
- struct smem_partition_header *partitions[SMEM_HOST_COUNT];
- size_t cacheline[SMEM_HOST_COUNT];
- u32 item_count;
-
- unsigned int num_regions;
- struct smem_region regions[0];
-};
-
-static struct smem_private_entry *
-phdr_to_last_uncached_entry(struct smem_partition_header *phdr)
-{
- void *p = phdr;
-
- return p + le32_to_cpu(phdr->offset_free_uncached);
-}
-
-static void *phdr_to_first_cached_entry(struct smem_partition_header *phdr,
- size_t cacheline)
-{
- void *p = phdr;
-
- return p + le32_to_cpu(phdr->size) - ALIGN(sizeof(*phdr), cacheline);
-}
-
-static void *phdr_to_last_cached_entry(struct smem_partition_header *phdr)
-{
- void *p = phdr;
-
- return p + le32_to_cpu(phdr->offset_free_cached);
-}
-
-static struct smem_private_entry *
-phdr_to_first_uncached_entry(struct smem_partition_header *phdr)
-{
- void *p = phdr;
-
- return p + sizeof(*phdr);
-}
-
-static struct smem_private_entry *
-uncached_entry_next(struct smem_private_entry *e)
-{
- void *p = e;
-
- return p + sizeof(*e) + le16_to_cpu(e->padding_hdr) +
- le32_to_cpu(e->size);
-}
-
-static struct smem_private_entry *
-cached_entry_next(struct smem_private_entry *e, size_t cacheline)
-{
- void *p = e;
-
- return p - le32_to_cpu(e->size) - ALIGN(sizeof(*e), cacheline);
-}
-
-static void *uncached_entry_to_item(struct smem_private_entry *e)
-{
- void *p = e;
-
- return p + sizeof(*e) + le16_to_cpu(e->padding_hdr);
-}
-
-static void *cached_entry_to_item(struct smem_private_entry *e)
-{
- void *p = e;
-
- return p - le32_to_cpu(e->size);
-}
-
-/* Pointer to the one and only smem handle */
-static struct qcom_smem *__smem;
-
-static int qcom_smem_alloc_private(struct qcom_smem *smem,
- struct smem_partition_header *phdr,
- unsigned int item,
- size_t size)
-{
- struct smem_private_entry *hdr, *end;
- size_t alloc_size;
- void *cached;
-
- hdr = phdr_to_first_uncached_entry(phdr);
- end = phdr_to_last_uncached_entry(phdr);
- cached = phdr_to_last_cached_entry(phdr);
-
- while (hdr < end) {
- if (hdr->canary != SMEM_PRIVATE_CANARY) {
- dev_err(smem->dev,
- "Found invalid canary in hosts %d:%d partition\n",
- phdr->host0, phdr->host1);
- return -EINVAL;
- }
-
- if (le16_to_cpu(hdr->item) == item)
- return -EEXIST;
-
- hdr = uncached_entry_next(hdr);
- }
-
- /* Check that we don't grow into the cached region */
- alloc_size = sizeof(*hdr) + ALIGN(size, 8);
- if ((void *)hdr + alloc_size >= cached) {
- dev_err(smem->dev, "Out of memory\n");
- return -ENOSPC;
- }
-
- hdr->canary = SMEM_PRIVATE_CANARY;
- hdr->item = cpu_to_le16(item);
- hdr->size = cpu_to_le32(ALIGN(size, 8));
- hdr->padding_data = cpu_to_le16(le32_to_cpu(hdr->size) - size);
- hdr->padding_hdr = 0;
-
- /*
- * Ensure the header is written before we advance the free offset, so
- * that remote processors that does not take the remote spinlock still
- * gets a consistent view of the linked list.
- */
- dmb();
- le32_add_cpu(&phdr->offset_free_uncached, alloc_size);
-
- return 0;
-}
-
-static int qcom_smem_alloc_global(struct qcom_smem *smem,
- unsigned int item,
- size_t size)
-{
- struct smem_global_entry *entry;
- struct smem_header *header;
-
- header = smem->regions[0].virt_base;
- entry = &header->toc[item];
- if (entry->allocated)
- return -EEXIST;
-
- size = ALIGN(size, 8);
- if (WARN_ON(size > le32_to_cpu(header->available)))
- return -ENOMEM;
-
- entry->offset = header->free_offset;
- entry->size = cpu_to_le32(size);
-
- /*
- * Ensure the header is consistent before we mark the item allocated,
- * so that remote processors will get a consistent view of the item
- * even though they do not take the spinlock on read.
- */
- dmb();
- entry->allocated = cpu_to_le32(1);
-
- le32_add_cpu(&header->free_offset, size);
- le32_add_cpu(&header->available, -size);
-
- return 0;
-}
-
-/**
- * qcom_smem_alloc() - allocate space for a smem item
- * @host: remote processor id, or -1
- * @item: smem item handle
- * @size: number of bytes to be allocated
- *
- * Allocate space for a given smem item of size @size, given that the item is
- * not yet allocated.
- */
-static int qcom_smem_alloc(unsigned int host, unsigned int item, size_t size)
-{
- struct smem_partition_header *phdr;
- int ret;
-
- if (!__smem)
- return -ENOMEM;
-
- if (item < SMEM_ITEM_LAST_FIXED) {
- dev_err(__smem->dev,
- "Rejecting allocation of static entry %d\n", item);
- return -EINVAL;
- }
-
- if (WARN_ON(item >= __smem->item_count))
- return -EINVAL;
-
- if (host < SMEM_HOST_COUNT && __smem->partitions[host]) {
- phdr = __smem->partitions[host];
- ret = qcom_smem_alloc_private(__smem, phdr, item, size);
- } else if (__smem->global_partition) {
- phdr = __smem->global_partition;
- ret = qcom_smem_alloc_private(__smem, phdr, item, size);
- } else {
- ret = qcom_smem_alloc_global(__smem, item, size);
- }
-
- return ret;
-}
-
-static void *qcom_smem_get_global(struct qcom_smem *smem,
- unsigned int item,
- size_t *size)
-{
- struct smem_header *header;
- struct smem_region *area;
- struct smem_global_entry *entry;
- u32 aux_base;
- unsigned int i;
-
- header = smem->regions[0].virt_base;
- entry = &header->toc[item];
- if (!entry->allocated)
- return ERR_PTR(-ENXIO);
-
- aux_base = le32_to_cpu(entry->aux_base) & AUX_BASE_MASK;
-
- for (i = 0; i < smem->num_regions; i++) {
- area = &smem->regions[i];
-
- if (area->aux_base == aux_base || !aux_base) {
- if (size != NULL)
- *size = le32_to_cpu(entry->size);
- return area->virt_base + le32_to_cpu(entry->offset);
- }
- }
-
- return ERR_PTR(-ENOENT);
-}
-
-static void *qcom_smem_get_private(struct qcom_smem *smem,
- struct smem_partition_header *phdr,
- size_t cacheline,
- unsigned int item,
- size_t *size)
-{
- struct smem_private_entry *e, *end;
-
- e = phdr_to_first_uncached_entry(phdr);
- end = phdr_to_last_uncached_entry(phdr);
-
- while (e < end) {
- if (e->canary != SMEM_PRIVATE_CANARY)
- goto invalid_canary;
-
- if (le16_to_cpu(e->item) == item) {
- if (size != NULL)
- *size = le32_to_cpu(e->size) -
- le16_to_cpu(e->padding_data);
-
- return uncached_entry_to_item(e);
- }
-
- e = uncached_entry_next(e);
- }
-
- /* Item was not found in the uncached list, search the cached list */
-
- e = phdr_to_first_cached_entry(phdr, cacheline);
- end = phdr_to_last_cached_entry(phdr);
-
- while (e > end) {
- if (e->canary != SMEM_PRIVATE_CANARY)
- goto invalid_canary;
-
- if (le16_to_cpu(e->item) == item) {
- if (size != NULL)
- *size = le32_to_cpu(e->size) -
- le16_to_cpu(e->padding_data);
-
- return cached_entry_to_item(e);
- }
-
- e = cached_entry_next(e, cacheline);
- }
-
- return ERR_PTR(-ENOENT);
-
-invalid_canary:
- dev_err(smem->dev, "Found invalid canary in hosts %d:%d partition\n",
- phdr->host0, phdr->host1);
-
- return ERR_PTR(-EINVAL);
-}
-
-/**
- * qcom_smem_get() - resolve ptr of size of a smem item
- * @host: the remote processor, or -1
- * @item: smem item handle
- * @size: pointer to be filled out with size of the item
- *
- * Looks up smem item and returns pointer to it. Size of smem
- * item is returned in @size.
- */
-static void *qcom_smem_get(unsigned int host, unsigned int item, size_t *size)
-{
- struct smem_partition_header *phdr;
- size_t cacheln;
- void *ptr = ERR_PTR(-ENOMEM);
-
- if (!__smem)
- return ptr;
-
- if (WARN_ON(item >= __smem->item_count))
- return ERR_PTR(-EINVAL);
-
- if (host < SMEM_HOST_COUNT && __smem->partitions[host]) {
- phdr = __smem->partitions[host];
- cacheln = __smem->cacheline[host];
- ptr = qcom_smem_get_private(__smem, phdr, cacheln, item, size);
- } else if (__smem->global_partition) {
- phdr = __smem->global_partition;
- cacheln = __smem->global_cacheline;
- ptr = qcom_smem_get_private(__smem, phdr, cacheln, item, size);
- } else {
- ptr = qcom_smem_get_global(__smem, item, size);
- }
-
- return ptr;
-
-}
-
-/**
- * qcom_smem_get_free_space() - retrieve amount of free space in a partition
- * @host: the remote processor identifying a partition, or -1
- *
- * To be used by smem clients as a quick way to determine if any new
- * allocations has been made.
- */
-static int qcom_smem_get_free_space(unsigned int host)
-{
- struct smem_partition_header *phdr;
- struct smem_header *header;
- unsigned int ret;
-
- if (!__smem)
- return -ENOMEM;
-
- if (host < SMEM_HOST_COUNT && __smem->partitions[host]) {
- phdr = __smem->partitions[host];
- ret = le32_to_cpu(phdr->offset_free_cached) -
- le32_to_cpu(phdr->offset_free_uncached);
- } else if (__smem->global_partition) {
- phdr = __smem->global_partition;
- ret = le32_to_cpu(phdr->offset_free_cached) -
- le32_to_cpu(phdr->offset_free_uncached);
- } else {
- header = __smem->regions[0].virt_base;
- ret = le32_to_cpu(header->available);
- }
-
- return ret;
-}
-
-static int qcom_smem_get_sbl_version(struct qcom_smem *smem)
-{
- struct smem_header *header;
- __le32 *versions;
-
- header = smem->regions[0].virt_base;
- versions = header->version;
-
- return le32_to_cpu(versions[SMEM_MASTER_SBL_VERSION_INDEX]);
-}
-
-static struct smem_ptable *qcom_smem_get_ptable(struct qcom_smem *smem)
-{
- struct smem_ptable *ptable;
- u32 version;
-
- ptable = smem->regions[0].virt_base + smem->regions[0].size - SZ_4K;
- if (memcmp(ptable->magic, SMEM_PTABLE_MAGIC, sizeof(ptable->magic)))
- return ERR_PTR(-ENOENT);
-
- version = le32_to_cpu(ptable->version);
- if (version != 1) {
- dev_err(smem->dev,
- "Unsupported partition header version %d\n", version);
- return ERR_PTR(-EINVAL);
- }
- return ptable;
-}
-
-static u32 qcom_smem_get_item_count(struct qcom_smem *smem)
-{
- struct smem_ptable *ptable;
- struct smem_info *info;
-
- ptable = qcom_smem_get_ptable(smem);
- if (IS_ERR_OR_NULL(ptable))
- return SMEM_ITEM_COUNT;
-
- info = (struct smem_info *)&ptable->entry[ptable->num_entries];
- if (memcmp(info->magic, SMEM_INFO_MAGIC, sizeof(info->magic)))
- return SMEM_ITEM_COUNT;
-
- return le16_to_cpu(info->num_items);
-}
-
-static int qcom_smem_set_global_partition(struct qcom_smem *smem)
-{
- struct smem_partition_header *header;
- struct smem_ptable_entry *entry = NULL;
- struct smem_ptable *ptable;
- u32 host0, host1, size;
- int i;
-
- ptable = qcom_smem_get_ptable(smem);
- if (IS_ERR(ptable))
- return PTR_ERR(ptable);
-
- for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
- entry = &ptable->entry[i];
- host0 = le16_to_cpu(entry->host0);
- host1 = le16_to_cpu(entry->host1);
-
- if (host0 == SMEM_GLOBAL_HOST && host0 == host1)
- break;
- }
-
- if (!entry) {
- dev_err(smem->dev, "Missing entry for global partition\n");
- return -EINVAL;
- }
-
- if (!le32_to_cpu(entry->offset) || !le32_to_cpu(entry->size)) {
- dev_err(smem->dev, "Invalid entry for global partition\n");
- return -EINVAL;
- }
-
- if (smem->global_partition) {
- dev_err(smem->dev, "Already found the global partition\n");
- return -EINVAL;
- }
-
- header = smem->regions[0].virt_base + le32_to_cpu(entry->offset);
- host0 = le16_to_cpu(header->host0);
- host1 = le16_to_cpu(header->host1);
-
- if (memcmp(header->magic, SMEM_PART_MAGIC, sizeof(header->magic))) {
- dev_err(smem->dev, "Global partition has invalid magic\n");
- return -EINVAL;
- }
-
- if (host0 != SMEM_GLOBAL_HOST && host1 != SMEM_GLOBAL_HOST) {
- dev_err(smem->dev, "Global partition hosts are invalid\n");
- return -EINVAL;
- }
-
- if (le32_to_cpu(header->size) != le32_to_cpu(entry->size)) {
- dev_err(smem->dev, "Global partition has invalid size\n");
- return -EINVAL;
- }
-
- size = le32_to_cpu(header->offset_free_uncached);
- if (size > le32_to_cpu(header->size)) {
- dev_err(smem->dev,
- "Global partition has invalid free pointer\n");
- return -EINVAL;
- }
-
- smem->global_partition = header;
- smem->global_cacheline = le32_to_cpu(entry->cacheline);
-
- return 0;
-}
-
-static int qcom_smem_enumerate_partitions(struct qcom_smem *smem,
- unsigned int local_host)
-{
- struct smem_partition_header *header;
- struct smem_ptable_entry *entry;
- struct smem_ptable *ptable;
- unsigned int remote_host;
- u32 host0, host1;
- int i;
-
- ptable = qcom_smem_get_ptable(smem);
- if (IS_ERR(ptable))
- return PTR_ERR(ptable);
-
- for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
- entry = &ptable->entry[i];
- host0 = le16_to_cpu(entry->host0);
- host1 = le16_to_cpu(entry->host1);
-
- if (host0 != local_host && host1 != local_host)
- continue;
-
- if (!le32_to_cpu(entry->offset))
- continue;
-
- if (!le32_to_cpu(entry->size))
- continue;
-
- if (host0 == local_host)
- remote_host = host1;
- else
- remote_host = host0;
-
- if (remote_host >= SMEM_HOST_COUNT) {
- dev_err(smem->dev,
- "Invalid remote host %d\n",
- remote_host);
- return -EINVAL;
- }
-
- if (smem->partitions[remote_host]) {
- dev_err(smem->dev,
- "Already found a partition for host %d\n",
- remote_host);
- return -EINVAL;
- }
-
- header = smem->regions[0].virt_base + le32_to_cpu(entry->offset);
- host0 = le16_to_cpu(header->host0);
- host1 = le16_to_cpu(header->host1);
-
- if (memcmp(header->magic, SMEM_PART_MAGIC,
- sizeof(header->magic))) {
- dev_err(smem->dev,
- "Partition %d has invalid magic\n", i);
- return -EINVAL;
- }
-
- if (host0 != local_host && host1 != local_host) {
- dev_err(smem->dev,
- "Partition %d hosts are invalid\n", i);
- return -EINVAL;
- }
-
- if (host0 != remote_host && host1 != remote_host) {
- dev_err(smem->dev,
- "Partition %d hosts are invalid\n", i);
- return -EINVAL;
- }
-
- if (le32_to_cpu(header->size) != le32_to_cpu(entry->size)) {
- dev_err(smem->dev,
- "Partition %d has invalid size\n", i);
- return -EINVAL;
- }
-
- if (le32_to_cpu(header->offset_free_uncached) > le32_to_cpu(header->size)) {
- dev_err(smem->dev,
- "Partition %d has invalid free pointer\n", i);
- return -EINVAL;
- }
-
- smem->partitions[remote_host] = header;
- smem->cacheline[remote_host] = le32_to_cpu(entry->cacheline);
- }
-
- return 0;
-}
-
-static int qcom_smem_map_memory(struct qcom_smem *smem, struct udevice *dev,
- const char *name, int i)
-{
- int ret;
- struct ofnode_phandle_args args;
- struct resource r;
-
- if (!dev_read_prop(dev, name, NULL)) {
- dev_err(dev, "%s prop not found\n", name);
- return -EINVAL;
- }
-
- ret = dev_read_phandle_with_args(dev, name, NULL, 0, 0, &args);
- if (ret) {
- dev_err(dev, "%s phandle read failed\n", name);
- return -EINVAL;
- }
-
- if (!ofnode_valid(args.node)) {
- dev_err(dev, "Invalid node from phandle args\n");
- return -EINVAL;
- }
-
- ret = ofnode_read_resource(args.node, 0, &r);
- if (ret) {
- dev_err(dev, "Can't get mmap base address(%d)\n", ret);
- return ret;
- }
- smem->regions[i].aux_base = (u32)r.start;
- smem->regions[i].size = resource_size(&r);
- smem->regions[i].virt_base = devm_ioremap(dev, r.start, resource_size(&r));
- if (!smem->regions[i].virt_base)
- return -ENOMEM;
-
- return 0;
-}
-
-static int qcom_smem_probe(struct udevice *dev)
-{
- struct smem_header *header;
- struct qcom_smem *smem;
- size_t array_size;
- int num_regions;
- u32 version;
- int ret;
- fdt_addr_t addr;
- fdt_size_t size;
-
- if (__smem)
- return 0;
-
- num_regions = 1;
- if (dev_read_prop(dev, "qcom,rpm-msg-ram", NULL))
- num_regions++;
-
- array_size = num_regions * sizeof(struct smem_region);
- smem = devm_kzalloc(dev, sizeof(*smem) + array_size, GFP_KERNEL);
- if (!smem)
- return -ENOMEM;
-
- smem->dev = dev;
- smem->num_regions = num_regions;
-
- addr = dev_read_addr_size(dev, &size);
- if (addr == FDT_ADDR_T_NONE) {
- ret = qcom_smem_map_memory(smem, dev, "memory-region", 0);
- if (ret)
- return ret;
- } else {
- smem->regions[0].aux_base = (u32)addr;
- smem->regions[0].size = size;
- smem->regions[0].virt_base = devm_ioremap(dev, addr, size);
- if (!smem->regions[0].virt_base)
- return -ENOMEM;
- }
-
- if (num_regions > 1) {
- ret = qcom_smem_map_memory(smem, dev,
- "qcom,rpm-msg-ram", 1);
- if (ret)
- return ret;
- }
-
- header = smem->regions[0].virt_base;
- if (le32_to_cpu(header->initialized) != 1 ||
- le32_to_cpu(header->reserved)) {
- dev_err(dev, "SMEM is not initialized by SBL\n");
- return -EINVAL;
- }
-
- version = qcom_smem_get_sbl_version(smem);
- switch (version >> 16) {
- case SMEM_GLOBAL_PART_VERSION:
- ret = qcom_smem_set_global_partition(smem);
- if (ret < 0)
- return ret;
- smem->item_count = qcom_smem_get_item_count(smem);
- break;
- case SMEM_GLOBAL_HEAP_VERSION:
- smem->item_count = SMEM_ITEM_COUNT;
- break;
- default:
- dev_err(dev, "Unsupported SMEM version 0x%x\n", version);
- return -EINVAL;
- }
-
- ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS);
- if (ret < 0 && ret != -ENOENT)
- return ret;
-
- __smem = smem;
-
- return 0;
-}
-
-static int qcom_smem_remove(struct udevice *dev)
-{
- __smem = NULL;
-
- return 0;
-}
-
-const struct udevice_id qcom_smem_of_match[] = {
- { .compatible = "qcom,smem" },
- { }
-};
-
-static const struct smem_ops msm_smem_ops = {
- .alloc = qcom_smem_alloc,
- .get = qcom_smem_get,
- .get_free_space = qcom_smem_get_free_space,
-};
-
-U_BOOT_DRIVER(qcom_smem) = {
- .name = "qcom_smem",
- .id = UCLASS_SMEM,
- .of_match = qcom_smem_of_match,
- .ops = &msm_smem_ops,
- .probe = qcom_smem_probe,
- .remove = qcom_smem_remove,
-};
diff --git a/drivers/smem/sandbox_smem.c b/drivers/smem/sandbox_smem.c
deleted file mode 100644
index fec98e5611d6..000000000000
--- a/drivers/smem/sandbox_smem.c
+++ /dev/null
@@ -1,44 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (c) 2018 Ramon Fried <ramon.fried@gmail.com>
- */
-
-#include <dm.h>
-#include <errno.h>
-#include <smem.h>
-#include <asm/test.h>
-
-static int sandbox_smem_alloc(unsigned int host,
- unsigned int item, size_t size)
-{
- return 0;
-}
-
-static void *sandbox_smem_get(unsigned int host,
- unsigned int item, size_t *size)
-{
- return NULL;
-}
-
-static int sandbox_smem_get_free_space(unsigned int host)
-{
- return 0;
-}
-
-static const struct smem_ops sandbox_smem_ops = {
- .alloc = sandbox_smem_alloc,
- .get = sandbox_smem_get,
- .get_free_space = sandbox_smem_get_free_space,
-};
-
-static const struct udevice_id sandbox_smem_ids[] = {
- { .compatible = "sandbox,smem" },
- { }
-};
-
-U_BOOT_DRIVER(smem_sandbox) = {
- .name = "smem_sandbox",
- .id = UCLASS_SMEM,
- .of_match = sandbox_smem_ids,
- .ops = &sandbox_smem_ops,
-};
--
2.53.0
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [PATCH v2 03/15] Revert "test: smem: add basic smem test"
2026-05-04 18:57 [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot Casey Connolly
2026-05-04 18:57 ` [PATCH v2 01/15] Revert "dm: SMEM (Shared memory) uclass" Casey Connolly
2026-05-04 18:57 ` [PATCH v2 02/15] smem: drop drivers/smem Casey Connolly
@ 2026-05-04 18:57 ` Casey Connolly
2026-05-05 10:26 ` Sumit Garg
2026-05-18 14:34 ` Neil Armstrong
2026-05-04 18:57 ` [PATCH v2 04/15] Revert "drivers: smem: sandbox" Casey Connolly
` (12 subsequent siblings)
15 siblings, 2 replies; 51+ messages in thread
From: Casey Connolly @ 2026-05-04 18:57 UTC (permalink / raw)
To: u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Casey Connolly, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
This reverts commit 20e7705764c4e5ea924f1ea54bb36ebbbeffffe7.
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
---
test/dm/Makefile | 1 -
test/dm/smem.c | 26 --------------------------
2 files changed, 27 deletions(-)
diff --git a/test/dm/Makefile b/test/dm/Makefile
index 771b703b737d..ace149d4a2b4 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -111,9 +111,8 @@ obj-$(CONFIG_SCSI) += scsi.o
obj-$(CONFIG_DM_SERIAL) += serial.o
obj-$(CONFIG_DM_SPI_FLASH) += sf.o
obj-$(CONFIG_SIMPLE_BUS) += simple-bus.o
obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o
-obj-$(CONFIG_SMEM) += smem.o
obj-$(CONFIG_SOC_DEVICE) += soc.o
obj-$(CONFIG_SOUND) += sound.o
obj-$(CONFIG_DM_SPI) += spi.o
obj-$(CONFIG_SPMI) += spmi.o
diff --git a/test/dm/smem.c b/test/dm/smem.c
deleted file mode 100644
index 89e74cccc574..000000000000
--- a/test/dm/smem.c
+++ /dev/null
@@ -1,26 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2018 Ramon Fried <ramon.fried@gmail.com>
- */
-
-#include <dm.h>
-#include <smem.h>
-#include <dm/test.h>
-#include <test/test.h>
-#include <test/ut.h>
-
-/* Basic test of the smem uclass */
-static int dm_test_smem_base(struct unit_test_state *uts)
-{
- struct udevice *dev;
- size_t size;
-
- ut_assertok(uclass_get_device(UCLASS_SMEM, 0, &dev));
- ut_assertnonnull(dev);
- ut_assertok(smem_alloc(dev, -1, 0, 16));
- ut_asserteq(0, smem_get_free_space(dev, -1));
- ut_assertnull(smem_get(dev, -1, 0, &size));
-
- return 0;
-}
-DM_TEST(dm_test_smem_base, UTF_SCAN_PDATA | UTF_SCAN_FDT);
--
2.53.0
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [PATCH v2 04/15] Revert "drivers: smem: sandbox"
2026-05-04 18:57 [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot Casey Connolly
` (2 preceding siblings ...)
2026-05-04 18:57 ` [PATCH v2 03/15] Revert "test: smem: add basic smem test" Casey Connolly
@ 2026-05-04 18:57 ` Casey Connolly
2026-05-05 10:26 ` Sumit Garg
` (2 more replies)
2026-05-04 18:57 ` [PATCH v2 05/15] mach-snapdragon: fix reserved memory carveout Casey Connolly
` (11 subsequent siblings)
15 siblings, 3 replies; 51+ messages in thread
From: Casey Connolly @ 2026-05-04 18:57 UTC (permalink / raw)
To: u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Casey Connolly, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
This reverts commit 7fd7e2cf339ea2ec570f6dae1cdfaf8e066eb4af.
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
---
arch/sandbox/dts/test.dts | 4 ----
configs/sandbox64_defconfig | 2 --
configs/sandbox_defconfig | 2 --
3 files changed, 8 deletions(-)
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index ac92ebf1afd7..50201ad61ccd 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -1521,12 +1521,8 @@
compatible = "sandbox,scsi";
sandbox,filepath = "scsi.img";
};
- smem@0 {
- compatible = "sandbox,smem";
- };
-
sound {
compatible = "sandbox,sound";
cpu {
sound-dai = <&i2s 0>;
diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig
index 60a30d048750..53f7af4270af 100644
--- a/configs/sandbox64_defconfig
+++ b/configs/sandbox64_defconfig
@@ -240,10 +240,8 @@ CONFIG_SANDBOX_RESET=y
CONFIG_DM_RTC=y
CONFIG_RTC_RV8803=y
CONFIG_SCSI=y
CONFIG_SANDBOX_SERIAL=y
-CONFIG_SMEM=y
-CONFIG_SANDBOX_SMEM=y
CONFIG_SOUND=y
CONFIG_SOUND_SANDBOX=y
CONFIG_SOC_DEVICE=y
CONFIG_SANDBOX_SPI=y
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index f26295103f1c..7f26405e6ed0 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -331,10 +331,8 @@ CONFIG_RTC_RV8803=y
CONFIG_RTC_HT1380=y
CONFIG_SCSI=y
CONFIG_SANDBOX_SERIAL=y
CONFIG_SANDBOX_SM=y
-CONFIG_SMEM=y
-CONFIG_SANDBOX_SMEM=y
CONFIG_SOUND=y
CONFIG_SOUND_DA7219=y
CONFIG_SOUND_MAX98357A=y
CONFIG_SOUND_SANDBOX=y
--
2.53.0
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [PATCH v2 05/15] mach-snapdragon: fix reserved memory carveout
2026-05-04 18:57 [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot Casey Connolly
` (3 preceding siblings ...)
2026-05-04 18:57 ` [PATCH v2 04/15] Revert "drivers: smem: sandbox" Casey Connolly
@ 2026-05-04 18:57 ` Casey Connolly
2026-05-05 12:25 ` Sumit Garg
2026-05-04 18:57 ` [PATCH v2 06/15] soc: qcom: import smem from Linux 6.11-rc2 Casey Connolly
` (10 subsequent siblings)
15 siblings, 1 reply; 51+ messages in thread
From: Casey Connolly @ 2026-05-04 18:57 UTC (permalink / raw)
To: u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Casey Connolly, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
The memory carveout logic was fairly limited and had a few issues,
rework it and teach it not to unmap regions that have a compatible
property (since they may be used in U-Boot) or that don't have the
no-map property.
The carveout process adds ~100ms to the boot time depending on the
platform.
This prepares us for using SMEM as a source of truth and improving
support for U-boot as a first stage bootloader since SMEMs memory map
doesn't already carve out some regions like ABL does.
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
---
arch/arm/mach-snapdragon/board.c | 86 +++++++++++++++++++++++++---------------
1 file changed, 53 insertions(+), 33 deletions(-)
diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c
index 829a0109ac78..e12d3d00caa4 100644
--- a/arch/arm/mach-snapdragon/board.c
+++ b/arch/arm/mach-snapdragon/board.c
@@ -622,27 +622,36 @@ u64 get_page_table_size(void)
{
return SZ_1M;
}
+struct mem_resource_attrs {
+ fdt_addr_t start;
+ fdt_addr_t size;
+ u64 attrs;
+};
+
static int fdt_cmp_res(const void *v1, const void *v2)
{
- const struct fdt_resource *res1 = v1, *res2 = v2;
+ const struct mem_resource_attrs *res1 = v1, *res2 = v2;
return res1->start - res2->start;
}
-#define N_RESERVED_REGIONS 32
+#define N_RESERVED_REGIONS 64
-/* Mark all no-map regions as PTE_TYPE_FAULT to prevent speculative access.
+/* Map and unmap reserved memory regions as appropriate.
+ * Mark all no-map regions as PTE_TYPE_FAULT to prevent speculative access.
* On some platforms this is enough to trigger a security violation and trap
* to EL3.
+ * Regions that may be accessed by drivers get mapped explicitly.
*/
-static void carve_out_reserved_memory(void)
+static void configure_reserved_memory(void)
{
- static struct fdt_resource res[N_RESERVED_REGIONS] = { 0 };
+ static struct mem_resource_attrs res[N_RESERVED_REGIONS] = { 0 };
int parent, rmem, count, i = 0;
phys_addr_t start;
size_t size;
+ u64 attrs;
/* Some reserved nodes must be carved out, as the cache-prefetcher may otherwise
* attempt to access them, causing a security exception.
*/
@@ -651,14 +660,19 @@ static void carve_out_reserved_memory(void)
log_err("No reserved memory regions found\n");
return;
}
- /* Collect the reserved memory regions */
+ /* Collect the reserved memory regions and appropriate attrs */
fdt_for_each_subnode(rmem, gd->fdt_blob, parent) {
const fdt32_t *ptr;
- int len;
+ attrs = PTE_TYPE_FAULT;
+ /* If the no-map property isn't set then the region is valid */
if (!fdt_getprop(gd->fdt_blob, rmem, "no-map", NULL))
- continue;
+ attrs = PTE_TYPE_VALID | PTE_BLOCK_MEMTYPE(MT_NORMAL);
+ /* If the compatible property is set then this region may be accessed by drivers and should
+ * be marked valid too. */
+ if (fdt_getprop(gd->fdt_blob, rmem, "compatible", NULL))
+ attrs = PTE_TYPE_VALID | PTE_BLOCK_MEMTYPE(MT_NORMAL);
if (i == N_RESERVED_REGIONS) {
log_err("Too many reserved regions!\n");
break;
@@ -667,50 +681,55 @@ static void carve_out_reserved_memory(void)
/* Read the address and size out from the reg property. Doing this "properly" with
* fdt_get_resource() takes ~70ms on SDM845, but open-coding the happy path here
* takes <1ms... Oh the woes of no dcache.
*/
- ptr = fdt_getprop(gd->fdt_blob, rmem, "reg", &len);
+ ptr = fdt_getprop(gd->fdt_blob, rmem, "reg", NULL);
if (ptr) {
/* Qualcomm devices use #address/size-cells = <2> but all reserved regions are within
* the 32-bit address space. So we can cheat here for speed.
*/
res[i].start = fdt32_to_cpu(ptr[1]);
- res[i].end = res[i].start + fdt32_to_cpu(ptr[3]);
+ res[i].size = fdt32_to_cpu(ptr[3]);
+ res[i].attrs = attrs;
i++;
}
}
/* Sort the reserved memory regions by address */
count = i;
- qsort(res, count, sizeof(struct fdt_resource), fdt_cmp_res);
+ qsort(res, count, sizeof(res[0]), fdt_cmp_res);
+ debug("Mapping %d regions!\n", count);
/* Now set the right attributes for them. Often a lot of the regions are tightly packed together
- * so we can optimise the number of calls to mmu_change_region_attr() by combining adjacent
+ * so we can optimise the number of calls to mmu_change_region_attr_nobreak() by combining adjacent
* regions.
*/
- start = ALIGN_DOWN(res[0].start, SZ_2M);
- size = ALIGN(res[0].end - start, SZ_2M);
+ start = res[0].start;
+ size = res[0].size;
+ attrs = res[0].attrs;
+ /* For each region after the first one, either increase the `size` to eventually be mapped or
+ * map the region we have and start a new one, this allows us to reduce the number of calls to
+ * mmu_map_region(). The loop is therefore "lagging" behind by one iteration. */
for (i = 1; i <= count; i++) {
- /* We ideally want to 2M align everything for more efficient pagetables, but we must avoid
- * overwriting reserved memory regions which shouldn't be mapped as FAULT (like those with
- * compatible properties).
- * If within 2M of the previous region, bump the size to include this region. Otherwise
- * start a new region.
- */
- if (i == count || start + size < res[i].start - SZ_2M) {
- debug(" 0x%016llx - 0x%016llx: reserved\n",
- start, start + size);
- mmu_change_region_attr(start, size, PTE_TYPE_FAULT);
- /* If this is the final region then quit here before we index
- * out of bounds...
- */
+ /* If i == count we are done, just map the last region. If the last region is
+ * too far away or the attrs don't match then map the meta-region we have and
+ * start a new one. */
+ if (i == count || start + size < res[i].start - SZ_8K || attrs != res[i].attrs) {
+ debug(" 0x%016llx - 0x%016llx: %s\n",
+ start, start + size, attrs == PTE_TYPE_FAULT ? "FAULT" : "VALID");
+ /* No need to break-before-make since dcache is disabled */
+ mmu_change_region_attr_nobreak(start, size, attrs);
+ /* We have now mapped all the regions */
if (i == count)
break;
- start = ALIGN_DOWN(res[i].start, SZ_2M);
- size = ALIGN(res[i].end - start, SZ_2M);
+ /* Start a new meta-region */
+ start = res[i].start;
+ size = res[i].size;
+ attrs = res[i].attrs;
} else {
- /* Bump size if this region is immediately after the previous one */
- size = ALIGN(res[i].end - start, SZ_2M);
+ /* This region is next to (<8K) the previous one so combine them.
+ * Accounting for any small (<8K) gap. */
+ size = (res[i].start - start) + res[i].size;
}
}
}
@@ -744,13 +763,14 @@ void enable_caches(void)
gd->arch.tlb_emerg = gd->arch.tlb_addr;
gd->arch.tlb_addr = tlb_addr;
gd->arch.tlb_size = tlb_size;
- /* We do the carveouts only for QCS404, for now. */
+ /* On some boards speculative access may trigger a NOC or XPU violation so explicitly mark reserved
+ * regions as inacessible (PTE_TYPE_FAULT) */
if (fdt_node_check_compatible(gd->fdt_blob, 0, "qcom,qcs404") == 0) {
carveout_start = get_timer(0);
/* Takes ~20-50ms on SDM845 */
- carve_out_reserved_memory();
+ configure_reserved_memory();
debug("carveout time: %lums\n", get_timer(carveout_start));
}
dcache_enable();
}
--
2.53.0
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [PATCH v2 06/15] soc: qcom: import smem from Linux 6.11-rc2
2026-05-04 18:57 [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot Casey Connolly
` (4 preceding siblings ...)
2026-05-04 18:57 ` [PATCH v2 05/15] mach-snapdragon: fix reserved memory carveout Casey Connolly
@ 2026-05-04 18:57 ` Casey Connolly
2026-05-05 12:40 ` Sumit Garg
2026-05-18 14:36 ` Neil Armstrong
2026-05-04 18:57 ` [PATCH v2 07/15] soc: qcom: smem: adjust headers for U-Boot Casey Connolly
` (9 subsequent siblings)
15 siblings, 2 replies; 51+ messages in thread
From: Casey Connolly @ 2026-05-04 18:57 UTC (permalink / raw)
To: u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Casey Connolly, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
Import the SMEM driver from Linux.
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
---
drivers/soc/qcom/smem.c | 1279 ++++++++++++++++++++++++++++++++++++++++++++
include/soc/qcom/smem.h | 20 +
include/soc/qcom/socinfo.h | 111 ++++
3 files changed, 1410 insertions(+)
diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
new file mode 100644
index 000000000000..8515b8ae7777
--- /dev/null
+++ b/drivers/soc/qcom/smem.c
@@ -0,0 +1,1279 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2015, Sony Mobile Communications AB.
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/hwspinlock.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/soc/qcom/smem.h>
+#include <linux/soc/qcom/socinfo.h>
+
+/*
+ * The Qualcomm shared memory system is a allocate only heap structure that
+ * consists of one of more memory areas that can be accessed by the processors
+ * in the SoC.
+ *
+ * All systems contains a global heap, accessible by all processors in the SoC,
+ * with a table of contents data structure (@smem_header) at the beginning of
+ * the main shared memory block.
+ *
+ * The global header contains meta data for allocations as well as a fixed list
+ * of 512 entries (@smem_global_entry) that can be initialized to reference
+ * parts of the shared memory space.
+ *
+ *
+ * In addition to this global heap a set of "private" heaps can be set up at
+ * boot time with access restrictions so that only certain processor pairs can
+ * access the data.
+ *
+ * These partitions are referenced from an optional partition table
+ * (@smem_ptable), that is found 4kB from the end of the main smem region. The
+ * partition table entries (@smem_ptable_entry) lists the involved processors
+ * (or hosts) and their location in the main shared memory region.
+ *
+ * Each partition starts with a header (@smem_partition_header) that identifies
+ * the partition and holds properties for the two internal memory regions. The
+ * two regions are cached and non-cached memory respectively. Each region
+ * contain a link list of allocation headers (@smem_private_entry) followed by
+ * their data.
+ *
+ * Items in the non-cached region are allocated from the start of the partition
+ * while items in the cached region are allocated from the end. The free area
+ * is hence the region between the cached and non-cached offsets. The header of
+ * cached items comes after the data.
+ *
+ * Version 12 (SMEM_GLOBAL_PART_VERSION) changes the item alloc/get procedure
+ * for the global heap. A new global partition is created from the global heap
+ * region with partition type (SMEM_GLOBAL_HOST) and the max smem item count is
+ * set by the bootloader.
+ *
+ * To synchronize allocations in the shared memory heaps a remote spinlock must
+ * be held - currently lock number 3 of the sfpb or tcsr is used for this on all
+ * platforms.
+ *
+ */
+
+/*
+ * The version member of the smem header contains an array of versions for the
+ * various software components in the SoC. We verify that the boot loader
+ * version is a valid version as a sanity check.
+ */
+#define SMEM_MASTER_SBL_VERSION_INDEX 7
+#define SMEM_GLOBAL_HEAP_VERSION 11
+#define SMEM_GLOBAL_PART_VERSION 12
+
+/*
+ * The first 8 items are only to be allocated by the boot loader while
+ * initializing the heap.
+ */
+#define SMEM_ITEM_LAST_FIXED 8
+
+/* Highest accepted item number, for both global and private heaps */
+#define SMEM_ITEM_COUNT 512
+
+/* Processor/host identifier for the application processor */
+#define SMEM_HOST_APPS 0
+
+/* Processor/host identifier for the global partition */
+#define SMEM_GLOBAL_HOST 0xfffe
+
+/* Max number of processors/hosts in a system */
+#define SMEM_HOST_COUNT 20
+
+/**
+ * struct smem_proc_comm - proc_comm communication struct (legacy)
+ * @command: current command to be executed
+ * @status: status of the currently requested command
+ * @params: parameters to the command
+ */
+struct smem_proc_comm {
+ __le32 command;
+ __le32 status;
+ __le32 params[2];
+};
+
+/**
+ * struct smem_global_entry - entry to reference smem items on the heap
+ * @allocated: boolean to indicate if this entry is used
+ * @offset: offset to the allocated space
+ * @size: size of the allocated space, 8 byte aligned
+ * @aux_base: base address for the memory region used by this unit, or 0 for
+ * the default region. bits 0,1 are reserved
+ */
+struct smem_global_entry {
+ __le32 allocated;
+ __le32 offset;
+ __le32 size;
+ __le32 aux_base; /* bits 1:0 reserved */
+};
+#define AUX_BASE_MASK 0xfffffffc
+
+/**
+ * struct smem_header - header found in beginning of primary smem region
+ * @proc_comm: proc_comm communication interface (legacy)
+ * @version: array of versions for the various subsystems
+ * @initialized: boolean to indicate that smem is initialized
+ * @free_offset: index of the first unallocated byte in smem
+ * @available: number of bytes available for allocation
+ * @reserved: reserved field, must be 0
+ * @toc: array of references to items
+ */
+struct smem_header {
+ struct smem_proc_comm proc_comm[4];
+ __le32 version[32];
+ __le32 initialized;
+ __le32 free_offset;
+ __le32 available;
+ __le32 reserved;
+ struct smem_global_entry toc[SMEM_ITEM_COUNT];
+};
+
+/**
+ * struct smem_ptable_entry - one entry in the @smem_ptable list
+ * @offset: offset, within the main shared memory region, of the partition
+ * @size: size of the partition
+ * @flags: flags for the partition (currently unused)
+ * @host0: first processor/host with access to this partition
+ * @host1: second processor/host with access to this partition
+ * @cacheline: alignment for "cached" entries
+ * @reserved: reserved entries for later use
+ */
+struct smem_ptable_entry {
+ __le32 offset;
+ __le32 size;
+ __le32 flags;
+ __le16 host0;
+ __le16 host1;
+ __le32 cacheline;
+ __le32 reserved[7];
+};
+
+/**
+ * struct smem_ptable - partition table for the private partitions
+ * @magic: magic number, must be SMEM_PTABLE_MAGIC
+ * @version: version of the partition table
+ * @num_entries: number of partitions in the table
+ * @reserved: for now reserved entries
+ * @entry: list of @smem_ptable_entry for the @num_entries partitions
+ */
+struct smem_ptable {
+ u8 magic[4];
+ __le32 version;
+ __le32 num_entries;
+ __le32 reserved[5];
+ struct smem_ptable_entry entry[];
+};
+
+static const u8 SMEM_PTABLE_MAGIC[] = { 0x24, 0x54, 0x4f, 0x43 }; /* "$TOC" */
+
+/**
+ * struct smem_partition_header - header of the partitions
+ * @magic: magic number, must be SMEM_PART_MAGIC
+ * @host0: first processor/host with access to this partition
+ * @host1: second processor/host with access to this partition
+ * @size: size of the partition
+ * @offset_free_uncached: offset to the first free byte of uncached memory in
+ * this partition
+ * @offset_free_cached: offset to the first free byte of cached memory in this
+ * partition
+ * @reserved: for now reserved entries
+ */
+struct smem_partition_header {
+ u8 magic[4];
+ __le16 host0;
+ __le16 host1;
+ __le32 size;
+ __le32 offset_free_uncached;
+ __le32 offset_free_cached;
+ __le32 reserved[3];
+};
+
+/**
+ * struct smem_partition - describes smem partition
+ * @virt_base: starting virtual address of partition
+ * @phys_base: starting physical address of partition
+ * @cacheline: alignment for "cached" entries
+ * @size: size of partition
+ */
+struct smem_partition {
+ void __iomem *virt_base;
+ phys_addr_t phys_base;
+ size_t cacheline;
+ size_t size;
+};
+
+static const u8 SMEM_PART_MAGIC[] = { 0x24, 0x50, 0x52, 0x54 };
+
+/**
+ * struct smem_private_entry - header of each item in the private partition
+ * @canary: magic number, must be SMEM_PRIVATE_CANARY
+ * @item: identifying number of the smem item
+ * @size: size of the data, including padding bytes
+ * @padding_data: number of bytes of padding of data
+ * @padding_hdr: number of bytes of padding between the header and the data
+ * @reserved: for now reserved entry
+ */
+struct smem_private_entry {
+ u16 canary; /* bytes are the same so no swapping needed */
+ __le16 item;
+ __le32 size; /* includes padding bytes */
+ __le16 padding_data;
+ __le16 padding_hdr;
+ __le32 reserved;
+};
+#define SMEM_PRIVATE_CANARY 0xa5a5
+
+/**
+ * struct smem_info - smem region info located after the table of contents
+ * @magic: magic number, must be SMEM_INFO_MAGIC
+ * @size: size of the smem region
+ * @base_addr: base address of the smem region
+ * @reserved: for now reserved entry
+ * @num_items: highest accepted item number
+ */
+struct smem_info {
+ u8 magic[4];
+ __le32 size;
+ __le32 base_addr;
+ __le32 reserved;
+ __le16 num_items;
+};
+
+static const u8 SMEM_INFO_MAGIC[] = { 0x53, 0x49, 0x49, 0x49 }; /* SIII */
+
+/**
+ * struct smem_region - representation of a chunk of memory used for smem
+ * @aux_base: identifier of aux_mem base
+ * @virt_base: virtual base address of memory with this aux_mem identifier
+ * @size: size of the memory region
+ */
+struct smem_region {
+ phys_addr_t aux_base;
+ void __iomem *virt_base;
+ size_t size;
+};
+
+/**
+ * struct qcom_smem - device data for the smem device
+ * @dev: device pointer
+ * @hwlock: reference to a hwspinlock
+ * @ptable: virtual base of partition table
+ * @global_partition: describes for global partition when in use
+ * @partitions: list of partitions of current processor/host
+ * @item_count: max accepted item number
+ * @socinfo: platform device pointer
+ * @num_regions: number of @regions
+ * @regions: list of the memory regions defining the shared memory
+ */
+struct qcom_smem {
+ struct device *dev;
+
+ struct hwspinlock *hwlock;
+
+ u32 item_count;
+ struct platform_device *socinfo;
+ struct smem_ptable *ptable;
+ struct smem_partition global_partition;
+ struct smem_partition partitions[SMEM_HOST_COUNT];
+
+ unsigned num_regions;
+ struct smem_region regions[] __counted_by(num_regions);
+};
+
+static void *
+phdr_to_last_uncached_entry(struct smem_partition_header *phdr)
+{
+ void *p = phdr;
+
+ return p + le32_to_cpu(phdr->offset_free_uncached);
+}
+
+static struct smem_private_entry *
+phdr_to_first_cached_entry(struct smem_partition_header *phdr,
+ size_t cacheline)
+{
+ void *p = phdr;
+ struct smem_private_entry *e;
+
+ return p + le32_to_cpu(phdr->size) - ALIGN(sizeof(*e), cacheline);
+}
+
+static void *
+phdr_to_last_cached_entry(struct smem_partition_header *phdr)
+{
+ void *p = phdr;
+
+ return p + le32_to_cpu(phdr->offset_free_cached);
+}
+
+static struct smem_private_entry *
+phdr_to_first_uncached_entry(struct smem_partition_header *phdr)
+{
+ void *p = phdr;
+
+ return p + sizeof(*phdr);
+}
+
+static struct smem_private_entry *
+uncached_entry_next(struct smem_private_entry *e)
+{
+ void *p = e;
+
+ return p + sizeof(*e) + le16_to_cpu(e->padding_hdr) +
+ le32_to_cpu(e->size);
+}
+
+static struct smem_private_entry *
+cached_entry_next(struct smem_private_entry *e, size_t cacheline)
+{
+ void *p = e;
+
+ return p - le32_to_cpu(e->size) - ALIGN(sizeof(*e), cacheline);
+}
+
+static void *uncached_entry_to_item(struct smem_private_entry *e)
+{
+ void *p = e;
+
+ return p + sizeof(*e) + le16_to_cpu(e->padding_hdr);
+}
+
+static void *cached_entry_to_item(struct smem_private_entry *e)
+{
+ void *p = e;
+
+ return p - le32_to_cpu(e->size);
+}
+
+/* Pointer to the one and only smem handle */
+static struct qcom_smem *__smem;
+
+/* Timeout (ms) for the trylock of remote spinlocks */
+#define HWSPINLOCK_TIMEOUT 1000
+
+/* The qcom hwspinlock id is always plus one from the smem host id */
+#define SMEM_HOST_ID_TO_HWSPINLOCK_ID(__x) ((__x) + 1)
+
+/**
+ * qcom_smem_bust_hwspin_lock_by_host() - bust the smem hwspinlock for a host
+ * @host: remote processor id
+ *
+ * Busts the hwspin_lock for the given smem host id. This helper is intended
+ * for remoteproc drivers that manage remoteprocs with an equivalent smem
+ * driver instance in the remote firmware. Drivers can force a release of the
+ * smem hwspin_lock if the rproc unexpectedly goes into a bad state.
+ *
+ * Context: Process context.
+ *
+ * Returns: 0 on success, otherwise negative errno.
+ */
+int qcom_smem_bust_hwspin_lock_by_host(unsigned int host)
+{
+ /* This function is for remote procs, so ignore SMEM_HOST_APPS */
+ if (host == SMEM_HOST_APPS || host >= SMEM_HOST_COUNT)
+ return -EINVAL;
+
+ return hwspin_lock_bust(__smem->hwlock, SMEM_HOST_ID_TO_HWSPINLOCK_ID(host));
+}
+EXPORT_SYMBOL_GPL(qcom_smem_bust_hwspin_lock_by_host);
+
+/**
+ * qcom_smem_is_available() - Check if SMEM is available
+ *
+ * Return: true if SMEM is available, false otherwise.
+ */
+bool qcom_smem_is_available(void)
+{
+ return !!__smem;
+}
+EXPORT_SYMBOL_GPL(qcom_smem_is_available);
+
+static int qcom_smem_alloc_private(struct qcom_smem *smem,
+ struct smem_partition *part,
+ unsigned item,
+ size_t size)
+{
+ struct smem_private_entry *hdr, *end;
+ struct smem_partition_header *phdr;
+ size_t alloc_size;
+ void *cached;
+ void *p_end;
+
+ phdr = (struct smem_partition_header __force *)part->virt_base;
+ p_end = (void *)phdr + part->size;
+
+ hdr = phdr_to_first_uncached_entry(phdr);
+ end = phdr_to_last_uncached_entry(phdr);
+ cached = phdr_to_last_cached_entry(phdr);
+
+ if (WARN_ON((void *)end > p_end || cached > p_end))
+ return -EINVAL;
+
+ while (hdr < end) {
+ if (hdr->canary != SMEM_PRIVATE_CANARY)
+ goto bad_canary;
+ if (le16_to_cpu(hdr->item) == item)
+ return -EEXIST;
+
+ hdr = uncached_entry_next(hdr);
+ }
+
+ if (WARN_ON((void *)hdr > p_end))
+ return -EINVAL;
+
+ /* Check that we don't grow into the cached region */
+ alloc_size = sizeof(*hdr) + ALIGN(size, 8);
+ if ((void *)hdr + alloc_size > cached) {
+ dev_err(smem->dev, "Out of memory\n");
+ return -ENOSPC;
+ }
+
+ hdr->canary = SMEM_PRIVATE_CANARY;
+ hdr->item = cpu_to_le16(item);
+ hdr->size = cpu_to_le32(ALIGN(size, 8));
+ hdr->padding_data = cpu_to_le16(le32_to_cpu(hdr->size) - size);
+ hdr->padding_hdr = 0;
+
+ /*
+ * Ensure the header is written before we advance the free offset, so
+ * that remote processors that does not take the remote spinlock still
+ * gets a consistent view of the linked list.
+ */
+ wmb();
+ le32_add_cpu(&phdr->offset_free_uncached, alloc_size);
+
+ return 0;
+bad_canary:
+ dev_err(smem->dev, "Found invalid canary in hosts %hu:%hu partition\n",
+ le16_to_cpu(phdr->host0), le16_to_cpu(phdr->host1));
+
+ return -EINVAL;
+}
+
+static int qcom_smem_alloc_global(struct qcom_smem *smem,
+ unsigned item,
+ size_t size)
+{
+ struct smem_global_entry *entry;
+ struct smem_header *header;
+
+ header = smem->regions[0].virt_base;
+ entry = &header->toc[item];
+ if (entry->allocated)
+ return -EEXIST;
+
+ size = ALIGN(size, 8);
+ if (WARN_ON(size > le32_to_cpu(header->available)))
+ return -ENOMEM;
+
+ entry->offset = header->free_offset;
+ entry->size = cpu_to_le32(size);
+
+ /*
+ * Ensure the header is consistent before we mark the item allocated,
+ * so that remote processors will get a consistent view of the item
+ * even though they do not take the spinlock on read.
+ */
+ wmb();
+ entry->allocated = cpu_to_le32(1);
+
+ le32_add_cpu(&header->free_offset, size);
+ le32_add_cpu(&header->available, -size);
+
+ return 0;
+}
+
+/**
+ * qcom_smem_alloc() - allocate space for a smem item
+ * @host: remote processor id, or -1
+ * @item: smem item handle
+ * @size: number of bytes to be allocated
+ *
+ * Allocate space for a given smem item of size @size, given that the item is
+ * not yet allocated.
+ */
+int qcom_smem_alloc(unsigned host, unsigned item, size_t size)
+{
+ struct smem_partition *part;
+ unsigned long flags;
+ int ret;
+
+ if (!__smem)
+ return -EPROBE_DEFER;
+
+ if (item < SMEM_ITEM_LAST_FIXED) {
+ dev_err(__smem->dev,
+ "Rejecting allocation of static entry %d\n", item);
+ return -EINVAL;
+ }
+
+ if (WARN_ON(item >= __smem->item_count))
+ return -EINVAL;
+
+ ret = hwspin_lock_timeout_irqsave(__smem->hwlock,
+ HWSPINLOCK_TIMEOUT,
+ &flags);
+ if (ret)
+ return ret;
+
+ if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
+ part = &__smem->partitions[host];
+ ret = qcom_smem_alloc_private(__smem, part, item, size);
+ } else if (__smem->global_partition.virt_base) {
+ part = &__smem->global_partition;
+ ret = qcom_smem_alloc_private(__smem, part, item, size);
+ } else {
+ ret = qcom_smem_alloc_global(__smem, item, size);
+ }
+
+ hwspin_unlock_irqrestore(__smem->hwlock, &flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(qcom_smem_alloc);
+
+static void *qcom_smem_get_global(struct qcom_smem *smem,
+ unsigned item,
+ size_t *size)
+{
+ struct smem_header *header;
+ struct smem_region *region;
+ struct smem_global_entry *entry;
+ u64 entry_offset;
+ u32 e_size;
+ u32 aux_base;
+ unsigned i;
+
+ header = smem->regions[0].virt_base;
+ entry = &header->toc[item];
+ if (!entry->allocated)
+ return ERR_PTR(-ENXIO);
+
+ aux_base = le32_to_cpu(entry->aux_base) & AUX_BASE_MASK;
+
+ for (i = 0; i < smem->num_regions; i++) {
+ region = &smem->regions[i];
+
+ if ((u32)region->aux_base == aux_base || !aux_base) {
+ e_size = le32_to_cpu(entry->size);
+ entry_offset = le32_to_cpu(entry->offset);
+
+ if (WARN_ON(e_size + entry_offset > region->size))
+ return ERR_PTR(-EINVAL);
+
+ if (size != NULL)
+ *size = e_size;
+
+ return region->virt_base + entry_offset;
+ }
+ }
+
+ return ERR_PTR(-ENOENT);
+}
+
+static void *qcom_smem_get_private(struct qcom_smem *smem,
+ struct smem_partition *part,
+ unsigned item,
+ size_t *size)
+{
+ struct smem_private_entry *e, *end;
+ struct smem_partition_header *phdr;
+ void *item_ptr, *p_end;
+ u32 padding_data;
+ u32 e_size;
+
+ phdr = (struct smem_partition_header __force *)part->virt_base;
+ p_end = (void *)phdr + part->size;
+
+ e = phdr_to_first_uncached_entry(phdr);
+ end = phdr_to_last_uncached_entry(phdr);
+
+ while (e < end) {
+ if (e->canary != SMEM_PRIVATE_CANARY)
+ goto invalid_canary;
+
+ if (le16_to_cpu(e->item) == item) {
+ if (size != NULL) {
+ e_size = le32_to_cpu(e->size);
+ padding_data = le16_to_cpu(e->padding_data);
+
+ if (WARN_ON(e_size > part->size || padding_data > e_size))
+ return ERR_PTR(-EINVAL);
+
+ *size = e_size - padding_data;
+ }
+
+ item_ptr = uncached_entry_to_item(e);
+ if (WARN_ON(item_ptr > p_end))
+ return ERR_PTR(-EINVAL);
+
+ return item_ptr;
+ }
+
+ e = uncached_entry_next(e);
+ }
+
+ if (WARN_ON((void *)e > p_end))
+ return ERR_PTR(-EINVAL);
+
+ /* Item was not found in the uncached list, search the cached list */
+
+ e = phdr_to_first_cached_entry(phdr, part->cacheline);
+ end = phdr_to_last_cached_entry(phdr);
+
+ if (WARN_ON((void *)e < (void *)phdr || (void *)end > p_end))
+ return ERR_PTR(-EINVAL);
+
+ while (e > end) {
+ if (e->canary != SMEM_PRIVATE_CANARY)
+ goto invalid_canary;
+
+ if (le16_to_cpu(e->item) == item) {
+ if (size != NULL) {
+ e_size = le32_to_cpu(e->size);
+ padding_data = le16_to_cpu(e->padding_data);
+
+ if (WARN_ON(e_size > part->size || padding_data > e_size))
+ return ERR_PTR(-EINVAL);
+
+ *size = e_size - padding_data;
+ }
+
+ item_ptr = cached_entry_to_item(e);
+ if (WARN_ON(item_ptr < (void *)phdr))
+ return ERR_PTR(-EINVAL);
+
+ return item_ptr;
+ }
+
+ e = cached_entry_next(e, part->cacheline);
+ }
+
+ if (WARN_ON((void *)e < (void *)phdr))
+ return ERR_PTR(-EINVAL);
+
+ return ERR_PTR(-ENOENT);
+
+invalid_canary:
+ dev_err(smem->dev, "Found invalid canary in hosts %hu:%hu partition\n",
+ le16_to_cpu(phdr->host0), le16_to_cpu(phdr->host1));
+
+ return ERR_PTR(-EINVAL);
+}
+
+/**
+ * qcom_smem_get() - resolve ptr of size of a smem item
+ * @host: the remote processor, or -1
+ * @item: smem item handle
+ * @size: pointer to be filled out with size of the item
+ *
+ * Looks up smem item and returns pointer to it. Size of smem
+ * item is returned in @size.
+ */
+void *qcom_smem_get(unsigned host, unsigned item, size_t *size)
+{
+ struct smem_partition *part;
+ void *ptr = ERR_PTR(-EPROBE_DEFER);
+
+ if (!__smem)
+ return ptr;
+
+ if (WARN_ON(item >= __smem->item_count))
+ return ERR_PTR(-EINVAL);
+
+ if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
+ part = &__smem->partitions[host];
+ ptr = qcom_smem_get_private(__smem, part, item, size);
+ } else if (__smem->global_partition.virt_base) {
+ part = &__smem->global_partition;
+ ptr = qcom_smem_get_private(__smem, part, item, size);
+ } else {
+ ptr = qcom_smem_get_global(__smem, item, size);
+ }
+
+ return ptr;
+}
+EXPORT_SYMBOL_GPL(qcom_smem_get);
+
+/**
+ * qcom_smem_get_free_space() - retrieve amount of free space in a partition
+ * @host: the remote processor identifying a partition, or -1
+ *
+ * To be used by smem clients as a quick way to determine if any new
+ * allocations has been made.
+ */
+int qcom_smem_get_free_space(unsigned host)
+{
+ struct smem_partition *part;
+ struct smem_partition_header *phdr;
+ struct smem_header *header;
+ unsigned ret;
+
+ if (!__smem)
+ return -EPROBE_DEFER;
+
+ if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
+ part = &__smem->partitions[host];
+ phdr = part->virt_base;
+ ret = le32_to_cpu(phdr->offset_free_cached) -
+ le32_to_cpu(phdr->offset_free_uncached);
+
+ if (ret > le32_to_cpu(part->size))
+ return -EINVAL;
+ } else if (__smem->global_partition.virt_base) {
+ part = &__smem->global_partition;
+ phdr = part->virt_base;
+ ret = le32_to_cpu(phdr->offset_free_cached) -
+ le32_to_cpu(phdr->offset_free_uncached);
+
+ if (ret > le32_to_cpu(part->size))
+ return -EINVAL;
+ } else {
+ header = __smem->regions[0].virt_base;
+ ret = le32_to_cpu(header->available);
+
+ if (ret > __smem->regions[0].size)
+ return -EINVAL;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(qcom_smem_get_free_space);
+
+static bool addr_in_range(void __iomem *base, size_t size, void *addr)
+{
+ return base && ((void __iomem *)addr >= base && (void __iomem *)addr < base + size);
+}
+
+/**
+ * qcom_smem_virt_to_phys() - return the physical address associated
+ * with an smem item pointer (previously returned by qcom_smem_get()
+ * @p: the virtual address to convert
+ *
+ * Returns 0 if the pointer provided is not within any smem region.
+ */
+phys_addr_t qcom_smem_virt_to_phys(void *p)
+{
+ struct smem_partition *part;
+ struct smem_region *area;
+ u64 offset;
+ u32 i;
+
+ for (i = 0; i < SMEM_HOST_COUNT; i++) {
+ part = &__smem->partitions[i];
+
+ if (addr_in_range(part->virt_base, part->size, p)) {
+ offset = p - part->virt_base;
+
+ return (phys_addr_t)part->phys_base + offset;
+ }
+ }
+
+ part = &__smem->global_partition;
+
+ if (addr_in_range(part->virt_base, part->size, p)) {
+ offset = p - part->virt_base;
+
+ return (phys_addr_t)part->phys_base + offset;
+ }
+
+ for (i = 0; i < __smem->num_regions; i++) {
+ area = &__smem->regions[i];
+
+ if (addr_in_range(area->virt_base, area->size, p)) {
+ offset = p - area->virt_base;
+
+ return (phys_addr_t)area->aux_base + offset;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qcom_smem_virt_to_phys);
+
+/**
+ * qcom_smem_get_soc_id() - return the SoC ID
+ * @id: On success, we return the SoC ID here.
+ *
+ * Look up SoC ID from HW/SW build ID and return it.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int qcom_smem_get_soc_id(u32 *id)
+{
+ struct socinfo *info;
+
+ info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, NULL);
+ if (IS_ERR(info))
+ return PTR_ERR(info);
+
+ *id = __le32_to_cpu(info->id);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qcom_smem_get_soc_id);
+
+/**
+ * qcom_smem_get_feature_code() - return the feature code
+ * @code: On success, return the feature code here.
+ *
+ * Look up the feature code identifier from SMEM and return it.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int qcom_smem_get_feature_code(u32 *code)
+{
+ struct socinfo *info;
+ u32 raw_code;
+
+ info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, NULL);
+ if (IS_ERR(info))
+ return PTR_ERR(info);
+
+ /* This only makes sense for socinfo >= 16 */
+ if (__le32_to_cpu(info->fmt) < SOCINFO_VERSION(0, 16))
+ return -EOPNOTSUPP;
+
+ raw_code = __le32_to_cpu(info->feature_code);
+
+ /* Ensure the value makes sense */
+ if (raw_code > SOCINFO_FC_INT_MAX)
+ raw_code = SOCINFO_FC_UNKNOWN;
+
+ *code = raw_code;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qcom_smem_get_feature_code);
+
+static int qcom_smem_get_sbl_version(struct qcom_smem *smem)
+{
+ struct smem_header *header;
+ __le32 *versions;
+
+ header = smem->regions[0].virt_base;
+ versions = header->version;
+
+ return le32_to_cpu(versions[SMEM_MASTER_SBL_VERSION_INDEX]);
+}
+
+static struct smem_ptable *qcom_smem_get_ptable(struct qcom_smem *smem)
+{
+ struct smem_ptable *ptable;
+ u32 version;
+
+ ptable = smem->ptable;
+ if (memcmp(ptable->magic, SMEM_PTABLE_MAGIC, sizeof(ptable->magic)))
+ return ERR_PTR(-ENOENT);
+
+ version = le32_to_cpu(ptable->version);
+ if (version != 1) {
+ dev_err(smem->dev,
+ "Unsupported partition header version %d\n", version);
+ return ERR_PTR(-EINVAL);
+ }
+ return ptable;
+}
+
+static u32 qcom_smem_get_item_count(struct qcom_smem *smem)
+{
+ struct smem_ptable *ptable;
+ struct smem_info *info;
+
+ ptable = qcom_smem_get_ptable(smem);
+ if (IS_ERR_OR_NULL(ptable))
+ return SMEM_ITEM_COUNT;
+
+ info = (struct smem_info *)&ptable->entry[ptable->num_entries];
+ if (memcmp(info->magic, SMEM_INFO_MAGIC, sizeof(info->magic)))
+ return SMEM_ITEM_COUNT;
+
+ return le16_to_cpu(info->num_items);
+}
+
+/*
+ * Validate the partition header for a partition whose partition
+ * table entry is supplied. Returns a pointer to its header if
+ * valid, or a null pointer otherwise.
+ */
+static struct smem_partition_header *
+qcom_smem_partition_header(struct qcom_smem *smem,
+ struct smem_ptable_entry *entry, u16 host0, u16 host1)
+{
+ struct smem_partition_header *header;
+ u32 phys_addr;
+ u32 size;
+
+ phys_addr = smem->regions[0].aux_base + le32_to_cpu(entry->offset);
+ header = devm_ioremap_wc(smem->dev, phys_addr, le32_to_cpu(entry->size));
+
+ if (!header)
+ return NULL;
+
+ if (memcmp(header->magic, SMEM_PART_MAGIC, sizeof(header->magic))) {
+ dev_err(smem->dev, "bad partition magic %4ph\n", header->magic);
+ return NULL;
+ }
+
+ if (host0 != le16_to_cpu(header->host0)) {
+ dev_err(smem->dev, "bad host0 (%hu != %hu)\n",
+ host0, le16_to_cpu(header->host0));
+ return NULL;
+ }
+ if (host1 != le16_to_cpu(header->host1)) {
+ dev_err(smem->dev, "bad host1 (%hu != %hu)\n",
+ host1, le16_to_cpu(header->host1));
+ return NULL;
+ }
+
+ size = le32_to_cpu(header->size);
+ if (size != le32_to_cpu(entry->size)) {
+ dev_err(smem->dev, "bad partition size (%u != %u)\n",
+ size, le32_to_cpu(entry->size));
+ return NULL;
+ }
+
+ if (le32_to_cpu(header->offset_free_uncached) > size) {
+ dev_err(smem->dev, "bad partition free uncached (%u > %u)\n",
+ le32_to_cpu(header->offset_free_uncached), size);
+ return NULL;
+ }
+
+ return header;
+}
+
+static int qcom_smem_set_global_partition(struct qcom_smem *smem)
+{
+ struct smem_partition_header *header;
+ struct smem_ptable_entry *entry;
+ struct smem_ptable *ptable;
+ bool found = false;
+ int i;
+
+ if (smem->global_partition.virt_base) {
+ dev_err(smem->dev, "Already found the global partition\n");
+ return -EINVAL;
+ }
+
+ ptable = qcom_smem_get_ptable(smem);
+ if (IS_ERR(ptable))
+ return PTR_ERR(ptable);
+
+ for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
+ entry = &ptable->entry[i];
+ if (!le32_to_cpu(entry->offset))
+ continue;
+ if (!le32_to_cpu(entry->size))
+ continue;
+
+ if (le16_to_cpu(entry->host0) != SMEM_GLOBAL_HOST)
+ continue;
+
+ if (le16_to_cpu(entry->host1) == SMEM_GLOBAL_HOST) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ dev_err(smem->dev, "Missing entry for global partition\n");
+ return -EINVAL;
+ }
+
+ header = qcom_smem_partition_header(smem, entry,
+ SMEM_GLOBAL_HOST, SMEM_GLOBAL_HOST);
+ if (!header)
+ return -EINVAL;
+
+ smem->global_partition.virt_base = (void __iomem *)header;
+ smem->global_partition.phys_base = smem->regions[0].aux_base +
+ le32_to_cpu(entry->offset);
+ smem->global_partition.size = le32_to_cpu(entry->size);
+ smem->global_partition.cacheline = le32_to_cpu(entry->cacheline);
+
+ return 0;
+}
+
+static int
+qcom_smem_enumerate_partitions(struct qcom_smem *smem, u16 local_host)
+{
+ struct smem_partition_header *header;
+ struct smem_ptable_entry *entry;
+ struct smem_ptable *ptable;
+ u16 remote_host;
+ u16 host0, host1;
+ int i;
+
+ ptable = qcom_smem_get_ptable(smem);
+ if (IS_ERR(ptable))
+ return PTR_ERR(ptable);
+
+ for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
+ entry = &ptable->entry[i];
+ if (!le32_to_cpu(entry->offset))
+ continue;
+ if (!le32_to_cpu(entry->size))
+ continue;
+
+ host0 = le16_to_cpu(entry->host0);
+ host1 = le16_to_cpu(entry->host1);
+ if (host0 == local_host)
+ remote_host = host1;
+ else if (host1 == local_host)
+ remote_host = host0;
+ else
+ continue;
+
+ if (remote_host >= SMEM_HOST_COUNT) {
+ dev_err(smem->dev, "bad host %u\n", remote_host);
+ return -EINVAL;
+ }
+
+ if (smem->partitions[remote_host].virt_base) {
+ dev_err(smem->dev, "duplicate host %u\n", remote_host);
+ return -EINVAL;
+ }
+
+ header = qcom_smem_partition_header(smem, entry, host0, host1);
+ if (!header)
+ return -EINVAL;
+
+ smem->partitions[remote_host].virt_base = (void __iomem *)header;
+ smem->partitions[remote_host].phys_base = smem->regions[0].aux_base +
+ le32_to_cpu(entry->offset);
+ smem->partitions[remote_host].size = le32_to_cpu(entry->size);
+ smem->partitions[remote_host].cacheline = le32_to_cpu(entry->cacheline);
+ }
+
+ return 0;
+}
+
+static int qcom_smem_map_toc(struct qcom_smem *smem, struct smem_region *region)
+{
+ u32 ptable_start;
+
+ /* map starting 4K for smem header */
+ region->virt_base = devm_ioremap_wc(smem->dev, region->aux_base, SZ_4K);
+ ptable_start = region->aux_base + region->size - SZ_4K;
+ /* map last 4k for toc */
+ smem->ptable = devm_ioremap_wc(smem->dev, ptable_start, SZ_4K);
+
+ if (!region->virt_base || !smem->ptable)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int qcom_smem_map_global(struct qcom_smem *smem, u32 size)
+{
+ u32 phys_addr;
+
+ phys_addr = smem->regions[0].aux_base;
+
+ smem->regions[0].size = size;
+ smem->regions[0].virt_base = devm_ioremap_wc(smem->dev, phys_addr, size);
+
+ if (!smem->regions[0].virt_base)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int qcom_smem_resolve_mem(struct qcom_smem *smem, const char *name,
+ struct smem_region *region)
+{
+ struct device *dev = smem->dev;
+ struct device_node *np;
+ struct resource r;
+ int ret;
+
+ np = of_parse_phandle(dev->of_node, name, 0);
+ if (!np) {
+ dev_err(dev, "No %s specified\n", name);
+ return -EINVAL;
+ }
+
+ ret = of_address_to_resource(np, 0, &r);
+ of_node_put(np);
+ if (ret)
+ return ret;
+
+ region->aux_base = r.start;
+ region->size = resource_size(&r);
+
+ return 0;
+}
+
+static int qcom_smem_probe(struct platform_device *pdev)
+{
+ struct smem_header *header;
+ struct reserved_mem *rmem;
+ struct qcom_smem *smem;
+ unsigned long flags;
+ int num_regions;
+ int hwlock_id;
+ u32 version;
+ u32 size;
+ int ret;
+ int i;
+
+ if (__smem)
+ return 0;
+
+ num_regions = 1;
+ if (of_property_present(pdev->dev.of_node, "qcom,rpm-msg-ram"))
+ num_regions++;
+
+ smem = devm_kzalloc(&pdev->dev, struct_size(smem, regions, num_regions),
+ GFP_KERNEL);
+ if (!smem)
+ return -ENOMEM;
+
+ smem->dev = &pdev->dev;
+ smem->num_regions = num_regions;
+
+ rmem = of_reserved_mem_lookup(pdev->dev.of_node);
+ if (rmem) {
+ smem->regions[0].aux_base = rmem->base;
+ smem->regions[0].size = rmem->size;
+ } else {
+ /*
+ * Fall back to the memory-region reference, if we're not a
+ * reserved-memory node.
+ */
+ ret = qcom_smem_resolve_mem(smem, "memory-region", &smem->regions[0]);
+ if (ret)
+ return ret;
+ }
+
+ if (num_regions > 1) {
+ ret = qcom_smem_resolve_mem(smem, "qcom,rpm-msg-ram", &smem->regions[1]);
+ if (ret)
+ return ret;
+ }
+
+
+ ret = qcom_smem_map_toc(smem, &smem->regions[0]);
+ if (ret)
+ return ret;
+
+ for (i = 1; i < num_regions; i++) {
+ smem->regions[i].virt_base = devm_ioremap_wc(&pdev->dev,
+ smem->regions[i].aux_base,
+ smem->regions[i].size);
+ if (!smem->regions[i].virt_base) {
+ dev_err(&pdev->dev, "failed to remap %pa\n", &smem->regions[i].aux_base);
+ return -ENOMEM;
+ }
+ }
+
+ header = smem->regions[0].virt_base;
+ if (le32_to_cpu(header->initialized) != 1 ||
+ le32_to_cpu(header->reserved)) {
+ dev_err(&pdev->dev, "SMEM is not initialized by SBL\n");
+ return -EINVAL;
+ }
+
+ hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0);
+ if (hwlock_id < 0) {
+ if (hwlock_id != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "failed to retrieve hwlock\n");
+ return hwlock_id;
+ }
+
+ smem->hwlock = hwspin_lock_request_specific(hwlock_id);
+ if (!smem->hwlock)
+ return -ENXIO;
+
+ ret = hwspin_lock_timeout_irqsave(smem->hwlock, HWSPINLOCK_TIMEOUT, &flags);
+ if (ret)
+ return ret;
+ size = readl_relaxed(&header->available) + readl_relaxed(&header->free_offset);
+ hwspin_unlock_irqrestore(smem->hwlock, &flags);
+
+ version = qcom_smem_get_sbl_version(smem);
+ /*
+ * smem header mapping is required only in heap version scheme, so unmap
+ * it here. It will be remapped in qcom_smem_map_global() when whole
+ * partition is mapped again.
+ */
+ devm_iounmap(smem->dev, smem->regions[0].virt_base);
+ switch (version >> 16) {
+ case SMEM_GLOBAL_PART_VERSION:
+ ret = qcom_smem_set_global_partition(smem);
+ if (ret < 0)
+ return ret;
+ smem->item_count = qcom_smem_get_item_count(smem);
+ break;
+ case SMEM_GLOBAL_HEAP_VERSION:
+ qcom_smem_map_global(smem, size);
+ smem->item_count = SMEM_ITEM_COUNT;
+ break;
+ default:
+ dev_err(&pdev->dev, "Unsupported SMEM version 0x%x\n", version);
+ return -EINVAL;
+ }
+
+ BUILD_BUG_ON(SMEM_HOST_APPS >= SMEM_HOST_COUNT);
+ ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS);
+ if (ret < 0 && ret != -ENOENT)
+ return ret;
+
+ __smem = smem;
+
+ smem->socinfo = platform_device_register_data(&pdev->dev, "qcom-socinfo",
+ PLATFORM_DEVID_NONE, NULL,
+ 0);
+ if (IS_ERR(smem->socinfo))
+ dev_dbg(&pdev->dev, "failed to register socinfo device\n");
+
+ return 0;
+}
+
+static void qcom_smem_remove(struct platform_device *pdev)
+{
+ platform_device_unregister(__smem->socinfo);
+
+ hwspin_lock_free(__smem->hwlock);
+ __smem = NULL;
+}
+
+static const struct of_device_id qcom_smem_of_match[] = {
+ { .compatible = "qcom,smem" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, qcom_smem_of_match);
+
+static struct platform_driver qcom_smem_driver = {
+ .probe = qcom_smem_probe,
+ .remove_new = qcom_smem_remove,
+ .driver = {
+ .name = "qcom-smem",
+ .of_match_table = qcom_smem_of_match,
+ .suppress_bind_attrs = true,
+ },
+};
+
+static int __init qcom_smem_init(void)
+{
+ return platform_driver_register(&qcom_smem_driver);
+}
+arch_initcall(qcom_smem_init);
+
+static void __exit qcom_smem_exit(void)
+{
+ platform_driver_unregister(&qcom_smem_driver);
+}
+module_exit(qcom_smem_exit)
+
+MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
+MODULE_DESCRIPTION("Qualcomm Shared Memory Manager");
+MODULE_LICENSE("GPL v2");
diff --git a/include/soc/qcom/smem.h b/include/soc/qcom/smem.h
new file mode 100644
index 000000000000..f946e3beca21
--- /dev/null
+++ b/include/soc/qcom/smem.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __QCOM_SMEM_H__
+#define __QCOM_SMEM_H__
+
+#define QCOM_SMEM_HOST_ANY -1
+
+bool qcom_smem_is_available(void);
+int qcom_smem_alloc(unsigned host, unsigned item, size_t size);
+void *qcom_smem_get(unsigned host, unsigned item, size_t *size);
+
+int qcom_smem_get_free_space(unsigned host);
+
+phys_addr_t qcom_smem_virt_to_phys(void *p);
+
+int qcom_smem_get_soc_id(u32 *id);
+int qcom_smem_get_feature_code(u32 *code);
+
+int qcom_smem_bust_hwspin_lock_by_host(unsigned int host);
+
+#endif
diff --git a/include/soc/qcom/socinfo.h b/include/soc/qcom/socinfo.h
new file mode 100644
index 000000000000..608950443eee
--- /dev/null
+++ b/include/soc/qcom/socinfo.h
@@ -0,0 +1,111 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __QCOM_SOCINFO_H__
+#define __QCOM_SOCINFO_H__
+
+#include <linux/types.h>
+
+/*
+ * SMEM item id, used to acquire handles to respective
+ * SMEM region.
+ */
+#define SMEM_HW_SW_BUILD_ID 137
+
+#define SMEM_SOCINFO_BUILD_ID_LENGTH 32
+#define SMEM_SOCINFO_CHIP_ID_LENGTH 32
+
+/*
+ * SoC version type with major number in the upper 16 bits and minor
+ * number in the lower 16 bits.
+ */
+#define SOCINFO_MAJOR(ver) (((ver) >> 16) & 0xffff)
+#define SOCINFO_MINOR(ver) ((ver) & 0xffff)
+#define SOCINFO_VERSION(maj, min) ((((maj) & 0xffff) << 16)|((min) & 0xffff))
+
+/* Socinfo SMEM item structure */
+struct socinfo {
+ __le32 fmt;
+ __le32 id;
+ __le32 ver;
+ char build_id[SMEM_SOCINFO_BUILD_ID_LENGTH];
+ /* Version 2 */
+ __le32 raw_id;
+ __le32 raw_ver;
+ /* Version 3 */
+ __le32 hw_plat;
+ /* Version 4 */
+ __le32 plat_ver;
+ /* Version 5 */
+ __le32 accessory_chip;
+ /* Version 6 */
+ __le32 hw_plat_subtype;
+ /* Version 7 */
+ __le32 pmic_model;
+ __le32 pmic_die_rev;
+ /* Version 8 */
+ __le32 pmic_model_1;
+ __le32 pmic_die_rev_1;
+ __le32 pmic_model_2;
+ __le32 pmic_die_rev_2;
+ /* Version 9 */
+ __le32 foundry_id;
+ /* Version 10 */
+ __le32 serial_num;
+ /* Version 11 */
+ __le32 num_pmics;
+ __le32 pmic_array_offset;
+ /* Version 12 */
+ __le32 chip_family;
+ __le32 raw_device_family;
+ __le32 raw_device_num;
+ /* Version 13 */
+ __le32 nproduct_id;
+ char chip_id[SMEM_SOCINFO_CHIP_ID_LENGTH];
+ /* Version 14 */
+ __le32 num_clusters;
+ __le32 ncluster_array_offset;
+ __le32 num_subset_parts;
+ __le32 nsubset_parts_array_offset;
+ /* Version 15 */
+ __le32 nmodem_supported;
+ /* Version 16 */
+ __le32 feature_code;
+ __le32 pcode;
+ __le32 npartnamemap_offset;
+ __le32 nnum_partname_mapping;
+ /* Version 17 */
+ __le32 oem_variant;
+ /* Version 18 */
+ __le32 num_kvps;
+ __le32 kvps_offset;
+ /* Version 19 */
+ __le32 num_func_clusters;
+ __le32 boot_cluster;
+ __le32 boot_core;
+};
+
+/* Internal feature codes */
+enum qcom_socinfo_feature_code {
+ /* External feature codes */
+ SOCINFO_FC_UNKNOWN = 0x0,
+ SOCINFO_FC_AA,
+ SOCINFO_FC_AB,
+ SOCINFO_FC_AC,
+ SOCINFO_FC_AD,
+ SOCINFO_FC_AE,
+ SOCINFO_FC_AF,
+ SOCINFO_FC_AG,
+ SOCINFO_FC_AH,
+};
+
+/* Internal feature codes */
+/* Valid values: 0 <= n <= 0xf */
+#define SOCINFO_FC_Yn(n) (0xf1 + (n))
+#define SOCINFO_FC_INT_MAX SOCINFO_FC_Yn(0xf)
+
+/* Product codes */
+#define SOCINFO_PC_UNKNOWN 0
+#define SOCINFO_PCn(n) ((n) + 1)
+#define SOCINFO_PC_RESERVE (BIT(31) - 1)
+
+#endif
--
2.53.0
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [PATCH v2 07/15] soc: qcom: smem: adjust headers for U-Boot
2026-05-04 18:57 [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot Casey Connolly
` (5 preceding siblings ...)
2026-05-04 18:57 ` [PATCH v2 06/15] soc: qcom: import smem from Linux 6.11-rc2 Casey Connolly
@ 2026-05-04 18:57 ` Casey Connolly
2026-05-05 12:43 ` Sumit Garg
2026-05-18 14:36 ` Neil Armstrong
2026-05-04 18:57 ` [PATCH v2 08/15] soc: qcom: smem: adjust " Casey Connolly
` (8 subsequent siblings)
15 siblings, 2 replies; 51+ messages in thread
From: Casey Connolly @ 2026-05-04 18:57 UTC (permalink / raw)
To: u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Casey Connolly, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
Drop Linux headers for the U-Boot ones.
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
---
drivers/soc/qcom/smem.c | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
index 8515b8ae7777..7143856e85c3 100644
--- a/drivers/soc/qcom/smem.c
+++ b/drivers/soc/qcom/smem.c
@@ -3,19 +3,17 @@
* Copyright (c) 2015, Sony Mobile Communications AB.
* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*/
-#include <linux/hwspinlock.h>
+#include <dm/device.h>
+#include <dm/device_compat.h>
+#include <dm/ofnode.h>
+#include <linux/bug.h>
#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_reserved_mem.h>
-#include <linux/platform_device.h>
+#include <linux/ioport.h>
#include <linux/sizes.h>
-#include <linux/slab.h>
-#include <linux/soc/qcom/smem.h>
-#include <linux/soc/qcom/socinfo.h>
+#include <soc/qcom/smem.h>
+#include <soc/qcom/socinfo.h>
/*
* The Qualcomm shared memory system is a allocate only heap structure that
* consists of one of more memory areas that can be accessed by the processors
--
2.53.0
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [PATCH v2 08/15] soc: qcom: smem: adjust for U-Boot
2026-05-04 18:57 [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot Casey Connolly
` (6 preceding siblings ...)
2026-05-04 18:57 ` [PATCH v2 07/15] soc: qcom: smem: adjust headers for U-Boot Casey Connolly
@ 2026-05-04 18:57 ` Casey Connolly
2026-05-08 10:43 ` Aswin Murugan
2026-05-18 11:03 ` Sumit Garg
2026-05-04 18:57 ` [PATCH v2 09/15] soc: qcom: smem: get serial number from socinfo Casey Connolly
` (7 subsequent siblings)
15 siblings, 2 replies; 51+ messages in thread
From: Casey Connolly @ 2026-05-04 18:57 UTC (permalink / raw)
To: u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Casey Connolly, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
Port over the smem code to U-Boot and ensure the smem region gets mapped
after dcache is enabled.
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
---
drivers/soc/qcom/smem.c | 399 +++++++++++++-----------------------------------
include/soc/qcom/smem.h | 9 +-
2 files changed, 104 insertions(+), 304 deletions(-)
diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
index 7143856e85c3..96288d7bcdef 100644
--- a/drivers/soc/qcom/smem.c
+++ b/drivers/soc/qcom/smem.c
@@ -3,17 +3,19 @@
* Copyright (c) 2015, Sony Mobile Communications AB.
* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*/
+#define pr_fmt(fmt) "smem: " fmt
+
+#include <asm/system.h>
#include <dm/device.h>
#include <dm/device_compat.h>
#include <dm/ofnode.h>
#include <linux/bug.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/sizes.h>
#include <soc/qcom/smem.h>
-#include <soc/qcom/socinfo.h>
/*
* The Qualcomm shared memory system is a allocate only heap structure that
* consists of one of more memory areas that can be accessed by the processors
@@ -246,8 +248,10 @@ struct smem_info {
};
static const u8 SMEM_INFO_MAGIC[] = { 0x53, 0x49, 0x49, 0x49 }; /* SIII */
+#define SMEM_MAX_REGIONS 2
+
/**
* struct smem_region - representation of a chunk of memory used for smem
* @aux_base: identifier of aux_mem base
* @virt_base: virtual base address of memory with this aux_mem identifier
@@ -260,33 +264,31 @@ struct smem_region {
};
/**
* struct qcom_smem - device data for the smem device
- * @dev: device pointer
- * @hwlock: reference to a hwspinlock
* @ptable: virtual base of partition table
* @global_partition: describes for global partition when in use
* @partitions: list of partitions of current processor/host
* @item_count: max accepted item number
- * @socinfo: platform device pointer
* @num_regions: number of @regions
* @regions: list of the memory regions defining the shared memory
*/
struct qcom_smem {
- struct device *dev;
-
- struct hwspinlock *hwlock;
-
u32 item_count;
- struct platform_device *socinfo;
struct smem_ptable *ptable;
struct smem_partition global_partition;
struct smem_partition partitions[SMEM_HOST_COUNT];
unsigned num_regions;
- struct smem_region regions[] __counted_by(num_regions);
+ struct smem_region regions[SMEM_MAX_REGIONS];
};
+/* Pre-allocated storage for SMEM */
+static struct qcom_smem __smem __section(".data") = { 0 };
+
+/* Pointer to the one and only smem handle */
+static struct qcom_smem *smem __section(".data") = NULL;
+
static void *
phdr_to_last_uncached_entry(struct smem_partition_header *phdr)
{
void *p = phdr;
@@ -350,48 +352,16 @@ static void *cached_entry_to_item(struct smem_private_entry *e)
return p - le32_to_cpu(e->size);
}
-/* Pointer to the one and only smem handle */
-static struct qcom_smem *__smem;
-
-/* Timeout (ms) for the trylock of remote spinlocks */
-#define HWSPINLOCK_TIMEOUT 1000
-
-/* The qcom hwspinlock id is always plus one from the smem host id */
-#define SMEM_HOST_ID_TO_HWSPINLOCK_ID(__x) ((__x) + 1)
-
-/**
- * qcom_smem_bust_hwspin_lock_by_host() - bust the smem hwspinlock for a host
- * @host: remote processor id
- *
- * Busts the hwspin_lock for the given smem host id. This helper is intended
- * for remoteproc drivers that manage remoteprocs with an equivalent smem
- * driver instance in the remote firmware. Drivers can force a release of the
- * smem hwspin_lock if the rproc unexpectedly goes into a bad state.
- *
- * Context: Process context.
- *
- * Returns: 0 on success, otherwise negative errno.
- */
-int qcom_smem_bust_hwspin_lock_by_host(unsigned int host)
-{
- /* This function is for remote procs, so ignore SMEM_HOST_APPS */
- if (host == SMEM_HOST_APPS || host >= SMEM_HOST_COUNT)
- return -EINVAL;
-
- return hwspin_lock_bust(__smem->hwlock, SMEM_HOST_ID_TO_HWSPINLOCK_ID(host));
-}
-EXPORT_SYMBOL_GPL(qcom_smem_bust_hwspin_lock_by_host);
-
/**
* qcom_smem_is_available() - Check if SMEM is available
*
* Return: true if SMEM is available, false otherwise.
*/
bool qcom_smem_is_available(void)
{
- return !!__smem;
+ return !!smem;
}
EXPORT_SYMBOL_GPL(qcom_smem_is_available);
static int qcom_smem_alloc_private(struct qcom_smem *smem,
@@ -429,9 +399,9 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem,
/* Check that we don't grow into the cached region */
alloc_size = sizeof(*hdr) + ALIGN(size, 8);
if ((void *)hdr + alloc_size > cached) {
- dev_err(smem->dev, "Out of memory\n");
+ log_err("Out of memory\n");
return -ENOSPC;
}
hdr->canary = SMEM_PRIVATE_CANARY;
@@ -449,9 +419,9 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem,
le32_add_cpu(&phdr->offset_free_uncached, alloc_size);
return 0;
bad_canary:
- dev_err(smem->dev, "Found invalid canary in hosts %hu:%hu partition\n",
+ log_err("Found invalid canary in hosts %hu:%hu partition\n",
le16_to_cpu(phdr->host0), le16_to_cpu(phdr->host1));
return -EINVAL;
}
@@ -500,41 +470,31 @@ static int qcom_smem_alloc_global(struct qcom_smem *smem,
*/
int qcom_smem_alloc(unsigned host, unsigned item, size_t size)
{
struct smem_partition *part;
- unsigned long flags;
int ret;
- if (!__smem)
+ if (!smem)
return -EPROBE_DEFER;
if (item < SMEM_ITEM_LAST_FIXED) {
- dev_err(__smem->dev,
- "Rejecting allocation of static entry %d\n", item);
+ log_err("Rejecting allocation of static entry %d\n", item);
return -EINVAL;
}
- if (WARN_ON(item >= __smem->item_count))
+ if (WARN_ON(item >= smem->item_count))
return -EINVAL;
- ret = hwspin_lock_timeout_irqsave(__smem->hwlock,
- HWSPINLOCK_TIMEOUT,
- &flags);
- if (ret)
- return ret;
-
- if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
- part = &__smem->partitions[host];
- ret = qcom_smem_alloc_private(__smem, part, item, size);
- } else if (__smem->global_partition.virt_base) {
- part = &__smem->global_partition;
- ret = qcom_smem_alloc_private(__smem, part, item, size);
+ if (host < SMEM_HOST_COUNT && smem->partitions[host].virt_base) {
+ part = &smem->partitions[host];
+ ret = qcom_smem_alloc_private(smem, part, item, size);
+ } else if (smem->global_partition.virt_base) {
+ part = &smem->global_partition;
+ ret = qcom_smem_alloc_private(smem, part, item, size);
} else {
- ret = qcom_smem_alloc_global(__smem, item, size);
+ ret = qcom_smem_alloc_global(smem, item, size);
}
- hwspin_unlock_irqrestore(__smem->hwlock, &flags);
-
return ret;
}
EXPORT_SYMBOL_GPL(qcom_smem_alloc);
@@ -660,9 +620,9 @@ static void *qcom_smem_get_private(struct qcom_smem *smem,
return ERR_PTR(-ENOENT);
invalid_canary:
- dev_err(smem->dev, "Found invalid canary in hosts %hu:%hu partition\n",
+ log_err("Found invalid canary in hosts %hu:%hu partition\n",
le16_to_cpu(phdr->host0), le16_to_cpu(phdr->host1));
return ERR_PTR(-EINVAL);
}
@@ -680,22 +640,22 @@ void *qcom_smem_get(unsigned host, unsigned item, size_t *size)
{
struct smem_partition *part;
void *ptr = ERR_PTR(-EPROBE_DEFER);
- if (!__smem)
+ if (!smem)
return ptr;
- if (WARN_ON(item >= __smem->item_count))
+ if (WARN_ON(item >= smem->item_count))
return ERR_PTR(-EINVAL);
- if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
- part = &__smem->partitions[host];
- ptr = qcom_smem_get_private(__smem, part, item, size);
- } else if (__smem->global_partition.virt_base) {
- part = &__smem->global_partition;
- ptr = qcom_smem_get_private(__smem, part, item, size);
+ if (host < SMEM_HOST_COUNT && smem->partitions[host].virt_base) {
+ part = &smem->partitions[host];
+ ptr = qcom_smem_get_private(smem, part, item, size);
+ } else if (smem->global_partition.virt_base) {
+ part = &smem->global_partition;
+ ptr = qcom_smem_get_private(smem, part, item, size);
} else {
- ptr = qcom_smem_get_global(__smem, item, size);
+ ptr = qcom_smem_get_global(smem, item, size);
}
return ptr;
}
@@ -714,32 +674,32 @@ int qcom_smem_get_free_space(unsigned host)
struct smem_partition_header *phdr;
struct smem_header *header;
unsigned ret;
- if (!__smem)
+ if (!smem)
return -EPROBE_DEFER;
- if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
- part = &__smem->partitions[host];
+ if (host < SMEM_HOST_COUNT && smem->partitions[host].virt_base) {
+ part = &smem->partitions[host];
phdr = part->virt_base;
ret = le32_to_cpu(phdr->offset_free_cached) -
le32_to_cpu(phdr->offset_free_uncached);
if (ret > le32_to_cpu(part->size))
return -EINVAL;
- } else if (__smem->global_partition.virt_base) {
- part = &__smem->global_partition;
+ } else if (smem->global_partition.virt_base) {
+ part = &smem->global_partition;
phdr = part->virt_base;
ret = le32_to_cpu(phdr->offset_free_cached) -
le32_to_cpu(phdr->offset_free_uncached);
if (ret > le32_to_cpu(part->size))
return -EINVAL;
} else {
- header = __smem->regions[0].virt_base;
+ header = smem->regions[0].virt_base;
ret = le32_to_cpu(header->available);
- if (ret > __smem->regions[0].size)
+ if (ret > smem->regions[0].size)
return -EINVAL;
}
return ret;
@@ -765,27 +725,27 @@ phys_addr_t qcom_smem_virt_to_phys(void *p)
u64 offset;
u32 i;
for (i = 0; i < SMEM_HOST_COUNT; i++) {
- part = &__smem->partitions[i];
+ part = &smem->partitions[i];
if (addr_in_range(part->virt_base, part->size, p)) {
offset = p - part->virt_base;
return (phys_addr_t)part->phys_base + offset;
}
}
- part = &__smem->global_partition;
+ part = &smem->global_partition;
if (addr_in_range(part->virt_base, part->size, p)) {
offset = p - part->virt_base;
return (phys_addr_t)part->phys_base + offset;
}
- for (i = 0; i < __smem->num_regions; i++) {
- area = &__smem->regions[i];
+ for (i = 0; i < smem->num_regions; i++) {
+ area = &smem->regions[i];
if (addr_in_range(area->virt_base, area->size, p)) {
offset = p - area->virt_base;
@@ -796,63 +756,8 @@ phys_addr_t qcom_smem_virt_to_phys(void *p)
return 0;
}
EXPORT_SYMBOL_GPL(qcom_smem_virt_to_phys);
-/**
- * qcom_smem_get_soc_id() - return the SoC ID
- * @id: On success, we return the SoC ID here.
- *
- * Look up SoC ID from HW/SW build ID and return it.
- *
- * Return: 0 on success, negative errno on failure.
- */
-int qcom_smem_get_soc_id(u32 *id)
-{
- struct socinfo *info;
-
- info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, NULL);
- if (IS_ERR(info))
- return PTR_ERR(info);
-
- *id = __le32_to_cpu(info->id);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(qcom_smem_get_soc_id);
-
-/**
- * qcom_smem_get_feature_code() - return the feature code
- * @code: On success, return the feature code here.
- *
- * Look up the feature code identifier from SMEM and return it.
- *
- * Return: 0 on success, negative errno on failure.
- */
-int qcom_smem_get_feature_code(u32 *code)
-{
- struct socinfo *info;
- u32 raw_code;
-
- info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, NULL);
- if (IS_ERR(info))
- return PTR_ERR(info);
-
- /* This only makes sense for socinfo >= 16 */
- if (__le32_to_cpu(info->fmt) < SOCINFO_VERSION(0, 16))
- return -EOPNOTSUPP;
-
- raw_code = __le32_to_cpu(info->feature_code);
-
- /* Ensure the value makes sense */
- if (raw_code > SOCINFO_FC_INT_MAX)
- raw_code = SOCINFO_FC_UNKNOWN;
-
- *code = raw_code;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(qcom_smem_get_feature_code);
-
static int qcom_smem_get_sbl_version(struct qcom_smem *smem)
{
struct smem_header *header;
__le32 *versions;
@@ -873,10 +778,9 @@ static struct smem_ptable *qcom_smem_get_ptable(struct qcom_smem *smem)
return ERR_PTR(-ENOENT);
version = le32_to_cpu(ptable->version);
if (version != 1) {
- dev_err(smem->dev,
- "Unsupported partition header version %d\n", version);
+ log_err("Unsupported partition header version %d\n", version);
return ERR_PTR(-EINVAL);
}
return ptable;
}
@@ -906,42 +810,42 @@ static struct smem_partition_header *
qcom_smem_partition_header(struct qcom_smem *smem,
struct smem_ptable_entry *entry, u16 host0, u16 host1)
{
struct smem_partition_header *header;
- u32 phys_addr;
+ u64 phys_addr;
u32 size;
phys_addr = smem->regions[0].aux_base + le32_to_cpu(entry->offset);
- header = devm_ioremap_wc(smem->dev, phys_addr, le32_to_cpu(entry->size));
+ header = (void *)phys_addr; // devm_ioremap_wc()
if (!header)
return NULL;
if (memcmp(header->magic, SMEM_PART_MAGIC, sizeof(header->magic))) {
- dev_err(smem->dev, "bad partition magic %4ph\n", header->magic);
+ log_err("bad partition magic %4ph\n", header->magic);
return NULL;
}
if (host0 != le16_to_cpu(header->host0)) {
- dev_err(smem->dev, "bad host0 (%hu != %hu)\n",
- host0, le16_to_cpu(header->host0));
+ log_err("bad host0 (%hu != %hu)\n",
+ host0, le16_to_cpu(header->host0));
return NULL;
}
if (host1 != le16_to_cpu(header->host1)) {
- dev_err(smem->dev, "bad host1 (%hu != %hu)\n",
- host1, le16_to_cpu(header->host1));
+ log_err("bad host1 (%hu != %hu)\n",
+ host1, le16_to_cpu(header->host1));
return NULL;
}
size = le32_to_cpu(header->size);
if (size != le32_to_cpu(entry->size)) {
- dev_err(smem->dev, "bad partition size (%u != %u)\n",
+ log_err("bad partition size (%u != %u)\n",
size, le32_to_cpu(entry->size));
return NULL;
}
if (le32_to_cpu(header->offset_free_uncached) > size) {
- dev_err(smem->dev, "bad partition free uncached (%u > %u)\n",
+ log_err("bad partition free uncached (%u > %u)\n",
le32_to_cpu(header->offset_free_uncached), size);
return NULL;
}
@@ -956,9 +860,9 @@ static int qcom_smem_set_global_partition(struct qcom_smem *smem)
bool found = false;
int i;
if (smem->global_partition.virt_base) {
- dev_err(smem->dev, "Already found the global partition\n");
+ log_err("Already found the global partition\n");
return -EINVAL;
}
ptable = qcom_smem_get_ptable(smem);
@@ -981,9 +885,9 @@ static int qcom_smem_set_global_partition(struct qcom_smem *smem)
}
}
if (!found) {
- dev_err(smem->dev, "Missing entry for global partition\n");
+ log_err("Missing entry for global partition\n");
return -EINVAL;
}
header = qcom_smem_partition_header(smem, entry,
@@ -1030,14 +934,14 @@ qcom_smem_enumerate_partitions(struct qcom_smem *smem, u16 local_host)
else
continue;
if (remote_host >= SMEM_HOST_COUNT) {
- dev_err(smem->dev, "bad host %u\n", remote_host);
+ log_err("bad host %u\n", remote_host);
return -EINVAL;
}
if (smem->partitions[remote_host].virt_base) {
- dev_err(smem->dev, "duplicate host %u\n", remote_host);
+ log_err("duplicate host %u\n", remote_host);
return -EINVAL;
}
header = qcom_smem_partition_header(smem, entry, host0, host1);
@@ -1058,153 +962,100 @@ static int qcom_smem_map_toc(struct qcom_smem *smem, struct smem_region *region)
{
u32 ptable_start;
/* map starting 4K for smem header */
- region->virt_base = devm_ioremap_wc(smem->dev, region->aux_base, SZ_4K);
+ region->virt_base = (void *)region->aux_base;
ptable_start = region->aux_base + region->size - SZ_4K;
/* map last 4k for toc */
- smem->ptable = devm_ioremap_wc(smem->dev, ptable_start, SZ_4K);
+ smem->ptable = (struct smem_ptable *)(u64)ptable_start;
if (!region->virt_base || !smem->ptable)
return -ENOMEM;
+ if (dcache_status())
+ mmu_map_region(region->aux_base, region->size, false);
+
return 0;
}
static int qcom_smem_map_global(struct qcom_smem *smem, u32 size)
{
- u32 phys_addr;
+ u64 phys_addr;
phys_addr = smem->regions[0].aux_base;
smem->regions[0].size = size;
- smem->regions[0].virt_base = devm_ioremap_wc(smem->dev, phys_addr, size);
+ smem->regions[0].virt_base = (void *)phys_addr;
if (!smem->regions[0].virt_base)
return -ENOMEM;
return 0;
}
-static int qcom_smem_resolve_mem(struct qcom_smem *smem, const char *name,
- struct smem_region *region)
-{
- struct device *dev = smem->dev;
- struct device_node *np;
- struct resource r;
- int ret;
-
- np = of_parse_phandle(dev->of_node, name, 0);
- if (!np) {
- dev_err(dev, "No %s specified\n", name);
- return -EINVAL;
- }
-
- ret = of_address_to_resource(np, 0, &r);
- of_node_put(np);
- if (ret)
- return ret;
-
- region->aux_base = r.start;
- region->size = resource_size(&r);
-
- return 0;
-}
-
-static int qcom_smem_probe(struct platform_device *pdev)
+int qcom_smem_init(void)
{
struct smem_header *header;
- struct reserved_mem *rmem;
- struct qcom_smem *smem;
- unsigned long flags;
int num_regions;
- int hwlock_id;
+ fdt_size_t reg_size = 0;
+ u32 phandle;
+ ofnode node, mem_node;
u32 version;
u32 size;
int ret;
int i;
- if (__smem)
+ if (dcache_status() && smem)
+ mmu_map_region(smem->regions[0].aux_base, smem->regions[0].size, false);
+
+ if (smem)
return 0;
+ smem = &__smem;
+
num_regions = 1;
- if (of_property_present(pdev->dev.of_node, "qcom,rpm-msg-ram"))
- num_regions++;
- smem = devm_kzalloc(&pdev->dev, struct_size(smem, regions, num_regions),
- GFP_KERNEL);
- if (!smem)
- return -ENOMEM;
+ node = ofnode_by_compatible(ofnode_root(), "qcom,smem");
+ if (!ofnode_valid(node))
+ return -ENODEV;
+
+ if (ofnode_has_property(node, "memory-region")) {
+ ofnode_read_u32(node, "memory-region", &phandle);
+ mem_node = ofnode_get_by_phandle(phandle);
+ } else {
+ mem_node = node;
+ }
- smem->dev = &pdev->dev;
smem->num_regions = num_regions;
- rmem = of_reserved_mem_lookup(pdev->dev.of_node);
- if (rmem) {
- smem->regions[0].aux_base = rmem->base;
- smem->regions[0].size = rmem->size;
- } else {
- /*
- * Fall back to the memory-region reference, if we're not a
- * reserved-memory node.
- */
- ret = qcom_smem_resolve_mem(smem, "memory-region", &smem->regions[0]);
- if (ret)
- return ret;
+ smem->regions[0].aux_base = ofnode_get_addr(mem_node);
+ reg_size = ofnode_get_size(mem_node);
+ if (smem->regions[0].aux_base == FDT_ADDR_T_NONE) {
+ log_err("Failed to get base address\n");
+ return -EINVAL;
}
-
- if (num_regions > 1) {
- ret = qcom_smem_resolve_mem(smem, "qcom,rpm-msg-ram", &smem->regions[1]);
- if (ret)
- return ret;
- }
-
+ smem->regions[0].size = reg_size;
ret = qcom_smem_map_toc(smem, &smem->regions[0]);
- if (ret)
+ if (ret) {
+ log_err("Failed to map toc\n");
return ret;
+ }
for (i = 1; i < num_regions; i++) {
- smem->regions[i].virt_base = devm_ioremap_wc(&pdev->dev,
- smem->regions[i].aux_base,
- smem->regions[i].size);
- if (!smem->regions[i].virt_base) {
- dev_err(&pdev->dev, "failed to remap %pa\n", &smem->regions[i].aux_base);
- return -ENOMEM;
- }
+ smem->regions[i].virt_base = (void *)smem->regions[i].aux_base;
}
header = smem->regions[0].virt_base;
if (le32_to_cpu(header->initialized) != 1 ||
le32_to_cpu(header->reserved)) {
- dev_err(&pdev->dev, "SMEM is not initialized by SBL\n");
+ log_err("SMEM is not initialized by SBL\n");
return -EINVAL;
}
- hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0);
- if (hwlock_id < 0) {
- if (hwlock_id != -EPROBE_DEFER)
- dev_err(&pdev->dev, "failed to retrieve hwlock\n");
- return hwlock_id;
- }
-
- smem->hwlock = hwspin_lock_request_specific(hwlock_id);
- if (!smem->hwlock)
- return -ENXIO;
-
- ret = hwspin_lock_timeout_irqsave(smem->hwlock, HWSPINLOCK_TIMEOUT, &flags);
- if (ret)
- return ret;
size = readl_relaxed(&header->available) + readl_relaxed(&header->free_offset);
- hwspin_unlock_irqrestore(smem->hwlock, &flags);
version = qcom_smem_get_sbl_version(smem);
- /*
- * smem header mapping is required only in heap version scheme, so unmap
- * it here. It will be remapped in qcom_smem_map_global() when whole
- * partition is mapped again.
- */
- devm_iounmap(smem->dev, smem->regions[0].virt_base);
switch (version >> 16) {
case SMEM_GLOBAL_PART_VERSION:
ret = qcom_smem_set_global_partition(smem);
if (ret < 0)
@@ -1215,63 +1066,17 @@ static int qcom_smem_probe(struct platform_device *pdev)
qcom_smem_map_global(smem, size);
smem->item_count = SMEM_ITEM_COUNT;
break;
default:
- dev_err(&pdev->dev, "Unsupported SMEM version 0x%x\n", version);
+ log_err("Unsupported SMEM version 0x%x\n", version);
return -EINVAL;
}
BUILD_BUG_ON(SMEM_HOST_APPS >= SMEM_HOST_COUNT);
ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS);
- if (ret < 0 && ret != -ENOENT)
+ if (ret < 0 && ret != -ENOENT) {
+ log_err("Failed to enumerate partitions\n");
return ret;
-
- __smem = smem;
-
- smem->socinfo = platform_device_register_data(&pdev->dev, "qcom-socinfo",
- PLATFORM_DEVID_NONE, NULL,
- 0);
- if (IS_ERR(smem->socinfo))
- dev_dbg(&pdev->dev, "failed to register socinfo device\n");
+ }
return 0;
}
-
-static void qcom_smem_remove(struct platform_device *pdev)
-{
- platform_device_unregister(__smem->socinfo);
-
- hwspin_lock_free(__smem->hwlock);
- __smem = NULL;
-}
-
-static const struct of_device_id qcom_smem_of_match[] = {
- { .compatible = "qcom,smem" },
- {}
-};
-MODULE_DEVICE_TABLE(of, qcom_smem_of_match);
-
-static struct platform_driver qcom_smem_driver = {
- .probe = qcom_smem_probe,
- .remove_new = qcom_smem_remove,
- .driver = {
- .name = "qcom-smem",
- .of_match_table = qcom_smem_of_match,
- .suppress_bind_attrs = true,
- },
-};
-
-static int __init qcom_smem_init(void)
-{
- return platform_driver_register(&qcom_smem_driver);
-}
-arch_initcall(qcom_smem_init);
-
-static void __exit qcom_smem_exit(void)
-{
- platform_driver_unregister(&qcom_smem_driver);
-}
-module_exit(qcom_smem_exit)
-
-MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
-MODULE_DESCRIPTION("Qualcomm Shared Memory Manager");
-MODULE_LICENSE("GPL v2");
diff --git a/include/soc/qcom/smem.h b/include/soc/qcom/smem.h
index f946e3beca21..a955db08c0f0 100644
--- a/include/soc/qcom/smem.h
+++ b/include/soc/qcom/smem.h
@@ -3,18 +3,13 @@
#define __QCOM_SMEM_H__
#define QCOM_SMEM_HOST_ANY -1
+int qcom_smem_init(void);
+
bool qcom_smem_is_available(void);
int qcom_smem_alloc(unsigned host, unsigned item, size_t size);
void *qcom_smem_get(unsigned host, unsigned item, size_t *size);
int qcom_smem_get_free_space(unsigned host);
-phys_addr_t qcom_smem_virt_to_phys(void *p);
-
-int qcom_smem_get_soc_id(u32 *id);
-int qcom_smem_get_feature_code(u32 *code);
-
-int qcom_smem_bust_hwspin_lock_by_host(unsigned int host);
-
#endif
--
2.53.0
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [PATCH v2 09/15] soc: qcom: smem: get serial number from socinfo
2026-05-04 18:57 [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot Casey Connolly
` (7 preceding siblings ...)
2026-05-04 18:57 ` [PATCH v2 08/15] soc: qcom: smem: adjust " Casey Connolly
@ 2026-05-04 18:57 ` Casey Connolly
2026-05-18 12:49 ` Sumit Garg
2026-05-04 18:57 ` [PATCH v2 10/15] soc: qcom: smem: stub functions Casey Connolly
` (6 subsequent siblings)
15 siblings, 1 reply; 51+ messages in thread
From: Casey Connolly @ 2026-05-04 18:57 UTC (permalink / raw)
To: u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Casey Connolly, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
Implement socinfo support to fetch the serial number if available.
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
---
drivers/soc/qcom/smem.c | 25 +++++++++++++++++++++++++
include/soc/qcom/smem.h | 1 +
2 files changed, 26 insertions(+)
diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
index 96288d7bcdef..e20523e2cec3 100644
--- a/drivers/soc/qcom/smem.c
+++ b/drivers/soc/qcom/smem.c
@@ -14,8 +14,10 @@
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/sizes.h>
#include <soc/qcom/smem.h>
+#include <soc/qcom/socinfo.h>
+#include <env.h>
/*
* The Qualcomm shared memory system is a allocate only heap structure that
* consists of one of more memory areas that can be accessed by the processors
@@ -991,8 +993,31 @@ static int qcom_smem_map_global(struct qcom_smem *smem, u32 size)
return 0;
}
+int qcom_socinfo_init(void)
+{
+ struct socinfo *info;
+ size_t item_size;
+ char buf[32] = { 0 };
+
+ info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID,
+ &item_size);
+ if (IS_ERR(info)) {
+ log_err("Couldn't find socinfo: %ld\n", PTR_ERR(info));
+ return PTR_ERR(info);
+ }
+
+ if (offsetof(struct socinfo, serial_num) + sizeof(info->serial_num) <= item_size) {
+ snprintf(buf, sizeof(buf), "%u", le32_to_cpu(info->serial_num));
+ env_set("serial#", buf);
+ } else {
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
int qcom_smem_init(void)
{
struct smem_header *header;
int num_regions;
diff --git a/include/soc/qcom/smem.h b/include/soc/qcom/smem.h
index a955db08c0f0..755003807dba 100644
--- a/include/soc/qcom/smem.h
+++ b/include/soc/qcom/smem.h
@@ -4,8 +4,9 @@
#define QCOM_SMEM_HOST_ANY -1
int qcom_smem_init(void);
+int qcom_socinfo_init(void);
bool qcom_smem_is_available(void);
int qcom_smem_alloc(unsigned host, unsigned item, size_t size);
void *qcom_smem_get(unsigned host, unsigned item, size_t *size);
--
2.53.0
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [PATCH v2 10/15] soc: qcom: smem: stub functions
2026-05-04 18:57 [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot Casey Connolly
` (8 preceding siblings ...)
2026-05-04 18:57 ` [PATCH v2 09/15] soc: qcom: smem: get serial number from socinfo Casey Connolly
@ 2026-05-04 18:57 ` Casey Connolly
2026-05-18 14:37 ` Neil Armstrong
2026-05-04 18:57 ` [PATCH v2 11/15] soc: qcom: smem: add build infra Casey Connolly
` (5 subsequent siblings)
15 siblings, 1 reply; 51+ messages in thread
From: Casey Connolly @ 2026-05-04 18:57 UTC (permalink / raw)
To: u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Casey Connolly, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
Allow smem to be optional for Qualcomm platforms by providing stub
functions.
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
---
include/soc/qcom/smem.h | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/include/soc/qcom/smem.h b/include/soc/qcom/smem.h
index 755003807dba..586432412eb8 100644
--- a/include/soc/qcom/smem.h
+++ b/include/soc/qcom/smem.h
@@ -1,16 +1,36 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __QCOM_SMEM_H__
#define __QCOM_SMEM_H__
+#include <linux/err.h>
+#include <stdbool.h>
+
#define QCOM_SMEM_HOST_ANY -1
+#if defined(CONFIG_QCOM_SMEM)
int qcom_smem_init(void);
int qcom_socinfo_init(void);
bool qcom_smem_is_available(void);
int qcom_smem_alloc(unsigned host, unsigned item, size_t size);
void *qcom_smem_get(unsigned host, unsigned item, size_t *size);
int qcom_smem_get_free_space(unsigned host);
+#else
+static int qcom_smem_init(void) { return -ENOSYS; }
+
+static bool qcom_smem_is_available(void) { return false; }
+int qcom_smem_alloc(unsigned host, unsigned item, size_t size)
+{
+ return -ENOSYS;
+}
+
+void *qcom_smem_get(unsigned host, unsigned item, size_t *size)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+int qcom_smem_get_free_space(unsigned host);
+#endif
#endif
--
2.53.0
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [PATCH v2 11/15] soc: qcom: smem: add build infra
2026-05-04 18:57 [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot Casey Connolly
` (9 preceding siblings ...)
2026-05-04 18:57 ` [PATCH v2 10/15] soc: qcom: smem: stub functions Casey Connolly
@ 2026-05-04 18:57 ` Casey Connolly
2026-05-18 12:53 ` Sumit Garg
2026-05-18 14:38 ` Neil Armstrong
2026-05-04 18:57 ` [PATCH v2 12/15] mach-snapdragon: move memory parsing to its own file Casey Connolly
` (4 subsequent siblings)
15 siblings, 2 replies; 51+ messages in thread
From: Casey Connolly @ 2026-05-04 18:57 UTC (permalink / raw)
To: u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Casey Connolly, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
Build the new SMEM driver port, and select it when ARCH_SNAPDRAGON is
selected, since it will be a hard dependency for Qualcomm platforms.
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
---
drivers/soc/qcom/Kconfig | 8 ++++++++
drivers/soc/qcom/Makefile | 1 +
2 files changed, 9 insertions(+)
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 8243805e46a3..cdd9e30f43e5 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -24,5 +24,13 @@ config QCOM_RPMH
help
Say y here to support the Qualcomm RPMh (resource peripheral manager)
if you need to control regulators on Qualcomm platforms, say y here.
+config QCOM_SMEM
+ bool "Qualcomm SMEM support"
+ help
+ Say y here to support the Qualcomm SMEM (shared memory) client driver.
+ SMEM is a shared memory region that is used to pass information about
+ the hardware (e.g. DRAM layout, PMIC configuration, etc) between bootloader
+ staged and the OS. If in doubt, say y.
+
endif # SOC_QCOM
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 78fae8bbfa16..f4102f9155a8 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0+
obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o
obj-$(CONFIG_QCOM_RPMH) += rpmh-rsc.o rpmh.o
+obj-$(CONFIG_QCOM_SMEM) += smem.o
--
2.53.0
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [PATCH v2 12/15] mach-snapdragon: move memory parsing to its own file
2026-05-04 18:57 [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot Casey Connolly
` (10 preceding siblings ...)
2026-05-04 18:57 ` [PATCH v2 11/15] soc: qcom: smem: add build infra Casey Connolly
@ 2026-05-04 18:57 ` Casey Connolly
2026-05-18 12:54 ` Sumit Garg
2026-05-18 14:40 ` Neil Armstrong
2026-05-04 18:57 ` [PATCH v2 13/15] mach-snapdragon: support parsing memory map from SMEM Casey Connolly
` (3 subsequent siblings)
15 siblings, 2 replies; 51+ messages in thread
From: Casey Connolly @ 2026-05-04 18:57 UTC (permalink / raw)
To: u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Casey Connolly, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
This code is getting a bit complicated, split it out to try and keep
things a bit better organised as we're going to be supporting populating
the memory layout from various other sources.
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
---
arch/arm/mach-snapdragon/Makefile | 2 +-
arch/arm/mach-snapdragon/board.c | 115 -------------------------------
arch/arm/mach-snapdragon/dram.c | 127 +++++++++++++++++++++++++++++++++++
arch/arm/mach-snapdragon/qcom-priv.h | 2 +
4 files changed, 130 insertions(+), 116 deletions(-)
diff --git a/arch/arm/mach-snapdragon/Makefile b/arch/arm/mach-snapdragon/Makefile
index 343e825c6fdd..e481e4f26e5c 100644
--- a/arch/arm/mach-snapdragon/Makefile
+++ b/arch/arm/mach-snapdragon/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
-obj-y += board.o
+obj-y += board.o dram.o
obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += capsule_update.o
obj-$(CONFIG_OF_LIVE) += of_fixup.o
diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c
index e12d3d00caa4..a2d97ad77910 100644
--- a/arch/arm/mach-snapdragon/board.c
+++ b/arch/arm/mach-snapdragon/board.c
@@ -42,123 +42,8 @@ enum qcom_boot_source qcom_boot_source __section(".data") = 0;
static struct mm_region rbx_mem_map[CONFIG_NR_DRAM_BANKS + 2] = { { 0 } };
struct mm_region *mem_map = rbx_mem_map;
-static struct {
- phys_addr_t start;
- phys_size_t size;
-} prevbl_ddr_banks[CONFIG_NR_DRAM_BANKS] __section(".data") = { 0 };
-
-int dram_init(void)
-{
- /*
- * gd->ram_base / ram_size have been setup already
- * in qcom_parse_memory().
- */
- return 0;
-}
-
-static int ddr_bank_cmp(const void *v1, const void *v2)
-{
- const struct {
- phys_addr_t start;
- phys_size_t size;
- } *res1 = v1, *res2 = v2;
-
- if (!res1->size)
- return 1;
- if (!res2->size)
- return -1;
-
- return (res1->start >> 24) - (res2->start >> 24);
-}
-
-/* This has to be done post-relocation since gd->bd isn't preserved */
-static void qcom_configure_bi_dram(void)
-{
- int i;
-
- for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
- gd->bd->bi_dram[i].start = prevbl_ddr_banks[i].start;
- gd->bd->bi_dram[i].size = prevbl_ddr_banks[i].size;
- }
-}
-
-int dram_init_banksize(void)
-{
- qcom_configure_bi_dram();
-
- return 0;
-}
-
-/**
- * The generic memory parsing code in U-Boot lacks a few things that we
- * need on Qualcomm:
- *
- * 1. It sets gd->ram_size and gd->ram_base to represent a single memory block
- * 2. setup_dest_addr() later relocates U-Boot to ram_base + ram_size, the end
- * of that first memory block.
- *
- * This results in all memory beyond U-Boot being unusable in Linux when booting
- * with EFI.
- *
- * Since the ranges in the memory node may be out of order, the only way for us
- * to correctly determine the relocation address for U-Boot is to parse all
- * memory regions and find the highest valid address.
- *
- * We can't use fdtdec_setup_memory_banksize() since it stores the result in
- * gd->bd, which is not yet allocated.
- *
- * @fdt: FDT blob to parse /memory node from
- *
- * Return: 0 on success or -ENODATA if /memory node is missing or incomplete
- */
-static int qcom_parse_memory(const void *fdt)
-{
- int offset;
- const fdt64_t *memory;
- int memsize;
- phys_addr_t ram_end = 0;
- int i, j, banks;
-
- offset = fdt_path_offset(fdt, "/memory");
- if (offset < 0)
- return -ENODATA;
-
- memory = fdt_getprop(fdt, offset, "reg", &memsize);
- if (!memory)
- return -ENODATA;
-
- banks = min(memsize / (2 * sizeof(u64)), (ulong)CONFIG_NR_DRAM_BANKS);
-
- if (memsize / sizeof(u64) > CONFIG_NR_DRAM_BANKS * 2)
- log_err("Provided more than the max of %d memory banks\n", CONFIG_NR_DRAM_BANKS);
-
- if (banks > CONFIG_NR_DRAM_BANKS)
- log_err("Provided more memory banks than we can handle\n");
-
- for (i = 0, j = 0; i < banks * 2; i += 2, j++) {
- prevbl_ddr_banks[j].start = get_unaligned_be64(&memory[i]);
- prevbl_ddr_banks[j].size = get_unaligned_be64(&memory[i + 1]);
- if (!prevbl_ddr_banks[j].size) {
- j--;
- continue;
- }
- ram_end = max(ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size);
- }
-
- if (!banks || !prevbl_ddr_banks[0].size)
- return -ENODATA;
-
- /* Sort our RAM banks -_- */
- qsort(prevbl_ddr_banks, banks, sizeof(prevbl_ddr_banks[0]), ddr_bank_cmp);
-
- gd->ram_base = prevbl_ddr_banks[0].start;
- gd->ram_size = ram_end - gd->ram_base;
-
- return 0;
-}
-
static void show_psci_version(void)
{
struct arm_smccc_res res;
diff --git a/arch/arm/mach-snapdragon/dram.c b/arch/arm/mach-snapdragon/dram.c
new file mode 100644
index 000000000000..ad73b685a935
--- /dev/null
+++ b/arch/arm/mach-snapdragon/dram.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Memory layout parsing for Qualcomm.
+ */
+
+#define LOG_CATEGORY LOGC_BOARD
+#define pr_fmt(fmt) "QCOM-DRAM: " fmt
+
+#include <asm-generic/unaligned.h>
+#include <dm.h>
+#include <log.h>
+#include <sort.h>
+
+static struct {
+ phys_addr_t start;
+ phys_size_t size;
+} prevbl_ddr_banks[CONFIG_NR_DRAM_BANKS] __section(".data") = { 0 };
+
+int dram_init(void)
+{
+ /*
+ * gd->ram_base / ram_size have been setup already
+ * in qcom_parse_memory().
+ */
+ return 0;
+}
+
+static int ddr_bank_cmp(const void *v1, const void *v2)
+{
+ const struct {
+ phys_addr_t start;
+ phys_size_t size;
+ } *res1 = v1, *res2 = v2;
+
+ if (!res1->size)
+ return 1;
+ if (!res2->size)
+ return -1;
+
+ return (res1->start >> 24) - (res2->start >> 24);
+}
+
+/* This has to be done post-relocation since gd->bd isn't preserved */
+static void qcom_configure_bi_dram(void)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
+ gd->bd->bi_dram[i].start = prevbl_ddr_banks[i].start;
+ gd->bd->bi_dram[i].size = prevbl_ddr_banks[i].size;
+ }
+}
+
+int dram_init_banksize(void)
+{
+ qcom_configure_bi_dram();
+
+ return 0;
+}
+
+/**
+ * The generic memory parsing code in U-Boot lacks a few things that we
+ * need on Qualcomm:
+ *
+ * 1. It sets gd->ram_size and gd->ram_base to represent a single memory block
+ * 2. setup_dest_addr() later relocates U-Boot to ram_base + ram_size, the end
+ * of that first memory block.
+ *
+ * This results in all memory beyond U-Boot being unusable in Linux when booting
+ * with EFI.
+ *
+ * Since the ranges in the memory node may be out of order, the only way for us
+ * to correctly determine the relocation address for U-Boot is to parse all
+ * memory regions and find the highest valid address.
+ *
+ * We can't use fdtdec_setup_memory_banksize() since it stores the result in
+ * gd->bd, which is not yet allocated.
+ *
+ * @fdt: FDT blob to parse /memory node from
+ *
+ * Return: 0 on success or -ENODATA if /memory node is missing or incomplete
+ */
+int qcom_parse_memory(const void *fdt)
+{
+ int offset;
+ const fdt64_t *memory;
+ int memsize;
+ phys_addr_t ram_end = 0;
+ int i, j, banks;
+
+ offset = fdt_path_offset(fdt, "/memory");
+ if (offset < 0)
+ return -ENODATA;
+
+ memory = fdt_getprop(fdt, offset, "reg", &memsize);
+ if (!memory)
+ return -ENODATA;
+
+ banks = min(memsize / (2 * sizeof(u64)), (ulong)CONFIG_NR_DRAM_BANKS);
+
+ if (memsize / sizeof(u64) > CONFIG_NR_DRAM_BANKS * 2)
+ log_err("Provided more than the max of %d memory banks\n", CONFIG_NR_DRAM_BANKS);
+
+ if (banks > CONFIG_NR_DRAM_BANKS)
+ log_err("Provided more memory banks than we can handle\n");
+
+ for (i = 0, j = 0; i < banks * 2; i += 2, j++) {
+ prevbl_ddr_banks[j].start = get_unaligned_be64(&memory[i]);
+ prevbl_ddr_banks[j].size = get_unaligned_be64(&memory[i + 1]);
+ if (!prevbl_ddr_banks[j].size) {
+ j--;
+ continue;
+ }
+ ram_end = max(ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size);
+ }
+
+ if (!banks || !prevbl_ddr_banks[0].size)
+ return -ENODATA;
+
+ /* Sort our RAM banks -_- */
+ qsort(prevbl_ddr_banks, banks, sizeof(prevbl_ddr_banks[0]), ddr_bank_cmp);
+
+ gd->ram_base = prevbl_ddr_banks[0].start;
+ gd->ram_size = ram_end - gd->ram_base;
+
+ return 0;
+}
diff --git a/arch/arm/mach-snapdragon/qcom-priv.h b/arch/arm/mach-snapdragon/qcom-priv.h
index b8bf574e8bbb..ce409314a98b 100644
--- a/arch/arm/mach-snapdragon/qcom-priv.h
+++ b/arch/arm/mach-snapdragon/qcom-priv.h
@@ -22,5 +22,7 @@ void qcom_configure_capsule_updates(void);
#else
void qcom_configure_capsule_updates(void) {}
#endif /* EFI_HAVE_CAPSULE_SUPPORT */
+int qcom_parse_memory(const void *fdt);
+
#endif /* __QCOM_PRIV_H__ */
--
2.53.0
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [PATCH v2 13/15] mach-snapdragon: support parsing memory map from SMEM
2026-05-04 18:57 [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot Casey Connolly
` (11 preceding siblings ...)
2026-05-04 18:57 ` [PATCH v2 12/15] mach-snapdragon: move memory parsing to its own file Casey Connolly
@ 2026-05-04 18:57 ` Casey Connolly
2026-05-18 14:48 ` Neil Armstrong
2026-05-21 13:36 ` Stephan Gerhold
2026-05-04 18:57 ` [PATCH v2 14/15] mach-snapdragon: fetch serial# " Casey Connolly
` (2 subsequent siblings)
15 siblings, 2 replies; 51+ messages in thread
From: Casey Connolly @ 2026-05-04 18:57 UTC (permalink / raw)
To: u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Casey Connolly, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
It is possible to derive the memory map for a Qualcomm platform from the
SMEM shared memory region. The memory map is populated by the preloader.
Introduce support for parsing this data and using it to populate
U-Boot's memory map. Since we aren't yet sure if this will work for
every platform, it is not yet used in all cases, if U-Boot is booted
with an internal FDT which has the memory map defined, this will
be used instead.
If the FDT is internal FDT with no memory map defined, then U-Boot will
try to use SMEM. This should remove the need to define the memory map
statically in a U-Boot overlay for boards that don't chainload.
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
---
arch/arm/Kconfig | 1 +
arch/arm/mach-snapdragon/board.c | 45 +++++---
arch/arm/mach-snapdragon/dram.c | 172 +++++++++++++++++++++++++------
arch/arm/mach-snapdragon/qcom-priv.h | 17 ++-
arch/arm/mach-snapdragon/rampart.h | 194 +++++++++++++++++++++++++++++++++++
5 files changed, 386 insertions(+), 43 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index a8fcfe76296c..1da0677d85b3 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1140,8 +1140,9 @@ config ARCH_SNAPDRAGON
select DM_RESET
select POWER_DOMAIN
select GPIO_EXTRA_HEADER
select OF_CONTROL
+ select QCOM_SMEM
select SPMI
select BOARD_LATE_INIT
select OF_BOARD
select SAVE_PREV_BL_FDT_ADDR if !ENABLE_ARM_SOC_BOOT0_HOOK
diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c
index a2d97ad77910..7610891191fd 100644
--- a/arch/arm/mach-snapdragon/board.c
+++ b/arch/arm/mach-snapdragon/board.c
@@ -29,16 +29,18 @@
#include <lmb.h>
#include <malloc.h>
#include <fdt_support.h>
#include <usb.h>
+#include <soc/qcom/smem.h>
#include <sort.h>
#include <time.h>
#include "qcom-priv.h"
DECLARE_GLOBAL_DATA_PTR;
enum qcom_boot_source qcom_boot_source __section(".data") = 0;
+enum qcom_memmap_source qcom_memmap_source __section(".data") = 0;
static struct mm_region rbx_mem_map[CONFIG_NR_DRAM_BANKS + 2] = { { 0 } };
struct mm_region *mem_map = rbx_mem_map;
@@ -109,18 +111,18 @@ int board_fdt_blob_setup(void **fdtp)
(phys_addr_t)external_fdt);
/* Prefer memory information from internal DT if it's present */
if (internal_valid)
- ret = qcom_parse_memory(internal_fdt);
+ ret = qcom_parse_memory(internal_fdt, true);
if (ret < 0 && external_valid) {
/* No internal FDT or it lacks a proper /memory node.
* The previous bootloader handed us something, let's try that.
*/
if (internal_valid)
debug("No memory info in internal FDT, falling back to external\n");
- ret = qcom_parse_memory(external_fdt);
+ ret = qcom_parse_memory(external_fdt, false);
}
if (ret < 0)
panic("No valid memory ranges found!\n");
@@ -373,23 +375,39 @@ static void configure_env(void)
qcom_set_serialno();
}
-void qcom_show_boot_source(void)
+static void qcom_show_boot_context(void)
{
- const char *name = "UNKNOWN";
+ const char *boot_source = "UNKNOWN";
+ const char *memmap_source = "UNKNOWN";
switch (qcom_boot_source) {
case QCOM_BOOT_SOURCE_ANDROID:
- name = "ABL";
+ boot_source = "ABL";
break;
case QCOM_BOOT_SOURCE_XBL:
- name = "XBL";
+ boot_source = "XBL";
break;
}
- log_info("U-Boot loaded from %s\n", name);
- env_set("boot_source", name);
+ log_info("U-Boot loaded from %s\n", boot_source);
+ env_set("boot_source", boot_source);
+
+ switch (qcom_memmap_source) {
+ case QCOM_MEMMAP_SOURCE_INTERNAL_FDT:
+ memmap_source = "INTERNAL_FDT";
+ break;
+ case QCOM_MEMMAP_SOURCE_EXTERNAL_FDT:
+ memmap_source = "EXTERNAL_FDT";
+ break;
+ case QCOM_MEMMAP_SOURCE_SMEM:
+ memmap_source = "SMEM";
+ break;
+ }
+
+ log_info("Memory map loaded from %s\n", memmap_source);
+ env_set("memmap_source", memmap_source);
}
void __weak qcom_late_init(void)
{
@@ -458,9 +476,9 @@ int board_late_init(void)
configure_env();
qcom_late_init();
- qcom_show_boot_source();
+ qcom_show_boot_context();
/* Configure the dfu_string for capsule updates */
qcom_configure_capsule_updates();
return 0;
@@ -648,11 +666,14 @@ void enable_caches(void)
gd->arch.tlb_emerg = gd->arch.tlb_addr;
gd->arch.tlb_addr = tlb_addr;
gd->arch.tlb_size = tlb_size;
- /* On some boards speculative access may trigger a NOC or XPU violation so explicitly mark reserved
- * regions as inacessible (PTE_TYPE_FAULT) */
- if (fdt_node_check_compatible(gd->fdt_blob, 0, "qcom,qcs404") == 0) {
+ /*
+ * On some boards speculative access may trigger a NOC or XPU violation so explicitly mark
+ * reserved regions as inacessible (PTE_TYPE_FAULT)
+ */
+ if (qcom_memmap_source == QCOM_MEMMAP_SOURCE_SMEM ||
+ fdt_node_check_compatible(gd->fdt_blob, 0, "qcom,qcs404") == 0) {
carveout_start = get_timer(0);
/* Takes ~20-50ms on SDM845 */
configure_reserved_memory();
debug("carveout time: %lums\n", get_timer(carveout_start));
diff --git a/arch/arm/mach-snapdragon/dram.c b/arch/arm/mach-snapdragon/dram.c
index ad73b685a935..af5f3c84882c 100644
--- a/arch/arm/mach-snapdragon/dram.c
+++ b/arch/arm/mach-snapdragon/dram.c
@@ -9,8 +9,12 @@
#include <asm-generic/unaligned.h>
#include <dm.h>
#include <log.h>
#include <sort.h>
+#include <soc/qcom/smem.h>
+
+#include "qcom-priv.h"
+#include "rampart.h"
static struct {
phys_addr_t start;
phys_size_t size;
@@ -47,8 +51,12 @@ static void qcom_configure_bi_dram(void)
for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
gd->bd->bi_dram[i].start = prevbl_ddr_banks[i].start;
gd->bd->bi_dram[i].size = prevbl_ddr_banks[i].size;
+ debug("Bank[%d]: start = %#011llx, size = %#011llx\n",
+ i, gd->bd->bi_dram[i].start, gd->bd->bi_dram[i].size);
+ if (!prevbl_ddr_banks[i].size)
+ break;
}
}
int dram_init_banksize(void)
@@ -57,8 +65,124 @@ int dram_init_banksize(void)
return 0;
}
+#define entry_field(v, e, field) (v == 0 ? ((struct ram_partition_entry_v0 *)(e))->field : \
+ (v == 1 ? ((struct ram_partition_entry_v1 *)(e))->field : \
+ ((struct ram_partition_entry_v3 *)(e))->field))
+#define entry_start(v, e) entry_field(v, e, start_address)
+#define entry_length(v, e) entry_field(v, e, length)
+#define entry_category(v, e) entry_field(v, e, partition_category)
+#define entry_domain(v, e) entry_field(v, e, partition_domain)
+#define entry_type(v, e) entry_field(v, e, partition_type)
+
+/* Parse memory map from SMEM, return the number of entries */
+static int qcom_parse_memory_smem(phys_addr_t *ram_end)
+{
+ size_t size;
+ int i, j = 0, ret;
+ struct usable_ram_partition_table_header *header;
+ u32 ver;
+ void *entry;
+ u32 entry_size; // Size of each RAM partition entry (version dependent)
+
+ ret = qcom_smem_init();
+ if (ret) {
+ debug("Failed to initialize SMEM: %d.\n", ret);
+ return ret;
+ }
+
+ header = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_USABLE_RAM_PARTITION_TABLE, &size);
+ if (!header) {
+ debug("Failed to find SMEM partition.\n");
+ return -ENODEV;
+ }
+
+ ver = header->version;
+ debug("SMEM RAM partition table version %u. %u entries\n", ver, header->num_partitions);
+
+ switch (ver) {
+ case 0:
+ entry_size = sizeof(struct ram_partition_entry_v0);
+ entry = ((struct usable_ram_partition_table_v0 *)header)->entries;
+ break;
+ case 1:
+ entry_size = sizeof(struct ram_partition_entry_v1);
+ entry = ((struct usable_ram_partition_table_v1 *)header)->entries;
+ break;
+ default:
+ pr_warn("Unknown SMEM ram partition table version!\n");
+ case 2:
+ case 3:
+ entry_size = sizeof(struct ram_partition_entry_v3);
+ entry = ((struct usable_ram_partition_table_v3 *)header)->entries;
+ break;
+ }
+
+ debug("SMEM RAM partition entry size: %u bytes\n", entry_size);
+
+ /* Check validy of RAM */
+ for (i = 0; i < header->num_partitions && j < CONFIG_NR_DRAM_BANKS; i++, entry += entry_size) {
+ debug("Entry %d: [%#010llx - %#010llx]\n", i, entry_start(ver, entry),
+ (u64)entry_start(ver, entry) + entry_length(ver, entry));
+ debug(" cat: %#04x type: %#04x domain: %#04x\n", entry_category(ver, entry),
+ entry_type(ver, entry), entry_domain(ver, entry));
+
+
+ if (entry_category(ver, entry) != RAM_PARTITION_SDRAM ||
+ entry_type(ver, entry) != RAM_PARTITION_SYS_MEMORY)
+ continue;
+ if (!entry_length(ver, entry) && !entry_start(ver, entry))
+ break;
+
+ prevbl_ddr_banks[j].start = entry_start(ver, entry);
+ prevbl_ddr_banks[j].size = entry_length(ver, entry);
+ *ram_end = max(*ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size);
+ j++;
+ }
+
+ if (j == CONFIG_NR_DRAM_BANKS)
+ pr_err("SMEM: More than CONFIG_NR_DRAM_BANKS (%u) entries!", CONFIG_NR_DRAM_BANKS);
+
+ return j;
+}
+
+static void qcom_parse_memory_dt(const fdt64_t *fdt, int *banks, phys_addr_t *ram_end)
+{
+ int offset;
+ const fdt64_t *memory;
+ int memsize;
+ int i, j;
+
+ *ram_end = 0;
+
+ offset = fdt_path_offset(fdt, "/memory");
+ if (offset < 0)
+ return;
+
+ memory = fdt_getprop(fdt, offset, "reg", &memsize);
+ if (!memory)
+ return;
+
+ *banks = min(memsize / (2 * sizeof(u64)), (ulong)CONFIG_NR_DRAM_BANKS);
+
+ if (memsize / sizeof(u64) > CONFIG_NR_DRAM_BANKS * 2)
+ log_err("Provided more than the max of %d memory banks\n", CONFIG_NR_DRAM_BANKS);
+
+ if (*banks > CONFIG_NR_DRAM_BANKS)
+ log_err("Provided more memory banks than we can handle\n");
+
+ for (i = 0, j = 0; i < *banks * 2; i += 2, j++) {
+ prevbl_ddr_banks[j].start = get_unaligned_be64(&memory[i]);
+ prevbl_ddr_banks[j].size = get_unaligned_be64(&memory[i + 1]);
+ if (!prevbl_ddr_banks[j].size) {
+ j--;
+ continue;
+ }
+ *ram_end = max(*ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size);
+ }
+}
+
/**
* The generic memory parsing code in U-Boot lacks a few things that we
* need on Qualcomm:
*
@@ -76,48 +200,36 @@ int dram_init_banksize(void)
* We can't use fdtdec_setup_memory_banksize() since it stores the result in
* gd->bd, which is not yet allocated.
*
* @fdt: FDT blob to parse /memory node from
+ * @fdt_is_internal: is the FDT one embedded into U-Boot or was it provided by a prior
+ * bootloader stage? This determined if we should try to rely on SMEM
+ * (internal FDT) or error. We currently assume that if we are passed
+ * an external FDT then it already has the memory map populated.
*
* Return: 0 on success or -ENODATA if /memory node is missing or incomplete
*/
-int qcom_parse_memory(const void *fdt)
+int qcom_parse_memory(const void *fdt, bool fdt_is_internal)
{
- int offset;
- const fdt64_t *memory;
- int memsize;
phys_addr_t ram_end = 0;
- int i, j, banks;
+ int banks;
- offset = fdt_path_offset(fdt, "/memory");
- if (offset < 0)
- return -ENODATA;
+ qcom_memmap_source = fdt_is_internal ? QCOM_MEMMAP_SOURCE_INTERNAL_FDT
+ : QCOM_MEMMAP_SOURCE_EXTERNAL_FDT;
- memory = fdt_getprop(fdt, offset, "reg", &memsize);
- if (!memory)
- return -ENODATA;
+ qcom_parse_memory_dt(fdt, &banks, &ram_end);
- banks = min(memsize / (2 * sizeof(u64)), (ulong)CONFIG_NR_DRAM_BANKS);
-
- if (memsize / sizeof(u64) > CONFIG_NR_DRAM_BANKS * 2)
- log_err("Provided more than the max of %d memory banks\n", CONFIG_NR_DRAM_BANKS);
-
- if (banks > CONFIG_NR_DRAM_BANKS)
- log_err("Provided more memory banks than we can handle\n");
-
- for (i = 0, j = 0; i < banks * 2; i += 2, j++) {
- prevbl_ddr_banks[j].start = get_unaligned_be64(&memory[i]);
- prevbl_ddr_banks[j].size = get_unaligned_be64(&memory[i + 1]);
- if (!prevbl_ddr_banks[j].size) {
- j--;
- continue;
- }
- ram_end = max(ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size);
+ /*
+ * If using an internal FDT but the memory node is empty
+ * then fall back to SMEM.
+ */
+ if (!prevbl_ddr_banks[0].size && fdt_is_internal) {
+ banks = qcom_parse_memory_smem(&ram_end);
+ if (banks < 0)
+ panic("Couldn't find a valid memory map!\n");
+ qcom_memmap_source = QCOM_MEMMAP_SOURCE_SMEM;
}
- if (!banks || !prevbl_ddr_banks[0].size)
- return -ENODATA;
-
/* Sort our RAM banks -_- */
qsort(prevbl_ddr_banks, banks, sizeof(prevbl_ddr_banks[0]), ddr_bank_cmp);
gd->ram_base = prevbl_ddr_banks[0].start;
diff --git a/arch/arm/mach-snapdragon/qcom-priv.h b/arch/arm/mach-snapdragon/qcom-priv.h
index ce409314a98b..39dc8fcc76ad 100644
--- a/arch/arm/mach-snapdragon/qcom-priv.h
+++ b/arch/arm/mach-snapdragon/qcom-priv.h
@@ -2,8 +2,10 @@
#ifndef __QCOM_PRIV_H__
#define __QCOM_PRIV_H__
+#include <stdbool.h>
+
/**
* enum qcom_boot_source - Track where we got loaded from.
* Used for capsule update logic.
*
@@ -16,13 +18,26 @@ enum qcom_boot_source {
};
extern enum qcom_boot_source qcom_boot_source;
+/*
+ * enum qcom_memmap_source - Track where we got the memory map from.
+ * used for debugging and validation.
+ */
+enum qcom_memmap_source {
+ QCOM_MEMMAP_SOURCE_INTERNAL_FDT = 1,
+ QCOM_MEMMAP_SOURCE_EXTERNAL_FDT,
+ QCOM_MEMMAP_SOURCE_SMEM,
+};
+
+/* Set by qcom_parse_memory() */
+extern enum qcom_memmap_source qcom_memmap_source;
+
#if IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_SUPPORT)
void qcom_configure_capsule_updates(void);
#else
void qcom_configure_capsule_updates(void) {}
#endif /* EFI_HAVE_CAPSULE_SUPPORT */
-int qcom_parse_memory(const void *fdt);
+int qcom_parse_memory(const void *fdt, bool fdt_is_internal);
#endif /* __QCOM_PRIV_H__ */
diff --git a/arch/arm/mach-snapdragon/rampart.h b/arch/arm/mach-snapdragon/rampart.h
new file mode 100644
index 000000000000..57f3dbb7eec0
--- /dev/null
+++ b/arch/arm/mach-snapdragon/rampart.h
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RAM partition table definitions
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ */
+
+#ifndef __QCOM_SMEM_RAMPART_H__
+#define __QCOM_SMEM_RAMPART_H__
+
+#include <linux/types.h>
+
+#define SMEM_USABLE_RAM_PARTITION_TABLE 402
+
+#define RAM_PARTITION_H_MAJOR 03
+#define RAM_PARTITION_H_MINOR 00
+
+/**
+ * Total length of zero filled name string. This is not a C
+ * string, as it can occupy the total number of bytes, and if
+ * it does, it does not require a zero terminator. It cannot
+ * be manipulated with standard string handling library functions.
+ */
+#define RAM_PART_NAME_LENGTH 16
+
+/**
+ * Number of RAM partition entries which are usable by APPS.
+ */
+#define RAM_NUM_PART_ENTRIES 32
+
+/**
+ * @name: Magic numbers
+ * Used in identifying valid RAM partition table.
+ */
+#define RAM_PART_MAGIC1 0x9da5e0a8
+#define RAM_PART_MAGIC2 0xaf9ec4e2
+
+/**
+ * RAM partition attributes.
+ */
+enum ram_partition_attribute_t {
+ RAM_PARTITION_READ_ONLY = 0, /* Read-only RAM partition */
+ RAM_PARTITION_READWRITE, /* Read/write RAM partition */
+};
+
+/**
+ * RAM partition categories.
+ */
+enum ram_partition_category_t {
+ RAM_PARTITION_IRAM = 4, /* IRAM RAM partition */
+ RAM_PARTITION_IMEM = 5, /* IMEM RAM partition */
+ RAM_PARTITION_SDRAM = 14, /* SDRAM type without specific bus information**/
+};
+
+/**
+ * RAM Partition domains.
+ * @note: For shared RAM partition, domain value would be 0b11:\n
+ * RAM_PARTITION_APPS_DOMAIN | RAM_PARTITION_MODEM_DOMAIN.
+ */
+enum ram_partition_domain_t {
+ RAM_PARTITION_DEFAULT_DOMAIN = 0, /* 0b00: No specific domain definition */
+ RAM_PARTITION_APPS_DOMAIN = 1, /* 0b01: APPS RAM partition */
+ RAM_PARTITION_MODEM_DOMAIN = 2, /* 0b10: MODEM RAM partition */
+};
+
+/**
+ * RAM Partition types.
+ * @note: The RAM_PARTITION_SYS_MEMORY type represents DDR rams that are attached
+ * to the current system.
+ */
+enum ram_partition_type_t {
+ RAM_PARTITION_SYS_MEMORY = 1, /* system memory */
+ RAM_PARTITION_BOOT_REGION_MEMORY1, /* boot loader memory 1 */
+ RAM_PARTITION_BOOT_REGION_MEMORY2, /* boot loader memory 2, reserved */
+ RAM_PARTITION_APPSBL_MEMORY, /* apps boot loader memory */
+ RAM_PARTITION_APPS_MEMORY, /* apps usage memory */
+ RAM_PARTITION_TOOLS_FV_MEMORY, /* tools usage memory */
+ RAM_PARTITION_QUANTUM_FV_MEMORY, /* quantum usage memory */
+ RAM_PARTITION_QUEST_FV_MEMORY, /* quest usage memory */
+};
+
+/* Common table header between versions */
+struct usable_ram_partition_table_header {
+ u32 magic1; /* Magic number to identify valid RAM partition table */
+ u32 magic2; /* Magic number to identify valid RAM partition table */
+ u32 version; /* Version number to track structure definition changes */
+ u32 reserved1; /* Reserved for future use */
+
+ u32 num_partitions; /* Number of RAM partition table entries */
+};
+
+/* Holds information for an entry in the RAM partition table */
+struct ram_partition_entry_v3 {
+ char name[RAM_PART_NAME_LENGTH]; /* Partition name, unused for now */
+ u64 start_address; /* Partition start address in RAM */
+ u64 length; /* Partition length in RAM in Bytes */
+ u32 partition_attribute; /* Partition attribute */
+ u32 partition_category; /* Partition category */
+ u32 partition_domain; /* Partition domain */
+ u32 partition_type; /* Partition type */
+ u32 num_partitions; /* Number of partitions on device */
+ u32 hw_info; /* hw information such as type and frequency */
+ u8 highest_bank_bit; /* Highest bit corresponding to a bank */
+ u8 reserve0; /* Reserved for future use */
+ u8 reserve1; /* Reserved for future use */
+ u8 reserve2; /* Reserved for future use */
+ u32 min_pasr_size; /* Minimum PASR size in MB */
+ u64 available_length; /* Available Partition length in RAM in Bytes */
+};
+
+/*
+ * Defines the RAM partition table structure
+ *
+ * Do not change the placement of the first four elements so that future
+ * compatibility will always be guaranteed at least for the identifiers.
+ *
+ * The other portion of the structure may be changed as necessary to accommodate
+ * new features. Be sure to increment version number if you change it.
+ */
+struct usable_ram_partition_table_v3 {
+ struct usable_ram_partition_table_header header;
+
+ u32 reserved2; /* Added for 8 bytes alignment of header */
+
+ /* RAM partition table entries */
+ struct ram_partition_entry_v3 entries[RAM_NUM_PART_ENTRIES];
+};
+
+/* Version 1 structure 32 Bit - Holds information for an entry in the RAM partition table */
+struct ram_partition_entry_v1 {
+ char name[RAM_PART_NAME_LENGTH]; /* Partition name, unused for now */
+ u64 start_address; /* Partition start address in RAM */
+ u64 length; /* Partition length in RAM in Bytes */
+ u32 partition_attribute; /* Partition attribute */
+ u32 partition_category; /* Partition category */
+ u32 partition_domain; /* Partition domain */
+ u32 partition_type; /* Partition type */
+ u32 num_partitions; /* Number of partitions on device */
+ u32 hw_info; /* hw information such as type and frequency */
+ u32 reserved4; /* Reserved for future use */
+ u32 reserved5; /* Reserved for future use */
+};
+
+/*
+ * Defines the RAM partition table structure (Version 1)
+ *
+ * Do not change the placement of the first four elements so that future
+ * compatibility will always be guaranteed at least for the identifiers.
+ *
+ * The other portion of the structure may be changed as necessary to accommodate
+ * new features. Be sure to increment version number if you change it.
+ */
+struct usable_ram_partition_table_v1 {
+ struct usable_ram_partition_table_header header;
+
+ u32 reserved2; /* Added for 8 bytes alignment of header */
+
+ /* RAM partition table entries */
+ struct ram_partition_entry_v1 entries[RAM_NUM_PART_ENTRIES];
+};
+
+/* Version 0 structure 32 Bit - Holds information for an entry in the RAM partition table */
+struct ram_partition_entry_v0 {
+ char name[RAM_PART_NAME_LENGTH]; /* Partition name, unused for now */
+ u32 start_address; /* Partition start address in RAM */
+ u32 length; /* Partition length in RAM in Bytes */
+ u32 partition_attribute; /* Partition attribute */
+ u32 partition_category; /* Partition category */
+ u32 partition_domain; /* Partition domain */
+ u32 partition_type; /* Partition type */
+ u32 num_partitions; /* Number of partitions on device */
+ u32 reserved3; /* Reserved for future use */
+ u32 reserved4; /* Reserved for future use */
+ u32 reserved5; /* Reserved for future use */
+};
+
+/*
+ * Defines the RAM partition table structure (Version 0)
+ *
+ * Do not change the placement of the first four elements so that future
+ * compatibility will always be guaranteed at least for the identifiers.
+ *
+ * The other portion of the structure may be changed as necessary to accommodate
+ * new features. Be sure to increment version number if you change it.
+ */
+struct usable_ram_partition_table_v0 {
+ struct usable_ram_partition_table_header header;
+
+ /* RAM partition table entries */
+ struct ram_partition_entry_v0 entries[RAM_NUM_PART_ENTRIES];
+};
+
+#endif // __QCOM_SMEM_RAMPART_H__
--
2.53.0
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [PATCH v2 14/15] mach-snapdragon: fetch serial# from SMEM
2026-05-04 18:57 [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot Casey Connolly
` (12 preceding siblings ...)
2026-05-04 18:57 ` [PATCH v2 13/15] mach-snapdragon: support parsing memory map from SMEM Casey Connolly
@ 2026-05-04 18:57 ` Casey Connolly
2026-05-18 13:15 ` Sumit Garg
2026-05-04 18:57 ` [PATCH v2 15/15] configs: add qcom_sm8650_defconfig and debug fragment Casey Connolly
2026-05-05 10:11 ` [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot Sumit Garg
15 siblings, 1 reply; 51+ messages in thread
From: Casey Connolly @ 2026-05-04 18:57 UTC (permalink / raw)
To: u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Casey Connolly, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
If available, otherwise fall back to cmdline.
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
---
arch/arm/mach-snapdragon/board.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c
index 7610891191fd..63055a60af44 100644
--- a/arch/arm/mach-snapdragon/board.c
+++ b/arch/arm/mach-snapdragon/board.c
@@ -31,8 +31,9 @@
#include <fdt_support.h>
#include <usb.h>
#include <soc/qcom/smem.h>
#include <sort.h>
+#include <soc/qcom/smem.h>
#include <time.h>
#include "qcom-priv.h"
@@ -251,11 +252,16 @@ static const char *get_cmdline(void)
}
void qcom_set_serialno(void)
{
- const char *cmdline = get_cmdline();
+ const char *cmdline;
char serial[32];
+ if (!qcom_socinfo_init())
+ return;
+
+ cmdline = get_cmdline();
+
if (!cmdline) {
log_debug("Failed to get bootargs\n");
return;
}
@@ -473,8 +479,11 @@ int board_late_init(void)
else
memcpy((void *)addr, (void *)gd->fdt_blob,
fdt32_to_cpu(fdt_blob->totalsize));
+ /* Initialise SMEM if it wasn't done already and ensure it's memory is mapped */
+ qcom_smem_init();
+
configure_env();
qcom_late_init();
qcom_show_boot_context();
--
2.53.0
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [PATCH v2 15/15] configs: add qcom_sm8650_defconfig and debug fragment
2026-05-04 18:57 [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot Casey Connolly
` (13 preceding siblings ...)
2026-05-04 18:57 ` [PATCH v2 14/15] mach-snapdragon: fetch serial# " Casey Connolly
@ 2026-05-04 18:57 ` Casey Connolly
2026-05-05 6:45 ` Luca Weiss
2026-05-05 10:11 ` [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot Sumit Garg
15 siblings, 1 reply; 51+ messages in thread
From: Casey Connolly @ 2026-05-04 18:57 UTC (permalink / raw)
To: u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Casey Connolly, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
Add a defconfig for running on sm8650 as the first stage bootloader and
associated debug config fragment.
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
---
board/qualcomm/debug-sm8650.config | 5 +++++
configs/qcom_sm8650_defconfig | 16 ++++++++++++++++
2 files changed, 21 insertions(+)
diff --git a/board/qualcomm/debug-sm8650.config b/board/qualcomm/debug-sm8650.config
new file mode 100644
index 000000000000..8323bf25e9c1
--- /dev/null
+++ b/board/qualcomm/debug-sm8650.config
@@ -0,0 +1,5 @@
+CONFIG_DEBUG_UART=y
+CONFIG_DEBUG_UART_ANNOUNCE=y
+CONFIG_DEBUG_UART_BASE=0x89c000
+CONFIG_DEBUG_UART_MSM_GENI=y
+CONFIG_DEBUG_UART_CLOCK=14745600
diff --git a/configs/qcom_sm8650_defconfig b/configs/qcom_sm8650_defconfig
new file mode 100644
index 000000000000..e0a82c460337
--- /dev/null
+++ b/configs/qcom_sm8650_defconfig
@@ -0,0 +1,16 @@
+# Configuration for building U-Boot to be flashed
+# to the uefi partition of QCM6490 dev boards with
+# the "Linux Embedded" partition layout (which have
+# a dedicated "uefi" partition for edk2/U-Boot)
+
+#include "qcom_defconfig"
+
+# Otherwise buildman thinks this isn't an ARM platform
+CONFIG_ARM=y
+
+# Address where U-Boot will be loaded
+CONFIG_TEXT_BASE=0xa7000000
+CONFIG_REMAKE_ELF=y
+
+CONFIG_DEFAULT_DEVICE_TREE="qcom/sm8650-hdk"
+
--
2.53.0
^ permalink raw reply related [flat|nested] 51+ messages in thread
* Re: [PATCH v2 15/15] configs: add qcom_sm8650_defconfig and debug fragment
2026-05-04 18:57 ` [PATCH v2 15/15] configs: add qcom_sm8650_defconfig and debug fragment Casey Connolly
@ 2026-05-05 6:45 ` Luca Weiss
0 siblings, 0 replies; 51+ messages in thread
From: Luca Weiss @ 2026-05-05 6:45 UTC (permalink / raw)
To: Casey Connolly, u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Peng Fan, Marek Vasut, Alice Guo,
Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
Hi Casey,
On Mon May 4, 2026 at 8:57 PM CEST, Casey Connolly wrote:
> Add a defconfig for running on sm8650 as the first stage bootloader and
> associated debug config fragment.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> board/qualcomm/debug-sm8650.config | 5 +++++
> configs/qcom_sm8650_defconfig | 16 ++++++++++++++++
> 2 files changed, 21 insertions(+)
>
> diff --git a/board/qualcomm/debug-sm8650.config b/board/qualcomm/debug-sm8650.config
> new file mode 100644
> index 000000000000..8323bf25e9c1
> --- /dev/null
> +++ b/board/qualcomm/debug-sm8650.config
> @@ -0,0 +1,5 @@
> +CONFIG_DEBUG_UART=y
> +CONFIG_DEBUG_UART_ANNOUNCE=y
> +CONFIG_DEBUG_UART_BASE=0x89c000
> +CONFIG_DEBUG_UART_MSM_GENI=y
> +CONFIG_DEBUG_UART_CLOCK=14745600
> diff --git a/configs/qcom_sm8650_defconfig b/configs/qcom_sm8650_defconfig
> new file mode 100644
> index 000000000000..e0a82c460337
> --- /dev/null
> +++ b/configs/qcom_sm8650_defconfig
> @@ -0,0 +1,16 @@
> +# Configuration for building U-Boot to be flashed
> +# to the uefi partition of QCM6490 dev boards with
QCM6490 copy-paste :)
Regards
Luca
> +# the "Linux Embedded" partition layout (which have
> +# a dedicated "uefi" partition for edk2/U-Boot)
> +
> +#include "qcom_defconfig"
> +
> +# Otherwise buildman thinks this isn't an ARM platform
> +CONFIG_ARM=y
> +
> +# Address where U-Boot will be loaded
> +CONFIG_TEXT_BASE=0xa7000000
> +CONFIG_REMAKE_ELF=y
> +
> +CONFIG_DEFAULT_DEVICE_TREE="qcom/sm8650-hdk"
> +
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot
2026-05-04 18:57 [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot Casey Connolly
` (14 preceding siblings ...)
2026-05-04 18:57 ` [PATCH v2 15/15] configs: add qcom_sm8650_defconfig and debug fragment Casey Connolly
@ 2026-05-05 10:11 ` Sumit Garg
2026-05-05 12:25 ` Casey Connolly
15 siblings, 1 reply; 51+ messages in thread
From: Sumit Garg @ 2026-05-05 10:11 UTC (permalink / raw)
To: Casey Connolly
Cc: u-boot, u-boot-qcom, Tom Rini, Simon Glass, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
Hi Casey,
Thanks for the much needed re-spin of the SMEM patch-set.
On Mon, May 04, 2026 at 08:57:28PM +0200, Casey Connolly wrote:
> Smem is currently implemented a uclass in U-Boot. This carries with it
> some implicit limitations about how and when we can actually talk with
> it, since its modelled as a device.
>
> The SMEM uclass is qualcomm specific and has no other users, smem is
> also largely useful for parsing the memory map and other tasks which we
> have to do before DM is alive anyway.
>
> Let's drop the old smem code and replace it with a straight port from
> Linux, providing a familiar interface and enabling us to access it much
> earlier in the boot process than would be possible with a standard
> udevice.
>
> Additionally, this will be easier to maintain going forwards since any
> patches from Linux can be more easily ported over.
>
> With this new smem framework in place, we can now use SMEM to parse the
> memory map when it's not provided via some other mechanism and to read
> the serial number of the board.
>
> Implement this functionality and split the snapdragon memory parsing off
> to its own file to try and organise things a bit better.
>
> Since the memory map stored in SMEM doesn't already have some carveouts
> let's also be correct about unmapping reserved memory regions, we
> should eventually do this on all Qualcomm boards but it's particularly
> important when we aren't chainloading from ABL.
This was really needed to unmap reserved regions in U-Boot memory map to
increase robustness.
>
> This depends on a fix for unmapping memory regions: https://lore.kernel.org/u-boot/20260504175511.585797-1-casey.connolly@linaro.org/
>
> ---
> Changes in v2:
> - Ensure reserved regions are unmapped appropriately when using SMEM as
> the source of truth for the memory map.
> - Support SMEM ram layout v0/v1 for older platforms using a header from
> Qualcomm.
It would be better if you can provide proper attribution in the commit
to which this header is being picked up.
> - Set an environment variable (memmap_source) providing info about
> where U-Boots memory map is from.
> - Correctly handle serial_num missing on msm8916
> - Link to v1: https://lore.kernel.org/r/20241124-b4-modernise-smem-v1-0-b7852c11b67c@linaro.org
>
FWIW, I tested this patch-set on RB3Gen2 and lemans-evk after dropping
the U-Boot DT overrides, so feel free to carry:
Tested-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
I will give a proper review to this series soon.
-Sumit
> To: u-boot@lists.denx.de
> To: Sumit Garg <sumit.garg@kernel.org>
> To: u-boot-qcom@groups.io
> Cc: Tom Rini <trini@konsulko.com>
> Cc: Simon Glass <sjg@chromium.org>
> Cc: Casey Connolly <casey.connolly@linaro.org>
> Cc: Peng Fan <peng.fan@nxp.com>
> Cc: Marek Vasut <marek.vasut+renesas@mailbox.org>
> Cc: Alice Guo <alice.guo@nxp.com>
> Cc: Quentin Schulz <quentin.schulz@cherry.de>
> Cc: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> Cc: Neil Armstrong <neil.armstrong@linaro.org>
> Cc: Mattijs Korpershoek <mkorpershoek@kernel.org>
> Cc: Kuan-Wei Chiu <visitorckw@gmail.com>
> Cc: Raymond Mao <raymond.mao@riscstar.com>
> Cc: Stefan Roese <stefan.roese@mailbox.org>
> Cc: Philip Molloy <philip.molloy@analog.com>
> Cc: Jerome Forissier <jerome.forissier@arm.com>
> Cc: Marek Vasut <marek.vasut@mailbox.org>
> Cc: Varadarajan Narayanan <varadarajan.narayanan@oss.qualcomm.com>
> Cc: Patrice Chotard <patrice.chotard@foss.st.com>
> Cc: Aswin Murugan <aswin.murugan@oss.qualcomm.com>
> Cc: Rasmus Villemoes <ravi@prevas.dk>
> Cc: Heiko Schocher <hs@nabladev.com>
> Cc: Michal Simek <michal.simek@amd.com>
> Cc: Sughosh Ganu <sughosh.ganu@arm.com>
> Cc: Antony Kurniawan Soemardi <linux@smankusors.com>
> Cc: Luca Weiss <luca.weiss@fairphone.com>
> Cc: Balaji Selvanathan <balaji.selvanathan@oss.qualcomm.com>
>
> ---
> Casey Connolly (15):
> Revert "dm: SMEM (Shared memory) uclass"
> smem: drop drivers/smem
> Revert "test: smem: add basic smem test"
> Revert "drivers: smem: sandbox"
> mach-snapdragon: fix reserved memory carveout
> soc: qcom: import smem from Linux 6.11-rc2
> soc: qcom: smem: adjust headers for U-Boot
> soc: qcom: smem: adjust for U-Boot
> soc: qcom: smem: get serial number from socinfo
> soc: qcom: smem: stub functions
> soc: qcom: smem: add build infra
> mach-snapdragon: move memory parsing to its own file
> mach-snapdragon: support parsing memory map from SMEM
> mach-snapdragon: fetch serial# from SMEM
> configs: add qcom_sm8650_defconfig and debug fragment
>
> MAINTAINERS | 1 -
> arch/arm/Kconfig | 3 +-
> arch/arm/mach-snapdragon/Makefile | 2 +-
> arch/arm/mach-snapdragon/board.c | 253 ++++-----
> arch/arm/mach-snapdragon/dram.c | 239 ++++++++
> arch/arm/mach-snapdragon/qcom-priv.h | 17 +
> arch/arm/mach-snapdragon/rampart.h | 194 +++++++
> arch/sandbox/dts/test.dts | 4 -
> board/qualcomm/debug-sm8650.config | 5 +
> configs/qcom_sm8650_defconfig | 16 +
> configs/sandbox64_defconfig | 2 -
> configs/sandbox_defconfig | 2 -
> drivers/Kconfig | 2 -
> drivers/Makefile | 1 -
> drivers/smem/Kconfig | 25 -
> drivers/smem/Makefile | 7 -
> drivers/smem/sandbox_smem.c | 44 --
> drivers/smem/smem-uclass.c | 46 --
> drivers/soc/qcom/Kconfig | 8 +
> drivers/soc/qcom/Makefile | 1 +
> drivers/{smem/msm_smem.c => soc/qcom/smem.c} | 777 ++++++++++++++++-----------
> include/dm/uclass-id.h | 1 -
> include/smem.h | 90 ----
> include/soc/qcom/smem.h | 36 ++
> include/soc/qcom/socinfo.h | 111 ++++
> test/dm/Makefile | 1 -
> test/dm/smem.c | 26 -
> 27 files changed, 1186 insertions(+), 728 deletions(-)
> ---
> base-commit: 2920b49fb117dd70b6219804bf0d298c96f184f2
>
> // Casey (she/they)
>
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 01/15] Revert "dm: SMEM (Shared memory) uclass"
2026-05-04 18:57 ` [PATCH v2 01/15] Revert "dm: SMEM (Shared memory) uclass" Casey Connolly
@ 2026-05-05 10:15 ` Sumit Garg
2026-05-07 15:31 ` Simon Glass
2026-05-18 14:34 ` Neil Armstrong
2 siblings, 0 replies; 51+ messages in thread
From: Sumit Garg @ 2026-05-05 10:15 UTC (permalink / raw)
To: Casey Connolly
Cc: u-boot, u-boot-qcom, Tom Rini, Simon Glass, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
On Mon, May 04, 2026 at 08:57:29PM +0200, Casey Connolly wrote:
> SMEM is a highly Qualcomm specific interface, while having a dedicated
> UCLASS for it offers a nice abstraction, for things like memory layout
> parsing we need to use it before the driver model is available.
>
> Therefore, it doesn't make sense to fit SMEM into the driver model.
> Instead let's adopt a model closer to Linux, and parse SMEM really early
> during boot (as soon as we have the FDT).
>
> This reverts commit 7b384eccc785b596f68448b155cbda26df57fb23.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> arch/arm/Kconfig | 1 -
> drivers/Kconfig | 2 --
> drivers/Makefile | 1 -
> drivers/smem/Kconfig | 25 -------------
> drivers/smem/Makefile | 7 ----
> drivers/smem/smem-uclass.c | 46 ------------------------
> include/dm/uclass-id.h | 1 -
> include/smem.h | 90 ----------------------------------------------
> 8 files changed, 173 deletions(-)
Reviewed-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
-Sumit
>
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index f624675eadf8..dadb18e0b8cd 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -1141,9 +1141,8 @@ config ARCH_SNAPDRAGON
> select POWER_DOMAIN
> select GPIO_EXTRA_HEADER
> select MSM_SMEM
> select OF_CONTROL
> - select SMEM
> select SPMI
> select BOARD_LATE_INIT
> select OF_BOARD
> select SAVE_PREV_BL_FDT_ADDR if !ENABLE_ARM_SOC_BOOT0_HOOK
> diff --git a/drivers/Kconfig b/drivers/Kconfig
> index 47606ddc6c8b..aa293df37c57 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -123,10 +123,8 @@ source "drivers/scsi/Kconfig"
> source "drivers/serial/Kconfig"
>
> source "drivers/sm/Kconfig"
>
> -source "drivers/smem/Kconfig"
> -
> source "drivers/sound/Kconfig"
>
> source "drivers/soc/Kconfig"
>
> diff --git a/drivers/Makefile b/drivers/Makefile
> index 43d0ba332818..ca3e87ffb102 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -118,9 +118,8 @@ obj-y += pwm/
> obj-y += reset/
> obj-y += input/
> obj-y += iommu/
> # SOC specific infrastructure drivers.
> -obj-y += smem/
> obj-y += thermal/
> obj-$(CONFIG_TEE) += tee/
> obj-$(CONFIG_ARM_FFA_TRANSPORT) += firmware/arm-ffa/
> obj-y += axi/
> diff --git a/drivers/smem/Kconfig b/drivers/smem/Kconfig
> deleted file mode 100644
> index e5d7dcc81b17..000000000000
> --- a/drivers/smem/Kconfig
> +++ /dev/null
> @@ -1,25 +0,0 @@
> -menuconfig SMEM
> - bool "SMEM (Shared Memory mamanger) support"
> -
> -if SMEM
> -
> -config SANDBOX_SMEM
> - bool "Sandbox Shared Memory Manager (SMEM)"
> - depends on SANDBOX && DM
> - help
> - enable SMEM support for sandbox. This is an emulation of a real SMEM
> - manager.
> - The sandbox driver allocates a shared memory from the heap and
> - initialzies it on start.
> -
> -config MSM_SMEM
> - bool "Qualcomm Shared Memory Manager (SMEM)"
> - depends on DM
> - depends on ARCH_SNAPDRAGON || ARCH_IPQ40XX
> - select DEVRES
> - help
> - Enable support for the Qualcomm Shared Memory Manager.
> - The driver provides an interface to items in a heap shared among all
> - processors in a Qualcomm platform.
> -
> -endif # menu "SMEM Support"
> diff --git a/drivers/smem/Makefile b/drivers/smem/Makefile
> deleted file mode 100644
> index af3e9b50883c..000000000000
> --- a/drivers/smem/Makefile
> +++ /dev/null
> @@ -1,7 +0,0 @@
> -# SPDX-License-Identifier: GPL-2.0+
> -#
> -# Makefile for the U-Boot SMEM interface drivers
> -
> -obj-$(CONFIG_SANDBOX_SMEM) += sandbox_smem.o
> -obj-$(CONFIG_SMEM) += smem-uclass.o
> -obj-$(CONFIG_MSM_SMEM) += msm_smem.o
> diff --git a/drivers/smem/smem-uclass.c b/drivers/smem/smem-uclass.c
> deleted file mode 100644
> index 4dea5cc4bf1c..000000000000
> --- a/drivers/smem/smem-uclass.c
> +++ /dev/null
> @@ -1,46 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0+
> -/*
> - * Copyright (c) 2018 Ramon Fried <ramon.fried@gmail.com>
> - */
> -
> -#define LOG_CATEGORY UCLASS_SMEM
> -
> -#include <dm.h>
> -#include <smem.h>
> -
> -int smem_alloc(struct udevice *dev, unsigned int host,
> - unsigned int item, size_t size)
> -{
> - struct smem_ops *ops = smem_get_ops(dev);
> -
> - if (!ops->alloc)
> - return -ENOSYS;
> -
> - return ops->alloc(host, item, size);
> -}
> -
> -void *smem_get(struct udevice *dev, unsigned int host,
> - unsigned int item, size_t *size)
> -{
> - struct smem_ops *ops = smem_get_ops(dev);
> -
> - if (!ops->get)
> - return NULL;
> -
> - return ops->get(host, item, size);
> -}
> -
> -int smem_get_free_space(struct udevice *dev, unsigned int host)
> -{
> - struct smem_ops *ops = smem_get_ops(dev);
> -
> - if (!ops->get_free_space)
> - return -ENOSYS;
> -
> - return ops->get_free_space(host);
> -}
> -
> -UCLASS_DRIVER(smem) = {
> - .id = UCLASS_SMEM,
> - .name = "smem",
> -};
> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
> index 36b5d87c304f..fe0aae2720ca 100644
> --- a/include/dm/uclass-id.h
> +++ b/include/dm/uclass-id.h
> @@ -133,9 +133,8 @@ enum uclass_id {
> UCLASS_SCMI_BASE, /* Interface for SCMI Base protocol */
> UCLASS_SCSI, /* SCSI device */
> UCLASS_SERIAL, /* Serial UART */
> UCLASS_SIMPLE_BUS, /* Bus with child devices */
> - UCLASS_SMEM, /* Shared memory interface */
> UCLASS_SOC, /* SOC Device */
> UCLASS_SOUND, /* Playing simple sounds */
> UCLASS_SPI, /* SPI bus */
> UCLASS_SPI_FLASH, /* SPI flash */
> diff --git a/include/smem.h b/include/smem.h
> deleted file mode 100644
> index b19c534ebc43..000000000000
> --- a/include/smem.h
> +++ /dev/null
> @@ -1,90 +0,0 @@
> -/* SPDX-License-Identifier: GPL-2.0+ */
> -/*
> - * The shared memory system is an allocate-only heap structure that
> - * consists of one of more memory areas that can be accessed by the processors
> - * in the SoC.
> - *
> - * Allocation can be done globally for all processors or to an individual processor.
> - * This is controlled by the @host parameter.
> - *
> - * Allocation and management of heap can be implemented in various ways,
> - * The @item parameter should be used as an index/hash to the memory region.
> - *
> - * Copyright (c) 2018 Ramon Fried <ramon.fried@gmail.com>
> - */
> -
> -#ifndef _smemh_
> -#define _smemh_
> -
> -/* struct smem_ops: Operations for the SMEM uclass */
> -struct smem_ops {
> - /**
> - * alloc() - allocate space for a smem item
> - *
> - * @host: remote processor id, or -1 for all processors.
> - * @item: smem item handle
> - * @size: number of bytes to be allocated
> - * @return 0 if OK, -ve on error
> - */
> - int (*alloc)(unsigned int host,
> - unsigned int item, size_t size);
> -
> - /**
> - * get() - Resolve ptr of size of a smem item
> - *
> - * @host: the remote processor, of -1 for all processors.
> - * @item: smem item handle
> - * @size: pointer to be filled out with the size of the item
> - * @return pointer on success, NULL on error
> - */
> - void *(*get)(unsigned int host,
> - unsigned int item, size_t *size);
> -
> - /**
> - * get_free_space() - Get free space in smem in bytes
> - *
> - * @host: the remote processor identifying a partition, or -1
> - * for all processors.
> - * @return free space, -ve on error
> - */
> - int (*get_free_space)(unsigned int host);
> -};
> -
> -#define smem_get_ops(dev) ((struct smem_ops *)(dev)->driver->ops)
> -
> -/**
> - * smem_alloc() - allocate space for a smem item
> - * @host: remote processor id, or -1
> - * @item: smem item handle
> - * @size: number of bytes to be allocated
> - * Return: 0 if OK, -ve on error
> - *
> - * Allocate space for a given smem item of size @size, given that the item is
> - * not yet allocated.
> - */
> -int smem_alloc(struct udevice *dev, unsigned int host, unsigned int item, size_t size);
> -
> -/**
> - * smem_get() - resolve ptr of size of a smem item
> - * @host: the remote processor, or -1 for all processors.
> - * @item: smem item handle
> - * @size: pointer to be filled out with size of the item
> - * Return: pointer on success, NULL on error
> - *
> - * Looks up smem item and returns pointer to it. Size of smem
> - * item is returned in @size.
> - */
> -void *smem_get(struct udevice *dev, unsigned int host, unsigned int item, size_t *size);
> -
> -/**
> - * smem_get_free_space() - retrieve amount of free space in a partition
> - * @host: the remote processor identifying a partition, or -1
> - * for all processors.
> - * Return: size in bytes, -ve on error
> - *
> - * To be used by smem clients as a quick way to determine if any new
> - * allocations has been made.
> - */
> -int smem_get_free_space(struct udevice *dev, unsigned int host);
> -
> -#endif /* _smem_h_ */
>
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 03/15] Revert "test: smem: add basic smem test"
2026-05-04 18:57 ` [PATCH v2 03/15] Revert "test: smem: add basic smem test" Casey Connolly
@ 2026-05-05 10:26 ` Sumit Garg
2026-05-18 14:34 ` Neil Armstrong
1 sibling, 0 replies; 51+ messages in thread
From: Sumit Garg @ 2026-05-05 10:26 UTC (permalink / raw)
To: Casey Connolly
Cc: u-boot, u-boot-qcom, Tom Rini, Simon Glass, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
On Mon, May 04, 2026 at 08:57:31PM +0200, Casey Connolly wrote:
> This reverts commit 20e7705764c4e5ea924f1ea54bb36ebbbeffffe7.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> test/dm/Makefile | 1 -
> test/dm/smem.c | 26 --------------------------
> 2 files changed, 27 deletions(-)
>
Reviewed-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
-Sumit
> diff --git a/test/dm/Makefile b/test/dm/Makefile
> index 771b703b737d..ace149d4a2b4 100644
> --- a/test/dm/Makefile
> +++ b/test/dm/Makefile
> @@ -111,9 +111,8 @@ obj-$(CONFIG_SCSI) += scsi.o
> obj-$(CONFIG_DM_SERIAL) += serial.o
> obj-$(CONFIG_DM_SPI_FLASH) += sf.o
> obj-$(CONFIG_SIMPLE_BUS) += simple-bus.o
> obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o
> -obj-$(CONFIG_SMEM) += smem.o
> obj-$(CONFIG_SOC_DEVICE) += soc.o
> obj-$(CONFIG_SOUND) += sound.o
> obj-$(CONFIG_DM_SPI) += spi.o
> obj-$(CONFIG_SPMI) += spmi.o
> diff --git a/test/dm/smem.c b/test/dm/smem.c
> deleted file mode 100644
> index 89e74cccc574..000000000000
> --- a/test/dm/smem.c
> +++ /dev/null
> @@ -1,26 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0+
> -/*
> - * Copyright (C) 2018 Ramon Fried <ramon.fried@gmail.com>
> - */
> -
> -#include <dm.h>
> -#include <smem.h>
> -#include <dm/test.h>
> -#include <test/test.h>
> -#include <test/ut.h>
> -
> -/* Basic test of the smem uclass */
> -static int dm_test_smem_base(struct unit_test_state *uts)
> -{
> - struct udevice *dev;
> - size_t size;
> -
> - ut_assertok(uclass_get_device(UCLASS_SMEM, 0, &dev));
> - ut_assertnonnull(dev);
> - ut_assertok(smem_alloc(dev, -1, 0, 16));
> - ut_asserteq(0, smem_get_free_space(dev, -1));
> - ut_assertnull(smem_get(dev, -1, 0, &size));
> -
> - return 0;
> -}
> -DM_TEST(dm_test_smem_base, UTF_SCAN_PDATA | UTF_SCAN_FDT);
>
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 04/15] Revert "drivers: smem: sandbox"
2026-05-04 18:57 ` [PATCH v2 04/15] Revert "drivers: smem: sandbox" Casey Connolly
@ 2026-05-05 10:26 ` Sumit Garg
2026-05-18 14:34 ` Neil Armstrong
2026-05-18 15:51 ` Fabio Estevam
2 siblings, 0 replies; 51+ messages in thread
From: Sumit Garg @ 2026-05-05 10:26 UTC (permalink / raw)
To: Casey Connolly
Cc: u-boot, u-boot-qcom, Tom Rini, Simon Glass, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
On Mon, May 04, 2026 at 08:57:32PM +0200, Casey Connolly wrote:
> This reverts commit 7fd7e2cf339ea2ec570f6dae1cdfaf8e066eb4af.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> arch/sandbox/dts/test.dts | 4 ----
> configs/sandbox64_defconfig | 2 --
> configs/sandbox_defconfig | 2 --
> 3 files changed, 8 deletions(-)
Reviewed-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
-Sumit
>
> diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
> index ac92ebf1afd7..50201ad61ccd 100644
> --- a/arch/sandbox/dts/test.dts
> +++ b/arch/sandbox/dts/test.dts
> @@ -1521,12 +1521,8 @@
> compatible = "sandbox,scsi";
> sandbox,filepath = "scsi.img";
> };
>
> - smem@0 {
> - compatible = "sandbox,smem";
> - };
> -
> sound {
> compatible = "sandbox,sound";
> cpu {
> sound-dai = <&i2s 0>;
> diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig
> index 60a30d048750..53f7af4270af 100644
> --- a/configs/sandbox64_defconfig
> +++ b/configs/sandbox64_defconfig
> @@ -240,10 +240,8 @@ CONFIG_SANDBOX_RESET=y
> CONFIG_DM_RTC=y
> CONFIG_RTC_RV8803=y
> CONFIG_SCSI=y
> CONFIG_SANDBOX_SERIAL=y
> -CONFIG_SMEM=y
> -CONFIG_SANDBOX_SMEM=y
> CONFIG_SOUND=y
> CONFIG_SOUND_SANDBOX=y
> CONFIG_SOC_DEVICE=y
> CONFIG_SANDBOX_SPI=y
> diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
> index f26295103f1c..7f26405e6ed0 100644
> --- a/configs/sandbox_defconfig
> +++ b/configs/sandbox_defconfig
> @@ -331,10 +331,8 @@ CONFIG_RTC_RV8803=y
> CONFIG_RTC_HT1380=y
> CONFIG_SCSI=y
> CONFIG_SANDBOX_SERIAL=y
> CONFIG_SANDBOX_SM=y
> -CONFIG_SMEM=y
> -CONFIG_SANDBOX_SMEM=y
> CONFIG_SOUND=y
> CONFIG_SOUND_DA7219=y
> CONFIG_SOUND_MAX98357A=y
> CONFIG_SOUND_SANDBOX=y
>
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot
2026-05-05 10:11 ` [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot Sumit Garg
@ 2026-05-05 12:25 ` Casey Connolly
2026-05-05 12:35 ` Sumit Garg
0 siblings, 1 reply; 51+ messages in thread
From: Casey Connolly @ 2026-05-05 12:25 UTC (permalink / raw)
To: Sumit Garg
Cc: u-boot, u-boot-qcom, Tom Rini, Simon Glass, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
On 05/05/2026 12:11, Sumit Garg wrote:
> Hi Casey,
>
> Thanks for the much needed re-spin of the SMEM patch-set.
>
> On Mon, May 04, 2026 at 08:57:28PM +0200, Casey Connolly wrote:
>> Smem is currently implemented a uclass in U-Boot. This carries with it
>> some implicit limitations about how and when we can actually talk with
>> it, since its modelled as a device.
>>
>> The SMEM uclass is qualcomm specific and has no other users, smem is
>> also largely useful for parsing the memory map and other tasks which we
>> have to do before DM is alive anyway.
>>
>> Let's drop the old smem code and replace it with a straight port from
>> Linux, providing a familiar interface and enabling us to access it much
>> earlier in the boot process than would be possible with a standard
>> udevice.
>>
>> Additionally, this will be easier to maintain going forwards since any
>> patches from Linux can be more easily ported over.
>>
>> With this new smem framework in place, we can now use SMEM to parse the
>> memory map when it's not provided via some other mechanism and to read
>> the serial number of the board.
>>
>> Implement this functionality and split the snapdragon memory parsing off
>> to its own file to try and organise things a bit better.
>>
>> Since the memory map stored in SMEM doesn't already have some carveouts
>> let's also be correct about unmapping reserved memory regions, we
>> should eventually do this on all Qualcomm boards but it's particularly
>> important when we aren't chainloading from ABL.
>
> This was really needed to unmap reserved regions in U-Boot memory map to
> increase robustness.
agreed, the ~100ms cost sucks but I hope we can bring it down by doing
this after dcache enable if we can figure that out (would probably rely
on starting with an initial minimal map to avoid the prefetcher issues)
but yeah, a problem for the future for sure.
>
>>
>> This depends on a fix for unmapping memory regions: https://lore.kernel.org/u-boot/20260504175511.585797-1-casey.connolly@linaro.org/
>>
>> ---
>> Changes in v2:
>> - Ensure reserved regions are unmapped appropriately when using SMEM as
>> the source of truth for the memory map.
>> - Support SMEM ram layout v0/v1 for older platforms using a header from
>> Qualcomm.
>
> It would be better if you can provide proper attribution in the commit
> to which this header is being picked up.
Where is the header sourced from? I can't really use another patch
posting as a source.
>
>> - Set an environment variable (memmap_source) providing info about
>> where U-Boots memory map is from.
>> - Correctly handle serial_num missing on msm8916
>> - Link to v1: https://lore.kernel.org/r/20241124-b4-modernise-smem-v1-0-b7852c11b67c@linaro.org
>>
>
> FWIW, I tested this patch-set on RB3Gen2 and lemans-evk after dropping
> the U-Boot DT overrides, so feel free to carry:
Good to know! I assume you removed the hardcoded DT memorymap for testing?
>
> Tested-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
>
> I will give a proper review to this series soon.
Thanks
>
> -Sumit
>
>> To: u-boot@lists.denx.de
>> To: Sumit Garg <sumit.garg@kernel.org>
>> To: u-boot-qcom@groups.io
>> Cc: Tom Rini <trini@konsulko.com>
>> Cc: Simon Glass <sjg@chromium.org>
>> Cc: Casey Connolly <casey.connolly@linaro.org>
>> Cc: Peng Fan <peng.fan@nxp.com>
>> Cc: Marek Vasut <marek.vasut+renesas@mailbox.org>
>> Cc: Alice Guo <alice.guo@nxp.com>
>> Cc: Quentin Schulz <quentin.schulz@cherry.de>
>> Cc: Ilias Apalodimas <ilias.apalodimas@linaro.org>
>> Cc: Neil Armstrong <neil.armstrong@linaro.org>
>> Cc: Mattijs Korpershoek <mkorpershoek@kernel.org>
>> Cc: Kuan-Wei Chiu <visitorckw@gmail.com>
>> Cc: Raymond Mao <raymond.mao@riscstar.com>
>> Cc: Stefan Roese <stefan.roese@mailbox.org>
>> Cc: Philip Molloy <philip.molloy@analog.com>
>> Cc: Jerome Forissier <jerome.forissier@arm.com>
>> Cc: Marek Vasut <marek.vasut@mailbox.org>
>> Cc: Varadarajan Narayanan <varadarajan.narayanan@oss.qualcomm.com>
>> Cc: Patrice Chotard <patrice.chotard@foss.st.com>
>> Cc: Aswin Murugan <aswin.murugan@oss.qualcomm.com>
>> Cc: Rasmus Villemoes <ravi@prevas.dk>
>> Cc: Heiko Schocher <hs@nabladev.com>
>> Cc: Michal Simek <michal.simek@amd.com>
>> Cc: Sughosh Ganu <sughosh.ganu@arm.com>
>> Cc: Antony Kurniawan Soemardi <linux@smankusors.com>
>> Cc: Luca Weiss <luca.weiss@fairphone.com>
>> Cc: Balaji Selvanathan <balaji.selvanathan@oss.qualcomm.com>
>>
>> ---
>> Casey Connolly (15):
>> Revert "dm: SMEM (Shared memory) uclass"
>> smem: drop drivers/smem
>> Revert "test: smem: add basic smem test"
>> Revert "drivers: smem: sandbox"
>> mach-snapdragon: fix reserved memory carveout
>> soc: qcom: import smem from Linux 6.11-rc2
>> soc: qcom: smem: adjust headers for U-Boot
>> soc: qcom: smem: adjust for U-Boot
>> soc: qcom: smem: get serial number from socinfo
>> soc: qcom: smem: stub functions
>> soc: qcom: smem: add build infra
>> mach-snapdragon: move memory parsing to its own file
>> mach-snapdragon: support parsing memory map from SMEM
>> mach-snapdragon: fetch serial# from SMEM
>> configs: add qcom_sm8650_defconfig and debug fragment
>>
>> MAINTAINERS | 1 -
>> arch/arm/Kconfig | 3 +-
>> arch/arm/mach-snapdragon/Makefile | 2 +-
>> arch/arm/mach-snapdragon/board.c | 253 ++++-----
>> arch/arm/mach-snapdragon/dram.c | 239 ++++++++
>> arch/arm/mach-snapdragon/qcom-priv.h | 17 +
>> arch/arm/mach-snapdragon/rampart.h | 194 +++++++
>> arch/sandbox/dts/test.dts | 4 -
>> board/qualcomm/debug-sm8650.config | 5 +
>> configs/qcom_sm8650_defconfig | 16 +
>> configs/sandbox64_defconfig | 2 -
>> configs/sandbox_defconfig | 2 -
>> drivers/Kconfig | 2 -
>> drivers/Makefile | 1 -
>> drivers/smem/Kconfig | 25 -
>> drivers/smem/Makefile | 7 -
>> drivers/smem/sandbox_smem.c | 44 --
>> drivers/smem/smem-uclass.c | 46 --
>> drivers/soc/qcom/Kconfig | 8 +
>> drivers/soc/qcom/Makefile | 1 +
>> drivers/{smem/msm_smem.c => soc/qcom/smem.c} | 777 ++++++++++++++++-----------
>> include/dm/uclass-id.h | 1 -
>> include/smem.h | 90 ----
>> include/soc/qcom/smem.h | 36 ++
>> include/soc/qcom/socinfo.h | 111 ++++
>> test/dm/Makefile | 1 -
>> test/dm/smem.c | 26 -
>> 27 files changed, 1186 insertions(+), 728 deletions(-)
>> ---
>> base-commit: 2920b49fb117dd70b6219804bf0d298c96f184f2
>>
>> // Casey (she/they)
>>
--
// Casey (she/her)
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 05/15] mach-snapdragon: fix reserved memory carveout
2026-05-04 18:57 ` [PATCH v2 05/15] mach-snapdragon: fix reserved memory carveout Casey Connolly
@ 2026-05-05 12:25 ` Sumit Garg
2026-05-05 12:39 ` Casey Connolly
0 siblings, 1 reply; 51+ messages in thread
From: Sumit Garg @ 2026-05-05 12:25 UTC (permalink / raw)
To: Casey Connolly
Cc: u-boot, u-boot-qcom, Tom Rini, Simon Glass, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
On Mon, May 04, 2026 at 08:57:33PM +0200, Casey Connolly wrote:
> The memory carveout logic was fairly limited and had a few issues,
> rework it and teach it not to unmap regions that have a compatible
> property (since they may be used in U-Boot) or that don't have the
> no-map property.
>
> The carveout process adds ~100ms to the boot time depending on the
> platform.
>
> This prepares us for using SMEM as a source of truth and improving
> support for U-boot as a first stage bootloader since SMEMs memory map
> doesn't already carve out some regions like ABL does.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> arch/arm/mach-snapdragon/board.c | 86 +++++++++++++++++++++++++---------------
> 1 file changed, 53 insertions(+), 33 deletions(-)
>
> diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c
> index 829a0109ac78..e12d3d00caa4 100644
> --- a/arch/arm/mach-snapdragon/board.c
> +++ b/arch/arm/mach-snapdragon/board.c
> @@ -622,27 +622,36 @@ u64 get_page_table_size(void)
> {
> return SZ_1M;
> }
>
> +struct mem_resource_attrs {
> + fdt_addr_t start;
> + fdt_addr_t size;
> + u64 attrs;
> +};
Let's move the struct declaration towards the top.
> +
> static int fdt_cmp_res(const void *v1, const void *v2)
This API should now be renamed as mem_cmp_resources(), no?
> {
> - const struct fdt_resource *res1 = v1, *res2 = v2;
> + const struct mem_resource_attrs *res1 = v1, *res2 = v2;
>
> return res1->start - res2->start;
> }
>
> -#define N_RESERVED_REGIONS 32
> +#define N_RESERVED_REGIONS 64
>
> -/* Mark all no-map regions as PTE_TYPE_FAULT to prevent speculative access.
> +/* Map and unmap reserved memory regions as appropriate.
> + * Mark all no-map regions as PTE_TYPE_FAULT to prevent speculative access.
> * On some platforms this is enough to trigger a security violation and trap
> * to EL3.
> + * Regions that may be accessed by drivers get mapped explicitly.
> */
> -static void carve_out_reserved_memory(void)
> +static void configure_reserved_memory(void)
> {
> - static struct fdt_resource res[N_RESERVED_REGIONS] = { 0 };
> + static struct mem_resource_attrs res[N_RESERVED_REGIONS] = { 0 };
> int parent, rmem, count, i = 0;
> phys_addr_t start;
> size_t size;
> + u64 attrs;
>
> /* Some reserved nodes must be carved out, as the cache-prefetcher may otherwise
> * attempt to access them, causing a security exception.
> */
> @@ -651,14 +660,19 @@ static void carve_out_reserved_memory(void)
> log_err("No reserved memory regions found\n");
> return;
> }
>
> - /* Collect the reserved memory regions */
> + /* Collect the reserved memory regions and appropriate attrs */
> fdt_for_each_subnode(rmem, gd->fdt_blob, parent) {
> const fdt32_t *ptr;
> - int len;
> + attrs = PTE_TYPE_FAULT;
> + /* If the no-map property isn't set then the region is valid */
> if (!fdt_getprop(gd->fdt_blob, rmem, "no-map", NULL))
> - continue;
> + attrs = PTE_TYPE_VALID | PTE_BLOCK_MEMTYPE(MT_NORMAL);
> + /* If the compatible property is set then this region may be accessed by drivers and should
> + * be marked valid too. */
> + if (fdt_getprop(gd->fdt_blob, rmem, "compatible", NULL))
> + attrs = PTE_TYPE_VALID | PTE_BLOCK_MEMTYPE(MT_NORMAL);
>
> if (i == N_RESERVED_REGIONS) {
> log_err("Too many reserved regions!\n");
> break;
> @@ -667,50 +681,55 @@ static void carve_out_reserved_memory(void)
> /* Read the address and size out from the reg property. Doing this "properly" with
> * fdt_get_resource() takes ~70ms on SDM845, but open-coding the happy path here
> * takes <1ms... Oh the woes of no dcache.
> */
> - ptr = fdt_getprop(gd->fdt_blob, rmem, "reg", &len);
> + ptr = fdt_getprop(gd->fdt_blob, rmem, "reg", NULL);
> if (ptr) {
> /* Qualcomm devices use #address/size-cells = <2> but all reserved regions are within
> * the 32-bit address space. So we can cheat here for speed.
> */
> res[i].start = fdt32_to_cpu(ptr[1]);
> - res[i].end = res[i].start + fdt32_to_cpu(ptr[3]);
> + res[i].size = fdt32_to_cpu(ptr[3]);
> + res[i].attrs = attrs;
> i++;
> }
> }
>
> /* Sort the reserved memory regions by address */
> count = i;
> - qsort(res, count, sizeof(struct fdt_resource), fdt_cmp_res);
> + qsort(res, count, sizeof(res[0]), fdt_cmp_res);
> + debug("Mapping %d regions!\n", count);
>
> /* Now set the right attributes for them. Often a lot of the regions are tightly packed together
> - * so we can optimise the number of calls to mmu_change_region_attr() by combining adjacent
> + * so we can optimise the number of calls to mmu_change_region_attr_nobreak() by combining adjacent
> * regions.
> */
> - start = ALIGN_DOWN(res[0].start, SZ_2M);
> - size = ALIGN(res[0].end - start, SZ_2M);
> + start = res[0].start;
> + size = res[0].size;
> + attrs = res[0].attrs;
> + /* For each region after the first one, either increase the `size` to eventually be mapped or
> + * map the region we have and start a new one, this allows us to reduce the number of calls to
> + * mmu_map_region(). The loop is therefore "lagging" behind by one iteration. */
> for (i = 1; i <= count; i++) {
> - /* We ideally want to 2M align everything for more efficient pagetables, but we must avoid
> - * overwriting reserved memory regions which shouldn't be mapped as FAULT (like those with
> - * compatible properties).
> - * If within 2M of the previous region, bump the size to include this region. Otherwise
> - * start a new region.
> - */
> - if (i == count || start + size < res[i].start - SZ_2M) {
> - debug(" 0x%016llx - 0x%016llx: reserved\n",
> - start, start + size);
> - mmu_change_region_attr(start, size, PTE_TYPE_FAULT);
> - /* If this is the final region then quit here before we index
> - * out of bounds...
> - */
> + /* If i == count we are done, just map the last region. If the last region is
> + * too far away or the attrs don't match then map the meta-region we have and
> + * start a new one. */
> + if (i == count || start + size < res[i].start - SZ_8K || attrs != res[i].attrs) {
I suppose this SZ_8K instead of SZ_2M is intentional here? It works now
due to page table optimization I guess?
-Sumit
> + debug(" 0x%016llx - 0x%016llx: %s\n",
> + start, start + size, attrs == PTE_TYPE_FAULT ? "FAULT" : "VALID");
> + /* No need to break-before-make since dcache is disabled */
> + mmu_change_region_attr_nobreak(start, size, attrs);
> + /* We have now mapped all the regions */
> if (i == count)
> break;
> - start = ALIGN_DOWN(res[i].start, SZ_2M);
> - size = ALIGN(res[i].end - start, SZ_2M);
> + /* Start a new meta-region */
> + start = res[i].start;
> + size = res[i].size;
> + attrs = res[i].attrs;
> } else {
> - /* Bump size if this region is immediately after the previous one */
> - size = ALIGN(res[i].end - start, SZ_2M);
> + /* This region is next to (<8K) the previous one so combine them.
> + * Accounting for any small (<8K) gap. */
> + size = (res[i].start - start) + res[i].size;
> }
> }
> }
>
> @@ -744,13 +763,14 @@ void enable_caches(void)
> gd->arch.tlb_emerg = gd->arch.tlb_addr;
> gd->arch.tlb_addr = tlb_addr;
> gd->arch.tlb_size = tlb_size;
>
> - /* We do the carveouts only for QCS404, for now. */
> + /* On some boards speculative access may trigger a NOC or XPU violation so explicitly mark reserved
> + * regions as inacessible (PTE_TYPE_FAULT) */
> if (fdt_node_check_compatible(gd->fdt_blob, 0, "qcom,qcs404") == 0) {
> carveout_start = get_timer(0);
> /* Takes ~20-50ms on SDM845 */
> - carve_out_reserved_memory();
> + configure_reserved_memory();
> debug("carveout time: %lums\n", get_timer(carveout_start));
> }
> dcache_enable();
> }
>
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot
2026-05-05 12:25 ` Casey Connolly
@ 2026-05-05 12:35 ` Sumit Garg
0 siblings, 0 replies; 51+ messages in thread
From: Sumit Garg @ 2026-05-05 12:35 UTC (permalink / raw)
To: Casey Connolly
Cc: u-boot, u-boot-qcom, Tom Rini, Simon Glass, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
On Tue, May 05, 2026 at 02:25:01PM +0200, Casey Connolly wrote:
>
>
> On 05/05/2026 12:11, Sumit Garg wrote:
> > Hi Casey,
> >
> > Thanks for the much needed re-spin of the SMEM patch-set.
> >
> > On Mon, May 04, 2026 at 08:57:28PM +0200, Casey Connolly wrote:
> >> Smem is currently implemented a uclass in U-Boot. This carries with it
> >> some implicit limitations about how and when we can actually talk with
> >> it, since its modelled as a device.
> >>
> >> The SMEM uclass is qualcomm specific and has no other users, smem is
> >> also largely useful for parsing the memory map and other tasks which we
> >> have to do before DM is alive anyway.
> >>
> >> Let's drop the old smem code and replace it with a straight port from
> >> Linux, providing a familiar interface and enabling us to access it much
> >> earlier in the boot process than would be possible with a standard
> >> udevice.
> >>
> >> Additionally, this will be easier to maintain going forwards since any
> >> patches from Linux can be more easily ported over.
> >>
> >> With this new smem framework in place, we can now use SMEM to parse the
> >> memory map when it's not provided via some other mechanism and to read
> >> the serial number of the board.
> >>
> >> Implement this functionality and split the snapdragon memory parsing off
> >> to its own file to try and organise things a bit better.
> >>
> >> Since the memory map stored in SMEM doesn't already have some carveouts
> >> let's also be correct about unmapping reserved memory regions, we
> >> should eventually do this on all Qualcomm boards but it's particularly
> >> important when we aren't chainloading from ABL.
> >
> > This was really needed to unmap reserved regions in U-Boot memory map to
> > increase robustness.
>
> agreed, the ~100ms cost sucks but I hope we can bring it down by doing
> this after dcache enable if we can figure that out (would probably rely
> on starting with an initial minimal map to avoid the prefetcher issues)
> but yeah, a problem for the future for sure.
+1
>
> >
> >>
> >> This depends on a fix for unmapping memory regions: https://lore.kernel.org/u-boot/20260504175511.585797-1-casey.connolly@linaro.org/
> >>
> >> ---
> >> Changes in v2:
> >> - Ensure reserved regions are unmapped appropriately when using SMEM as
> >> the source of truth for the memory map.
> >> - Support SMEM ram layout v0/v1 for older platforms using a header from
> >> Qualcomm.
> >
> > It would be better if you can provide proper attribution in the commit
> > to which this header is being picked up.
>
> Where is the header sourced from? I can't really use another patch
> posting as a source.
I would have just mentioned the header to be contributed by:
Aswin Murugan <aswin.murugan@oss.qualcomm.com> in commit message.
>
> >
> >> - Set an environment variable (memmap_source) providing info about
> >> where U-Boots memory map is from.
> >> - Correctly handle serial_num missing on msm8916
> >> - Link to v1: https://lore.kernel.org/r/20241124-b4-modernise-smem-v1-0-b7852c11b67c@linaro.org
> >>
> >
> > FWIW, I tested this patch-set on RB3Gen2 and lemans-evk after dropping
> > the U-Boot DT overrides, so feel free to carry:
>
> Good to know! I assume you removed the hardcoded DT memorymap for testing?
Yeah I did drop the hardcoded DT memory map as you can see here [1].
Feel free to skip picking up DT memory override for Lemans EVK with this
series merged.
[1] https://github.com/qualcomm-linux/u-boot/pull/56
-Sumit
>
> >
> > Tested-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
> >
> > I will give a proper review to this series soon.
>
> Thanks
> >
> > -Sumit
> >
> >> To: u-boot@lists.denx.de
> >> To: Sumit Garg <sumit.garg@kernel.org>
> >> To: u-boot-qcom@groups.io
> >> Cc: Tom Rini <trini@konsulko.com>
> >> Cc: Simon Glass <sjg@chromium.org>
> >> Cc: Casey Connolly <casey.connolly@linaro.org>
> >> Cc: Peng Fan <peng.fan@nxp.com>
> >> Cc: Marek Vasut <marek.vasut+renesas@mailbox.org>
> >> Cc: Alice Guo <alice.guo@nxp.com>
> >> Cc: Quentin Schulz <quentin.schulz@cherry.de>
> >> Cc: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> >> Cc: Neil Armstrong <neil.armstrong@linaro.org>
> >> Cc: Mattijs Korpershoek <mkorpershoek@kernel.org>
> >> Cc: Kuan-Wei Chiu <visitorckw@gmail.com>
> >> Cc: Raymond Mao <raymond.mao@riscstar.com>
> >> Cc: Stefan Roese <stefan.roese@mailbox.org>
> >> Cc: Philip Molloy <philip.molloy@analog.com>
> >> Cc: Jerome Forissier <jerome.forissier@arm.com>
> >> Cc: Marek Vasut <marek.vasut@mailbox.org>
> >> Cc: Varadarajan Narayanan <varadarajan.narayanan@oss.qualcomm.com>
> >> Cc: Patrice Chotard <patrice.chotard@foss.st.com>
> >> Cc: Aswin Murugan <aswin.murugan@oss.qualcomm.com>
> >> Cc: Rasmus Villemoes <ravi@prevas.dk>
> >> Cc: Heiko Schocher <hs@nabladev.com>
> >> Cc: Michal Simek <michal.simek@amd.com>
> >> Cc: Sughosh Ganu <sughosh.ganu@arm.com>
> >> Cc: Antony Kurniawan Soemardi <linux@smankusors.com>
> >> Cc: Luca Weiss <luca.weiss@fairphone.com>
> >> Cc: Balaji Selvanathan <balaji.selvanathan@oss.qualcomm.com>
> >>
> >> ---
> >> Casey Connolly (15):
> >> Revert "dm: SMEM (Shared memory) uclass"
> >> smem: drop drivers/smem
> >> Revert "test: smem: add basic smem test"
> >> Revert "drivers: smem: sandbox"
> >> mach-snapdragon: fix reserved memory carveout
> >> soc: qcom: import smem from Linux 6.11-rc2
> >> soc: qcom: smem: adjust headers for U-Boot
> >> soc: qcom: smem: adjust for U-Boot
> >> soc: qcom: smem: get serial number from socinfo
> >> soc: qcom: smem: stub functions
> >> soc: qcom: smem: add build infra
> >> mach-snapdragon: move memory parsing to its own file
> >> mach-snapdragon: support parsing memory map from SMEM
> >> mach-snapdragon: fetch serial# from SMEM
> >> configs: add qcom_sm8650_defconfig and debug fragment
> >>
> >> MAINTAINERS | 1 -
> >> arch/arm/Kconfig | 3 +-
> >> arch/arm/mach-snapdragon/Makefile | 2 +-
> >> arch/arm/mach-snapdragon/board.c | 253 ++++-----
> >> arch/arm/mach-snapdragon/dram.c | 239 ++++++++
> >> arch/arm/mach-snapdragon/qcom-priv.h | 17 +
> >> arch/arm/mach-snapdragon/rampart.h | 194 +++++++
> >> arch/sandbox/dts/test.dts | 4 -
> >> board/qualcomm/debug-sm8650.config | 5 +
> >> configs/qcom_sm8650_defconfig | 16 +
> >> configs/sandbox64_defconfig | 2 -
> >> configs/sandbox_defconfig | 2 -
> >> drivers/Kconfig | 2 -
> >> drivers/Makefile | 1 -
> >> drivers/smem/Kconfig | 25 -
> >> drivers/smem/Makefile | 7 -
> >> drivers/smem/sandbox_smem.c | 44 --
> >> drivers/smem/smem-uclass.c | 46 --
> >> drivers/soc/qcom/Kconfig | 8 +
> >> drivers/soc/qcom/Makefile | 1 +
> >> drivers/{smem/msm_smem.c => soc/qcom/smem.c} | 777 ++++++++++++++++-----------
> >> include/dm/uclass-id.h | 1 -
> >> include/smem.h | 90 ----
> >> include/soc/qcom/smem.h | 36 ++
> >> include/soc/qcom/socinfo.h | 111 ++++
> >> test/dm/Makefile | 1 -
> >> test/dm/smem.c | 26 -
> >> 27 files changed, 1186 insertions(+), 728 deletions(-)
> >> ---
> >> base-commit: 2920b49fb117dd70b6219804bf0d298c96f184f2
> >>
> >> // Casey (she/they)
> >>
>
> --
> // Casey (she/her)
>
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 05/15] mach-snapdragon: fix reserved memory carveout
2026-05-05 12:25 ` Sumit Garg
@ 2026-05-05 12:39 ` Casey Connolly
2026-05-07 7:45 ` Sumit Garg
0 siblings, 1 reply; 51+ messages in thread
From: Casey Connolly @ 2026-05-05 12:39 UTC (permalink / raw)
To: Sumit Garg
Cc: u-boot, u-boot-qcom, Tom Rini, Simon Glass, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
On 05/05/2026 14:25, Sumit Garg wrote:
> On Mon, May 04, 2026 at 08:57:33PM +0200, Casey Connolly wrote:
>> The memory carveout logic was fairly limited and had a few issues,
>> rework it and teach it not to unmap regions that have a compatible
>> property (since they may be used in U-Boot) or that don't have the
>> no-map property.
>>
>> The carveout process adds ~100ms to the boot time depending on the
>> platform.
>>
>> This prepares us for using SMEM as a source of truth and improving
>> support for U-boot as a first stage bootloader since SMEMs memory map
>> doesn't already carve out some regions like ABL does.
>>
>> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
>> ---
>> arch/arm/mach-snapdragon/board.c | 86 +++++++++++++++++++++++++---------------
>> 1 file changed, 53 insertions(+), 33 deletions(-)
>>
>> diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c
>> index 829a0109ac78..e12d3d00caa4 100644
>> --- a/arch/arm/mach-snapdragon/board.c
>> +++ b/arch/arm/mach-snapdragon/board.c
>> @@ -622,27 +622,36 @@ u64 get_page_table_size(void)
>> {
>> return SZ_1M;
>> }
>>
>> +struct mem_resource_attrs {
>> + fdt_addr_t start;
>> + fdt_addr_t size;
>> + u64 attrs;
>> +};
>
> Let's move the struct declaration towards the top.
it's only used by this one function, it felt easier to keep things
readable this way. Probably we'll move this stuff to its own file at
some point.
>
>> +
>> static int fdt_cmp_res(const void *v1, const void *v2)
>
> This API should now be renamed as mem_cmp_resources(), no?
yeah
>
>> {
>> - const struct fdt_resource *res1 = v1, *res2 = v2;
>> + const struct mem_resource_attrs *res1 = v1, *res2 = v2;
>>
>> return res1->start - res2->start;
>> }
>>
>> -#define N_RESERVED_REGIONS 32
>> +#define N_RESERVED_REGIONS 64
>>
>> -/* Mark all no-map regions as PTE_TYPE_FAULT to prevent speculative access.
>> +/* Map and unmap reserved memory regions as appropriate.
>> + * Mark all no-map regions as PTE_TYPE_FAULT to prevent speculative access.
>> * On some platforms this is enough to trigger a security violation and trap
>> * to EL3.
>> + * Regions that may be accessed by drivers get mapped explicitly.
>> */
>> -static void carve_out_reserved_memory(void)
>> +static void configure_reserved_memory(void)
>> {
>> - static struct fdt_resource res[N_RESERVED_REGIONS] = { 0 };
>> + static struct mem_resource_attrs res[N_RESERVED_REGIONS] = { 0 };
>> int parent, rmem, count, i = 0;
>> phys_addr_t start;
>> size_t size;
>> + u64 attrs;
>>
>> /* Some reserved nodes must be carved out, as the cache-prefetcher may otherwise
>> * attempt to access them, causing a security exception.
>> */
>> @@ -651,14 +660,19 @@ static void carve_out_reserved_memory(void)
>> log_err("No reserved memory regions found\n");
>> return;
>> }
>>
>> - /* Collect the reserved memory regions */
>> + /* Collect the reserved memory regions and appropriate attrs */
>> fdt_for_each_subnode(rmem, gd->fdt_blob, parent) {
>> const fdt32_t *ptr;
>> - int len;
>> + attrs = PTE_TYPE_FAULT;
>> + /* If the no-map property isn't set then the region is valid */
>> if (!fdt_getprop(gd->fdt_blob, rmem, "no-map", NULL))
>> - continue;
>> + attrs = PTE_TYPE_VALID | PTE_BLOCK_MEMTYPE(MT_NORMAL);
>> + /* If the compatible property is set then this region may be accessed by drivers and should
>> + * be marked valid too. */
>> + if (fdt_getprop(gd->fdt_blob, rmem, "compatible", NULL))
>> + attrs = PTE_TYPE_VALID | PTE_BLOCK_MEMTYPE(MT_NORMAL);
>>
>> if (i == N_RESERVED_REGIONS) {
>> log_err("Too many reserved regions!\n");
>> break;
>> @@ -667,50 +681,55 @@ static void carve_out_reserved_memory(void)
>> /* Read the address and size out from the reg property. Doing this "properly" with
>> * fdt_get_resource() takes ~70ms on SDM845, but open-coding the happy path here
>> * takes <1ms... Oh the woes of no dcache.
>> */
>> - ptr = fdt_getprop(gd->fdt_blob, rmem, "reg", &len);
>> + ptr = fdt_getprop(gd->fdt_blob, rmem, "reg", NULL);
>> if (ptr) {
>> /* Qualcomm devices use #address/size-cells = <2> but all reserved regions are within
>> * the 32-bit address space. So we can cheat here for speed.
>> */
>> res[i].start = fdt32_to_cpu(ptr[1]);
>> - res[i].end = res[i].start + fdt32_to_cpu(ptr[3]);
>> + res[i].size = fdt32_to_cpu(ptr[3]);
>> + res[i].attrs = attrs;
>> i++;
>> }
>> }
>>
>> /* Sort the reserved memory regions by address */
>> count = i;
>> - qsort(res, count, sizeof(struct fdt_resource), fdt_cmp_res);
>> + qsort(res, count, sizeof(res[0]), fdt_cmp_res);
>> + debug("Mapping %d regions!\n", count);
>>
>> /* Now set the right attributes for them. Often a lot of the regions are tightly packed together
>> - * so we can optimise the number of calls to mmu_change_region_attr() by combining adjacent
>> + * so we can optimise the number of calls to mmu_change_region_attr_nobreak() by combining adjacent
>> * regions.
>> */
>> - start = ALIGN_DOWN(res[0].start, SZ_2M);
>> - size = ALIGN(res[0].end - start, SZ_2M);
>> + start = res[0].start;
>> + size = res[0].size;
>> + attrs = res[0].attrs;
>> + /* For each region after the first one, either increase the `size` to eventually be mapped or
>> + * map the region we have and start a new one, this allows us to reduce the number of calls to
>> + * mmu_map_region(). The loop is therefore "lagging" behind by one iteration. */
>> for (i = 1; i <= count; i++) {
>> - /* We ideally want to 2M align everything for more efficient pagetables, but we must avoid
>> - * overwriting reserved memory regions which shouldn't be mapped as FAULT (like those with
>> - * compatible properties).
>> - * If within 2M of the previous region, bump the size to include this region. Otherwise
>> - * start a new region.
>> - */
>> - if (i == count || start + size < res[i].start - SZ_2M) {
>> - debug(" 0x%016llx - 0x%016llx: reserved\n",
>> - start, start + size);
>> - mmu_change_region_attr(start, size, PTE_TYPE_FAULT);
>> - /* If this is the final region then quit here before we index
>> - * out of bounds...
>> - */
>> + /* If i == count we are done, just map the last region. If the last region is
>> + * too far away or the attrs don't match then map the meta-region we have and
>> + * start a new one. */
>> + if (i == count || start + size < res[i].start - SZ_8K || attrs != res[i].attrs) {
>
> I suppose this SZ_8K instead of SZ_2M is intentional here? It works now
> due to page table optimization I guess?
I realised there was a possibility for incorrectly mapping stuff with
2M. Probably not with the logic as it is but e.g. if you don't
explicitly map reserved regions that need to be mapped then they can get
unmapped here.
>
> -Sumit
>
>> + debug(" 0x%016llx - 0x%016llx: %s\n",
>> + start, start + size, attrs == PTE_TYPE_FAULT ? "FAULT" : "VALID");
>> + /* No need to break-before-make since dcache is disabled */
>> + mmu_change_region_attr_nobreak(start, size, attrs);
>> + /* We have now mapped all the regions */
>> if (i == count)
>> break;
>> - start = ALIGN_DOWN(res[i].start, SZ_2M);
>> - size = ALIGN(res[i].end - start, SZ_2M);
>> + /* Start a new meta-region */
>> + start = res[i].start;
>> + size = res[i].size;
>> + attrs = res[i].attrs;
>> } else {
>> - /* Bump size if this region is immediately after the previous one */
>> - size = ALIGN(res[i].end - start, SZ_2M);
>> + /* This region is next to (<8K) the previous one so combine them.
>> + * Accounting for any small (<8K) gap. */
>> + size = (res[i].start - start) + res[i].size;
>> }
>> }
>> }
>>
>> @@ -744,13 +763,14 @@ void enable_caches(void)
>> gd->arch.tlb_emerg = gd->arch.tlb_addr;
>> gd->arch.tlb_addr = tlb_addr;
>> gd->arch.tlb_size = tlb_size;
>>
>> - /* We do the carveouts only for QCS404, for now. */
>> + /* On some boards speculative access may trigger a NOC or XPU violation so explicitly mark reserved
>> + * regions as inacessible (PTE_TYPE_FAULT) */
>> if (fdt_node_check_compatible(gd->fdt_blob, 0, "qcom,qcs404") == 0) {
>> carveout_start = get_timer(0);
>> /* Takes ~20-50ms on SDM845 */
>> - carve_out_reserved_memory();
>> + configure_reserved_memory();
>> debug("carveout time: %lums\n", get_timer(carveout_start));
>> }
>> dcache_enable();
>> }
>>
>> --
>> 2.53.0
>>
--
// Casey (she/her)
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 06/15] soc: qcom: import smem from Linux 6.11-rc2
2026-05-04 18:57 ` [PATCH v2 06/15] soc: qcom: import smem from Linux 6.11-rc2 Casey Connolly
@ 2026-05-05 12:40 ` Sumit Garg
2026-05-05 12:45 ` Casey Connolly
2026-05-18 14:36 ` Neil Armstrong
1 sibling, 1 reply; 51+ messages in thread
From: Sumit Garg @ 2026-05-05 12:40 UTC (permalink / raw)
To: Casey Connolly
Cc: u-boot, u-boot-qcom, Tom Rini, Simon Glass, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
On Mon, May 04, 2026 at 08:57:34PM +0200, Casey Connolly wrote:
> Import the SMEM driver from Linux.
Linux v6.11-rc2 seems quite old, can we rather just sync from v7.1-rc2
directly?
-Sumit
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> drivers/soc/qcom/smem.c | 1279 ++++++++++++++++++++++++++++++++++++++++++++
> include/soc/qcom/smem.h | 20 +
> include/soc/qcom/socinfo.h | 111 ++++
> 3 files changed, 1410 insertions(+)
>
> diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
> new file mode 100644
> index 000000000000..8515b8ae7777
> --- /dev/null
> +++ b/drivers/soc/qcom/smem.c
> @@ -0,0 +1,1279 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2015, Sony Mobile Communications AB.
> + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
> + */
> +
> +#include <linux/hwspinlock.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_reserved_mem.h>
> +#include <linux/platform_device.h>
> +#include <linux/sizes.h>
> +#include <linux/slab.h>
> +#include <linux/soc/qcom/smem.h>
> +#include <linux/soc/qcom/socinfo.h>
> +
> +/*
> + * The Qualcomm shared memory system is a allocate only heap structure that
> + * consists of one of more memory areas that can be accessed by the processors
> + * in the SoC.
> + *
> + * All systems contains a global heap, accessible by all processors in the SoC,
> + * with a table of contents data structure (@smem_header) at the beginning of
> + * the main shared memory block.
> + *
> + * The global header contains meta data for allocations as well as a fixed list
> + * of 512 entries (@smem_global_entry) that can be initialized to reference
> + * parts of the shared memory space.
> + *
> + *
> + * In addition to this global heap a set of "private" heaps can be set up at
> + * boot time with access restrictions so that only certain processor pairs can
> + * access the data.
> + *
> + * These partitions are referenced from an optional partition table
> + * (@smem_ptable), that is found 4kB from the end of the main smem region. The
> + * partition table entries (@smem_ptable_entry) lists the involved processors
> + * (or hosts) and their location in the main shared memory region.
> + *
> + * Each partition starts with a header (@smem_partition_header) that identifies
> + * the partition and holds properties for the two internal memory regions. The
> + * two regions are cached and non-cached memory respectively. Each region
> + * contain a link list of allocation headers (@smem_private_entry) followed by
> + * their data.
> + *
> + * Items in the non-cached region are allocated from the start of the partition
> + * while items in the cached region are allocated from the end. The free area
> + * is hence the region between the cached and non-cached offsets. The header of
> + * cached items comes after the data.
> + *
> + * Version 12 (SMEM_GLOBAL_PART_VERSION) changes the item alloc/get procedure
> + * for the global heap. A new global partition is created from the global heap
> + * region with partition type (SMEM_GLOBAL_HOST) and the max smem item count is
> + * set by the bootloader.
> + *
> + * To synchronize allocations in the shared memory heaps a remote spinlock must
> + * be held - currently lock number 3 of the sfpb or tcsr is used for this on all
> + * platforms.
> + *
> + */
> +
> +/*
> + * The version member of the smem header contains an array of versions for the
> + * various software components in the SoC. We verify that the boot loader
> + * version is a valid version as a sanity check.
> + */
> +#define SMEM_MASTER_SBL_VERSION_INDEX 7
> +#define SMEM_GLOBAL_HEAP_VERSION 11
> +#define SMEM_GLOBAL_PART_VERSION 12
> +
> +/*
> + * The first 8 items are only to be allocated by the boot loader while
> + * initializing the heap.
> + */
> +#define SMEM_ITEM_LAST_FIXED 8
> +
> +/* Highest accepted item number, for both global and private heaps */
> +#define SMEM_ITEM_COUNT 512
> +
> +/* Processor/host identifier for the application processor */
> +#define SMEM_HOST_APPS 0
> +
> +/* Processor/host identifier for the global partition */
> +#define SMEM_GLOBAL_HOST 0xfffe
> +
> +/* Max number of processors/hosts in a system */
> +#define SMEM_HOST_COUNT 20
> +
> +/**
> + * struct smem_proc_comm - proc_comm communication struct (legacy)
> + * @command: current command to be executed
> + * @status: status of the currently requested command
> + * @params: parameters to the command
> + */
> +struct smem_proc_comm {
> + __le32 command;
> + __le32 status;
> + __le32 params[2];
> +};
> +
> +/**
> + * struct smem_global_entry - entry to reference smem items on the heap
> + * @allocated: boolean to indicate if this entry is used
> + * @offset: offset to the allocated space
> + * @size: size of the allocated space, 8 byte aligned
> + * @aux_base: base address for the memory region used by this unit, or 0 for
> + * the default region. bits 0,1 are reserved
> + */
> +struct smem_global_entry {
> + __le32 allocated;
> + __le32 offset;
> + __le32 size;
> + __le32 aux_base; /* bits 1:0 reserved */
> +};
> +#define AUX_BASE_MASK 0xfffffffc
> +
> +/**
> + * struct smem_header - header found in beginning of primary smem region
> + * @proc_comm: proc_comm communication interface (legacy)
> + * @version: array of versions for the various subsystems
> + * @initialized: boolean to indicate that smem is initialized
> + * @free_offset: index of the first unallocated byte in smem
> + * @available: number of bytes available for allocation
> + * @reserved: reserved field, must be 0
> + * @toc: array of references to items
> + */
> +struct smem_header {
> + struct smem_proc_comm proc_comm[4];
> + __le32 version[32];
> + __le32 initialized;
> + __le32 free_offset;
> + __le32 available;
> + __le32 reserved;
> + struct smem_global_entry toc[SMEM_ITEM_COUNT];
> +};
> +
> +/**
> + * struct smem_ptable_entry - one entry in the @smem_ptable list
> + * @offset: offset, within the main shared memory region, of the partition
> + * @size: size of the partition
> + * @flags: flags for the partition (currently unused)
> + * @host0: first processor/host with access to this partition
> + * @host1: second processor/host with access to this partition
> + * @cacheline: alignment for "cached" entries
> + * @reserved: reserved entries for later use
> + */
> +struct smem_ptable_entry {
> + __le32 offset;
> + __le32 size;
> + __le32 flags;
> + __le16 host0;
> + __le16 host1;
> + __le32 cacheline;
> + __le32 reserved[7];
> +};
> +
> +/**
> + * struct smem_ptable - partition table for the private partitions
> + * @magic: magic number, must be SMEM_PTABLE_MAGIC
> + * @version: version of the partition table
> + * @num_entries: number of partitions in the table
> + * @reserved: for now reserved entries
> + * @entry: list of @smem_ptable_entry for the @num_entries partitions
> + */
> +struct smem_ptable {
> + u8 magic[4];
> + __le32 version;
> + __le32 num_entries;
> + __le32 reserved[5];
> + struct smem_ptable_entry entry[];
> +};
> +
> +static const u8 SMEM_PTABLE_MAGIC[] = { 0x24, 0x54, 0x4f, 0x43 }; /* "$TOC" */
> +
> +/**
> + * struct smem_partition_header - header of the partitions
> + * @magic: magic number, must be SMEM_PART_MAGIC
> + * @host0: first processor/host with access to this partition
> + * @host1: second processor/host with access to this partition
> + * @size: size of the partition
> + * @offset_free_uncached: offset to the first free byte of uncached memory in
> + * this partition
> + * @offset_free_cached: offset to the first free byte of cached memory in this
> + * partition
> + * @reserved: for now reserved entries
> + */
> +struct smem_partition_header {
> + u8 magic[4];
> + __le16 host0;
> + __le16 host1;
> + __le32 size;
> + __le32 offset_free_uncached;
> + __le32 offset_free_cached;
> + __le32 reserved[3];
> +};
> +
> +/**
> + * struct smem_partition - describes smem partition
> + * @virt_base: starting virtual address of partition
> + * @phys_base: starting physical address of partition
> + * @cacheline: alignment for "cached" entries
> + * @size: size of partition
> + */
> +struct smem_partition {
> + void __iomem *virt_base;
> + phys_addr_t phys_base;
> + size_t cacheline;
> + size_t size;
> +};
> +
> +static const u8 SMEM_PART_MAGIC[] = { 0x24, 0x50, 0x52, 0x54 };
> +
> +/**
> + * struct smem_private_entry - header of each item in the private partition
> + * @canary: magic number, must be SMEM_PRIVATE_CANARY
> + * @item: identifying number of the smem item
> + * @size: size of the data, including padding bytes
> + * @padding_data: number of bytes of padding of data
> + * @padding_hdr: number of bytes of padding between the header and the data
> + * @reserved: for now reserved entry
> + */
> +struct smem_private_entry {
> + u16 canary; /* bytes are the same so no swapping needed */
> + __le16 item;
> + __le32 size; /* includes padding bytes */
> + __le16 padding_data;
> + __le16 padding_hdr;
> + __le32 reserved;
> +};
> +#define SMEM_PRIVATE_CANARY 0xa5a5
> +
> +/**
> + * struct smem_info - smem region info located after the table of contents
> + * @magic: magic number, must be SMEM_INFO_MAGIC
> + * @size: size of the smem region
> + * @base_addr: base address of the smem region
> + * @reserved: for now reserved entry
> + * @num_items: highest accepted item number
> + */
> +struct smem_info {
> + u8 magic[4];
> + __le32 size;
> + __le32 base_addr;
> + __le32 reserved;
> + __le16 num_items;
> +};
> +
> +static const u8 SMEM_INFO_MAGIC[] = { 0x53, 0x49, 0x49, 0x49 }; /* SIII */
> +
> +/**
> + * struct smem_region - representation of a chunk of memory used for smem
> + * @aux_base: identifier of aux_mem base
> + * @virt_base: virtual base address of memory with this aux_mem identifier
> + * @size: size of the memory region
> + */
> +struct smem_region {
> + phys_addr_t aux_base;
> + void __iomem *virt_base;
> + size_t size;
> +};
> +
> +/**
> + * struct qcom_smem - device data for the smem device
> + * @dev: device pointer
> + * @hwlock: reference to a hwspinlock
> + * @ptable: virtual base of partition table
> + * @global_partition: describes for global partition when in use
> + * @partitions: list of partitions of current processor/host
> + * @item_count: max accepted item number
> + * @socinfo: platform device pointer
> + * @num_regions: number of @regions
> + * @regions: list of the memory regions defining the shared memory
> + */
> +struct qcom_smem {
> + struct device *dev;
> +
> + struct hwspinlock *hwlock;
> +
> + u32 item_count;
> + struct platform_device *socinfo;
> + struct smem_ptable *ptable;
> + struct smem_partition global_partition;
> + struct smem_partition partitions[SMEM_HOST_COUNT];
> +
> + unsigned num_regions;
> + struct smem_region regions[] __counted_by(num_regions);
> +};
> +
> +static void *
> +phdr_to_last_uncached_entry(struct smem_partition_header *phdr)
> +{
> + void *p = phdr;
> +
> + return p + le32_to_cpu(phdr->offset_free_uncached);
> +}
> +
> +static struct smem_private_entry *
> +phdr_to_first_cached_entry(struct smem_partition_header *phdr,
> + size_t cacheline)
> +{
> + void *p = phdr;
> + struct smem_private_entry *e;
> +
> + return p + le32_to_cpu(phdr->size) - ALIGN(sizeof(*e), cacheline);
> +}
> +
> +static void *
> +phdr_to_last_cached_entry(struct smem_partition_header *phdr)
> +{
> + void *p = phdr;
> +
> + return p + le32_to_cpu(phdr->offset_free_cached);
> +}
> +
> +static struct smem_private_entry *
> +phdr_to_first_uncached_entry(struct smem_partition_header *phdr)
> +{
> + void *p = phdr;
> +
> + return p + sizeof(*phdr);
> +}
> +
> +static struct smem_private_entry *
> +uncached_entry_next(struct smem_private_entry *e)
> +{
> + void *p = e;
> +
> + return p + sizeof(*e) + le16_to_cpu(e->padding_hdr) +
> + le32_to_cpu(e->size);
> +}
> +
> +static struct smem_private_entry *
> +cached_entry_next(struct smem_private_entry *e, size_t cacheline)
> +{
> + void *p = e;
> +
> + return p - le32_to_cpu(e->size) - ALIGN(sizeof(*e), cacheline);
> +}
> +
> +static void *uncached_entry_to_item(struct smem_private_entry *e)
> +{
> + void *p = e;
> +
> + return p + sizeof(*e) + le16_to_cpu(e->padding_hdr);
> +}
> +
> +static void *cached_entry_to_item(struct smem_private_entry *e)
> +{
> + void *p = e;
> +
> + return p - le32_to_cpu(e->size);
> +}
> +
> +/* Pointer to the one and only smem handle */
> +static struct qcom_smem *__smem;
> +
> +/* Timeout (ms) for the trylock of remote spinlocks */
> +#define HWSPINLOCK_TIMEOUT 1000
> +
> +/* The qcom hwspinlock id is always plus one from the smem host id */
> +#define SMEM_HOST_ID_TO_HWSPINLOCK_ID(__x) ((__x) + 1)
> +
> +/**
> + * qcom_smem_bust_hwspin_lock_by_host() - bust the smem hwspinlock for a host
> + * @host: remote processor id
> + *
> + * Busts the hwspin_lock for the given smem host id. This helper is intended
> + * for remoteproc drivers that manage remoteprocs with an equivalent smem
> + * driver instance in the remote firmware. Drivers can force a release of the
> + * smem hwspin_lock if the rproc unexpectedly goes into a bad state.
> + *
> + * Context: Process context.
> + *
> + * Returns: 0 on success, otherwise negative errno.
> + */
> +int qcom_smem_bust_hwspin_lock_by_host(unsigned int host)
> +{
> + /* This function is for remote procs, so ignore SMEM_HOST_APPS */
> + if (host == SMEM_HOST_APPS || host >= SMEM_HOST_COUNT)
> + return -EINVAL;
> +
> + return hwspin_lock_bust(__smem->hwlock, SMEM_HOST_ID_TO_HWSPINLOCK_ID(host));
> +}
> +EXPORT_SYMBOL_GPL(qcom_smem_bust_hwspin_lock_by_host);
> +
> +/**
> + * qcom_smem_is_available() - Check if SMEM is available
> + *
> + * Return: true if SMEM is available, false otherwise.
> + */
> +bool qcom_smem_is_available(void)
> +{
> + return !!__smem;
> +}
> +EXPORT_SYMBOL_GPL(qcom_smem_is_available);
> +
> +static int qcom_smem_alloc_private(struct qcom_smem *smem,
> + struct smem_partition *part,
> + unsigned item,
> + size_t size)
> +{
> + struct smem_private_entry *hdr, *end;
> + struct smem_partition_header *phdr;
> + size_t alloc_size;
> + void *cached;
> + void *p_end;
> +
> + phdr = (struct smem_partition_header __force *)part->virt_base;
> + p_end = (void *)phdr + part->size;
> +
> + hdr = phdr_to_first_uncached_entry(phdr);
> + end = phdr_to_last_uncached_entry(phdr);
> + cached = phdr_to_last_cached_entry(phdr);
> +
> + if (WARN_ON((void *)end > p_end || cached > p_end))
> + return -EINVAL;
> +
> + while (hdr < end) {
> + if (hdr->canary != SMEM_PRIVATE_CANARY)
> + goto bad_canary;
> + if (le16_to_cpu(hdr->item) == item)
> + return -EEXIST;
> +
> + hdr = uncached_entry_next(hdr);
> + }
> +
> + if (WARN_ON((void *)hdr > p_end))
> + return -EINVAL;
> +
> + /* Check that we don't grow into the cached region */
> + alloc_size = sizeof(*hdr) + ALIGN(size, 8);
> + if ((void *)hdr + alloc_size > cached) {
> + dev_err(smem->dev, "Out of memory\n");
> + return -ENOSPC;
> + }
> +
> + hdr->canary = SMEM_PRIVATE_CANARY;
> + hdr->item = cpu_to_le16(item);
> + hdr->size = cpu_to_le32(ALIGN(size, 8));
> + hdr->padding_data = cpu_to_le16(le32_to_cpu(hdr->size) - size);
> + hdr->padding_hdr = 0;
> +
> + /*
> + * Ensure the header is written before we advance the free offset, so
> + * that remote processors that does not take the remote spinlock still
> + * gets a consistent view of the linked list.
> + */
> + wmb();
> + le32_add_cpu(&phdr->offset_free_uncached, alloc_size);
> +
> + return 0;
> +bad_canary:
> + dev_err(smem->dev, "Found invalid canary in hosts %hu:%hu partition\n",
> + le16_to_cpu(phdr->host0), le16_to_cpu(phdr->host1));
> +
> + return -EINVAL;
> +}
> +
> +static int qcom_smem_alloc_global(struct qcom_smem *smem,
> + unsigned item,
> + size_t size)
> +{
> + struct smem_global_entry *entry;
> + struct smem_header *header;
> +
> + header = smem->regions[0].virt_base;
> + entry = &header->toc[item];
> + if (entry->allocated)
> + return -EEXIST;
> +
> + size = ALIGN(size, 8);
> + if (WARN_ON(size > le32_to_cpu(header->available)))
> + return -ENOMEM;
> +
> + entry->offset = header->free_offset;
> + entry->size = cpu_to_le32(size);
> +
> + /*
> + * Ensure the header is consistent before we mark the item allocated,
> + * so that remote processors will get a consistent view of the item
> + * even though they do not take the spinlock on read.
> + */
> + wmb();
> + entry->allocated = cpu_to_le32(1);
> +
> + le32_add_cpu(&header->free_offset, size);
> + le32_add_cpu(&header->available, -size);
> +
> + return 0;
> +}
> +
> +/**
> + * qcom_smem_alloc() - allocate space for a smem item
> + * @host: remote processor id, or -1
> + * @item: smem item handle
> + * @size: number of bytes to be allocated
> + *
> + * Allocate space for a given smem item of size @size, given that the item is
> + * not yet allocated.
> + */
> +int qcom_smem_alloc(unsigned host, unsigned item, size_t size)
> +{
> + struct smem_partition *part;
> + unsigned long flags;
> + int ret;
> +
> + if (!__smem)
> + return -EPROBE_DEFER;
> +
> + if (item < SMEM_ITEM_LAST_FIXED) {
> + dev_err(__smem->dev,
> + "Rejecting allocation of static entry %d\n", item);
> + return -EINVAL;
> + }
> +
> + if (WARN_ON(item >= __smem->item_count))
> + return -EINVAL;
> +
> + ret = hwspin_lock_timeout_irqsave(__smem->hwlock,
> + HWSPINLOCK_TIMEOUT,
> + &flags);
> + if (ret)
> + return ret;
> +
> + if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
> + part = &__smem->partitions[host];
> + ret = qcom_smem_alloc_private(__smem, part, item, size);
> + } else if (__smem->global_partition.virt_base) {
> + part = &__smem->global_partition;
> + ret = qcom_smem_alloc_private(__smem, part, item, size);
> + } else {
> + ret = qcom_smem_alloc_global(__smem, item, size);
> + }
> +
> + hwspin_unlock_irqrestore(__smem->hwlock, &flags);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(qcom_smem_alloc);
> +
> +static void *qcom_smem_get_global(struct qcom_smem *smem,
> + unsigned item,
> + size_t *size)
> +{
> + struct smem_header *header;
> + struct smem_region *region;
> + struct smem_global_entry *entry;
> + u64 entry_offset;
> + u32 e_size;
> + u32 aux_base;
> + unsigned i;
> +
> + header = smem->regions[0].virt_base;
> + entry = &header->toc[item];
> + if (!entry->allocated)
> + return ERR_PTR(-ENXIO);
> +
> + aux_base = le32_to_cpu(entry->aux_base) & AUX_BASE_MASK;
> +
> + for (i = 0; i < smem->num_regions; i++) {
> + region = &smem->regions[i];
> +
> + if ((u32)region->aux_base == aux_base || !aux_base) {
> + e_size = le32_to_cpu(entry->size);
> + entry_offset = le32_to_cpu(entry->offset);
> +
> + if (WARN_ON(e_size + entry_offset > region->size))
> + return ERR_PTR(-EINVAL);
> +
> + if (size != NULL)
> + *size = e_size;
> +
> + return region->virt_base + entry_offset;
> + }
> + }
> +
> + return ERR_PTR(-ENOENT);
> +}
> +
> +static void *qcom_smem_get_private(struct qcom_smem *smem,
> + struct smem_partition *part,
> + unsigned item,
> + size_t *size)
> +{
> + struct smem_private_entry *e, *end;
> + struct smem_partition_header *phdr;
> + void *item_ptr, *p_end;
> + u32 padding_data;
> + u32 e_size;
> +
> + phdr = (struct smem_partition_header __force *)part->virt_base;
> + p_end = (void *)phdr + part->size;
> +
> + e = phdr_to_first_uncached_entry(phdr);
> + end = phdr_to_last_uncached_entry(phdr);
> +
> + while (e < end) {
> + if (e->canary != SMEM_PRIVATE_CANARY)
> + goto invalid_canary;
> +
> + if (le16_to_cpu(e->item) == item) {
> + if (size != NULL) {
> + e_size = le32_to_cpu(e->size);
> + padding_data = le16_to_cpu(e->padding_data);
> +
> + if (WARN_ON(e_size > part->size || padding_data > e_size))
> + return ERR_PTR(-EINVAL);
> +
> + *size = e_size - padding_data;
> + }
> +
> + item_ptr = uncached_entry_to_item(e);
> + if (WARN_ON(item_ptr > p_end))
> + return ERR_PTR(-EINVAL);
> +
> + return item_ptr;
> + }
> +
> + e = uncached_entry_next(e);
> + }
> +
> + if (WARN_ON((void *)e > p_end))
> + return ERR_PTR(-EINVAL);
> +
> + /* Item was not found in the uncached list, search the cached list */
> +
> + e = phdr_to_first_cached_entry(phdr, part->cacheline);
> + end = phdr_to_last_cached_entry(phdr);
> +
> + if (WARN_ON((void *)e < (void *)phdr || (void *)end > p_end))
> + return ERR_PTR(-EINVAL);
> +
> + while (e > end) {
> + if (e->canary != SMEM_PRIVATE_CANARY)
> + goto invalid_canary;
> +
> + if (le16_to_cpu(e->item) == item) {
> + if (size != NULL) {
> + e_size = le32_to_cpu(e->size);
> + padding_data = le16_to_cpu(e->padding_data);
> +
> + if (WARN_ON(e_size > part->size || padding_data > e_size))
> + return ERR_PTR(-EINVAL);
> +
> + *size = e_size - padding_data;
> + }
> +
> + item_ptr = cached_entry_to_item(e);
> + if (WARN_ON(item_ptr < (void *)phdr))
> + return ERR_PTR(-EINVAL);
> +
> + return item_ptr;
> + }
> +
> + e = cached_entry_next(e, part->cacheline);
> + }
> +
> + if (WARN_ON((void *)e < (void *)phdr))
> + return ERR_PTR(-EINVAL);
> +
> + return ERR_PTR(-ENOENT);
> +
> +invalid_canary:
> + dev_err(smem->dev, "Found invalid canary in hosts %hu:%hu partition\n",
> + le16_to_cpu(phdr->host0), le16_to_cpu(phdr->host1));
> +
> + return ERR_PTR(-EINVAL);
> +}
> +
> +/**
> + * qcom_smem_get() - resolve ptr of size of a smem item
> + * @host: the remote processor, or -1
> + * @item: smem item handle
> + * @size: pointer to be filled out with size of the item
> + *
> + * Looks up smem item and returns pointer to it. Size of smem
> + * item is returned in @size.
> + */
> +void *qcom_smem_get(unsigned host, unsigned item, size_t *size)
> +{
> + struct smem_partition *part;
> + void *ptr = ERR_PTR(-EPROBE_DEFER);
> +
> + if (!__smem)
> + return ptr;
> +
> + if (WARN_ON(item >= __smem->item_count))
> + return ERR_PTR(-EINVAL);
> +
> + if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
> + part = &__smem->partitions[host];
> + ptr = qcom_smem_get_private(__smem, part, item, size);
> + } else if (__smem->global_partition.virt_base) {
> + part = &__smem->global_partition;
> + ptr = qcom_smem_get_private(__smem, part, item, size);
> + } else {
> + ptr = qcom_smem_get_global(__smem, item, size);
> + }
> +
> + return ptr;
> +}
> +EXPORT_SYMBOL_GPL(qcom_smem_get);
> +
> +/**
> + * qcom_smem_get_free_space() - retrieve amount of free space in a partition
> + * @host: the remote processor identifying a partition, or -1
> + *
> + * To be used by smem clients as a quick way to determine if any new
> + * allocations has been made.
> + */
> +int qcom_smem_get_free_space(unsigned host)
> +{
> + struct smem_partition *part;
> + struct smem_partition_header *phdr;
> + struct smem_header *header;
> + unsigned ret;
> +
> + if (!__smem)
> + return -EPROBE_DEFER;
> +
> + if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
> + part = &__smem->partitions[host];
> + phdr = part->virt_base;
> + ret = le32_to_cpu(phdr->offset_free_cached) -
> + le32_to_cpu(phdr->offset_free_uncached);
> +
> + if (ret > le32_to_cpu(part->size))
> + return -EINVAL;
> + } else if (__smem->global_partition.virt_base) {
> + part = &__smem->global_partition;
> + phdr = part->virt_base;
> + ret = le32_to_cpu(phdr->offset_free_cached) -
> + le32_to_cpu(phdr->offset_free_uncached);
> +
> + if (ret > le32_to_cpu(part->size))
> + return -EINVAL;
> + } else {
> + header = __smem->regions[0].virt_base;
> + ret = le32_to_cpu(header->available);
> +
> + if (ret > __smem->regions[0].size)
> + return -EINVAL;
> + }
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(qcom_smem_get_free_space);
> +
> +static bool addr_in_range(void __iomem *base, size_t size, void *addr)
> +{
> + return base && ((void __iomem *)addr >= base && (void __iomem *)addr < base + size);
> +}
> +
> +/**
> + * qcom_smem_virt_to_phys() - return the physical address associated
> + * with an smem item pointer (previously returned by qcom_smem_get()
> + * @p: the virtual address to convert
> + *
> + * Returns 0 if the pointer provided is not within any smem region.
> + */
> +phys_addr_t qcom_smem_virt_to_phys(void *p)
> +{
> + struct smem_partition *part;
> + struct smem_region *area;
> + u64 offset;
> + u32 i;
> +
> + for (i = 0; i < SMEM_HOST_COUNT; i++) {
> + part = &__smem->partitions[i];
> +
> + if (addr_in_range(part->virt_base, part->size, p)) {
> + offset = p - part->virt_base;
> +
> + return (phys_addr_t)part->phys_base + offset;
> + }
> + }
> +
> + part = &__smem->global_partition;
> +
> + if (addr_in_range(part->virt_base, part->size, p)) {
> + offset = p - part->virt_base;
> +
> + return (phys_addr_t)part->phys_base + offset;
> + }
> +
> + for (i = 0; i < __smem->num_regions; i++) {
> + area = &__smem->regions[i];
> +
> + if (addr_in_range(area->virt_base, area->size, p)) {
> + offset = p - area->virt_base;
> +
> + return (phys_addr_t)area->aux_base + offset;
> + }
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(qcom_smem_virt_to_phys);
> +
> +/**
> + * qcom_smem_get_soc_id() - return the SoC ID
> + * @id: On success, we return the SoC ID here.
> + *
> + * Look up SoC ID from HW/SW build ID and return it.
> + *
> + * Return: 0 on success, negative errno on failure.
> + */
> +int qcom_smem_get_soc_id(u32 *id)
> +{
> + struct socinfo *info;
> +
> + info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, NULL);
> + if (IS_ERR(info))
> + return PTR_ERR(info);
> +
> + *id = __le32_to_cpu(info->id);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(qcom_smem_get_soc_id);
> +
> +/**
> + * qcom_smem_get_feature_code() - return the feature code
> + * @code: On success, return the feature code here.
> + *
> + * Look up the feature code identifier from SMEM and return it.
> + *
> + * Return: 0 on success, negative errno on failure.
> + */
> +int qcom_smem_get_feature_code(u32 *code)
> +{
> + struct socinfo *info;
> + u32 raw_code;
> +
> + info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, NULL);
> + if (IS_ERR(info))
> + return PTR_ERR(info);
> +
> + /* This only makes sense for socinfo >= 16 */
> + if (__le32_to_cpu(info->fmt) < SOCINFO_VERSION(0, 16))
> + return -EOPNOTSUPP;
> +
> + raw_code = __le32_to_cpu(info->feature_code);
> +
> + /* Ensure the value makes sense */
> + if (raw_code > SOCINFO_FC_INT_MAX)
> + raw_code = SOCINFO_FC_UNKNOWN;
> +
> + *code = raw_code;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(qcom_smem_get_feature_code);
> +
> +static int qcom_smem_get_sbl_version(struct qcom_smem *smem)
> +{
> + struct smem_header *header;
> + __le32 *versions;
> +
> + header = smem->regions[0].virt_base;
> + versions = header->version;
> +
> + return le32_to_cpu(versions[SMEM_MASTER_SBL_VERSION_INDEX]);
> +}
> +
> +static struct smem_ptable *qcom_smem_get_ptable(struct qcom_smem *smem)
> +{
> + struct smem_ptable *ptable;
> + u32 version;
> +
> + ptable = smem->ptable;
> + if (memcmp(ptable->magic, SMEM_PTABLE_MAGIC, sizeof(ptable->magic)))
> + return ERR_PTR(-ENOENT);
> +
> + version = le32_to_cpu(ptable->version);
> + if (version != 1) {
> + dev_err(smem->dev,
> + "Unsupported partition header version %d\n", version);
> + return ERR_PTR(-EINVAL);
> + }
> + return ptable;
> +}
> +
> +static u32 qcom_smem_get_item_count(struct qcom_smem *smem)
> +{
> + struct smem_ptable *ptable;
> + struct smem_info *info;
> +
> + ptable = qcom_smem_get_ptable(smem);
> + if (IS_ERR_OR_NULL(ptable))
> + return SMEM_ITEM_COUNT;
> +
> + info = (struct smem_info *)&ptable->entry[ptable->num_entries];
> + if (memcmp(info->magic, SMEM_INFO_MAGIC, sizeof(info->magic)))
> + return SMEM_ITEM_COUNT;
> +
> + return le16_to_cpu(info->num_items);
> +}
> +
> +/*
> + * Validate the partition header for a partition whose partition
> + * table entry is supplied. Returns a pointer to its header if
> + * valid, or a null pointer otherwise.
> + */
> +static struct smem_partition_header *
> +qcom_smem_partition_header(struct qcom_smem *smem,
> + struct smem_ptable_entry *entry, u16 host0, u16 host1)
> +{
> + struct smem_partition_header *header;
> + u32 phys_addr;
> + u32 size;
> +
> + phys_addr = smem->regions[0].aux_base + le32_to_cpu(entry->offset);
> + header = devm_ioremap_wc(smem->dev, phys_addr, le32_to_cpu(entry->size));
> +
> + if (!header)
> + return NULL;
> +
> + if (memcmp(header->magic, SMEM_PART_MAGIC, sizeof(header->magic))) {
> + dev_err(smem->dev, "bad partition magic %4ph\n", header->magic);
> + return NULL;
> + }
> +
> + if (host0 != le16_to_cpu(header->host0)) {
> + dev_err(smem->dev, "bad host0 (%hu != %hu)\n",
> + host0, le16_to_cpu(header->host0));
> + return NULL;
> + }
> + if (host1 != le16_to_cpu(header->host1)) {
> + dev_err(smem->dev, "bad host1 (%hu != %hu)\n",
> + host1, le16_to_cpu(header->host1));
> + return NULL;
> + }
> +
> + size = le32_to_cpu(header->size);
> + if (size != le32_to_cpu(entry->size)) {
> + dev_err(smem->dev, "bad partition size (%u != %u)\n",
> + size, le32_to_cpu(entry->size));
> + return NULL;
> + }
> +
> + if (le32_to_cpu(header->offset_free_uncached) > size) {
> + dev_err(smem->dev, "bad partition free uncached (%u > %u)\n",
> + le32_to_cpu(header->offset_free_uncached), size);
> + return NULL;
> + }
> +
> + return header;
> +}
> +
> +static int qcom_smem_set_global_partition(struct qcom_smem *smem)
> +{
> + struct smem_partition_header *header;
> + struct smem_ptable_entry *entry;
> + struct smem_ptable *ptable;
> + bool found = false;
> + int i;
> +
> + if (smem->global_partition.virt_base) {
> + dev_err(smem->dev, "Already found the global partition\n");
> + return -EINVAL;
> + }
> +
> + ptable = qcom_smem_get_ptable(smem);
> + if (IS_ERR(ptable))
> + return PTR_ERR(ptable);
> +
> + for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
> + entry = &ptable->entry[i];
> + if (!le32_to_cpu(entry->offset))
> + continue;
> + if (!le32_to_cpu(entry->size))
> + continue;
> +
> + if (le16_to_cpu(entry->host0) != SMEM_GLOBAL_HOST)
> + continue;
> +
> + if (le16_to_cpu(entry->host1) == SMEM_GLOBAL_HOST) {
> + found = true;
> + break;
> + }
> + }
> +
> + if (!found) {
> + dev_err(smem->dev, "Missing entry for global partition\n");
> + return -EINVAL;
> + }
> +
> + header = qcom_smem_partition_header(smem, entry,
> + SMEM_GLOBAL_HOST, SMEM_GLOBAL_HOST);
> + if (!header)
> + return -EINVAL;
> +
> + smem->global_partition.virt_base = (void __iomem *)header;
> + smem->global_partition.phys_base = smem->regions[0].aux_base +
> + le32_to_cpu(entry->offset);
> + smem->global_partition.size = le32_to_cpu(entry->size);
> + smem->global_partition.cacheline = le32_to_cpu(entry->cacheline);
> +
> + return 0;
> +}
> +
> +static int
> +qcom_smem_enumerate_partitions(struct qcom_smem *smem, u16 local_host)
> +{
> + struct smem_partition_header *header;
> + struct smem_ptable_entry *entry;
> + struct smem_ptable *ptable;
> + u16 remote_host;
> + u16 host0, host1;
> + int i;
> +
> + ptable = qcom_smem_get_ptable(smem);
> + if (IS_ERR(ptable))
> + return PTR_ERR(ptable);
> +
> + for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
> + entry = &ptable->entry[i];
> + if (!le32_to_cpu(entry->offset))
> + continue;
> + if (!le32_to_cpu(entry->size))
> + continue;
> +
> + host0 = le16_to_cpu(entry->host0);
> + host1 = le16_to_cpu(entry->host1);
> + if (host0 == local_host)
> + remote_host = host1;
> + else if (host1 == local_host)
> + remote_host = host0;
> + else
> + continue;
> +
> + if (remote_host >= SMEM_HOST_COUNT) {
> + dev_err(smem->dev, "bad host %u\n", remote_host);
> + return -EINVAL;
> + }
> +
> + if (smem->partitions[remote_host].virt_base) {
> + dev_err(smem->dev, "duplicate host %u\n", remote_host);
> + return -EINVAL;
> + }
> +
> + header = qcom_smem_partition_header(smem, entry, host0, host1);
> + if (!header)
> + return -EINVAL;
> +
> + smem->partitions[remote_host].virt_base = (void __iomem *)header;
> + smem->partitions[remote_host].phys_base = smem->regions[0].aux_base +
> + le32_to_cpu(entry->offset);
> + smem->partitions[remote_host].size = le32_to_cpu(entry->size);
> + smem->partitions[remote_host].cacheline = le32_to_cpu(entry->cacheline);
> + }
> +
> + return 0;
> +}
> +
> +static int qcom_smem_map_toc(struct qcom_smem *smem, struct smem_region *region)
> +{
> + u32 ptable_start;
> +
> + /* map starting 4K for smem header */
> + region->virt_base = devm_ioremap_wc(smem->dev, region->aux_base, SZ_4K);
> + ptable_start = region->aux_base + region->size - SZ_4K;
> + /* map last 4k for toc */
> + smem->ptable = devm_ioremap_wc(smem->dev, ptable_start, SZ_4K);
> +
> + if (!region->virt_base || !smem->ptable)
> + return -ENOMEM;
> +
> + return 0;
> +}
> +
> +static int qcom_smem_map_global(struct qcom_smem *smem, u32 size)
> +{
> + u32 phys_addr;
> +
> + phys_addr = smem->regions[0].aux_base;
> +
> + smem->regions[0].size = size;
> + smem->regions[0].virt_base = devm_ioremap_wc(smem->dev, phys_addr, size);
> +
> + if (!smem->regions[0].virt_base)
> + return -ENOMEM;
> +
> + return 0;
> +}
> +
> +static int qcom_smem_resolve_mem(struct qcom_smem *smem, const char *name,
> + struct smem_region *region)
> +{
> + struct device *dev = smem->dev;
> + struct device_node *np;
> + struct resource r;
> + int ret;
> +
> + np = of_parse_phandle(dev->of_node, name, 0);
> + if (!np) {
> + dev_err(dev, "No %s specified\n", name);
> + return -EINVAL;
> + }
> +
> + ret = of_address_to_resource(np, 0, &r);
> + of_node_put(np);
> + if (ret)
> + return ret;
> +
> + region->aux_base = r.start;
> + region->size = resource_size(&r);
> +
> + return 0;
> +}
> +
> +static int qcom_smem_probe(struct platform_device *pdev)
> +{
> + struct smem_header *header;
> + struct reserved_mem *rmem;
> + struct qcom_smem *smem;
> + unsigned long flags;
> + int num_regions;
> + int hwlock_id;
> + u32 version;
> + u32 size;
> + int ret;
> + int i;
> +
> + if (__smem)
> + return 0;
> +
> + num_regions = 1;
> + if (of_property_present(pdev->dev.of_node, "qcom,rpm-msg-ram"))
> + num_regions++;
> +
> + smem = devm_kzalloc(&pdev->dev, struct_size(smem, regions, num_regions),
> + GFP_KERNEL);
> + if (!smem)
> + return -ENOMEM;
> +
> + smem->dev = &pdev->dev;
> + smem->num_regions = num_regions;
> +
> + rmem = of_reserved_mem_lookup(pdev->dev.of_node);
> + if (rmem) {
> + smem->regions[0].aux_base = rmem->base;
> + smem->regions[0].size = rmem->size;
> + } else {
> + /*
> + * Fall back to the memory-region reference, if we're not a
> + * reserved-memory node.
> + */
> + ret = qcom_smem_resolve_mem(smem, "memory-region", &smem->regions[0]);
> + if (ret)
> + return ret;
> + }
> +
> + if (num_regions > 1) {
> + ret = qcom_smem_resolve_mem(smem, "qcom,rpm-msg-ram", &smem->regions[1]);
> + if (ret)
> + return ret;
> + }
> +
> +
> + ret = qcom_smem_map_toc(smem, &smem->regions[0]);
> + if (ret)
> + return ret;
> +
> + for (i = 1; i < num_regions; i++) {
> + smem->regions[i].virt_base = devm_ioremap_wc(&pdev->dev,
> + smem->regions[i].aux_base,
> + smem->regions[i].size);
> + if (!smem->regions[i].virt_base) {
> + dev_err(&pdev->dev, "failed to remap %pa\n", &smem->regions[i].aux_base);
> + return -ENOMEM;
> + }
> + }
> +
> + header = smem->regions[0].virt_base;
> + if (le32_to_cpu(header->initialized) != 1 ||
> + le32_to_cpu(header->reserved)) {
> + dev_err(&pdev->dev, "SMEM is not initialized by SBL\n");
> + return -EINVAL;
> + }
> +
> + hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0);
> + if (hwlock_id < 0) {
> + if (hwlock_id != -EPROBE_DEFER)
> + dev_err(&pdev->dev, "failed to retrieve hwlock\n");
> + return hwlock_id;
> + }
> +
> + smem->hwlock = hwspin_lock_request_specific(hwlock_id);
> + if (!smem->hwlock)
> + return -ENXIO;
> +
> + ret = hwspin_lock_timeout_irqsave(smem->hwlock, HWSPINLOCK_TIMEOUT, &flags);
> + if (ret)
> + return ret;
> + size = readl_relaxed(&header->available) + readl_relaxed(&header->free_offset);
> + hwspin_unlock_irqrestore(smem->hwlock, &flags);
> +
> + version = qcom_smem_get_sbl_version(smem);
> + /*
> + * smem header mapping is required only in heap version scheme, so unmap
> + * it here. It will be remapped in qcom_smem_map_global() when whole
> + * partition is mapped again.
> + */
> + devm_iounmap(smem->dev, smem->regions[0].virt_base);
> + switch (version >> 16) {
> + case SMEM_GLOBAL_PART_VERSION:
> + ret = qcom_smem_set_global_partition(smem);
> + if (ret < 0)
> + return ret;
> + smem->item_count = qcom_smem_get_item_count(smem);
> + break;
> + case SMEM_GLOBAL_HEAP_VERSION:
> + qcom_smem_map_global(smem, size);
> + smem->item_count = SMEM_ITEM_COUNT;
> + break;
> + default:
> + dev_err(&pdev->dev, "Unsupported SMEM version 0x%x\n", version);
> + return -EINVAL;
> + }
> +
> + BUILD_BUG_ON(SMEM_HOST_APPS >= SMEM_HOST_COUNT);
> + ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS);
> + if (ret < 0 && ret != -ENOENT)
> + return ret;
> +
> + __smem = smem;
> +
> + smem->socinfo = platform_device_register_data(&pdev->dev, "qcom-socinfo",
> + PLATFORM_DEVID_NONE, NULL,
> + 0);
> + if (IS_ERR(smem->socinfo))
> + dev_dbg(&pdev->dev, "failed to register socinfo device\n");
> +
> + return 0;
> +}
> +
> +static void qcom_smem_remove(struct platform_device *pdev)
> +{
> + platform_device_unregister(__smem->socinfo);
> +
> + hwspin_lock_free(__smem->hwlock);
> + __smem = NULL;
> +}
> +
> +static const struct of_device_id qcom_smem_of_match[] = {
> + { .compatible = "qcom,smem" },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, qcom_smem_of_match);
> +
> +static struct platform_driver qcom_smem_driver = {
> + .probe = qcom_smem_probe,
> + .remove_new = qcom_smem_remove,
> + .driver = {
> + .name = "qcom-smem",
> + .of_match_table = qcom_smem_of_match,
> + .suppress_bind_attrs = true,
> + },
> +};
> +
> +static int __init qcom_smem_init(void)
> +{
> + return platform_driver_register(&qcom_smem_driver);
> +}
> +arch_initcall(qcom_smem_init);
> +
> +static void __exit qcom_smem_exit(void)
> +{
> + platform_driver_unregister(&qcom_smem_driver);
> +}
> +module_exit(qcom_smem_exit)
> +
> +MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
> +MODULE_DESCRIPTION("Qualcomm Shared Memory Manager");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/soc/qcom/smem.h b/include/soc/qcom/smem.h
> new file mode 100644
> index 000000000000..f946e3beca21
> --- /dev/null
> +++ b/include/soc/qcom/smem.h
> @@ -0,0 +1,20 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef __QCOM_SMEM_H__
> +#define __QCOM_SMEM_H__
> +
> +#define QCOM_SMEM_HOST_ANY -1
> +
> +bool qcom_smem_is_available(void);
> +int qcom_smem_alloc(unsigned host, unsigned item, size_t size);
> +void *qcom_smem_get(unsigned host, unsigned item, size_t *size);
> +
> +int qcom_smem_get_free_space(unsigned host);
> +
> +phys_addr_t qcom_smem_virt_to_phys(void *p);
> +
> +int qcom_smem_get_soc_id(u32 *id);
> +int qcom_smem_get_feature_code(u32 *code);
> +
> +int qcom_smem_bust_hwspin_lock_by_host(unsigned int host);
> +
> +#endif
> diff --git a/include/soc/qcom/socinfo.h b/include/soc/qcom/socinfo.h
> new file mode 100644
> index 000000000000..608950443eee
> --- /dev/null
> +++ b/include/soc/qcom/socinfo.h
> @@ -0,0 +1,111 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#ifndef __QCOM_SOCINFO_H__
> +#define __QCOM_SOCINFO_H__
> +
> +#include <linux/types.h>
> +
> +/*
> + * SMEM item id, used to acquire handles to respective
> + * SMEM region.
> + */
> +#define SMEM_HW_SW_BUILD_ID 137
> +
> +#define SMEM_SOCINFO_BUILD_ID_LENGTH 32
> +#define SMEM_SOCINFO_CHIP_ID_LENGTH 32
> +
> +/*
> + * SoC version type with major number in the upper 16 bits and minor
> + * number in the lower 16 bits.
> + */
> +#define SOCINFO_MAJOR(ver) (((ver) >> 16) & 0xffff)
> +#define SOCINFO_MINOR(ver) ((ver) & 0xffff)
> +#define SOCINFO_VERSION(maj, min) ((((maj) & 0xffff) << 16)|((min) & 0xffff))
> +
> +/* Socinfo SMEM item structure */
> +struct socinfo {
> + __le32 fmt;
> + __le32 id;
> + __le32 ver;
> + char build_id[SMEM_SOCINFO_BUILD_ID_LENGTH];
> + /* Version 2 */
> + __le32 raw_id;
> + __le32 raw_ver;
> + /* Version 3 */
> + __le32 hw_plat;
> + /* Version 4 */
> + __le32 plat_ver;
> + /* Version 5 */
> + __le32 accessory_chip;
> + /* Version 6 */
> + __le32 hw_plat_subtype;
> + /* Version 7 */
> + __le32 pmic_model;
> + __le32 pmic_die_rev;
> + /* Version 8 */
> + __le32 pmic_model_1;
> + __le32 pmic_die_rev_1;
> + __le32 pmic_model_2;
> + __le32 pmic_die_rev_2;
> + /* Version 9 */
> + __le32 foundry_id;
> + /* Version 10 */
> + __le32 serial_num;
> + /* Version 11 */
> + __le32 num_pmics;
> + __le32 pmic_array_offset;
> + /* Version 12 */
> + __le32 chip_family;
> + __le32 raw_device_family;
> + __le32 raw_device_num;
> + /* Version 13 */
> + __le32 nproduct_id;
> + char chip_id[SMEM_SOCINFO_CHIP_ID_LENGTH];
> + /* Version 14 */
> + __le32 num_clusters;
> + __le32 ncluster_array_offset;
> + __le32 num_subset_parts;
> + __le32 nsubset_parts_array_offset;
> + /* Version 15 */
> + __le32 nmodem_supported;
> + /* Version 16 */
> + __le32 feature_code;
> + __le32 pcode;
> + __le32 npartnamemap_offset;
> + __le32 nnum_partname_mapping;
> + /* Version 17 */
> + __le32 oem_variant;
> + /* Version 18 */
> + __le32 num_kvps;
> + __le32 kvps_offset;
> + /* Version 19 */
> + __le32 num_func_clusters;
> + __le32 boot_cluster;
> + __le32 boot_core;
> +};
> +
> +/* Internal feature codes */
> +enum qcom_socinfo_feature_code {
> + /* External feature codes */
> + SOCINFO_FC_UNKNOWN = 0x0,
> + SOCINFO_FC_AA,
> + SOCINFO_FC_AB,
> + SOCINFO_FC_AC,
> + SOCINFO_FC_AD,
> + SOCINFO_FC_AE,
> + SOCINFO_FC_AF,
> + SOCINFO_FC_AG,
> + SOCINFO_FC_AH,
> +};
> +
> +/* Internal feature codes */
> +/* Valid values: 0 <= n <= 0xf */
> +#define SOCINFO_FC_Yn(n) (0xf1 + (n))
> +#define SOCINFO_FC_INT_MAX SOCINFO_FC_Yn(0xf)
> +
> +/* Product codes */
> +#define SOCINFO_PC_UNKNOWN 0
> +#define SOCINFO_PCn(n) ((n) + 1)
> +#define SOCINFO_PC_RESERVE (BIT(31) - 1)
> +
> +#endif
>
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 07/15] soc: qcom: smem: adjust headers for U-Boot
2026-05-04 18:57 ` [PATCH v2 07/15] soc: qcom: smem: adjust headers for U-Boot Casey Connolly
@ 2026-05-05 12:43 ` Sumit Garg
2026-05-18 14:36 ` Neil Armstrong
1 sibling, 0 replies; 51+ messages in thread
From: Sumit Garg @ 2026-05-05 12:43 UTC (permalink / raw)
To: Casey Connolly
Cc: u-boot, u-boot-qcom, Tom Rini, Simon Glass, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
On Mon, May 04, 2026 at 08:57:35PM +0200, Casey Connolly wrote:
> Drop Linux headers for the U-Boot ones.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> drivers/soc/qcom/smem.c | 16 +++++++---------
> 1 file changed, 7 insertions(+), 9 deletions(-)
Reviewed-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
-Sumit
>
> diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
> index 8515b8ae7777..7143856e85c3 100644
> --- a/drivers/soc/qcom/smem.c
> +++ b/drivers/soc/qcom/smem.c
> @@ -3,19 +3,17 @@
> * Copyright (c) 2015, Sony Mobile Communications AB.
> * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
> */
>
> -#include <linux/hwspinlock.h>
> +#include <dm/device.h>
> +#include <dm/device_compat.h>
> +#include <dm/ofnode.h>
> +#include <linux/bug.h>
> #include <linux/io.h>
> -#include <linux/module.h>
> -#include <linux/of.h>
> -#include <linux/of_address.h>
> -#include <linux/of_reserved_mem.h>
> -#include <linux/platform_device.h>
> +#include <linux/ioport.h>
> #include <linux/sizes.h>
> -#include <linux/slab.h>
> -#include <linux/soc/qcom/smem.h>
> -#include <linux/soc/qcom/socinfo.h>
> +#include <soc/qcom/smem.h>
> +#include <soc/qcom/socinfo.h>
>
> /*
> * The Qualcomm shared memory system is a allocate only heap structure that
> * consists of one of more memory areas that can be accessed by the processors
>
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 06/15] soc: qcom: import smem from Linux 6.11-rc2
2026-05-05 12:40 ` Sumit Garg
@ 2026-05-05 12:45 ` Casey Connolly
0 siblings, 0 replies; 51+ messages in thread
From: Casey Connolly @ 2026-05-05 12:45 UTC (permalink / raw)
To: Sumit Garg
Cc: u-boot, u-boot-qcom, Tom Rini, Simon Glass, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
On 05/05/2026 14:40, Sumit Garg wrote:
> On Mon, May 04, 2026 at 08:57:34PM +0200, Casey Connolly wrote:
>> Import the SMEM driver from Linux.
>
> Linux v6.11-rc2 seems quite old, can we rather just sync from v7.1-rc2
> directly?
I had a look but it doesn't feel worth rebasing, the driver has had just
a handful of patches in the last few years and most of them don't seem
relevant to U-boot.
>
> -Sumit
>
>>
>> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
>> ---
>> drivers/soc/qcom/smem.c | 1279 ++++++++++++++++++++++++++++++++++++++++++++
>> include/soc/qcom/smem.h | 20 +
>> include/soc/qcom/socinfo.h | 111 ++++
>> 3 files changed, 1410 insertions(+)
>>
>> diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
>> new file mode 100644
>> index 000000000000..8515b8ae7777
>> --- /dev/null
>> +++ b/drivers/soc/qcom/smem.c
>> @@ -0,0 +1,1279 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (c) 2015, Sony Mobile Communications AB.
>> + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
>> + */
>> +
>> +#include <linux/hwspinlock.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_reserved_mem.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/sizes.h>
>> +#include <linux/slab.h>
>> +#include <linux/soc/qcom/smem.h>
>> +#include <linux/soc/qcom/socinfo.h>
>> +
>> +/*
>> + * The Qualcomm shared memory system is a allocate only heap structure that
>> + * consists of one of more memory areas that can be accessed by the processors
>> + * in the SoC.
>> + *
>> + * All systems contains a global heap, accessible by all processors in the SoC,
>> + * with a table of contents data structure (@smem_header) at the beginning of
>> + * the main shared memory block.
>> + *
>> + * The global header contains meta data for allocations as well as a fixed list
>> + * of 512 entries (@smem_global_entry) that can be initialized to reference
>> + * parts of the shared memory space.
>> + *
>> + *
>> + * In addition to this global heap a set of "private" heaps can be set up at
>> + * boot time with access restrictions so that only certain processor pairs can
>> + * access the data.
>> + *
>> + * These partitions are referenced from an optional partition table
>> + * (@smem_ptable), that is found 4kB from the end of the main smem region. The
>> + * partition table entries (@smem_ptable_entry) lists the involved processors
>> + * (or hosts) and their location in the main shared memory region.
>> + *
>> + * Each partition starts with a header (@smem_partition_header) that identifies
>> + * the partition and holds properties for the two internal memory regions. The
>> + * two regions are cached and non-cached memory respectively. Each region
>> + * contain a link list of allocation headers (@smem_private_entry) followed by
>> + * their data.
>> + *
>> + * Items in the non-cached region are allocated from the start of the partition
>> + * while items in the cached region are allocated from the end. The free area
>> + * is hence the region between the cached and non-cached offsets. The header of
>> + * cached items comes after the data.
>> + *
>> + * Version 12 (SMEM_GLOBAL_PART_VERSION) changes the item alloc/get procedure
>> + * for the global heap. A new global partition is created from the global heap
>> + * region with partition type (SMEM_GLOBAL_HOST) and the max smem item count is
>> + * set by the bootloader.
>> + *
>> + * To synchronize allocations in the shared memory heaps a remote spinlock must
>> + * be held - currently lock number 3 of the sfpb or tcsr is used for this on all
>> + * platforms.
>> + *
>> + */
>> +
>> +/*
>> + * The version member of the smem header contains an array of versions for the
>> + * various software components in the SoC. We verify that the boot loader
>> + * version is a valid version as a sanity check.
>> + */
>> +#define SMEM_MASTER_SBL_VERSION_INDEX 7
>> +#define SMEM_GLOBAL_HEAP_VERSION 11
>> +#define SMEM_GLOBAL_PART_VERSION 12
>> +
>> +/*
>> + * The first 8 items are only to be allocated by the boot loader while
>> + * initializing the heap.
>> + */
>> +#define SMEM_ITEM_LAST_FIXED 8
>> +
>> +/* Highest accepted item number, for both global and private heaps */
>> +#define SMEM_ITEM_COUNT 512
>> +
>> +/* Processor/host identifier for the application processor */
>> +#define SMEM_HOST_APPS 0
>> +
>> +/* Processor/host identifier for the global partition */
>> +#define SMEM_GLOBAL_HOST 0xfffe
>> +
>> +/* Max number of processors/hosts in a system */
>> +#define SMEM_HOST_COUNT 20
>> +
>> +/**
>> + * struct smem_proc_comm - proc_comm communication struct (legacy)
>> + * @command: current command to be executed
>> + * @status: status of the currently requested command
>> + * @params: parameters to the command
>> + */
>> +struct smem_proc_comm {
>> + __le32 command;
>> + __le32 status;
>> + __le32 params[2];
>> +};
>> +
>> +/**
>> + * struct smem_global_entry - entry to reference smem items on the heap
>> + * @allocated: boolean to indicate if this entry is used
>> + * @offset: offset to the allocated space
>> + * @size: size of the allocated space, 8 byte aligned
>> + * @aux_base: base address for the memory region used by this unit, or 0 for
>> + * the default region. bits 0,1 are reserved
>> + */
>> +struct smem_global_entry {
>> + __le32 allocated;
>> + __le32 offset;
>> + __le32 size;
>> + __le32 aux_base; /* bits 1:0 reserved */
>> +};
>> +#define AUX_BASE_MASK 0xfffffffc
>> +
>> +/**
>> + * struct smem_header - header found in beginning of primary smem region
>> + * @proc_comm: proc_comm communication interface (legacy)
>> + * @version: array of versions for the various subsystems
>> + * @initialized: boolean to indicate that smem is initialized
>> + * @free_offset: index of the first unallocated byte in smem
>> + * @available: number of bytes available for allocation
>> + * @reserved: reserved field, must be 0
>> + * @toc: array of references to items
>> + */
>> +struct smem_header {
>> + struct smem_proc_comm proc_comm[4];
>> + __le32 version[32];
>> + __le32 initialized;
>> + __le32 free_offset;
>> + __le32 available;
>> + __le32 reserved;
>> + struct smem_global_entry toc[SMEM_ITEM_COUNT];
>> +};
>> +
>> +/**
>> + * struct smem_ptable_entry - one entry in the @smem_ptable list
>> + * @offset: offset, within the main shared memory region, of the partition
>> + * @size: size of the partition
>> + * @flags: flags for the partition (currently unused)
>> + * @host0: first processor/host with access to this partition
>> + * @host1: second processor/host with access to this partition
>> + * @cacheline: alignment for "cached" entries
>> + * @reserved: reserved entries for later use
>> + */
>> +struct smem_ptable_entry {
>> + __le32 offset;
>> + __le32 size;
>> + __le32 flags;
>> + __le16 host0;
>> + __le16 host1;
>> + __le32 cacheline;
>> + __le32 reserved[7];
>> +};
>> +
>> +/**
>> + * struct smem_ptable - partition table for the private partitions
>> + * @magic: magic number, must be SMEM_PTABLE_MAGIC
>> + * @version: version of the partition table
>> + * @num_entries: number of partitions in the table
>> + * @reserved: for now reserved entries
>> + * @entry: list of @smem_ptable_entry for the @num_entries partitions
>> + */
>> +struct smem_ptable {
>> + u8 magic[4];
>> + __le32 version;
>> + __le32 num_entries;
>> + __le32 reserved[5];
>> + struct smem_ptable_entry entry[];
>> +};
>> +
>> +static const u8 SMEM_PTABLE_MAGIC[] = { 0x24, 0x54, 0x4f, 0x43 }; /* "$TOC" */
>> +
>> +/**
>> + * struct smem_partition_header - header of the partitions
>> + * @magic: magic number, must be SMEM_PART_MAGIC
>> + * @host0: first processor/host with access to this partition
>> + * @host1: second processor/host with access to this partition
>> + * @size: size of the partition
>> + * @offset_free_uncached: offset to the first free byte of uncached memory in
>> + * this partition
>> + * @offset_free_cached: offset to the first free byte of cached memory in this
>> + * partition
>> + * @reserved: for now reserved entries
>> + */
>> +struct smem_partition_header {
>> + u8 magic[4];
>> + __le16 host0;
>> + __le16 host1;
>> + __le32 size;
>> + __le32 offset_free_uncached;
>> + __le32 offset_free_cached;
>> + __le32 reserved[3];
>> +};
>> +
>> +/**
>> + * struct smem_partition - describes smem partition
>> + * @virt_base: starting virtual address of partition
>> + * @phys_base: starting physical address of partition
>> + * @cacheline: alignment for "cached" entries
>> + * @size: size of partition
>> + */
>> +struct smem_partition {
>> + void __iomem *virt_base;
>> + phys_addr_t phys_base;
>> + size_t cacheline;
>> + size_t size;
>> +};
>> +
>> +static const u8 SMEM_PART_MAGIC[] = { 0x24, 0x50, 0x52, 0x54 };
>> +
>> +/**
>> + * struct smem_private_entry - header of each item in the private partition
>> + * @canary: magic number, must be SMEM_PRIVATE_CANARY
>> + * @item: identifying number of the smem item
>> + * @size: size of the data, including padding bytes
>> + * @padding_data: number of bytes of padding of data
>> + * @padding_hdr: number of bytes of padding between the header and the data
>> + * @reserved: for now reserved entry
>> + */
>> +struct smem_private_entry {
>> + u16 canary; /* bytes are the same so no swapping needed */
>> + __le16 item;
>> + __le32 size; /* includes padding bytes */
>> + __le16 padding_data;
>> + __le16 padding_hdr;
>> + __le32 reserved;
>> +};
>> +#define SMEM_PRIVATE_CANARY 0xa5a5
>> +
>> +/**
>> + * struct smem_info - smem region info located after the table of contents
>> + * @magic: magic number, must be SMEM_INFO_MAGIC
>> + * @size: size of the smem region
>> + * @base_addr: base address of the smem region
>> + * @reserved: for now reserved entry
>> + * @num_items: highest accepted item number
>> + */
>> +struct smem_info {
>> + u8 magic[4];
>> + __le32 size;
>> + __le32 base_addr;
>> + __le32 reserved;
>> + __le16 num_items;
>> +};
>> +
>> +static const u8 SMEM_INFO_MAGIC[] = { 0x53, 0x49, 0x49, 0x49 }; /* SIII */
>> +
>> +/**
>> + * struct smem_region - representation of a chunk of memory used for smem
>> + * @aux_base: identifier of aux_mem base
>> + * @virt_base: virtual base address of memory with this aux_mem identifier
>> + * @size: size of the memory region
>> + */
>> +struct smem_region {
>> + phys_addr_t aux_base;
>> + void __iomem *virt_base;
>> + size_t size;
>> +};
>> +
>> +/**
>> + * struct qcom_smem - device data for the smem device
>> + * @dev: device pointer
>> + * @hwlock: reference to a hwspinlock
>> + * @ptable: virtual base of partition table
>> + * @global_partition: describes for global partition when in use
>> + * @partitions: list of partitions of current processor/host
>> + * @item_count: max accepted item number
>> + * @socinfo: platform device pointer
>> + * @num_regions: number of @regions
>> + * @regions: list of the memory regions defining the shared memory
>> + */
>> +struct qcom_smem {
>> + struct device *dev;
>> +
>> + struct hwspinlock *hwlock;
>> +
>> + u32 item_count;
>> + struct platform_device *socinfo;
>> + struct smem_ptable *ptable;
>> + struct smem_partition global_partition;
>> + struct smem_partition partitions[SMEM_HOST_COUNT];
>> +
>> + unsigned num_regions;
>> + struct smem_region regions[] __counted_by(num_regions);
>> +};
>> +
>> +static void *
>> +phdr_to_last_uncached_entry(struct smem_partition_header *phdr)
>> +{
>> + void *p = phdr;
>> +
>> + return p + le32_to_cpu(phdr->offset_free_uncached);
>> +}
>> +
>> +static struct smem_private_entry *
>> +phdr_to_first_cached_entry(struct smem_partition_header *phdr,
>> + size_t cacheline)
>> +{
>> + void *p = phdr;
>> + struct smem_private_entry *e;
>> +
>> + return p + le32_to_cpu(phdr->size) - ALIGN(sizeof(*e), cacheline);
>> +}
>> +
>> +static void *
>> +phdr_to_last_cached_entry(struct smem_partition_header *phdr)
>> +{
>> + void *p = phdr;
>> +
>> + return p + le32_to_cpu(phdr->offset_free_cached);
>> +}
>> +
>> +static struct smem_private_entry *
>> +phdr_to_first_uncached_entry(struct smem_partition_header *phdr)
>> +{
>> + void *p = phdr;
>> +
>> + return p + sizeof(*phdr);
>> +}
>> +
>> +static struct smem_private_entry *
>> +uncached_entry_next(struct smem_private_entry *e)
>> +{
>> + void *p = e;
>> +
>> + return p + sizeof(*e) + le16_to_cpu(e->padding_hdr) +
>> + le32_to_cpu(e->size);
>> +}
>> +
>> +static struct smem_private_entry *
>> +cached_entry_next(struct smem_private_entry *e, size_t cacheline)
>> +{
>> + void *p = e;
>> +
>> + return p - le32_to_cpu(e->size) - ALIGN(sizeof(*e), cacheline);
>> +}
>> +
>> +static void *uncached_entry_to_item(struct smem_private_entry *e)
>> +{
>> + void *p = e;
>> +
>> + return p + sizeof(*e) + le16_to_cpu(e->padding_hdr);
>> +}
>> +
>> +static void *cached_entry_to_item(struct smem_private_entry *e)
>> +{
>> + void *p = e;
>> +
>> + return p - le32_to_cpu(e->size);
>> +}
>> +
>> +/* Pointer to the one and only smem handle */
>> +static struct qcom_smem *__smem;
>> +
>> +/* Timeout (ms) for the trylock of remote spinlocks */
>> +#define HWSPINLOCK_TIMEOUT 1000
>> +
>> +/* The qcom hwspinlock id is always plus one from the smem host id */
>> +#define SMEM_HOST_ID_TO_HWSPINLOCK_ID(__x) ((__x) + 1)
>> +
>> +/**
>> + * qcom_smem_bust_hwspin_lock_by_host() - bust the smem hwspinlock for a host
>> + * @host: remote processor id
>> + *
>> + * Busts the hwspin_lock for the given smem host id. This helper is intended
>> + * for remoteproc drivers that manage remoteprocs with an equivalent smem
>> + * driver instance in the remote firmware. Drivers can force a release of the
>> + * smem hwspin_lock if the rproc unexpectedly goes into a bad state.
>> + *
>> + * Context: Process context.
>> + *
>> + * Returns: 0 on success, otherwise negative errno.
>> + */
>> +int qcom_smem_bust_hwspin_lock_by_host(unsigned int host)
>> +{
>> + /* This function is for remote procs, so ignore SMEM_HOST_APPS */
>> + if (host == SMEM_HOST_APPS || host >= SMEM_HOST_COUNT)
>> + return -EINVAL;
>> +
>> + return hwspin_lock_bust(__smem->hwlock, SMEM_HOST_ID_TO_HWSPINLOCK_ID(host));
>> +}
>> +EXPORT_SYMBOL_GPL(qcom_smem_bust_hwspin_lock_by_host);
>> +
>> +/**
>> + * qcom_smem_is_available() - Check if SMEM is available
>> + *
>> + * Return: true if SMEM is available, false otherwise.
>> + */
>> +bool qcom_smem_is_available(void)
>> +{
>> + return !!__smem;
>> +}
>> +EXPORT_SYMBOL_GPL(qcom_smem_is_available);
>> +
>> +static int qcom_smem_alloc_private(struct qcom_smem *smem,
>> + struct smem_partition *part,
>> + unsigned item,
>> + size_t size)
>> +{
>> + struct smem_private_entry *hdr, *end;
>> + struct smem_partition_header *phdr;
>> + size_t alloc_size;
>> + void *cached;
>> + void *p_end;
>> +
>> + phdr = (struct smem_partition_header __force *)part->virt_base;
>> + p_end = (void *)phdr + part->size;
>> +
>> + hdr = phdr_to_first_uncached_entry(phdr);
>> + end = phdr_to_last_uncached_entry(phdr);
>> + cached = phdr_to_last_cached_entry(phdr);
>> +
>> + if (WARN_ON((void *)end > p_end || cached > p_end))
>> + return -EINVAL;
>> +
>> + while (hdr < end) {
>> + if (hdr->canary != SMEM_PRIVATE_CANARY)
>> + goto bad_canary;
>> + if (le16_to_cpu(hdr->item) == item)
>> + return -EEXIST;
>> +
>> + hdr = uncached_entry_next(hdr);
>> + }
>> +
>> + if (WARN_ON((void *)hdr > p_end))
>> + return -EINVAL;
>> +
>> + /* Check that we don't grow into the cached region */
>> + alloc_size = sizeof(*hdr) + ALIGN(size, 8);
>> + if ((void *)hdr + alloc_size > cached) {
>> + dev_err(smem->dev, "Out of memory\n");
>> + return -ENOSPC;
>> + }
>> +
>> + hdr->canary = SMEM_PRIVATE_CANARY;
>> + hdr->item = cpu_to_le16(item);
>> + hdr->size = cpu_to_le32(ALIGN(size, 8));
>> + hdr->padding_data = cpu_to_le16(le32_to_cpu(hdr->size) - size);
>> + hdr->padding_hdr = 0;
>> +
>> + /*
>> + * Ensure the header is written before we advance the free offset, so
>> + * that remote processors that does not take the remote spinlock still
>> + * gets a consistent view of the linked list.
>> + */
>> + wmb();
>> + le32_add_cpu(&phdr->offset_free_uncached, alloc_size);
>> +
>> + return 0;
>> +bad_canary:
>> + dev_err(smem->dev, "Found invalid canary in hosts %hu:%hu partition\n",
>> + le16_to_cpu(phdr->host0), le16_to_cpu(phdr->host1));
>> +
>> + return -EINVAL;
>> +}
>> +
>> +static int qcom_smem_alloc_global(struct qcom_smem *smem,
>> + unsigned item,
>> + size_t size)
>> +{
>> + struct smem_global_entry *entry;
>> + struct smem_header *header;
>> +
>> + header = smem->regions[0].virt_base;
>> + entry = &header->toc[item];
>> + if (entry->allocated)
>> + return -EEXIST;
>> +
>> + size = ALIGN(size, 8);
>> + if (WARN_ON(size > le32_to_cpu(header->available)))
>> + return -ENOMEM;
>> +
>> + entry->offset = header->free_offset;
>> + entry->size = cpu_to_le32(size);
>> +
>> + /*
>> + * Ensure the header is consistent before we mark the item allocated,
>> + * so that remote processors will get a consistent view of the item
>> + * even though they do not take the spinlock on read.
>> + */
>> + wmb();
>> + entry->allocated = cpu_to_le32(1);
>> +
>> + le32_add_cpu(&header->free_offset, size);
>> + le32_add_cpu(&header->available, -size);
>> +
>> + return 0;
>> +}
>> +
>> +/**
>> + * qcom_smem_alloc() - allocate space for a smem item
>> + * @host: remote processor id, or -1
>> + * @item: smem item handle
>> + * @size: number of bytes to be allocated
>> + *
>> + * Allocate space for a given smem item of size @size, given that the item is
>> + * not yet allocated.
>> + */
>> +int qcom_smem_alloc(unsigned host, unsigned item, size_t size)
>> +{
>> + struct smem_partition *part;
>> + unsigned long flags;
>> + int ret;
>> +
>> + if (!__smem)
>> + return -EPROBE_DEFER;
>> +
>> + if (item < SMEM_ITEM_LAST_FIXED) {
>> + dev_err(__smem->dev,
>> + "Rejecting allocation of static entry %d\n", item);
>> + return -EINVAL;
>> + }
>> +
>> + if (WARN_ON(item >= __smem->item_count))
>> + return -EINVAL;
>> +
>> + ret = hwspin_lock_timeout_irqsave(__smem->hwlock,
>> + HWSPINLOCK_TIMEOUT,
>> + &flags);
>> + if (ret)
>> + return ret;
>> +
>> + if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
>> + part = &__smem->partitions[host];
>> + ret = qcom_smem_alloc_private(__smem, part, item, size);
>> + } else if (__smem->global_partition.virt_base) {
>> + part = &__smem->global_partition;
>> + ret = qcom_smem_alloc_private(__smem, part, item, size);
>> + } else {
>> + ret = qcom_smem_alloc_global(__smem, item, size);
>> + }
>> +
>> + hwspin_unlock_irqrestore(__smem->hwlock, &flags);
>> +
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(qcom_smem_alloc);
>> +
>> +static void *qcom_smem_get_global(struct qcom_smem *smem,
>> + unsigned item,
>> + size_t *size)
>> +{
>> + struct smem_header *header;
>> + struct smem_region *region;
>> + struct smem_global_entry *entry;
>> + u64 entry_offset;
>> + u32 e_size;
>> + u32 aux_base;
>> + unsigned i;
>> +
>> + header = smem->regions[0].virt_base;
>> + entry = &header->toc[item];
>> + if (!entry->allocated)
>> + return ERR_PTR(-ENXIO);
>> +
>> + aux_base = le32_to_cpu(entry->aux_base) & AUX_BASE_MASK;
>> +
>> + for (i = 0; i < smem->num_regions; i++) {
>> + region = &smem->regions[i];
>> +
>> + if ((u32)region->aux_base == aux_base || !aux_base) {
>> + e_size = le32_to_cpu(entry->size);
>> + entry_offset = le32_to_cpu(entry->offset);
>> +
>> + if (WARN_ON(e_size + entry_offset > region->size))
>> + return ERR_PTR(-EINVAL);
>> +
>> + if (size != NULL)
>> + *size = e_size;
>> +
>> + return region->virt_base + entry_offset;
>> + }
>> + }
>> +
>> + return ERR_PTR(-ENOENT);
>> +}
>> +
>> +static void *qcom_smem_get_private(struct qcom_smem *smem,
>> + struct smem_partition *part,
>> + unsigned item,
>> + size_t *size)
>> +{
>> + struct smem_private_entry *e, *end;
>> + struct smem_partition_header *phdr;
>> + void *item_ptr, *p_end;
>> + u32 padding_data;
>> + u32 e_size;
>> +
>> + phdr = (struct smem_partition_header __force *)part->virt_base;
>> + p_end = (void *)phdr + part->size;
>> +
>> + e = phdr_to_first_uncached_entry(phdr);
>> + end = phdr_to_last_uncached_entry(phdr);
>> +
>> + while (e < end) {
>> + if (e->canary != SMEM_PRIVATE_CANARY)
>> + goto invalid_canary;
>> +
>> + if (le16_to_cpu(e->item) == item) {
>> + if (size != NULL) {
>> + e_size = le32_to_cpu(e->size);
>> + padding_data = le16_to_cpu(e->padding_data);
>> +
>> + if (WARN_ON(e_size > part->size || padding_data > e_size))
>> + return ERR_PTR(-EINVAL);
>> +
>> + *size = e_size - padding_data;
>> + }
>> +
>> + item_ptr = uncached_entry_to_item(e);
>> + if (WARN_ON(item_ptr > p_end))
>> + return ERR_PTR(-EINVAL);
>> +
>> + return item_ptr;
>> + }
>> +
>> + e = uncached_entry_next(e);
>> + }
>> +
>> + if (WARN_ON((void *)e > p_end))
>> + return ERR_PTR(-EINVAL);
>> +
>> + /* Item was not found in the uncached list, search the cached list */
>> +
>> + e = phdr_to_first_cached_entry(phdr, part->cacheline);
>> + end = phdr_to_last_cached_entry(phdr);
>> +
>> + if (WARN_ON((void *)e < (void *)phdr || (void *)end > p_end))
>> + return ERR_PTR(-EINVAL);
>> +
>> + while (e > end) {
>> + if (e->canary != SMEM_PRIVATE_CANARY)
>> + goto invalid_canary;
>> +
>> + if (le16_to_cpu(e->item) == item) {
>> + if (size != NULL) {
>> + e_size = le32_to_cpu(e->size);
>> + padding_data = le16_to_cpu(e->padding_data);
>> +
>> + if (WARN_ON(e_size > part->size || padding_data > e_size))
>> + return ERR_PTR(-EINVAL);
>> +
>> + *size = e_size - padding_data;
>> + }
>> +
>> + item_ptr = cached_entry_to_item(e);
>> + if (WARN_ON(item_ptr < (void *)phdr))
>> + return ERR_PTR(-EINVAL);
>> +
>> + return item_ptr;
>> + }
>> +
>> + e = cached_entry_next(e, part->cacheline);
>> + }
>> +
>> + if (WARN_ON((void *)e < (void *)phdr))
>> + return ERR_PTR(-EINVAL);
>> +
>> + return ERR_PTR(-ENOENT);
>> +
>> +invalid_canary:
>> + dev_err(smem->dev, "Found invalid canary in hosts %hu:%hu partition\n",
>> + le16_to_cpu(phdr->host0), le16_to_cpu(phdr->host1));
>> +
>> + return ERR_PTR(-EINVAL);
>> +}
>> +
>> +/**
>> + * qcom_smem_get() - resolve ptr of size of a smem item
>> + * @host: the remote processor, or -1
>> + * @item: smem item handle
>> + * @size: pointer to be filled out with size of the item
>> + *
>> + * Looks up smem item and returns pointer to it. Size of smem
>> + * item is returned in @size.
>> + */
>> +void *qcom_smem_get(unsigned host, unsigned item, size_t *size)
>> +{
>> + struct smem_partition *part;
>> + void *ptr = ERR_PTR(-EPROBE_DEFER);
>> +
>> + if (!__smem)
>> + return ptr;
>> +
>> + if (WARN_ON(item >= __smem->item_count))
>> + return ERR_PTR(-EINVAL);
>> +
>> + if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
>> + part = &__smem->partitions[host];
>> + ptr = qcom_smem_get_private(__smem, part, item, size);
>> + } else if (__smem->global_partition.virt_base) {
>> + part = &__smem->global_partition;
>> + ptr = qcom_smem_get_private(__smem, part, item, size);
>> + } else {
>> + ptr = qcom_smem_get_global(__smem, item, size);
>> + }
>> +
>> + return ptr;
>> +}
>> +EXPORT_SYMBOL_GPL(qcom_smem_get);
>> +
>> +/**
>> + * qcom_smem_get_free_space() - retrieve amount of free space in a partition
>> + * @host: the remote processor identifying a partition, or -1
>> + *
>> + * To be used by smem clients as a quick way to determine if any new
>> + * allocations has been made.
>> + */
>> +int qcom_smem_get_free_space(unsigned host)
>> +{
>> + struct smem_partition *part;
>> + struct smem_partition_header *phdr;
>> + struct smem_header *header;
>> + unsigned ret;
>> +
>> + if (!__smem)
>> + return -EPROBE_DEFER;
>> +
>> + if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
>> + part = &__smem->partitions[host];
>> + phdr = part->virt_base;
>> + ret = le32_to_cpu(phdr->offset_free_cached) -
>> + le32_to_cpu(phdr->offset_free_uncached);
>> +
>> + if (ret > le32_to_cpu(part->size))
>> + return -EINVAL;
>> + } else if (__smem->global_partition.virt_base) {
>> + part = &__smem->global_partition;
>> + phdr = part->virt_base;
>> + ret = le32_to_cpu(phdr->offset_free_cached) -
>> + le32_to_cpu(phdr->offset_free_uncached);
>> +
>> + if (ret > le32_to_cpu(part->size))
>> + return -EINVAL;
>> + } else {
>> + header = __smem->regions[0].virt_base;
>> + ret = le32_to_cpu(header->available);
>> +
>> + if (ret > __smem->regions[0].size)
>> + return -EINVAL;
>> + }
>> +
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(qcom_smem_get_free_space);
>> +
>> +static bool addr_in_range(void __iomem *base, size_t size, void *addr)
>> +{
>> + return base && ((void __iomem *)addr >= base && (void __iomem *)addr < base + size);
>> +}
>> +
>> +/**
>> + * qcom_smem_virt_to_phys() - return the physical address associated
>> + * with an smem item pointer (previously returned by qcom_smem_get()
>> + * @p: the virtual address to convert
>> + *
>> + * Returns 0 if the pointer provided is not within any smem region.
>> + */
>> +phys_addr_t qcom_smem_virt_to_phys(void *p)
>> +{
>> + struct smem_partition *part;
>> + struct smem_region *area;
>> + u64 offset;
>> + u32 i;
>> +
>> + for (i = 0; i < SMEM_HOST_COUNT; i++) {
>> + part = &__smem->partitions[i];
>> +
>> + if (addr_in_range(part->virt_base, part->size, p)) {
>> + offset = p - part->virt_base;
>> +
>> + return (phys_addr_t)part->phys_base + offset;
>> + }
>> + }
>> +
>> + part = &__smem->global_partition;
>> +
>> + if (addr_in_range(part->virt_base, part->size, p)) {
>> + offset = p - part->virt_base;
>> +
>> + return (phys_addr_t)part->phys_base + offset;
>> + }
>> +
>> + for (i = 0; i < __smem->num_regions; i++) {
>> + area = &__smem->regions[i];
>> +
>> + if (addr_in_range(area->virt_base, area->size, p)) {
>> + offset = p - area->virt_base;
>> +
>> + return (phys_addr_t)area->aux_base + offset;
>> + }
>> + }
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(qcom_smem_virt_to_phys);
>> +
>> +/**
>> + * qcom_smem_get_soc_id() - return the SoC ID
>> + * @id: On success, we return the SoC ID here.
>> + *
>> + * Look up SoC ID from HW/SW build ID and return it.
>> + *
>> + * Return: 0 on success, negative errno on failure.
>> + */
>> +int qcom_smem_get_soc_id(u32 *id)
>> +{
>> + struct socinfo *info;
>> +
>> + info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, NULL);
>> + if (IS_ERR(info))
>> + return PTR_ERR(info);
>> +
>> + *id = __le32_to_cpu(info->id);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(qcom_smem_get_soc_id);
>> +
>> +/**
>> + * qcom_smem_get_feature_code() - return the feature code
>> + * @code: On success, return the feature code here.
>> + *
>> + * Look up the feature code identifier from SMEM and return it.
>> + *
>> + * Return: 0 on success, negative errno on failure.
>> + */
>> +int qcom_smem_get_feature_code(u32 *code)
>> +{
>> + struct socinfo *info;
>> + u32 raw_code;
>> +
>> + info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, NULL);
>> + if (IS_ERR(info))
>> + return PTR_ERR(info);
>> +
>> + /* This only makes sense for socinfo >= 16 */
>> + if (__le32_to_cpu(info->fmt) < SOCINFO_VERSION(0, 16))
>> + return -EOPNOTSUPP;
>> +
>> + raw_code = __le32_to_cpu(info->feature_code);
>> +
>> + /* Ensure the value makes sense */
>> + if (raw_code > SOCINFO_FC_INT_MAX)
>> + raw_code = SOCINFO_FC_UNKNOWN;
>> +
>> + *code = raw_code;
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(qcom_smem_get_feature_code);
>> +
>> +static int qcom_smem_get_sbl_version(struct qcom_smem *smem)
>> +{
>> + struct smem_header *header;
>> + __le32 *versions;
>> +
>> + header = smem->regions[0].virt_base;
>> + versions = header->version;
>> +
>> + return le32_to_cpu(versions[SMEM_MASTER_SBL_VERSION_INDEX]);
>> +}
>> +
>> +static struct smem_ptable *qcom_smem_get_ptable(struct qcom_smem *smem)
>> +{
>> + struct smem_ptable *ptable;
>> + u32 version;
>> +
>> + ptable = smem->ptable;
>> + if (memcmp(ptable->magic, SMEM_PTABLE_MAGIC, sizeof(ptable->magic)))
>> + return ERR_PTR(-ENOENT);
>> +
>> + version = le32_to_cpu(ptable->version);
>> + if (version != 1) {
>> + dev_err(smem->dev,
>> + "Unsupported partition header version %d\n", version);
>> + return ERR_PTR(-EINVAL);
>> + }
>> + return ptable;
>> +}
>> +
>> +static u32 qcom_smem_get_item_count(struct qcom_smem *smem)
>> +{
>> + struct smem_ptable *ptable;
>> + struct smem_info *info;
>> +
>> + ptable = qcom_smem_get_ptable(smem);
>> + if (IS_ERR_OR_NULL(ptable))
>> + return SMEM_ITEM_COUNT;
>> +
>> + info = (struct smem_info *)&ptable->entry[ptable->num_entries];
>> + if (memcmp(info->magic, SMEM_INFO_MAGIC, sizeof(info->magic)))
>> + return SMEM_ITEM_COUNT;
>> +
>> + return le16_to_cpu(info->num_items);
>> +}
>> +
>> +/*
>> + * Validate the partition header for a partition whose partition
>> + * table entry is supplied. Returns a pointer to its header if
>> + * valid, or a null pointer otherwise.
>> + */
>> +static struct smem_partition_header *
>> +qcom_smem_partition_header(struct qcom_smem *smem,
>> + struct smem_ptable_entry *entry, u16 host0, u16 host1)
>> +{
>> + struct smem_partition_header *header;
>> + u32 phys_addr;
>> + u32 size;
>> +
>> + phys_addr = smem->regions[0].aux_base + le32_to_cpu(entry->offset);
>> + header = devm_ioremap_wc(smem->dev, phys_addr, le32_to_cpu(entry->size));
>> +
>> + if (!header)
>> + return NULL;
>> +
>> + if (memcmp(header->magic, SMEM_PART_MAGIC, sizeof(header->magic))) {
>> + dev_err(smem->dev, "bad partition magic %4ph\n", header->magic);
>> + return NULL;
>> + }
>> +
>> + if (host0 != le16_to_cpu(header->host0)) {
>> + dev_err(smem->dev, "bad host0 (%hu != %hu)\n",
>> + host0, le16_to_cpu(header->host0));
>> + return NULL;
>> + }
>> + if (host1 != le16_to_cpu(header->host1)) {
>> + dev_err(smem->dev, "bad host1 (%hu != %hu)\n",
>> + host1, le16_to_cpu(header->host1));
>> + return NULL;
>> + }
>> +
>> + size = le32_to_cpu(header->size);
>> + if (size != le32_to_cpu(entry->size)) {
>> + dev_err(smem->dev, "bad partition size (%u != %u)\n",
>> + size, le32_to_cpu(entry->size));
>> + return NULL;
>> + }
>> +
>> + if (le32_to_cpu(header->offset_free_uncached) > size) {
>> + dev_err(smem->dev, "bad partition free uncached (%u > %u)\n",
>> + le32_to_cpu(header->offset_free_uncached), size);
>> + return NULL;
>> + }
>> +
>> + return header;
>> +}
>> +
>> +static int qcom_smem_set_global_partition(struct qcom_smem *smem)
>> +{
>> + struct smem_partition_header *header;
>> + struct smem_ptable_entry *entry;
>> + struct smem_ptable *ptable;
>> + bool found = false;
>> + int i;
>> +
>> + if (smem->global_partition.virt_base) {
>> + dev_err(smem->dev, "Already found the global partition\n");
>> + return -EINVAL;
>> + }
>> +
>> + ptable = qcom_smem_get_ptable(smem);
>> + if (IS_ERR(ptable))
>> + return PTR_ERR(ptable);
>> +
>> + for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
>> + entry = &ptable->entry[i];
>> + if (!le32_to_cpu(entry->offset))
>> + continue;
>> + if (!le32_to_cpu(entry->size))
>> + continue;
>> +
>> + if (le16_to_cpu(entry->host0) != SMEM_GLOBAL_HOST)
>> + continue;
>> +
>> + if (le16_to_cpu(entry->host1) == SMEM_GLOBAL_HOST) {
>> + found = true;
>> + break;
>> + }
>> + }
>> +
>> + if (!found) {
>> + dev_err(smem->dev, "Missing entry for global partition\n");
>> + return -EINVAL;
>> + }
>> +
>> + header = qcom_smem_partition_header(smem, entry,
>> + SMEM_GLOBAL_HOST, SMEM_GLOBAL_HOST);
>> + if (!header)
>> + return -EINVAL;
>> +
>> + smem->global_partition.virt_base = (void __iomem *)header;
>> + smem->global_partition.phys_base = smem->regions[0].aux_base +
>> + le32_to_cpu(entry->offset);
>> + smem->global_partition.size = le32_to_cpu(entry->size);
>> + smem->global_partition.cacheline = le32_to_cpu(entry->cacheline);
>> +
>> + return 0;
>> +}
>> +
>> +static int
>> +qcom_smem_enumerate_partitions(struct qcom_smem *smem, u16 local_host)
>> +{
>> + struct smem_partition_header *header;
>> + struct smem_ptable_entry *entry;
>> + struct smem_ptable *ptable;
>> + u16 remote_host;
>> + u16 host0, host1;
>> + int i;
>> +
>> + ptable = qcom_smem_get_ptable(smem);
>> + if (IS_ERR(ptable))
>> + return PTR_ERR(ptable);
>> +
>> + for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
>> + entry = &ptable->entry[i];
>> + if (!le32_to_cpu(entry->offset))
>> + continue;
>> + if (!le32_to_cpu(entry->size))
>> + continue;
>> +
>> + host0 = le16_to_cpu(entry->host0);
>> + host1 = le16_to_cpu(entry->host1);
>> + if (host0 == local_host)
>> + remote_host = host1;
>> + else if (host1 == local_host)
>> + remote_host = host0;
>> + else
>> + continue;
>> +
>> + if (remote_host >= SMEM_HOST_COUNT) {
>> + dev_err(smem->dev, "bad host %u\n", remote_host);
>> + return -EINVAL;
>> + }
>> +
>> + if (smem->partitions[remote_host].virt_base) {
>> + dev_err(smem->dev, "duplicate host %u\n", remote_host);
>> + return -EINVAL;
>> + }
>> +
>> + header = qcom_smem_partition_header(smem, entry, host0, host1);
>> + if (!header)
>> + return -EINVAL;
>> +
>> + smem->partitions[remote_host].virt_base = (void __iomem *)header;
>> + smem->partitions[remote_host].phys_base = smem->regions[0].aux_base +
>> + le32_to_cpu(entry->offset);
>> + smem->partitions[remote_host].size = le32_to_cpu(entry->size);
>> + smem->partitions[remote_host].cacheline = le32_to_cpu(entry->cacheline);
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int qcom_smem_map_toc(struct qcom_smem *smem, struct smem_region *region)
>> +{
>> + u32 ptable_start;
>> +
>> + /* map starting 4K for smem header */
>> + region->virt_base = devm_ioremap_wc(smem->dev, region->aux_base, SZ_4K);
>> + ptable_start = region->aux_base + region->size - SZ_4K;
>> + /* map last 4k for toc */
>> + smem->ptable = devm_ioremap_wc(smem->dev, ptable_start, SZ_4K);
>> +
>> + if (!region->virt_base || !smem->ptable)
>> + return -ENOMEM;
>> +
>> + return 0;
>> +}
>> +
>> +static int qcom_smem_map_global(struct qcom_smem *smem, u32 size)
>> +{
>> + u32 phys_addr;
>> +
>> + phys_addr = smem->regions[0].aux_base;
>> +
>> + smem->regions[0].size = size;
>> + smem->regions[0].virt_base = devm_ioremap_wc(smem->dev, phys_addr, size);
>> +
>> + if (!smem->regions[0].virt_base)
>> + return -ENOMEM;
>> +
>> + return 0;
>> +}
>> +
>> +static int qcom_smem_resolve_mem(struct qcom_smem *smem, const char *name,
>> + struct smem_region *region)
>> +{
>> + struct device *dev = smem->dev;
>> + struct device_node *np;
>> + struct resource r;
>> + int ret;
>> +
>> + np = of_parse_phandle(dev->of_node, name, 0);
>> + if (!np) {
>> + dev_err(dev, "No %s specified\n", name);
>> + return -EINVAL;
>> + }
>> +
>> + ret = of_address_to_resource(np, 0, &r);
>> + of_node_put(np);
>> + if (ret)
>> + return ret;
>> +
>> + region->aux_base = r.start;
>> + region->size = resource_size(&r);
>> +
>> + return 0;
>> +}
>> +
>> +static int qcom_smem_probe(struct platform_device *pdev)
>> +{
>> + struct smem_header *header;
>> + struct reserved_mem *rmem;
>> + struct qcom_smem *smem;
>> + unsigned long flags;
>> + int num_regions;
>> + int hwlock_id;
>> + u32 version;
>> + u32 size;
>> + int ret;
>> + int i;
>> +
>> + if (__smem)
>> + return 0;
>> +
>> + num_regions = 1;
>> + if (of_property_present(pdev->dev.of_node, "qcom,rpm-msg-ram"))
>> + num_regions++;
>> +
>> + smem = devm_kzalloc(&pdev->dev, struct_size(smem, regions, num_regions),
>> + GFP_KERNEL);
>> + if (!smem)
>> + return -ENOMEM;
>> +
>> + smem->dev = &pdev->dev;
>> + smem->num_regions = num_regions;
>> +
>> + rmem = of_reserved_mem_lookup(pdev->dev.of_node);
>> + if (rmem) {
>> + smem->regions[0].aux_base = rmem->base;
>> + smem->regions[0].size = rmem->size;
>> + } else {
>> + /*
>> + * Fall back to the memory-region reference, if we're not a
>> + * reserved-memory node.
>> + */
>> + ret = qcom_smem_resolve_mem(smem, "memory-region", &smem->regions[0]);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + if (num_regions > 1) {
>> + ret = qcom_smem_resolve_mem(smem, "qcom,rpm-msg-ram", &smem->regions[1]);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> +
>> + ret = qcom_smem_map_toc(smem, &smem->regions[0]);
>> + if (ret)
>> + return ret;
>> +
>> + for (i = 1; i < num_regions; i++) {
>> + smem->regions[i].virt_base = devm_ioremap_wc(&pdev->dev,
>> + smem->regions[i].aux_base,
>> + smem->regions[i].size);
>> + if (!smem->regions[i].virt_base) {
>> + dev_err(&pdev->dev, "failed to remap %pa\n", &smem->regions[i].aux_base);
>> + return -ENOMEM;
>> + }
>> + }
>> +
>> + header = smem->regions[0].virt_base;
>> + if (le32_to_cpu(header->initialized) != 1 ||
>> + le32_to_cpu(header->reserved)) {
>> + dev_err(&pdev->dev, "SMEM is not initialized by SBL\n");
>> + return -EINVAL;
>> + }
>> +
>> + hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0);
>> + if (hwlock_id < 0) {
>> + if (hwlock_id != -EPROBE_DEFER)
>> + dev_err(&pdev->dev, "failed to retrieve hwlock\n");
>> + return hwlock_id;
>> + }
>> +
>> + smem->hwlock = hwspin_lock_request_specific(hwlock_id);
>> + if (!smem->hwlock)
>> + return -ENXIO;
>> +
>> + ret = hwspin_lock_timeout_irqsave(smem->hwlock, HWSPINLOCK_TIMEOUT, &flags);
>> + if (ret)
>> + return ret;
>> + size = readl_relaxed(&header->available) + readl_relaxed(&header->free_offset);
>> + hwspin_unlock_irqrestore(smem->hwlock, &flags);
>> +
>> + version = qcom_smem_get_sbl_version(smem);
>> + /*
>> + * smem header mapping is required only in heap version scheme, so unmap
>> + * it here. It will be remapped in qcom_smem_map_global() when whole
>> + * partition is mapped again.
>> + */
>> + devm_iounmap(smem->dev, smem->regions[0].virt_base);
>> + switch (version >> 16) {
>> + case SMEM_GLOBAL_PART_VERSION:
>> + ret = qcom_smem_set_global_partition(smem);
>> + if (ret < 0)
>> + return ret;
>> + smem->item_count = qcom_smem_get_item_count(smem);
>> + break;
>> + case SMEM_GLOBAL_HEAP_VERSION:
>> + qcom_smem_map_global(smem, size);
>> + smem->item_count = SMEM_ITEM_COUNT;
>> + break;
>> + default:
>> + dev_err(&pdev->dev, "Unsupported SMEM version 0x%x\n", version);
>> + return -EINVAL;
>> + }
>> +
>> + BUILD_BUG_ON(SMEM_HOST_APPS >= SMEM_HOST_COUNT);
>> + ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS);
>> + if (ret < 0 && ret != -ENOENT)
>> + return ret;
>> +
>> + __smem = smem;
>> +
>> + smem->socinfo = platform_device_register_data(&pdev->dev, "qcom-socinfo",
>> + PLATFORM_DEVID_NONE, NULL,
>> + 0);
>> + if (IS_ERR(smem->socinfo))
>> + dev_dbg(&pdev->dev, "failed to register socinfo device\n");
>> +
>> + return 0;
>> +}
>> +
>> +static void qcom_smem_remove(struct platform_device *pdev)
>> +{
>> + platform_device_unregister(__smem->socinfo);
>> +
>> + hwspin_lock_free(__smem->hwlock);
>> + __smem = NULL;
>> +}
>> +
>> +static const struct of_device_id qcom_smem_of_match[] = {
>> + { .compatible = "qcom,smem" },
>> + {}
>> +};
>> +MODULE_DEVICE_TABLE(of, qcom_smem_of_match);
>> +
>> +static struct platform_driver qcom_smem_driver = {
>> + .probe = qcom_smem_probe,
>> + .remove_new = qcom_smem_remove,
>> + .driver = {
>> + .name = "qcom-smem",
>> + .of_match_table = qcom_smem_of_match,
>> + .suppress_bind_attrs = true,
>> + },
>> +};
>> +
>> +static int __init qcom_smem_init(void)
>> +{
>> + return platform_driver_register(&qcom_smem_driver);
>> +}
>> +arch_initcall(qcom_smem_init);
>> +
>> +static void __exit qcom_smem_exit(void)
>> +{
>> + platform_driver_unregister(&qcom_smem_driver);
>> +}
>> +module_exit(qcom_smem_exit)
>> +
>> +MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
>> +MODULE_DESCRIPTION("Qualcomm Shared Memory Manager");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/include/soc/qcom/smem.h b/include/soc/qcom/smem.h
>> new file mode 100644
>> index 000000000000..f946e3beca21
>> --- /dev/null
>> +++ b/include/soc/qcom/smem.h
>> @@ -0,0 +1,20 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +#ifndef __QCOM_SMEM_H__
>> +#define __QCOM_SMEM_H__
>> +
>> +#define QCOM_SMEM_HOST_ANY -1
>> +
>> +bool qcom_smem_is_available(void);
>> +int qcom_smem_alloc(unsigned host, unsigned item, size_t size);
>> +void *qcom_smem_get(unsigned host, unsigned item, size_t *size);
>> +
>> +int qcom_smem_get_free_space(unsigned host);
>> +
>> +phys_addr_t qcom_smem_virt_to_phys(void *p);
>> +
>> +int qcom_smem_get_soc_id(u32 *id);
>> +int qcom_smem_get_feature_code(u32 *code);
>> +
>> +int qcom_smem_bust_hwspin_lock_by_host(unsigned int host);
>> +
>> +#endif
>> diff --git a/include/soc/qcom/socinfo.h b/include/soc/qcom/socinfo.h
>> new file mode 100644
>> index 000000000000..608950443eee
>> --- /dev/null
>> +++ b/include/soc/qcom/socinfo.h
>> @@ -0,0 +1,111 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +
>> +#ifndef __QCOM_SOCINFO_H__
>> +#define __QCOM_SOCINFO_H__
>> +
>> +#include <linux/types.h>
>> +
>> +/*
>> + * SMEM item id, used to acquire handles to respective
>> + * SMEM region.
>> + */
>> +#define SMEM_HW_SW_BUILD_ID 137
>> +
>> +#define SMEM_SOCINFO_BUILD_ID_LENGTH 32
>> +#define SMEM_SOCINFO_CHIP_ID_LENGTH 32
>> +
>> +/*
>> + * SoC version type with major number in the upper 16 bits and minor
>> + * number in the lower 16 bits.
>> + */
>> +#define SOCINFO_MAJOR(ver) (((ver) >> 16) & 0xffff)
>> +#define SOCINFO_MINOR(ver) ((ver) & 0xffff)
>> +#define SOCINFO_VERSION(maj, min) ((((maj) & 0xffff) << 16)|((min) & 0xffff))
>> +
>> +/* Socinfo SMEM item structure */
>> +struct socinfo {
>> + __le32 fmt;
>> + __le32 id;
>> + __le32 ver;
>> + char build_id[SMEM_SOCINFO_BUILD_ID_LENGTH];
>> + /* Version 2 */
>> + __le32 raw_id;
>> + __le32 raw_ver;
>> + /* Version 3 */
>> + __le32 hw_plat;
>> + /* Version 4 */
>> + __le32 plat_ver;
>> + /* Version 5 */
>> + __le32 accessory_chip;
>> + /* Version 6 */
>> + __le32 hw_plat_subtype;
>> + /* Version 7 */
>> + __le32 pmic_model;
>> + __le32 pmic_die_rev;
>> + /* Version 8 */
>> + __le32 pmic_model_1;
>> + __le32 pmic_die_rev_1;
>> + __le32 pmic_model_2;
>> + __le32 pmic_die_rev_2;
>> + /* Version 9 */
>> + __le32 foundry_id;
>> + /* Version 10 */
>> + __le32 serial_num;
>> + /* Version 11 */
>> + __le32 num_pmics;
>> + __le32 pmic_array_offset;
>> + /* Version 12 */
>> + __le32 chip_family;
>> + __le32 raw_device_family;
>> + __le32 raw_device_num;
>> + /* Version 13 */
>> + __le32 nproduct_id;
>> + char chip_id[SMEM_SOCINFO_CHIP_ID_LENGTH];
>> + /* Version 14 */
>> + __le32 num_clusters;
>> + __le32 ncluster_array_offset;
>> + __le32 num_subset_parts;
>> + __le32 nsubset_parts_array_offset;
>> + /* Version 15 */
>> + __le32 nmodem_supported;
>> + /* Version 16 */
>> + __le32 feature_code;
>> + __le32 pcode;
>> + __le32 npartnamemap_offset;
>> + __le32 nnum_partname_mapping;
>> + /* Version 17 */
>> + __le32 oem_variant;
>> + /* Version 18 */
>> + __le32 num_kvps;
>> + __le32 kvps_offset;
>> + /* Version 19 */
>> + __le32 num_func_clusters;
>> + __le32 boot_cluster;
>> + __le32 boot_core;
>> +};
>> +
>> +/* Internal feature codes */
>> +enum qcom_socinfo_feature_code {
>> + /* External feature codes */
>> + SOCINFO_FC_UNKNOWN = 0x0,
>> + SOCINFO_FC_AA,
>> + SOCINFO_FC_AB,
>> + SOCINFO_FC_AC,
>> + SOCINFO_FC_AD,
>> + SOCINFO_FC_AE,
>> + SOCINFO_FC_AF,
>> + SOCINFO_FC_AG,
>> + SOCINFO_FC_AH,
>> +};
>> +
>> +/* Internal feature codes */
>> +/* Valid values: 0 <= n <= 0xf */
>> +#define SOCINFO_FC_Yn(n) (0xf1 + (n))
>> +#define SOCINFO_FC_INT_MAX SOCINFO_FC_Yn(0xf)
>> +
>> +/* Product codes */
>> +#define SOCINFO_PC_UNKNOWN 0
>> +#define SOCINFO_PCn(n) ((n) + 1)
>> +#define SOCINFO_PC_RESERVE (BIT(31) - 1)
>> +
>> +#endif
>>
>> --
>> 2.53.0
>>
--
// Casey (she/her)
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 05/15] mach-snapdragon: fix reserved memory carveout
2026-05-05 12:39 ` Casey Connolly
@ 2026-05-07 7:45 ` Sumit Garg
2026-05-07 20:29 ` Casey Connolly
0 siblings, 1 reply; 51+ messages in thread
From: Sumit Garg @ 2026-05-07 7:45 UTC (permalink / raw)
To: Casey Connolly
Cc: u-boot, u-boot-qcom, Tom Rini, Simon Glass, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
On Tue, May 05, 2026 at 02:39:35PM +0200, Casey Connolly wrote:
>
>
> On 05/05/2026 14:25, Sumit Garg wrote:
> > On Mon, May 04, 2026 at 08:57:33PM +0200, Casey Connolly wrote:
> >> The memory carveout logic was fairly limited and had a few issues,
> >> rework it and teach it not to unmap regions that have a compatible
> >> property (since they may be used in U-Boot) or that don't have the
> >> no-map property.
> >>
> >> The carveout process adds ~100ms to the boot time depending on the
> >> platform.
> >>
> >> This prepares us for using SMEM as a source of truth and improving
> >> support for U-boot as a first stage bootloader since SMEMs memory map
> >> doesn't already carve out some regions like ABL does.
> >>
> >> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> >> ---
> >> arch/arm/mach-snapdragon/board.c | 86 +++++++++++++++++++++++++---------------
> >> 1 file changed, 53 insertions(+), 33 deletions(-)
> >>
> >> diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c
> >> index 829a0109ac78..e12d3d00caa4 100644
> >> --- a/arch/arm/mach-snapdragon/board.c
> >> +++ b/arch/arm/mach-snapdragon/board.c
> >> @@ -622,27 +622,36 @@ u64 get_page_table_size(void)
> >> {
> >> return SZ_1M;
> >> }
> >>
> >> +struct mem_resource_attrs {
> >> + fdt_addr_t start;
> >> + fdt_addr_t size;
> >> + u64 attrs;
> >> +};
> >
> > Let's move the struct declaration towards the top.
>
> it's only used by this one function, it felt easier to keep things
> readable this way. Probably we'll move this stuff to its own file at
> some point.
>
> >
> >> +
> >> static int fdt_cmp_res(const void *v1, const void *v2)
> >
> > This API should now be renamed as mem_cmp_resources(), no?
>
> yeah
>
> >
> >> {
> >> - const struct fdt_resource *res1 = v1, *res2 = v2;
> >> + const struct mem_resource_attrs *res1 = v1, *res2 = v2;
> >>
> >> return res1->start - res2->start;
> >> }
> >>
> >> -#define N_RESERVED_REGIONS 32
> >> +#define N_RESERVED_REGIONS 64
> >>
> >> -/* Mark all no-map regions as PTE_TYPE_FAULT to prevent speculative access.
> >> +/* Map and unmap reserved memory regions as appropriate.
> >> + * Mark all no-map regions as PTE_TYPE_FAULT to prevent speculative access.
> >> * On some platforms this is enough to trigger a security violation and trap
> >> * to EL3.
> >> + * Regions that may be accessed by drivers get mapped explicitly.
> >> */
> >> -static void carve_out_reserved_memory(void)
> >> +static void configure_reserved_memory(void)
> >> {
> >> - static struct fdt_resource res[N_RESERVED_REGIONS] = { 0 };
> >> + static struct mem_resource_attrs res[N_RESERVED_REGIONS] = { 0 };
> >> int parent, rmem, count, i = 0;
> >> phys_addr_t start;
> >> size_t size;
> >> + u64 attrs;
> >>
> >> /* Some reserved nodes must be carved out, as the cache-prefetcher may otherwise
> >> * attempt to access them, causing a security exception.
> >> */
> >> @@ -651,14 +660,19 @@ static void carve_out_reserved_memory(void)
> >> log_err("No reserved memory regions found\n");
> >> return;
> >> }
> >>
> >> - /* Collect the reserved memory regions */
> >> + /* Collect the reserved memory regions and appropriate attrs */
> >> fdt_for_each_subnode(rmem, gd->fdt_blob, parent) {
> >> const fdt32_t *ptr;
> >> - int len;
> >> + attrs = PTE_TYPE_FAULT;
> >> + /* If the no-map property isn't set then the region is valid */
> >> if (!fdt_getprop(gd->fdt_blob, rmem, "no-map", NULL))
> >> - continue;
> >> + attrs = PTE_TYPE_VALID | PTE_BLOCK_MEMTYPE(MT_NORMAL);
> >> + /* If the compatible property is set then this region may be accessed by drivers and should
> >> + * be marked valid too. */
> >> + if (fdt_getprop(gd->fdt_blob, rmem, "compatible", NULL))
> >> + attrs = PTE_TYPE_VALID | PTE_BLOCK_MEMTYPE(MT_NORMAL);
> >>
> >> if (i == N_RESERVED_REGIONS) {
> >> log_err("Too many reserved regions!\n");
> >> break;
> >> @@ -667,50 +681,55 @@ static void carve_out_reserved_memory(void)
> >> /* Read the address and size out from the reg property. Doing this "properly" with
> >> * fdt_get_resource() takes ~70ms on SDM845, but open-coding the happy path here
> >> * takes <1ms... Oh the woes of no dcache.
> >> */
> >> - ptr = fdt_getprop(gd->fdt_blob, rmem, "reg", &len);
> >> + ptr = fdt_getprop(gd->fdt_blob, rmem, "reg", NULL);
> >> if (ptr) {
> >> /* Qualcomm devices use #address/size-cells = <2> but all reserved regions are within
> >> * the 32-bit address space. So we can cheat here for speed.
> >> */
> >> res[i].start = fdt32_to_cpu(ptr[1]);
> >> - res[i].end = res[i].start + fdt32_to_cpu(ptr[3]);
> >> + res[i].size = fdt32_to_cpu(ptr[3]);
> >> + res[i].attrs = attrs;
> >> i++;
> >> }
> >> }
> >>
> >> /* Sort the reserved memory regions by address */
> >> count = i;
> >> - qsort(res, count, sizeof(struct fdt_resource), fdt_cmp_res);
> >> + qsort(res, count, sizeof(res[0]), fdt_cmp_res);
> >> + debug("Mapping %d regions!\n", count);
> >>
> >> /* Now set the right attributes for them. Often a lot of the regions are tightly packed together
> >> - * so we can optimise the number of calls to mmu_change_region_attr() by combining adjacent
> >> + * so we can optimise the number of calls to mmu_change_region_attr_nobreak() by combining adjacent
> >> * regions.
> >> */
> >> - start = ALIGN_DOWN(res[0].start, SZ_2M);
> >> - size = ALIGN(res[0].end - start, SZ_2M);
> >> + start = res[0].start;
> >> + size = res[0].size;
> >> + attrs = res[0].attrs;
> >> + /* For each region after the first one, either increase the `size` to eventually be mapped or
> >> + * map the region we have and start a new one, this allows us to reduce the number of calls to
> >> + * mmu_map_region(). The loop is therefore "lagging" behind by one iteration. */
> >> for (i = 1; i <= count; i++) {
> >> - /* We ideally want to 2M align everything for more efficient pagetables, but we must avoid
> >> - * overwriting reserved memory regions which shouldn't be mapped as FAULT (like those with
> >> - * compatible properties).
> >> - * If within 2M of the previous region, bump the size to include this region. Otherwise
> >> - * start a new region.
> >> - */
> >> - if (i == count || start + size < res[i].start - SZ_2M) {
> >> - debug(" 0x%016llx - 0x%016llx: reserved\n",
> >> - start, start + size);
> >> - mmu_change_region_attr(start, size, PTE_TYPE_FAULT);
> >> - /* If this is the final region then quit here before we index
> >> - * out of bounds...
> >> - */
> >> + /* If i == count we are done, just map the last region. If the last region is
> >> + * too far away or the attrs don't match then map the meta-region we have and
> >> + * start a new one. */
> >> + if (i == count || start + size < res[i].start - SZ_8K || attrs != res[i].attrs) {
> >
> > I suppose this SZ_8K instead of SZ_2M is intentional here? It works now
> > due to page table optimization I guess?
>
> I realised there was a possibility for incorrectly mapping stuff with
> 2M. Probably not with the logic as it is but e.g. if you don't
> explicitly map reserved regions that need to be mapped then they can get
> unmapped here.
I think we rather need to unmap all the no-map regions first via
aligning to 2MB block mappings and then map regions required to be
mapped with the needed granularity. This way we can optimize the page
tables usage.
-Sumit
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 01/15] Revert "dm: SMEM (Shared memory) uclass"
2026-05-04 18:57 ` [PATCH v2 01/15] Revert "dm: SMEM (Shared memory) uclass" Casey Connolly
2026-05-05 10:15 ` Sumit Garg
@ 2026-05-07 15:31 ` Simon Glass
2026-05-07 20:05 ` Casey Connolly
2026-05-18 14:34 ` Neil Armstrong
2 siblings, 1 reply; 51+ messages in thread
From: Simon Glass @ 2026-05-07 15:31 UTC (permalink / raw)
To: Casey Connolly
Cc: u-boot, Sumit Garg, u-boot-qcom, Tom Rini, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
Hi Casey,
On Mon, 4 May 2026 at 12:57, Casey Connolly <casey.connolly@linaro.org> wrote:
>
> SMEM is a highly Qualcomm specific interface, while having a dedicated
> UCLASS for it offers a nice abstraction, for things like memory layout
> parsing we need to use it before the driver model is available.
Do you mean before or after relocation?
>
> Therefore, it doesn't make sense to fit SMEM into the driver model.
> Instead let's adopt a model closer to Linux, and parse SMEM really early
> during boot (as soon as we have the FDT).
>
> This reverts commit 7b384eccc785b596f68448b155cbda26df57fb23.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> arch/arm/Kconfig | 1 -
> drivers/Kconfig | 2 --
> drivers/Makefile | 1 -
> drivers/smem/Kconfig | 25 -------------
> drivers/smem/Makefile | 7 ----
> drivers/smem/smem-uclass.c | 46 ------------------------
> include/dm/uclass-id.h | 1 -
> include/smem.h | 90 ----------------------------------------------
> 8 files changed, 173 deletions(-)
[..]
Regards,
Simon
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 01/15] Revert "dm: SMEM (Shared memory) uclass"
2026-05-07 15:31 ` Simon Glass
@ 2026-05-07 20:05 ` Casey Connolly
0 siblings, 0 replies; 51+ messages in thread
From: Casey Connolly @ 2026-05-07 20:05 UTC (permalink / raw)
To: Simon Glass
Cc: u-boot, Sumit Garg, u-boot-qcom, Tom Rini, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
On 07/05/2026 17:31, Simon Glass wrote:
> Hi Casey,
>
> On Mon, 4 May 2026 at 12:57, Casey Connolly <casey.connolly@linaro.org> wrote:
>>
>> SMEM is a highly Qualcomm specific interface, while having a dedicated
>> UCLASS for it offers a nice abstraction, for things like memory layout
>> parsing we need to use it before the driver model is available.
>
> Do you mean before or after relocation?
Before, we actually use it and read the memory map in
board_fdt_blob_setup() since we support the usecase where you chainload
U-Boot from ABL but with its own DTB baked in. This allows you to leave
the original Android DTB and read the memory map from it while using a
proper DTB for U-Boot without having to hardcode the memory map.
Basically it exists because some Android phones have awful bootloaders
that will crash or break stuff if you don't have very specific arbitrary
things in your devicetree.
>
>>
>> Therefore, it doesn't make sense to fit SMEM into the driver model.
>> Instead let's adopt a model closer to Linux, and parse SMEM really early
>> during boot (as soon as we have the FDT).
>>
>> This reverts commit 7b384eccc785b596f68448b155cbda26df57fb23.
>>
>> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
>> ---
>> arch/arm/Kconfig | 1 -
>> drivers/Kconfig | 2 --
>> drivers/Makefile | 1 -
>> drivers/smem/Kconfig | 25 -------------
>> drivers/smem/Makefile | 7 ----
>> drivers/smem/smem-uclass.c | 46 ------------------------
>> include/dm/uclass-id.h | 1 -
>> include/smem.h | 90 ----------------------------------------------
>> 8 files changed, 173 deletions(-)
>
> [..]
>
> Regards,
> Simon
--
// Casey (she/her)
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 05/15] mach-snapdragon: fix reserved memory carveout
2026-05-07 7:45 ` Sumit Garg
@ 2026-05-07 20:29 ` Casey Connolly
0 siblings, 0 replies; 51+ messages in thread
From: Casey Connolly @ 2026-05-07 20:29 UTC (permalink / raw)
To: Sumit Garg
Cc: u-boot, u-boot-qcom, Tom Rini, Simon Glass, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
On 07/05/2026 09:45, Sumit Garg wrote:
> On Tue, May 05, 2026 at 02:39:35PM +0200, Casey Connolly wrote:
>>
>>
>> On 05/05/2026 14:25, Sumit Garg wrote:
>>> On Mon, May 04, 2026 at 08:57:33PM +0200, Casey Connolly wrote:
>>>> The memory carveout logic was fairly limited and had a few issues,
>>>> rework it and teach it not to unmap regions that have a compatible
>>>> property (since they may be used in U-Boot) or that don't have the
>>>> no-map property.
>>>>
>>>> The carveout process adds ~100ms to the boot time depending on the
>>>> platform.
>>>>
>>>> This prepares us for using SMEM as a source of truth and improving
>>>> support for U-boot as a first stage bootloader since SMEMs memory map
>>>> doesn't already carve out some regions like ABL does.
>>>>
>>>> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
>>>> ---
>>>> arch/arm/mach-snapdragon/board.c | 86 +++++++++++++++++++++++++---------------
>>>> 1 file changed, 53 insertions(+), 33 deletions(-)
>>>>
>>>> diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c
>>>> index 829a0109ac78..e12d3d00caa4 100644
>>>> --- a/arch/arm/mach-snapdragon/board.c
>>>> +++ b/arch/arm/mach-snapdragon/board.c
>>>> @@ -622,27 +622,36 @@ u64 get_page_table_size(void)
>>>> {
>>>> return SZ_1M;
>>>> }
>>>>
>>>> +struct mem_resource_attrs {
>>>> + fdt_addr_t start;
>>>> + fdt_addr_t size;
>>>> + u64 attrs;
>>>> +};
>>>
>>> Let's move the struct declaration towards the top.
>>
>> it's only used by this one function, it felt easier to keep things
>> readable this way. Probably we'll move this stuff to its own file at
>> some point.
>>
>>>
>>>> +
>>>> static int fdt_cmp_res(const void *v1, const void *v2)
>>>
>>> This API should now be renamed as mem_cmp_resources(), no?
>>
>> yeah
>>
>>>
>>>> {
>>>> - const struct fdt_resource *res1 = v1, *res2 = v2;
>>>> + const struct mem_resource_attrs *res1 = v1, *res2 = v2;
>>>>
>>>> return res1->start - res2->start;
>>>> }
>>>>
>>>> -#define N_RESERVED_REGIONS 32
>>>> +#define N_RESERVED_REGIONS 64
>>>>
>>>> -/* Mark all no-map regions as PTE_TYPE_FAULT to prevent speculative access.
>>>> +/* Map and unmap reserved memory regions as appropriate.
>>>> + * Mark all no-map regions as PTE_TYPE_FAULT to prevent speculative access.
>>>> * On some platforms this is enough to trigger a security violation and trap
>>>> * to EL3.
>>>> + * Regions that may be accessed by drivers get mapped explicitly.
>>>> */
>>>> -static void carve_out_reserved_memory(void)
>>>> +static void configure_reserved_memory(void)
>>>> {
>>>> - static struct fdt_resource res[N_RESERVED_REGIONS] = { 0 };
>>>> + static struct mem_resource_attrs res[N_RESERVED_REGIONS] = { 0 };
>>>> int parent, rmem, count, i = 0;
>>>> phys_addr_t start;
>>>> size_t size;
>>>> + u64 attrs;
>>>>
>>>> /* Some reserved nodes must be carved out, as the cache-prefetcher may otherwise
>>>> * attempt to access them, causing a security exception.
>>>> */
>>>> @@ -651,14 +660,19 @@ static void carve_out_reserved_memory(void)
>>>> log_err("No reserved memory regions found\n");
>>>> return;
>>>> }
>>>>
>>>> - /* Collect the reserved memory regions */
>>>> + /* Collect the reserved memory regions and appropriate attrs */
>>>> fdt_for_each_subnode(rmem, gd->fdt_blob, parent) {
>>>> const fdt32_t *ptr;
>>>> - int len;
>>>> + attrs = PTE_TYPE_FAULT;
>>>> + /* If the no-map property isn't set then the region is valid */
>>>> if (!fdt_getprop(gd->fdt_blob, rmem, "no-map", NULL))
>>>> - continue;
>>>> + attrs = PTE_TYPE_VALID | PTE_BLOCK_MEMTYPE(MT_NORMAL);
>>>> + /* If the compatible property is set then this region may be accessed by drivers and should
>>>> + * be marked valid too. */
>>>> + if (fdt_getprop(gd->fdt_blob, rmem, "compatible", NULL))
>>>> + attrs = PTE_TYPE_VALID | PTE_BLOCK_MEMTYPE(MT_NORMAL);
>>>>
>>>> if (i == N_RESERVED_REGIONS) {
>>>> log_err("Too many reserved regions!\n");
>>>> break;
>>>> @@ -667,50 +681,55 @@ static void carve_out_reserved_memory(void)
>>>> /* Read the address and size out from the reg property. Doing this "properly" with
>>>> * fdt_get_resource() takes ~70ms on SDM845, but open-coding the happy path here
>>>> * takes <1ms... Oh the woes of no dcache.
>>>> */
>>>> - ptr = fdt_getprop(gd->fdt_blob, rmem, "reg", &len);
>>>> + ptr = fdt_getprop(gd->fdt_blob, rmem, "reg", NULL);
>>>> if (ptr) {
>>>> /* Qualcomm devices use #address/size-cells = <2> but all reserved regions are within
>>>> * the 32-bit address space. So we can cheat here for speed.
>>>> */
>>>> res[i].start = fdt32_to_cpu(ptr[1]);
>>>> - res[i].end = res[i].start + fdt32_to_cpu(ptr[3]);
>>>> + res[i].size = fdt32_to_cpu(ptr[3]);
>>>> + res[i].attrs = attrs;
>>>> i++;
>>>> }
>>>> }
>>>>
>>>> /* Sort the reserved memory regions by address */
>>>> count = i;
>>>> - qsort(res, count, sizeof(struct fdt_resource), fdt_cmp_res);
>>>> + qsort(res, count, sizeof(res[0]), fdt_cmp_res);
>>>> + debug("Mapping %d regions!\n", count);
>>>>
>>>> /* Now set the right attributes for them. Often a lot of the regions are tightly packed together
>>>> - * so we can optimise the number of calls to mmu_change_region_attr() by combining adjacent
>>>> + * so we can optimise the number of calls to mmu_change_region_attr_nobreak() by combining adjacent
>>>> * regions.
>>>> */
>>>> - start = ALIGN_DOWN(res[0].start, SZ_2M);
>>>> - size = ALIGN(res[0].end - start, SZ_2M);
>>>> + start = res[0].start;
>>>> + size = res[0].size;
>>>> + attrs = res[0].attrs;
>>>> + /* For each region after the first one, either increase the `size` to eventually be mapped or
>>>> + * map the region we have and start a new one, this allows us to reduce the number of calls to
>>>> + * mmu_map_region(). The loop is therefore "lagging" behind by one iteration. */
>>>> for (i = 1; i <= count; i++) {
>>>> - /* We ideally want to 2M align everything for more efficient pagetables, but we must avoid
>>>> - * overwriting reserved memory regions which shouldn't be mapped as FAULT (like those with
>>>> - * compatible properties).
>>>> - * If within 2M of the previous region, bump the size to include this region. Otherwise
>>>> - * start a new region.
>>>> - */
>>>> - if (i == count || start + size < res[i].start - SZ_2M) {
>>>> - debug(" 0x%016llx - 0x%016llx: reserved\n",
>>>> - start, start + size);
>>>> - mmu_change_region_attr(start, size, PTE_TYPE_FAULT);
>>>> - /* If this is the final region then quit here before we index
>>>> - * out of bounds...
>>>> - */
>>>> + /* If i == count we are done, just map the last region. If the last region is
>>>> + * too far away or the attrs don't match then map the meta-region we have and
>>>> + * start a new one. */
>>>> + if (i == count || start + size < res[i].start - SZ_8K || attrs != res[i].attrs) {
>>>
>>> I suppose this SZ_8K instead of SZ_2M is intentional here? It works now
>>> due to page table optimization I guess?
>>
>> I realised there was a possibility for incorrectly mapping stuff with
>> 2M. Probably not with the logic as it is but e.g. if you don't
>> explicitly map reserved regions that need to be mapped then they can get
>> unmapped here.
>
> I think we rather need to unmap all the no-map regions first via
> aligning to 2MB block mappings and then map regions required to be
> mapped with the needed granularity. This way we can optimize the page
> tables usage.
Just tried this now since both cmd-db and smem explicitly map themselves
and that brings things down a whole lot. from 200ms to 100ms on sm8650,
although that's with debug logs enabled heh, with them disabled it's
30ms and on qcs6490 only 5ms so much more acceptable.
It's realllly not ideal this way but I can't immediately think of any
significant issues...
>
> -Sumit
--
// Casey (she/her)
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 08/15] soc: qcom: smem: adjust for U-Boot
2026-05-04 18:57 ` [PATCH v2 08/15] soc: qcom: smem: adjust " Casey Connolly
@ 2026-05-08 10:43 ` Aswin Murugan
2026-05-11 12:22 ` Casey Connolly
2026-05-18 11:03 ` Sumit Garg
1 sibling, 1 reply; 51+ messages in thread
From: Aswin Murugan @ 2026-05-08 10:43 UTC (permalink / raw)
To: Casey Connolly, u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Peng Fan, Marek Vasut, Alice Guo,
Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Rasmus Villemoes,
Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
Hi Casey,
On 5/5/2026 12:27 AM, Casey Connolly wrote:
> Port over the smem code to U-Boot and ensure the smem region gets mapped
> after dcache is enabled.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> drivers/soc/qcom/smem.c | 399 +++++++++++++-----------------------------------
> include/soc/qcom/smem.h | 9 +-
> 2 files changed, 104 insertions(+), 304 deletions(-)
>
> diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
> index 7143856e85c3..96288d7bcdef 100644
> --- a/drivers/soc/qcom/smem.c
> +++ b/drivers/soc/qcom/smem.c
> @@ -3,17 +3,19 @@
> * Copyright (c) 2015, Sony Mobile Communications AB.
> * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
> */
>
> +#define pr_fmt(fmt) "smem: " fmt
> +
> +#include <asm/system.h>
> #include <dm/device.h>
> #include <dm/device_compat.h>
> #include <dm/ofnode.h>
> #include <linux/bug.h>
> #include <linux/io.h>
> #include <linux/ioport.h>
> #include <linux/sizes.h>
> #include <soc/qcom/smem.h>
> -#include <soc/qcom/socinfo.h>
>
> /*
> * The Qualcomm shared memory system is a allocate only heap structure that
> * consists of one of more memory areas that can be accessed by the processors
> @@ -246,8 +248,10 @@ struct smem_info {
> };
>
> static const u8 SMEM_INFO_MAGIC[] = { 0x53, 0x49, 0x49, 0x49 }; /* SIII */
>
> +#define SMEM_MAX_REGIONS 2
> +
> /**
> * struct smem_region - representation of a chunk of memory used for smem
> * @aux_base: identifier of aux_mem base
> * @virt_base: virtual base address of memory with this aux_mem identifier
> @@ -260,33 +264,31 @@ struct smem_region {
> };
>
> /**
> * struct qcom_smem - device data for the smem device
> - * @dev: device pointer
> - * @hwlock: reference to a hwspinlock
> * @ptable: virtual base of partition table
> * @global_partition: describes for global partition when in use
> * @partitions: list of partitions of current processor/host
> * @item_count: max accepted item number
> - * @socinfo: platform device pointer
> * @num_regions: number of @regions
> * @regions: list of the memory regions defining the shared memory
> */
> struct qcom_smem {
> - struct device *dev;
> -
> - struct hwspinlock *hwlock;
> -
> u32 item_count;
> - struct platform_device *socinfo;
> struct smem_ptable *ptable;
> struct smem_partition global_partition;
> struct smem_partition partitions[SMEM_HOST_COUNT];
>
> unsigned num_regions;
> - struct smem_region regions[] __counted_by(num_regions);
> + struct smem_region regions[SMEM_MAX_REGIONS];
> };
>
> +/* Pre-allocated storage for SMEM */
> +static struct qcom_smem __smem __section(".data") = { 0 };
> +
> +/* Pointer to the one and only smem handle */
> +static struct qcom_smem *smem __section(".data") = NULL;
> +
> static void *
> phdr_to_last_uncached_entry(struct smem_partition_header *phdr)
> {
> void *p = phdr;
> @@ -350,48 +352,16 @@ static void *cached_entry_to_item(struct smem_private_entry *e)
>
> return p - le32_to_cpu(e->size);
> }
>
> -/* Pointer to the one and only smem handle */
> -static struct qcom_smem *__smem;
> -
> -/* Timeout (ms) for the trylock of remote spinlocks */
> -#define HWSPINLOCK_TIMEOUT 1000
> -
> -/* The qcom hwspinlock id is always plus one from the smem host id */
> -#define SMEM_HOST_ID_TO_HWSPINLOCK_ID(__x) ((__x) + 1)
> -
> -/**
> - * qcom_smem_bust_hwspin_lock_by_host() - bust the smem hwspinlock for a host
> - * @host: remote processor id
> - *
> - * Busts the hwspin_lock for the given smem host id. This helper is intended
> - * for remoteproc drivers that manage remoteprocs with an equivalent smem
> - * driver instance in the remote firmware. Drivers can force a release of the
> - * smem hwspin_lock if the rproc unexpectedly goes into a bad state.
> - *
> - * Context: Process context.
> - *
> - * Returns: 0 on success, otherwise negative errno.
> - */
> -int qcom_smem_bust_hwspin_lock_by_host(unsigned int host)
> -{
> - /* This function is for remote procs, so ignore SMEM_HOST_APPS */
> - if (host == SMEM_HOST_APPS || host >= SMEM_HOST_COUNT)
> - return -EINVAL;
> -
> - return hwspin_lock_bust(__smem->hwlock, SMEM_HOST_ID_TO_HWSPINLOCK_ID(host));
> -}
> -EXPORT_SYMBOL_GPL(qcom_smem_bust_hwspin_lock_by_host);
> -
> /**
> * qcom_smem_is_available() - Check if SMEM is available
> *
> * Return: true if SMEM is available, false otherwise.
> */
> bool qcom_smem_is_available(void)
> {
> - return !!__smem;
> + return !!smem;
> }
> EXPORT_SYMBOL_GPL(qcom_smem_is_available);
>
> static int qcom_smem_alloc_private(struct qcom_smem *smem,
> @@ -429,9 +399,9 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem,
>
> /* Check that we don't grow into the cached region */
> alloc_size = sizeof(*hdr) + ALIGN(size, 8);
> if ((void *)hdr + alloc_size > cached) {
> - dev_err(smem->dev, "Out of memory\n");
> + log_err("Out of memory\n");
> return -ENOSPC;
> }
>
> hdr->canary = SMEM_PRIVATE_CANARY;
> @@ -449,9 +419,9 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem,
> le32_add_cpu(&phdr->offset_free_uncached, alloc_size);
>
> return 0;
> bad_canary:
> - dev_err(smem->dev, "Found invalid canary in hosts %hu:%hu partition\n",
> + log_err("Found invalid canary in hosts %hu:%hu partition\n",
> le16_to_cpu(phdr->host0), le16_to_cpu(phdr->host1));
>
> return -EINVAL;
> }
> @@ -500,41 +470,31 @@ static int qcom_smem_alloc_global(struct qcom_smem *smem,
> */
> int qcom_smem_alloc(unsigned host, unsigned item, size_t size)
> {
> struct smem_partition *part;
> - unsigned long flags;
> int ret;
>
> - if (!__smem)
> + if (!smem)
> return -EPROBE_DEFER;
>
> if (item < SMEM_ITEM_LAST_FIXED) {
> - dev_err(__smem->dev,
> - "Rejecting allocation of static entry %d\n", item);
> + log_err("Rejecting allocation of static entry %d\n", item);
> return -EINVAL;
> }
>
> - if (WARN_ON(item >= __smem->item_count))
> + if (WARN_ON(item >= smem->item_count))
> return -EINVAL;
>
> - ret = hwspin_lock_timeout_irqsave(__smem->hwlock,
> - HWSPINLOCK_TIMEOUT,
> - &flags);
> - if (ret)
> - return ret;
> -
> - if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
> - part = &__smem->partitions[host];
> - ret = qcom_smem_alloc_private(__smem, part, item, size);
> - } else if (__smem->global_partition.virt_base) {
> - part = &__smem->global_partition;
> - ret = qcom_smem_alloc_private(__smem, part, item, size);
> + if (host < SMEM_HOST_COUNT && smem->partitions[host].virt_base) {
> + part = &smem->partitions[host];
> + ret = qcom_smem_alloc_private(smem, part, item, size);
> + } else if (smem->global_partition.virt_base) {
> + part = &smem->global_partition;
> + ret = qcom_smem_alloc_private(smem, part, item, size);
> } else {
> - ret = qcom_smem_alloc_global(__smem, item, size);
> + ret = qcom_smem_alloc_global(smem, item, size);
> }
>
> - hwspin_unlock_irqrestore(__smem->hwlock, &flags);
> -
> return ret;
> }
> EXPORT_SYMBOL_GPL(qcom_smem_alloc);
>
> @@ -660,9 +620,9 @@ static void *qcom_smem_get_private(struct qcom_smem *smem,
>
> return ERR_PTR(-ENOENT);
>
> invalid_canary:
> - dev_err(smem->dev, "Found invalid canary in hosts %hu:%hu partition\n",
> + log_err("Found invalid canary in hosts %hu:%hu partition\n",
> le16_to_cpu(phdr->host0), le16_to_cpu(phdr->host1));
>
> return ERR_PTR(-EINVAL);
> }
> @@ -680,22 +640,22 @@ void *qcom_smem_get(unsigned host, unsigned item, size_t *size)
> {
> struct smem_partition *part;
> void *ptr = ERR_PTR(-EPROBE_DEFER);
>
> - if (!__smem)
> + if (!smem)
> return ptr;
>
> - if (WARN_ON(item >= __smem->item_count))
> + if (WARN_ON(item >= smem->item_count))
> return ERR_PTR(-EINVAL);
>
> - if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
> - part = &__smem->partitions[host];
> - ptr = qcom_smem_get_private(__smem, part, item, size);
> - } else if (__smem->global_partition.virt_base) {
> - part = &__smem->global_partition;
> - ptr = qcom_smem_get_private(__smem, part, item, size);
> + if (host < SMEM_HOST_COUNT && smem->partitions[host].virt_base) {
> + part = &smem->partitions[host];
> + ptr = qcom_smem_get_private(smem, part, item, size);
> + } else if (smem->global_partition.virt_base) {
> + part = &smem->global_partition;
> + ptr = qcom_smem_get_private(smem, part, item, size);
> } else {
> - ptr = qcom_smem_get_global(__smem, item, size);
> + ptr = qcom_smem_get_global(smem, item, size);
> }
>
> return ptr;
> }
> @@ -714,32 +674,32 @@ int qcom_smem_get_free_space(unsigned host)
> struct smem_partition_header *phdr;
> struct smem_header *header;
> unsigned ret;
>
> - if (!__smem)
> + if (!smem)
> return -EPROBE_DEFER;
>
> - if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
> - part = &__smem->partitions[host];
> + if (host < SMEM_HOST_COUNT && smem->partitions[host].virt_base) {
> + part = &smem->partitions[host];
> phdr = part->virt_base;
> ret = le32_to_cpu(phdr->offset_free_cached) -
> le32_to_cpu(phdr->offset_free_uncached);
>
> if (ret > le32_to_cpu(part->size))
> return -EINVAL;
> - } else if (__smem->global_partition.virt_base) {
> - part = &__smem->global_partition;
> + } else if (smem->global_partition.virt_base) {
> + part = &smem->global_partition;
> phdr = part->virt_base;
> ret = le32_to_cpu(phdr->offset_free_cached) -
> le32_to_cpu(phdr->offset_free_uncached);
>
> if (ret > le32_to_cpu(part->size))
> return -EINVAL;
> } else {
> - header = __smem->regions[0].virt_base;
> + header = smem->regions[0].virt_base;
> ret = le32_to_cpu(header->available);
>
> - if (ret > __smem->regions[0].size)
> + if (ret > smem->regions[0].size)
> return -EINVAL;
> }
>
> return ret;
> @@ -765,27 +725,27 @@ phys_addr_t qcom_smem_virt_to_phys(void *p)
> u64 offset;
> u32 i;
>
> for (i = 0; i < SMEM_HOST_COUNT; i++) {
> - part = &__smem->partitions[i];
> + part = &smem->partitions[i];
>
> if (addr_in_range(part->virt_base, part->size, p)) {
> offset = p - part->virt_base;
>
> return (phys_addr_t)part->phys_base + offset;
> }
> }
>
> - part = &__smem->global_partition;
> + part = &smem->global_partition;
>
> if (addr_in_range(part->virt_base, part->size, p)) {
> offset = p - part->virt_base;
>
> return (phys_addr_t)part->phys_base + offset;
> }
>
> - for (i = 0; i < __smem->num_regions; i++) {
> - area = &__smem->regions[i];
> + for (i = 0; i < smem->num_regions; i++) {
> + area = &smem->regions[i];
>
> if (addr_in_range(area->virt_base, area->size, p)) {
> offset = p - area->virt_base;
>
> @@ -796,63 +756,8 @@ phys_addr_t qcom_smem_virt_to_phys(void *p)
> return 0;
> }
> EXPORT_SYMBOL_GPL(qcom_smem_virt_to_phys);
>
> -/**
> - * qcom_smem_get_soc_id() - return the SoC ID
> - * @id: On success, we return the SoC ID here.
> - *
> - * Look up SoC ID from HW/SW build ID and return it.
> - *
> - * Return: 0 on success, negative errno on failure.
> - */
> -int qcom_smem_get_soc_id(u32 *id)
> -{
> - struct socinfo *info;
> -
> - info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, NULL);
> - if (IS_ERR(info))
> - return PTR_ERR(info);
> -
> - *id = __le32_to_cpu(info->id);
> -
> - return 0;
> -}
> -EXPORT_SYMBOL_GPL(qcom_smem_get_soc_id);
> -
> -/**
> - * qcom_smem_get_feature_code() - return the feature code
> - * @code: On success, return the feature code here.
> - *
> - * Look up the feature code identifier from SMEM and return it.
> - *
> - * Return: 0 on success, negative errno on failure.
> - */
> -int qcom_smem_get_feature_code(u32 *code)
> -{
> - struct socinfo *info;
> - u32 raw_code;
> -
> - info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, NULL);
> - if (IS_ERR(info))
> - return PTR_ERR(info);
> -
> - /* This only makes sense for socinfo >= 16 */
> - if (__le32_to_cpu(info->fmt) < SOCINFO_VERSION(0, 16))
> - return -EOPNOTSUPP;
> -
> - raw_code = __le32_to_cpu(info->feature_code);
> -
> - /* Ensure the value makes sense */
> - if (raw_code > SOCINFO_FC_INT_MAX)
> - raw_code = SOCINFO_FC_UNKNOWN;
> -
> - *code = raw_code;
> -
> - return 0;
> -}
> -EXPORT_SYMBOL_GPL(qcom_smem_get_feature_code);
> -
> static int qcom_smem_get_sbl_version(struct qcom_smem *smem)
> {
> struct smem_header *header;
> __le32 *versions;
> @@ -873,10 +778,9 @@ static struct smem_ptable *qcom_smem_get_ptable(struct qcom_smem *smem)
> return ERR_PTR(-ENOENT);
>
> version = le32_to_cpu(ptable->version);
> if (version != 1) {
> - dev_err(smem->dev,
> - "Unsupported partition header version %d\n", version);
> + log_err("Unsupported partition header version %d\n", version);
> return ERR_PTR(-EINVAL);
> }
> return ptable;
> }
> @@ -906,42 +810,42 @@ static struct smem_partition_header *
> qcom_smem_partition_header(struct qcom_smem *smem,
> struct smem_ptable_entry *entry, u16 host0, u16 host1)
> {
> struct smem_partition_header *header;
> - u32 phys_addr;
> + u64 phys_addr;
> u32 size;
>
> phys_addr = smem->regions[0].aux_base + le32_to_cpu(entry->offset);
> - header = devm_ioremap_wc(smem->dev, phys_addr, le32_to_cpu(entry->size));
> + header = (void *)phys_addr; // devm_ioremap_wc()
>
> if (!header)
> return NULL;
>
> if (memcmp(header->magic, SMEM_PART_MAGIC, sizeof(header->magic))) {
> - dev_err(smem->dev, "bad partition magic %4ph\n", header->magic);
> + log_err("bad partition magic %4ph\n", header->magic);
> return NULL;
> }
>
> if (host0 != le16_to_cpu(header->host0)) {
> - dev_err(smem->dev, "bad host0 (%hu != %hu)\n",
> - host0, le16_to_cpu(header->host0));
> + log_err("bad host0 (%hu != %hu)\n",
> + host0, le16_to_cpu(header->host0));
> return NULL;
> }
> if (host1 != le16_to_cpu(header->host1)) {
> - dev_err(smem->dev, "bad host1 (%hu != %hu)\n",
> - host1, le16_to_cpu(header->host1));
> + log_err("bad host1 (%hu != %hu)\n",
> + host1, le16_to_cpu(header->host1));
> return NULL;
> }
>
> size = le32_to_cpu(header->size);
> if (size != le32_to_cpu(entry->size)) {
> - dev_err(smem->dev, "bad partition size (%u != %u)\n",
> + log_err("bad partition size (%u != %u)\n",
> size, le32_to_cpu(entry->size));
> return NULL;
> }
>
> if (le32_to_cpu(header->offset_free_uncached) > size) {
> - dev_err(smem->dev, "bad partition free uncached (%u > %u)\n",
> + log_err("bad partition free uncached (%u > %u)\n",
> le32_to_cpu(header->offset_free_uncached), size);
> return NULL;
> }
>
> @@ -956,9 +860,9 @@ static int qcom_smem_set_global_partition(struct qcom_smem *smem)
> bool found = false;
> int i;
>
> if (smem->global_partition.virt_base) {
> - dev_err(smem->dev, "Already found the global partition\n");
> + log_err("Already found the global partition\n");
> return -EINVAL;
> }
>
> ptable = qcom_smem_get_ptable(smem);
> @@ -981,9 +885,9 @@ static int qcom_smem_set_global_partition(struct qcom_smem *smem)
> }
> }
>
> if (!found) {
> - dev_err(smem->dev, "Missing entry for global partition\n");
> + log_err("Missing entry for global partition\n");
> return -EINVAL;
> }
>
> header = qcom_smem_partition_header(smem, entry,
> @@ -1030,14 +934,14 @@ qcom_smem_enumerate_partitions(struct qcom_smem *smem, u16 local_host)
> else
> continue;
>
> if (remote_host >= SMEM_HOST_COUNT) {
> - dev_err(smem->dev, "bad host %u\n", remote_host);
> + log_err("bad host %u\n", remote_host);
> return -EINVAL;
> }
>
> if (smem->partitions[remote_host].virt_base) {
> - dev_err(smem->dev, "duplicate host %u\n", remote_host);
> + log_err("duplicate host %u\n", remote_host);
> return -EINVAL;
> }
>
> header = qcom_smem_partition_header(smem, entry, host0, host1);
> @@ -1058,153 +962,100 @@ static int qcom_smem_map_toc(struct qcom_smem *smem, struct smem_region *region)
> {
> u32 ptable_start;
>
> /* map starting 4K for smem header */
> - region->virt_base = devm_ioremap_wc(smem->dev, region->aux_base, SZ_4K);
> + region->virt_base = (void *)region->aux_base;
> ptable_start = region->aux_base + region->size - SZ_4K;
> /* map last 4k for toc */
> - smem->ptable = devm_ioremap_wc(smem->dev, ptable_start, SZ_4K);
> + smem->ptable = (struct smem_ptable *)(u64)ptable_start;
>
> if (!region->virt_base || !smem->ptable)
> return -ENOMEM;
>
> + if (dcache_status())
> + mmu_map_region(region->aux_base, region->size, false);
> +
dcache_status() can return true before the MMU and page tables are
initialized, which makes calling mmu_map_region() unsafe at this point.
Since the intent here is to check whether MMU operations are safe,
this should use mmu_status(), which correctly reflects MMU readiness.
> return 0;
> }
>
> static int qcom_smem_map_global(struct qcom_smem *smem, u32 size)
> {
> - u32 phys_addr;
> + u64 phys_addr;
>
> phys_addr = smem->regions[0].aux_base;
>
> smem->regions[0].size = size;
> - smem->regions[0].virt_base = devm_ioremap_wc(smem->dev, phys_addr, size);
> + smem->regions[0].virt_base = (void *)phys_addr;
>
> if (!smem->regions[0].virt_base)
> return -ENOMEM;
>
> return 0;
> }
>
> -static int qcom_smem_resolve_mem(struct qcom_smem *smem, const char *name,
> - struct smem_region *region)
> -{
> - struct device *dev = smem->dev;
> - struct device_node *np;
> - struct resource r;
> - int ret;
> -
> - np = of_parse_phandle(dev->of_node, name, 0);
> - if (!np) {
> - dev_err(dev, "No %s specified\n", name);
> - return -EINVAL;
> - }
> -
> - ret = of_address_to_resource(np, 0, &r);
> - of_node_put(np);
> - if (ret)
> - return ret;
> -
> - region->aux_base = r.start;
> - region->size = resource_size(&r);
> -
> - return 0;
> -}
> -
> -static int qcom_smem_probe(struct platform_device *pdev)
> +int qcom_smem_init(void)
> {
> struct smem_header *header;
> - struct reserved_mem *rmem;
> - struct qcom_smem *smem;
> - unsigned long flags;
> int num_regions;
> - int hwlock_id;
> + fdt_size_t reg_size = 0;
> + u32 phandle;
> + ofnode node, mem_node;
> u32 version;
> u32 size;
> int ret;
> int i;
>
> - if (__smem)
> + if (dcache_status() && smem)
> + mmu_map_region(smem->regions[0].aux_base, smem->regions[0].size, false);
need to use mmu_status() API here also
Regards,
Aswin
> +
> + if (smem)
> return 0;
>
> + smem = &__smem;
> +
> num_regions = 1;
> - if (of_property_present(pdev->dev.of_node, "qcom,rpm-msg-ram"))
> - num_regions++;
>
> - smem = devm_kzalloc(&pdev->dev, struct_size(smem, regions, num_regions),
> - GFP_KERNEL);
> - if (!smem)
> - return -ENOMEM;
> + node = ofnode_by_compatible(ofnode_root(), "qcom,smem");
> + if (!ofnode_valid(node))
> + return -ENODEV;
> +
> + if (ofnode_has_property(node, "memory-region")) {
> + ofnode_read_u32(node, "memory-region", &phandle);
> + mem_node = ofnode_get_by_phandle(phandle);
> + } else {
> + mem_node = node;
> + }
>
> - smem->dev = &pdev->dev;
> smem->num_regions = num_regions;
>
> - rmem = of_reserved_mem_lookup(pdev->dev.of_node);
> - if (rmem) {
> - smem->regions[0].aux_base = rmem->base;
> - smem->regions[0].size = rmem->size;
> - } else {
> - /*
> - * Fall back to the memory-region reference, if we're not a
> - * reserved-memory node.
> - */
> - ret = qcom_smem_resolve_mem(smem, "memory-region", &smem->regions[0]);
> - if (ret)
> - return ret;
> + smem->regions[0].aux_base = ofnode_get_addr(mem_node);
> + reg_size = ofnode_get_size(mem_node);
> + if (smem->regions[0].aux_base == FDT_ADDR_T_NONE) {
> + log_err("Failed to get base address\n");
> + return -EINVAL;
> }
> -
> - if (num_regions > 1) {
> - ret = qcom_smem_resolve_mem(smem, "qcom,rpm-msg-ram", &smem->regions[1]);
> - if (ret)
> - return ret;
> - }
> -
> + smem->regions[0].size = reg_size;
>
> ret = qcom_smem_map_toc(smem, &smem->regions[0]);
> - if (ret)
> + if (ret) {
> + log_err("Failed to map toc\n");
> return ret;
> + }
>
> for (i = 1; i < num_regions; i++) {
> - smem->regions[i].virt_base = devm_ioremap_wc(&pdev->dev,
> - smem->regions[i].aux_base,
> - smem->regions[i].size);
> - if (!smem->regions[i].virt_base) {
> - dev_err(&pdev->dev, "failed to remap %pa\n", &smem->regions[i].aux_base);
> - return -ENOMEM;
> - }
> + smem->regions[i].virt_base = (void *)smem->regions[i].aux_base;
> }
>
> header = smem->regions[0].virt_base;
> if (le32_to_cpu(header->initialized) != 1 ||
> le32_to_cpu(header->reserved)) {
> - dev_err(&pdev->dev, "SMEM is not initialized by SBL\n");
> + log_err("SMEM is not initialized by SBL\n");
> return -EINVAL;
> }
>
> - hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0);
> - if (hwlock_id < 0) {
> - if (hwlock_id != -EPROBE_DEFER)
> - dev_err(&pdev->dev, "failed to retrieve hwlock\n");
> - return hwlock_id;
> - }
> -
> - smem->hwlock = hwspin_lock_request_specific(hwlock_id);
> - if (!smem->hwlock)
> - return -ENXIO;
> -
> - ret = hwspin_lock_timeout_irqsave(smem->hwlock, HWSPINLOCK_TIMEOUT, &flags);
> - if (ret)
> - return ret;
> size = readl_relaxed(&header->available) + readl_relaxed(&header->free_offset);
> - hwspin_unlock_irqrestore(smem->hwlock, &flags);
>
> version = qcom_smem_get_sbl_version(smem);
> - /*
> - * smem header mapping is required only in heap version scheme, so unmap
> - * it here. It will be remapped in qcom_smem_map_global() when whole
> - * partition is mapped again.
> - */
> - devm_iounmap(smem->dev, smem->regions[0].virt_base);
> switch (version >> 16) {
> case SMEM_GLOBAL_PART_VERSION:
> ret = qcom_smem_set_global_partition(smem);
> if (ret < 0)
> @@ -1215,63 +1066,17 @@ static int qcom_smem_probe(struct platform_device *pdev)
> qcom_smem_map_global(smem, size);
> smem->item_count = SMEM_ITEM_COUNT;
> break;
> default:
> - dev_err(&pdev->dev, "Unsupported SMEM version 0x%x\n", version);
> + log_err("Unsupported SMEM version 0x%x\n", version);
> return -EINVAL;
> }
>
> BUILD_BUG_ON(SMEM_HOST_APPS >= SMEM_HOST_COUNT);
> ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS);
> - if (ret < 0 && ret != -ENOENT)
> + if (ret < 0 && ret != -ENOENT) {
> + log_err("Failed to enumerate partitions\n");
> return ret;
> -
> - __smem = smem;
> -
> - smem->socinfo = platform_device_register_data(&pdev->dev, "qcom-socinfo",
> - PLATFORM_DEVID_NONE, NULL,
> - 0);
> - if (IS_ERR(smem->socinfo))
> - dev_dbg(&pdev->dev, "failed to register socinfo device\n");
> + }
>
> return 0;
> }
> -
> -static void qcom_smem_remove(struct platform_device *pdev)
> -{
> - platform_device_unregister(__smem->socinfo);
> -
> - hwspin_lock_free(__smem->hwlock);
> - __smem = NULL;
> -}
> -
> -static const struct of_device_id qcom_smem_of_match[] = {
> - { .compatible = "qcom,smem" },
> - {}
> -};
> -MODULE_DEVICE_TABLE(of, qcom_smem_of_match);
> -
> -static struct platform_driver qcom_smem_driver = {
> - .probe = qcom_smem_probe,
> - .remove_new = qcom_smem_remove,
> - .driver = {
> - .name = "qcom-smem",
> - .of_match_table = qcom_smem_of_match,
> - .suppress_bind_attrs = true,
> - },
> -};
> -
> -static int __init qcom_smem_init(void)
> -{
> - return platform_driver_register(&qcom_smem_driver);
> -}
> -arch_initcall(qcom_smem_init);
> -
> -static void __exit qcom_smem_exit(void)
> -{
> - platform_driver_unregister(&qcom_smem_driver);
> -}
> -module_exit(qcom_smem_exit)
> -
> -MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
> -MODULE_DESCRIPTION("Qualcomm Shared Memory Manager");
> -MODULE_LICENSE("GPL v2");
> diff --git a/include/soc/qcom/smem.h b/include/soc/qcom/smem.h
> index f946e3beca21..a955db08c0f0 100644
> --- a/include/soc/qcom/smem.h
> +++ b/include/soc/qcom/smem.h
> @@ -3,18 +3,13 @@
> #define __QCOM_SMEM_H__
>
> #define QCOM_SMEM_HOST_ANY -1
>
> +int qcom_smem_init(void);
> +
> bool qcom_smem_is_available(void);
> int qcom_smem_alloc(unsigned host, unsigned item, size_t size);
> void *qcom_smem_get(unsigned host, unsigned item, size_t *size);
>
> int qcom_smem_get_free_space(unsigned host);
>
> -phys_addr_t qcom_smem_virt_to_phys(void *p);
> -
> -int qcom_smem_get_soc_id(u32 *id);
> -int qcom_smem_get_feature_code(u32 *code);
> -
> -int qcom_smem_bust_hwspin_lock_by_host(unsigned int host);
> -
> #endif
>
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 08/15] soc: qcom: smem: adjust for U-Boot
2026-05-08 10:43 ` Aswin Murugan
@ 2026-05-11 12:22 ` Casey Connolly
0 siblings, 0 replies; 51+ messages in thread
From: Casey Connolly @ 2026-05-11 12:22 UTC (permalink / raw)
To: Aswin Murugan, u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Peng Fan, Marek Vasut, Alice Guo,
Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Rasmus Villemoes,
Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
On 08/05/2026 12:43, Aswin Murugan wrote:
> Hi Casey,
>
> On 5/5/2026 12:27 AM, Casey Connolly wrote:
>> Port over the smem code to U-Boot and ensure the smem region gets mapped
>> after dcache is enabled.
>>
>> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
>> ---
>> drivers/soc/qcom/smem.c | 399 ++++++++++++
>> +-----------------------------------
>> include/soc/qcom/smem.h | 9 +-
>> 2 files changed, 104 insertions(+), 304 deletions(-)
>>
>> diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
>> index 7143856e85c3..96288d7bcdef 100644
>> --- a/drivers/soc/qcom/smem.c
>> +++ b/drivers/soc/qcom/smem.c
[...]
>> @@ -1058,153 +962,100 @@ static int qcom_smem_map_toc(struct qcom_smem
>> *smem, struct smem_region *region)
>> {
>> u32 ptable_start;
>> /* map starting 4K for smem header */
>> - region->virt_base = devm_ioremap_wc(smem->dev, region->aux_base,
>> SZ_4K);
>> + region->virt_base = (void *)region->aux_base;
>> ptable_start = region->aux_base + region->size - SZ_4K;
>> /* map last 4k for toc */
>> - smem->ptable = devm_ioremap_wc(smem->dev, ptable_start, SZ_4K);
>> + smem->ptable = (struct smem_ptable *)(u64)ptable_start;
>> if (!region->virt_base || !smem->ptable)
>> return -ENOMEM;
>> + if (dcache_status())
>> + mmu_map_region(region->aux_base, region->size, false);
>> +
> dcache_status() can return true before the MMU and page tables are
> initialized, which makes calling mmu_map_region() unsafe at this point.
> Since the intent here is to check whether MMU operations are safe,
> this should use mmu_status(), which correctly reflects MMU readiness.
ah good point, thanks! I'll fix that in v3.
--
// Casey (she/her)
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 08/15] soc: qcom: smem: adjust for U-Boot
2026-05-04 18:57 ` [PATCH v2 08/15] soc: qcom: smem: adjust " Casey Connolly
2026-05-08 10:43 ` Aswin Murugan
@ 2026-05-18 11:03 ` Sumit Garg
1 sibling, 0 replies; 51+ messages in thread
From: Sumit Garg @ 2026-05-18 11:03 UTC (permalink / raw)
To: Casey Connolly
Cc: u-boot, u-boot-qcom, Tom Rini, Simon Glass, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
On Mon, May 04, 2026 at 08:57:36PM +0200, Casey Connolly wrote:
> Port over the smem code to U-Boot and ensure the smem region gets mapped
> after dcache is enabled.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> drivers/soc/qcom/smem.c | 399 +++++++++++++-----------------------------------
> include/soc/qcom/smem.h | 9 +-
> 2 files changed, 104 insertions(+), 304 deletions(-)
>
> diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
> index 7143856e85c3..96288d7bcdef 100644
> --- a/drivers/soc/qcom/smem.c
> +++ b/drivers/soc/qcom/smem.c
> @@ -3,17 +3,19 @@
> * Copyright (c) 2015, Sony Mobile Communications AB.
> * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
> */
>
> +#define pr_fmt(fmt) "smem: " fmt
> +
> +#include <asm/system.h>
> #include <dm/device.h>
> #include <dm/device_compat.h>
> #include <dm/ofnode.h>
> #include <linux/bug.h>
> #include <linux/io.h>
> #include <linux/ioport.h>
> #include <linux/sizes.h>
> #include <soc/qcom/smem.h>
> -#include <soc/qcom/socinfo.h>
>
> /*
> * The Qualcomm shared memory system is a allocate only heap structure that
> * consists of one of more memory areas that can be accessed by the processors
> @@ -246,8 +248,10 @@ struct smem_info {
> };
>
> static const u8 SMEM_INFO_MAGIC[] = { 0x53, 0x49, 0x49, 0x49 }; /* SIII */
>
> +#define SMEM_MAX_REGIONS 2
> +
> /**
> * struct smem_region - representation of a chunk of memory used for smem
> * @aux_base: identifier of aux_mem base
> * @virt_base: virtual base address of memory with this aux_mem identifier
> @@ -260,33 +264,31 @@ struct smem_region {
> };
>
> /**
> * struct qcom_smem - device data for the smem device
> - * @dev: device pointer
> - * @hwlock: reference to a hwspinlock
> * @ptable: virtual base of partition table
> * @global_partition: describes for global partition when in use
> * @partitions: list of partitions of current processor/host
> * @item_count: max accepted item number
> - * @socinfo: platform device pointer
> * @num_regions: number of @regions
> * @regions: list of the memory regions defining the shared memory
> */
> struct qcom_smem {
> - struct device *dev;
> -
> - struct hwspinlock *hwlock;
> -
> u32 item_count;
> - struct platform_device *socinfo;
> struct smem_ptable *ptable;
> struct smem_partition global_partition;
> struct smem_partition partitions[SMEM_HOST_COUNT];
>
> unsigned num_regions;
> - struct smem_region regions[] __counted_by(num_regions);
> + struct smem_region regions[SMEM_MAX_REGIONS];
> };
>
> +/* Pre-allocated storage for SMEM */
> +static struct qcom_smem __smem __section(".data") = { 0 };
> +
> +/* Pointer to the one and only smem handle */
> +static struct qcom_smem *smem __section(".data") = NULL;
> +
> static void *
> phdr_to_last_uncached_entry(struct smem_partition_header *phdr)
> {
> void *p = phdr;
> @@ -350,48 +352,16 @@ static void *cached_entry_to_item(struct smem_private_entry *e)
>
> return p - le32_to_cpu(e->size);
> }
>
> -/* Pointer to the one and only smem handle */
> -static struct qcom_smem *__smem;
> -
> -/* Timeout (ms) for the trylock of remote spinlocks */
> -#define HWSPINLOCK_TIMEOUT 1000
> -
> -/* The qcom hwspinlock id is always plus one from the smem host id */
> -#define SMEM_HOST_ID_TO_HWSPINLOCK_ID(__x) ((__x) + 1)
> -
> -/**
> - * qcom_smem_bust_hwspin_lock_by_host() - bust the smem hwspinlock for a host
> - * @host: remote processor id
> - *
> - * Busts the hwspin_lock for the given smem host id. This helper is intended
> - * for remoteproc drivers that manage remoteprocs with an equivalent smem
> - * driver instance in the remote firmware. Drivers can force a release of the
> - * smem hwspin_lock if the rproc unexpectedly goes into a bad state.
> - *
> - * Context: Process context.
> - *
> - * Returns: 0 on success, otherwise negative errno.
> - */
> -int qcom_smem_bust_hwspin_lock_by_host(unsigned int host)
> -{
> - /* This function is for remote procs, so ignore SMEM_HOST_APPS */
> - if (host == SMEM_HOST_APPS || host >= SMEM_HOST_COUNT)
> - return -EINVAL;
> -
> - return hwspin_lock_bust(__smem->hwlock, SMEM_HOST_ID_TO_HWSPINLOCK_ID(host));
> -}
> -EXPORT_SYMBOL_GPL(qcom_smem_bust_hwspin_lock_by_host);
> -
> /**
> * qcom_smem_is_available() - Check if SMEM is available
> *
> * Return: true if SMEM is available, false otherwise.
> */
> bool qcom_smem_is_available(void)
Is this API going to be used in U-Boot? If not then we can drop it.
> {
> - return !!__smem;
> + return !!smem;
> }
> EXPORT_SYMBOL_GPL(qcom_smem_is_available);
>
> static int qcom_smem_alloc_private(struct qcom_smem *smem,
> @@ -429,9 +399,9 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem,
>
> /* Check that we don't grow into the cached region */
> alloc_size = sizeof(*hdr) + ALIGN(size, 8);
> if ((void *)hdr + alloc_size > cached) {
> - dev_err(smem->dev, "Out of memory\n");
> + log_err("Out of memory\n");
> return -ENOSPC;
> }
>
> hdr->canary = SMEM_PRIVATE_CANARY;
> @@ -449,9 +419,9 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem,
> le32_add_cpu(&phdr->offset_free_uncached, alloc_size);
>
> return 0;
> bad_canary:
> - dev_err(smem->dev, "Found invalid canary in hosts %hu:%hu partition\n",
> + log_err("Found invalid canary in hosts %hu:%hu partition\n",
> le16_to_cpu(phdr->host0), le16_to_cpu(phdr->host1));
>
> return -EINVAL;
> }
> @@ -500,41 +470,31 @@ static int qcom_smem_alloc_global(struct qcom_smem *smem,
> */
> int qcom_smem_alloc(unsigned host, unsigned item, size_t size)
> {
> struct smem_partition *part;
> - unsigned long flags;
> int ret;
>
> - if (!__smem)
> + if (!smem)
> return -EPROBE_DEFER;
The probe defer is specific to Linux, can be dropped for U-Boot.
>
> if (item < SMEM_ITEM_LAST_FIXED) {
> - dev_err(__smem->dev,
> - "Rejecting allocation of static entry %d\n", item);
> + log_err("Rejecting allocation of static entry %d\n", item);
> return -EINVAL;
> }
>
> - if (WARN_ON(item >= __smem->item_count))
> + if (WARN_ON(item >= smem->item_count))
> return -EINVAL;
>
> - ret = hwspin_lock_timeout_irqsave(__smem->hwlock,
> - HWSPINLOCK_TIMEOUT,
> - &flags);
> - if (ret)
> - return ret;
> -
> - if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
> - part = &__smem->partitions[host];
> - ret = qcom_smem_alloc_private(__smem, part, item, size);
> - } else if (__smem->global_partition.virt_base) {
> - part = &__smem->global_partition;
> - ret = qcom_smem_alloc_private(__smem, part, item, size);
> + if (host < SMEM_HOST_COUNT && smem->partitions[host].virt_base) {
> + part = &smem->partitions[host];
> + ret = qcom_smem_alloc_private(smem, part, item, size);
> + } else if (smem->global_partition.virt_base) {
> + part = &smem->global_partition;
> + ret = qcom_smem_alloc_private(smem, part, item, size);
> } else {
> - ret = qcom_smem_alloc_global(__smem, item, size);
> + ret = qcom_smem_alloc_global(smem, item, size);
> }
>
> - hwspin_unlock_irqrestore(__smem->hwlock, &flags);
> -
> return ret;
> }
> EXPORT_SYMBOL_GPL(qcom_smem_alloc);
These symbol exports can also be dropped.
>
> @@ -660,9 +620,9 @@ static void *qcom_smem_get_private(struct qcom_smem *smem,
>
> return ERR_PTR(-ENOENT);
>
> invalid_canary:
> - dev_err(smem->dev, "Found invalid canary in hosts %hu:%hu partition\n",
> + log_err("Found invalid canary in hosts %hu:%hu partition\n",
> le16_to_cpu(phdr->host0), le16_to_cpu(phdr->host1));
>
> return ERR_PTR(-EINVAL);
> }
> @@ -680,22 +640,22 @@ void *qcom_smem_get(unsigned host, unsigned item, size_t *size)
> {
> struct smem_partition *part;
> void *ptr = ERR_PTR(-EPROBE_DEFER);
>
> - if (!__smem)
> + if (!smem)
> return ptr;
>
> - if (WARN_ON(item >= __smem->item_count))
> + if (WARN_ON(item >= smem->item_count))
> return ERR_PTR(-EINVAL);
>
> - if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
> - part = &__smem->partitions[host];
> - ptr = qcom_smem_get_private(__smem, part, item, size);
> - } else if (__smem->global_partition.virt_base) {
> - part = &__smem->global_partition;
> - ptr = qcom_smem_get_private(__smem, part, item, size);
> + if (host < SMEM_HOST_COUNT && smem->partitions[host].virt_base) {
> + part = &smem->partitions[host];
> + ptr = qcom_smem_get_private(smem, part, item, size);
> + } else if (smem->global_partition.virt_base) {
> + part = &smem->global_partition;
> + ptr = qcom_smem_get_private(smem, part, item, size);
> } else {
> - ptr = qcom_smem_get_global(__smem, item, size);
> + ptr = qcom_smem_get_global(smem, item, size);
> }
>
> return ptr;
> }
> @@ -714,32 +674,32 @@ int qcom_smem_get_free_space(unsigned host)
> struct smem_partition_header *phdr;
> struct smem_header *header;
> unsigned ret;
>
> - if (!__smem)
> + if (!smem)
> return -EPROBE_DEFER;
Ditto here.
>
> - if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
> - part = &__smem->partitions[host];
> + if (host < SMEM_HOST_COUNT && smem->partitions[host].virt_base) {
> + part = &smem->partitions[host];
> phdr = part->virt_base;
> ret = le32_to_cpu(phdr->offset_free_cached) -
> le32_to_cpu(phdr->offset_free_uncached);
>
> if (ret > le32_to_cpu(part->size))
> return -EINVAL;
> - } else if (__smem->global_partition.virt_base) {
> - part = &__smem->global_partition;
> + } else if (smem->global_partition.virt_base) {
> + part = &smem->global_partition;
> phdr = part->virt_base;
> ret = le32_to_cpu(phdr->offset_free_cached) -
> le32_to_cpu(phdr->offset_free_uncached);
>
> if (ret > le32_to_cpu(part->size))
> return -EINVAL;
> } else {
> - header = __smem->regions[0].virt_base;
> + header = smem->regions[0].virt_base;
> ret = le32_to_cpu(header->available);
>
> - if (ret > __smem->regions[0].size)
> + if (ret > smem->regions[0].size)
> return -EINVAL;
> }
>
> return ret;
> @@ -765,27 +725,27 @@ phys_addr_t qcom_smem_virt_to_phys(void *p)
> u64 offset;
> u32 i;
>
> for (i = 0; i < SMEM_HOST_COUNT; i++) {
> - part = &__smem->partitions[i];
> + part = &smem->partitions[i];
>
> if (addr_in_range(part->virt_base, part->size, p)) {
> offset = p - part->virt_base;
>
> return (phys_addr_t)part->phys_base + offset;
> }
> }
>
> - part = &__smem->global_partition;
> + part = &smem->global_partition;
>
> if (addr_in_range(part->virt_base, part->size, p)) {
> offset = p - part->virt_base;
>
> return (phys_addr_t)part->phys_base + offset;
> }
>
> - for (i = 0; i < __smem->num_regions; i++) {
> - area = &__smem->regions[i];
> + for (i = 0; i < smem->num_regions; i++) {
> + area = &smem->regions[i];
>
> if (addr_in_range(area->virt_base, area->size, p)) {
> offset = p - area->virt_base;
>
> @@ -796,63 +756,8 @@ phys_addr_t qcom_smem_virt_to_phys(void *p)
> return 0;
> }
> EXPORT_SYMBOL_GPL(qcom_smem_virt_to_phys);
>
> -/**
> - * qcom_smem_get_soc_id() - return the SoC ID
> - * @id: On success, we return the SoC ID here.
> - *
> - * Look up SoC ID from HW/SW build ID and return it.
> - *
> - * Return: 0 on success, negative errno on failure.
> - */
> -int qcom_smem_get_soc_id(u32 *id)
> -{
> - struct socinfo *info;
> -
> - info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, NULL);
> - if (IS_ERR(info))
> - return PTR_ERR(info);
> -
> - *id = __le32_to_cpu(info->id);
> -
> - return 0;
> -}
> -EXPORT_SYMBOL_GPL(qcom_smem_get_soc_id);
> -
> -/**
> - * qcom_smem_get_feature_code() - return the feature code
> - * @code: On success, return the feature code here.
> - *
> - * Look up the feature code identifier from SMEM and return it.
> - *
> - * Return: 0 on success, negative errno on failure.
> - */
> -int qcom_smem_get_feature_code(u32 *code)
> -{
> - struct socinfo *info;
> - u32 raw_code;
> -
> - info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, NULL);
> - if (IS_ERR(info))
> - return PTR_ERR(info);
> -
> - /* This only makes sense for socinfo >= 16 */
> - if (__le32_to_cpu(info->fmt) < SOCINFO_VERSION(0, 16))
> - return -EOPNOTSUPP;
> -
> - raw_code = __le32_to_cpu(info->feature_code);
> -
> - /* Ensure the value makes sense */
> - if (raw_code > SOCINFO_FC_INT_MAX)
> - raw_code = SOCINFO_FC_UNKNOWN;
> -
> - *code = raw_code;
> -
> - return 0;
> -}
> -EXPORT_SYMBOL_GPL(qcom_smem_get_feature_code);
> -
> static int qcom_smem_get_sbl_version(struct qcom_smem *smem)
> {
> struct smem_header *header;
> __le32 *versions;
> @@ -873,10 +778,9 @@ static struct smem_ptable *qcom_smem_get_ptable(struct qcom_smem *smem)
> return ERR_PTR(-ENOENT);
>
> version = le32_to_cpu(ptable->version);
> if (version != 1) {
> - dev_err(smem->dev,
> - "Unsupported partition header version %d\n", version);
> + log_err("Unsupported partition header version %d\n", version);
> return ERR_PTR(-EINVAL);
> }
> return ptable;
> }
> @@ -906,42 +810,42 @@ static struct smem_partition_header *
> qcom_smem_partition_header(struct qcom_smem *smem,
> struct smem_ptable_entry *entry, u16 host0, u16 host1)
> {
> struct smem_partition_header *header;
> - u32 phys_addr;
> + u64 phys_addr;
> u32 size;
>
> phys_addr = smem->regions[0].aux_base + le32_to_cpu(entry->offset);
> - header = devm_ioremap_wc(smem->dev, phys_addr, le32_to_cpu(entry->size));
> + header = (void *)phys_addr; // devm_ioremap_wc()
>
> if (!header)
> return NULL;
>
> if (memcmp(header->magic, SMEM_PART_MAGIC, sizeof(header->magic))) {
> - dev_err(smem->dev, "bad partition magic %4ph\n", header->magic);
> + log_err("bad partition magic %4ph\n", header->magic);
> return NULL;
> }
>
> if (host0 != le16_to_cpu(header->host0)) {
> - dev_err(smem->dev, "bad host0 (%hu != %hu)\n",
> - host0, le16_to_cpu(header->host0));
> + log_err("bad host0 (%hu != %hu)\n",
> + host0, le16_to_cpu(header->host0));
> return NULL;
> }
> if (host1 != le16_to_cpu(header->host1)) {
> - dev_err(smem->dev, "bad host1 (%hu != %hu)\n",
> - host1, le16_to_cpu(header->host1));
> + log_err("bad host1 (%hu != %hu)\n",
> + host1, le16_to_cpu(header->host1));
> return NULL;
> }
>
> size = le32_to_cpu(header->size);
> if (size != le32_to_cpu(entry->size)) {
> - dev_err(smem->dev, "bad partition size (%u != %u)\n",
> + log_err("bad partition size (%u != %u)\n",
> size, le32_to_cpu(entry->size));
> return NULL;
> }
>
> if (le32_to_cpu(header->offset_free_uncached) > size) {
> - dev_err(smem->dev, "bad partition free uncached (%u > %u)\n",
> + log_err("bad partition free uncached (%u > %u)\n",
> le32_to_cpu(header->offset_free_uncached), size);
> return NULL;
> }
>
> @@ -956,9 +860,9 @@ static int qcom_smem_set_global_partition(struct qcom_smem *smem)
> bool found = false;
> int i;
>
> if (smem->global_partition.virt_base) {
> - dev_err(smem->dev, "Already found the global partition\n");
> + log_err("Already found the global partition\n");
> return -EINVAL;
> }
>
> ptable = qcom_smem_get_ptable(smem);
> @@ -981,9 +885,9 @@ static int qcom_smem_set_global_partition(struct qcom_smem *smem)
> }
> }
>
> if (!found) {
> - dev_err(smem->dev, "Missing entry for global partition\n");
> + log_err("Missing entry for global partition\n");
> return -EINVAL;
> }
>
> header = qcom_smem_partition_header(smem, entry,
> @@ -1030,14 +934,14 @@ qcom_smem_enumerate_partitions(struct qcom_smem *smem, u16 local_host)
> else
> continue;
>
> if (remote_host >= SMEM_HOST_COUNT) {
> - dev_err(smem->dev, "bad host %u\n", remote_host);
> + log_err("bad host %u\n", remote_host);
> return -EINVAL;
> }
>
> if (smem->partitions[remote_host].virt_base) {
> - dev_err(smem->dev, "duplicate host %u\n", remote_host);
> + log_err("duplicate host %u\n", remote_host);
> return -EINVAL;
> }
>
> header = qcom_smem_partition_header(smem, entry, host0, host1);
> @@ -1058,153 +962,100 @@ static int qcom_smem_map_toc(struct qcom_smem *smem, struct smem_region *region)
> {
> u32 ptable_start;
>
> /* map starting 4K for smem header */
> - region->virt_base = devm_ioremap_wc(smem->dev, region->aux_base, SZ_4K);
> + region->virt_base = (void *)region->aux_base;
> ptable_start = region->aux_base + region->size - SZ_4K;
> /* map last 4k for toc */
> - smem->ptable = devm_ioremap_wc(smem->dev, ptable_start, SZ_4K);
> + smem->ptable = (struct smem_ptable *)(u64)ptable_start;
>
> if (!region->virt_base || !smem->ptable)
> return -ENOMEM;
>
> + if (dcache_status())
> + mmu_map_region(region->aux_base, region->size, false);
> +
> return 0;
> }
>
> static int qcom_smem_map_global(struct qcom_smem *smem, u32 size)
> {
> - u32 phys_addr;
> + u64 phys_addr;
>
> phys_addr = smem->regions[0].aux_base;
>
> smem->regions[0].size = size;
> - smem->regions[0].virt_base = devm_ioremap_wc(smem->dev, phys_addr, size);
> + smem->regions[0].virt_base = (void *)phys_addr;
>
> if (!smem->regions[0].virt_base)
> return -ENOMEM;
>
> return 0;
> }
>
> -static int qcom_smem_resolve_mem(struct qcom_smem *smem, const char *name,
> - struct smem_region *region)
> -{
> - struct device *dev = smem->dev;
> - struct device_node *np;
> - struct resource r;
> - int ret;
> -
> - np = of_parse_phandle(dev->of_node, name, 0);
> - if (!np) {
> - dev_err(dev, "No %s specified\n", name);
> - return -EINVAL;
> - }
> -
> - ret = of_address_to_resource(np, 0, &r);
> - of_node_put(np);
> - if (ret)
> - return ret;
> -
> - region->aux_base = r.start;
> - region->size = resource_size(&r);
> -
> - return 0;
> -}
> -
> -static int qcom_smem_probe(struct platform_device *pdev)
> +int qcom_smem_init(void)
> {
> struct smem_header *header;
> - struct reserved_mem *rmem;
> - struct qcom_smem *smem;
> - unsigned long flags;
> int num_regions;
> - int hwlock_id;
> + fdt_size_t reg_size = 0;
> + u32 phandle;
> + ofnode node, mem_node;
> u32 version;
> u32 size;
> int ret;
> int i;
>
> - if (__smem)
> + if (dcache_status() && smem)
> + mmu_map_region(smem->regions[0].aux_base, smem->regions[0].size, false);
The kernel does WC mapping for SMEM which is uncached, so in U-Boot we
should follow the same. This especially becomes important when running
in EL2 mode.
-Sumit
> +
> + if (smem)
> return 0;
>
> + smem = &__smem;
> +
> num_regions = 1;
> - if (of_property_present(pdev->dev.of_node, "qcom,rpm-msg-ram"))
> - num_regions++;
>
> - smem = devm_kzalloc(&pdev->dev, struct_size(smem, regions, num_regions),
> - GFP_KERNEL);
> - if (!smem)
> - return -ENOMEM;
> + node = ofnode_by_compatible(ofnode_root(), "qcom,smem");
> + if (!ofnode_valid(node))
> + return -ENODEV;
> +
> + if (ofnode_has_property(node, "memory-region")) {
> + ofnode_read_u32(node, "memory-region", &phandle);
> + mem_node = ofnode_get_by_phandle(phandle);
> + } else {
> + mem_node = node;
> + }
>
> - smem->dev = &pdev->dev;
> smem->num_regions = num_regions;
>
> - rmem = of_reserved_mem_lookup(pdev->dev.of_node);
> - if (rmem) {
> - smem->regions[0].aux_base = rmem->base;
> - smem->regions[0].size = rmem->size;
> - } else {
> - /*
> - * Fall back to the memory-region reference, if we're not a
> - * reserved-memory node.
> - */
> - ret = qcom_smem_resolve_mem(smem, "memory-region", &smem->regions[0]);
> - if (ret)
> - return ret;
> + smem->regions[0].aux_base = ofnode_get_addr(mem_node);
> + reg_size = ofnode_get_size(mem_node);
> + if (smem->regions[0].aux_base == FDT_ADDR_T_NONE) {
> + log_err("Failed to get base address\n");
> + return -EINVAL;
> }
> -
> - if (num_regions > 1) {
> - ret = qcom_smem_resolve_mem(smem, "qcom,rpm-msg-ram", &smem->regions[1]);
> - if (ret)
> - return ret;
> - }
> -
> + smem->regions[0].size = reg_size;
>
> ret = qcom_smem_map_toc(smem, &smem->regions[0]);
> - if (ret)
> + if (ret) {
> + log_err("Failed to map toc\n");
> return ret;
> + }
>
> for (i = 1; i < num_regions; i++) {
> - smem->regions[i].virt_base = devm_ioremap_wc(&pdev->dev,
> - smem->regions[i].aux_base,
> - smem->regions[i].size);
> - if (!smem->regions[i].virt_base) {
> - dev_err(&pdev->dev, "failed to remap %pa\n", &smem->regions[i].aux_base);
> - return -ENOMEM;
> - }
> + smem->regions[i].virt_base = (void *)smem->regions[i].aux_base;
> }
>
> header = smem->regions[0].virt_base;
> if (le32_to_cpu(header->initialized) != 1 ||
> le32_to_cpu(header->reserved)) {
> - dev_err(&pdev->dev, "SMEM is not initialized by SBL\n");
> + log_err("SMEM is not initialized by SBL\n");
> return -EINVAL;
> }
>
> - hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0);
> - if (hwlock_id < 0) {
> - if (hwlock_id != -EPROBE_DEFER)
> - dev_err(&pdev->dev, "failed to retrieve hwlock\n");
> - return hwlock_id;
> - }
> -
> - smem->hwlock = hwspin_lock_request_specific(hwlock_id);
> - if (!smem->hwlock)
> - return -ENXIO;
> -
> - ret = hwspin_lock_timeout_irqsave(smem->hwlock, HWSPINLOCK_TIMEOUT, &flags);
> - if (ret)
> - return ret;
> size = readl_relaxed(&header->available) + readl_relaxed(&header->free_offset);
> - hwspin_unlock_irqrestore(smem->hwlock, &flags);
>
> version = qcom_smem_get_sbl_version(smem);
> - /*
> - * smem header mapping is required only in heap version scheme, so unmap
> - * it here. It will be remapped in qcom_smem_map_global() when whole
> - * partition is mapped again.
> - */
> - devm_iounmap(smem->dev, smem->regions[0].virt_base);
> switch (version >> 16) {
> case SMEM_GLOBAL_PART_VERSION:
> ret = qcom_smem_set_global_partition(smem);
> if (ret < 0)
> @@ -1215,63 +1066,17 @@ static int qcom_smem_probe(struct platform_device *pdev)
> qcom_smem_map_global(smem, size);
> smem->item_count = SMEM_ITEM_COUNT;
> break;
> default:
> - dev_err(&pdev->dev, "Unsupported SMEM version 0x%x\n", version);
> + log_err("Unsupported SMEM version 0x%x\n", version);
> return -EINVAL;
> }
>
> BUILD_BUG_ON(SMEM_HOST_APPS >= SMEM_HOST_COUNT);
> ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS);
> - if (ret < 0 && ret != -ENOENT)
> + if (ret < 0 && ret != -ENOENT) {
> + log_err("Failed to enumerate partitions\n");
> return ret;
> -
> - __smem = smem;
> -
> - smem->socinfo = platform_device_register_data(&pdev->dev, "qcom-socinfo",
> - PLATFORM_DEVID_NONE, NULL,
> - 0);
> - if (IS_ERR(smem->socinfo))
> - dev_dbg(&pdev->dev, "failed to register socinfo device\n");
> + }
>
> return 0;
> }
> -
> -static void qcom_smem_remove(struct platform_device *pdev)
> -{
> - platform_device_unregister(__smem->socinfo);
> -
> - hwspin_lock_free(__smem->hwlock);
> - __smem = NULL;
> -}
> -
> -static const struct of_device_id qcom_smem_of_match[] = {
> - { .compatible = "qcom,smem" },
> - {}
> -};
> -MODULE_DEVICE_TABLE(of, qcom_smem_of_match);
> -
> -static struct platform_driver qcom_smem_driver = {
> - .probe = qcom_smem_probe,
> - .remove_new = qcom_smem_remove,
> - .driver = {
> - .name = "qcom-smem",
> - .of_match_table = qcom_smem_of_match,
> - .suppress_bind_attrs = true,
> - },
> -};
> -
> -static int __init qcom_smem_init(void)
> -{
> - return platform_driver_register(&qcom_smem_driver);
> -}
> -arch_initcall(qcom_smem_init);
> -
> -static void __exit qcom_smem_exit(void)
> -{
> - platform_driver_unregister(&qcom_smem_driver);
> -}
> -module_exit(qcom_smem_exit)
> -
> -MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
> -MODULE_DESCRIPTION("Qualcomm Shared Memory Manager");
> -MODULE_LICENSE("GPL v2");
> diff --git a/include/soc/qcom/smem.h b/include/soc/qcom/smem.h
> index f946e3beca21..a955db08c0f0 100644
> --- a/include/soc/qcom/smem.h
> +++ b/include/soc/qcom/smem.h
> @@ -3,18 +3,13 @@
> #define __QCOM_SMEM_H__
>
> #define QCOM_SMEM_HOST_ANY -1
>
> +int qcom_smem_init(void);
> +
> bool qcom_smem_is_available(void);
> int qcom_smem_alloc(unsigned host, unsigned item, size_t size);
> void *qcom_smem_get(unsigned host, unsigned item, size_t *size);
>
> int qcom_smem_get_free_space(unsigned host);
>
> -phys_addr_t qcom_smem_virt_to_phys(void *p);
> -
> -int qcom_smem_get_soc_id(u32 *id);
> -int qcom_smem_get_feature_code(u32 *code);
> -
> -int qcom_smem_bust_hwspin_lock_by_host(unsigned int host);
> -
> #endif
>
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 09/15] soc: qcom: smem: get serial number from socinfo
2026-05-04 18:57 ` [PATCH v2 09/15] soc: qcom: smem: get serial number from socinfo Casey Connolly
@ 2026-05-18 12:49 ` Sumit Garg
0 siblings, 0 replies; 51+ messages in thread
From: Sumit Garg @ 2026-05-18 12:49 UTC (permalink / raw)
To: Casey Connolly
Cc: u-boot, u-boot-qcom, Tom Rini, Simon Glass, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
On Mon, May 04, 2026 at 08:57:37PM +0200, Casey Connolly wrote:
> Implement socinfo support to fetch the serial number if available.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> drivers/soc/qcom/smem.c | 25 +++++++++++++++++++++++++
> include/soc/qcom/smem.h | 1 +
> 2 files changed, 26 insertions(+)
>
> diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
> index 96288d7bcdef..e20523e2cec3 100644
> --- a/drivers/soc/qcom/smem.c
> +++ b/drivers/soc/qcom/smem.c
> @@ -14,8 +14,10 @@
> #include <linux/io.h>
> #include <linux/ioport.h>
> #include <linux/sizes.h>
> #include <soc/qcom/smem.h>
> +#include <soc/qcom/socinfo.h>
> +#include <env.h>
>
> /*
> * The Qualcomm shared memory system is a allocate only heap structure that
> * consists of one of more memory areas that can be accessed by the processors
> @@ -991,8 +993,31 @@ static int qcom_smem_map_global(struct qcom_smem *smem, u32 size)
>
> return 0;
> }
>
> +int qcom_socinfo_init(void)
> +{
> + struct socinfo *info;
> + size_t item_size;
> + char buf[32] = { 0 };
> +
Here you should check if the environment already has serial number added
as follows:
if (env_get("serial#"))
return 0;
As otherwise the env_set() throws error.
-Sumit
> + info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID,
> + &item_size);
> + if (IS_ERR(info)) {
> + log_err("Couldn't find socinfo: %ld\n", PTR_ERR(info));
> + return PTR_ERR(info);
> + }
> +
> + if (offsetof(struct socinfo, serial_num) + sizeof(info->serial_num) <= item_size) {
> + snprintf(buf, sizeof(buf), "%u", le32_to_cpu(info->serial_num));
> + env_set("serial#", buf);
> + } else {
> + return -ENOENT;
> + }
> +
> + return 0;
> +}
> +
> int qcom_smem_init(void)
> {
> struct smem_header *header;
> int num_regions;
> diff --git a/include/soc/qcom/smem.h b/include/soc/qcom/smem.h
> index a955db08c0f0..755003807dba 100644
> --- a/include/soc/qcom/smem.h
> +++ b/include/soc/qcom/smem.h
> @@ -4,8 +4,9 @@
>
> #define QCOM_SMEM_HOST_ANY -1
>
> int qcom_smem_init(void);
> +int qcom_socinfo_init(void);
>
> bool qcom_smem_is_available(void);
> int qcom_smem_alloc(unsigned host, unsigned item, size_t size);
> void *qcom_smem_get(unsigned host, unsigned item, size_t *size);
>
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 11/15] soc: qcom: smem: add build infra
2026-05-04 18:57 ` [PATCH v2 11/15] soc: qcom: smem: add build infra Casey Connolly
@ 2026-05-18 12:53 ` Sumit Garg
2026-05-18 14:38 ` Neil Armstrong
1 sibling, 0 replies; 51+ messages in thread
From: Sumit Garg @ 2026-05-18 12:53 UTC (permalink / raw)
To: Casey Connolly
Cc: u-boot, u-boot-qcom, Tom Rini, Simon Glass, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
On Mon, May 04, 2026 at 08:57:39PM +0200, Casey Connolly wrote:
> Build the new SMEM driver port, and select it when ARCH_SNAPDRAGON is
> selected, since it will be a hard dependency for Qualcomm platforms.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> drivers/soc/qcom/Kconfig | 8 ++++++++
> drivers/soc/qcom/Makefile | 1 +
> 2 files changed, 9 insertions(+)
Reviewed-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
-Sumit
>
> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
> index 8243805e46a3..cdd9e30f43e5 100644
> --- a/drivers/soc/qcom/Kconfig
> +++ b/drivers/soc/qcom/Kconfig
> @@ -24,5 +24,13 @@ config QCOM_RPMH
> help
> Say y here to support the Qualcomm RPMh (resource peripheral manager)
> if you need to control regulators on Qualcomm platforms, say y here.
>
> +config QCOM_SMEM
> + bool "Qualcomm SMEM support"
> + help
> + Say y here to support the Qualcomm SMEM (shared memory) client driver.
> + SMEM is a shared memory region that is used to pass information about
> + the hardware (e.g. DRAM layout, PMIC configuration, etc) between bootloader
> + staged and the OS. If in doubt, say y.
> +
> endif # SOC_QCOM
> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
> index 78fae8bbfa16..f4102f9155a8 100644
> --- a/drivers/soc/qcom/Makefile
> +++ b/drivers/soc/qcom/Makefile
> @@ -1,4 +1,5 @@
> # SPDX-License-Identifier: GPL-2.0+
>
> obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o
> obj-$(CONFIG_QCOM_RPMH) += rpmh-rsc.o rpmh.o
> +obj-$(CONFIG_QCOM_SMEM) += smem.o
>
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 12/15] mach-snapdragon: move memory parsing to its own file
2026-05-04 18:57 ` [PATCH v2 12/15] mach-snapdragon: move memory parsing to its own file Casey Connolly
@ 2026-05-18 12:54 ` Sumit Garg
2026-05-18 14:40 ` Neil Armstrong
1 sibling, 0 replies; 51+ messages in thread
From: Sumit Garg @ 2026-05-18 12:54 UTC (permalink / raw)
To: Casey Connolly
Cc: u-boot, u-boot-qcom, Tom Rini, Simon Glass, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
On Mon, May 04, 2026 at 08:57:40PM +0200, Casey Connolly wrote:
> This code is getting a bit complicated, split it out to try and keep
> things a bit better organised as we're going to be supporting populating
> the memory layout from various other sources.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> arch/arm/mach-snapdragon/Makefile | 2 +-
> arch/arm/mach-snapdragon/board.c | 115 -------------------------------
> arch/arm/mach-snapdragon/dram.c | 127 +++++++++++++++++++++++++++++++++++
> arch/arm/mach-snapdragon/qcom-priv.h | 2 +
> 4 files changed, 130 insertions(+), 116 deletions(-)
Reviewed-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
-Sumit
>
> diff --git a/arch/arm/mach-snapdragon/Makefile b/arch/arm/mach-snapdragon/Makefile
> index 343e825c6fdd..e481e4f26e5c 100644
> --- a/arch/arm/mach-snapdragon/Makefile
> +++ b/arch/arm/mach-snapdragon/Makefile
> @@ -1,7 +1,7 @@
> # SPDX-License-Identifier: GPL-2.0+
> #
> # (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
>
> -obj-y += board.o
> +obj-y += board.o dram.o
> obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += capsule_update.o
> obj-$(CONFIG_OF_LIVE) += of_fixup.o
> diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c
> index e12d3d00caa4..a2d97ad77910 100644
> --- a/arch/arm/mach-snapdragon/board.c
> +++ b/arch/arm/mach-snapdragon/board.c
> @@ -42,123 +42,8 @@ enum qcom_boot_source qcom_boot_source __section(".data") = 0;
> static struct mm_region rbx_mem_map[CONFIG_NR_DRAM_BANKS + 2] = { { 0 } };
>
> struct mm_region *mem_map = rbx_mem_map;
>
> -static struct {
> - phys_addr_t start;
> - phys_size_t size;
> -} prevbl_ddr_banks[CONFIG_NR_DRAM_BANKS] __section(".data") = { 0 };
> -
> -int dram_init(void)
> -{
> - /*
> - * gd->ram_base / ram_size have been setup already
> - * in qcom_parse_memory().
> - */
> - return 0;
> -}
> -
> -static int ddr_bank_cmp(const void *v1, const void *v2)
> -{
> - const struct {
> - phys_addr_t start;
> - phys_size_t size;
> - } *res1 = v1, *res2 = v2;
> -
> - if (!res1->size)
> - return 1;
> - if (!res2->size)
> - return -1;
> -
> - return (res1->start >> 24) - (res2->start >> 24);
> -}
> -
> -/* This has to be done post-relocation since gd->bd isn't preserved */
> -static void qcom_configure_bi_dram(void)
> -{
> - int i;
> -
> - for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
> - gd->bd->bi_dram[i].start = prevbl_ddr_banks[i].start;
> - gd->bd->bi_dram[i].size = prevbl_ddr_banks[i].size;
> - }
> -}
> -
> -int dram_init_banksize(void)
> -{
> - qcom_configure_bi_dram();
> -
> - return 0;
> -}
> -
> -/**
> - * The generic memory parsing code in U-Boot lacks a few things that we
> - * need on Qualcomm:
> - *
> - * 1. It sets gd->ram_size and gd->ram_base to represent a single memory block
> - * 2. setup_dest_addr() later relocates U-Boot to ram_base + ram_size, the end
> - * of that first memory block.
> - *
> - * This results in all memory beyond U-Boot being unusable in Linux when booting
> - * with EFI.
> - *
> - * Since the ranges in the memory node may be out of order, the only way for us
> - * to correctly determine the relocation address for U-Boot is to parse all
> - * memory regions and find the highest valid address.
> - *
> - * We can't use fdtdec_setup_memory_banksize() since it stores the result in
> - * gd->bd, which is not yet allocated.
> - *
> - * @fdt: FDT blob to parse /memory node from
> - *
> - * Return: 0 on success or -ENODATA if /memory node is missing or incomplete
> - */
> -static int qcom_parse_memory(const void *fdt)
> -{
> - int offset;
> - const fdt64_t *memory;
> - int memsize;
> - phys_addr_t ram_end = 0;
> - int i, j, banks;
> -
> - offset = fdt_path_offset(fdt, "/memory");
> - if (offset < 0)
> - return -ENODATA;
> -
> - memory = fdt_getprop(fdt, offset, "reg", &memsize);
> - if (!memory)
> - return -ENODATA;
> -
> - banks = min(memsize / (2 * sizeof(u64)), (ulong)CONFIG_NR_DRAM_BANKS);
> -
> - if (memsize / sizeof(u64) > CONFIG_NR_DRAM_BANKS * 2)
> - log_err("Provided more than the max of %d memory banks\n", CONFIG_NR_DRAM_BANKS);
> -
> - if (banks > CONFIG_NR_DRAM_BANKS)
> - log_err("Provided more memory banks than we can handle\n");
> -
> - for (i = 0, j = 0; i < banks * 2; i += 2, j++) {
> - prevbl_ddr_banks[j].start = get_unaligned_be64(&memory[i]);
> - prevbl_ddr_banks[j].size = get_unaligned_be64(&memory[i + 1]);
> - if (!prevbl_ddr_banks[j].size) {
> - j--;
> - continue;
> - }
> - ram_end = max(ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size);
> - }
> -
> - if (!banks || !prevbl_ddr_banks[0].size)
> - return -ENODATA;
> -
> - /* Sort our RAM banks -_- */
> - qsort(prevbl_ddr_banks, banks, sizeof(prevbl_ddr_banks[0]), ddr_bank_cmp);
> -
> - gd->ram_base = prevbl_ddr_banks[0].start;
> - gd->ram_size = ram_end - gd->ram_base;
> -
> - return 0;
> -}
> -
> static void show_psci_version(void)
> {
> struct arm_smccc_res res;
>
> diff --git a/arch/arm/mach-snapdragon/dram.c b/arch/arm/mach-snapdragon/dram.c
> new file mode 100644
> index 000000000000..ad73b685a935
> --- /dev/null
> +++ b/arch/arm/mach-snapdragon/dram.c
> @@ -0,0 +1,127 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Memory layout parsing for Qualcomm.
> + */
> +
> +#define LOG_CATEGORY LOGC_BOARD
> +#define pr_fmt(fmt) "QCOM-DRAM: " fmt
> +
> +#include <asm-generic/unaligned.h>
> +#include <dm.h>
> +#include <log.h>
> +#include <sort.h>
> +
> +static struct {
> + phys_addr_t start;
> + phys_size_t size;
> +} prevbl_ddr_banks[CONFIG_NR_DRAM_BANKS] __section(".data") = { 0 };
> +
> +int dram_init(void)
> +{
> + /*
> + * gd->ram_base / ram_size have been setup already
> + * in qcom_parse_memory().
> + */
> + return 0;
> +}
> +
> +static int ddr_bank_cmp(const void *v1, const void *v2)
> +{
> + const struct {
> + phys_addr_t start;
> + phys_size_t size;
> + } *res1 = v1, *res2 = v2;
> +
> + if (!res1->size)
> + return 1;
> + if (!res2->size)
> + return -1;
> +
> + return (res1->start >> 24) - (res2->start >> 24);
> +}
> +
> +/* This has to be done post-relocation since gd->bd isn't preserved */
> +static void qcom_configure_bi_dram(void)
> +{
> + int i;
> +
> + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
> + gd->bd->bi_dram[i].start = prevbl_ddr_banks[i].start;
> + gd->bd->bi_dram[i].size = prevbl_ddr_banks[i].size;
> + }
> +}
> +
> +int dram_init_banksize(void)
> +{
> + qcom_configure_bi_dram();
> +
> + return 0;
> +}
> +
> +/**
> + * The generic memory parsing code in U-Boot lacks a few things that we
> + * need on Qualcomm:
> + *
> + * 1. It sets gd->ram_size and gd->ram_base to represent a single memory block
> + * 2. setup_dest_addr() later relocates U-Boot to ram_base + ram_size, the end
> + * of that first memory block.
> + *
> + * This results in all memory beyond U-Boot being unusable in Linux when booting
> + * with EFI.
> + *
> + * Since the ranges in the memory node may be out of order, the only way for us
> + * to correctly determine the relocation address for U-Boot is to parse all
> + * memory regions and find the highest valid address.
> + *
> + * We can't use fdtdec_setup_memory_banksize() since it stores the result in
> + * gd->bd, which is not yet allocated.
> + *
> + * @fdt: FDT blob to parse /memory node from
> + *
> + * Return: 0 on success or -ENODATA if /memory node is missing or incomplete
> + */
> +int qcom_parse_memory(const void *fdt)
> +{
> + int offset;
> + const fdt64_t *memory;
> + int memsize;
> + phys_addr_t ram_end = 0;
> + int i, j, banks;
> +
> + offset = fdt_path_offset(fdt, "/memory");
> + if (offset < 0)
> + return -ENODATA;
> +
> + memory = fdt_getprop(fdt, offset, "reg", &memsize);
> + if (!memory)
> + return -ENODATA;
> +
> + banks = min(memsize / (2 * sizeof(u64)), (ulong)CONFIG_NR_DRAM_BANKS);
> +
> + if (memsize / sizeof(u64) > CONFIG_NR_DRAM_BANKS * 2)
> + log_err("Provided more than the max of %d memory banks\n", CONFIG_NR_DRAM_BANKS);
> +
> + if (banks > CONFIG_NR_DRAM_BANKS)
> + log_err("Provided more memory banks than we can handle\n");
> +
> + for (i = 0, j = 0; i < banks * 2; i += 2, j++) {
> + prevbl_ddr_banks[j].start = get_unaligned_be64(&memory[i]);
> + prevbl_ddr_banks[j].size = get_unaligned_be64(&memory[i + 1]);
> + if (!prevbl_ddr_banks[j].size) {
> + j--;
> + continue;
> + }
> + ram_end = max(ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size);
> + }
> +
> + if (!banks || !prevbl_ddr_banks[0].size)
> + return -ENODATA;
> +
> + /* Sort our RAM banks -_- */
> + qsort(prevbl_ddr_banks, banks, sizeof(prevbl_ddr_banks[0]), ddr_bank_cmp);
> +
> + gd->ram_base = prevbl_ddr_banks[0].start;
> + gd->ram_size = ram_end - gd->ram_base;
> +
> + return 0;
> +}
> diff --git a/arch/arm/mach-snapdragon/qcom-priv.h b/arch/arm/mach-snapdragon/qcom-priv.h
> index b8bf574e8bbb..ce409314a98b 100644
> --- a/arch/arm/mach-snapdragon/qcom-priv.h
> +++ b/arch/arm/mach-snapdragon/qcom-priv.h
> @@ -22,5 +22,7 @@ void qcom_configure_capsule_updates(void);
> #else
> void qcom_configure_capsule_updates(void) {}
> #endif /* EFI_HAVE_CAPSULE_SUPPORT */
>
> +int qcom_parse_memory(const void *fdt);
> +
> #endif /* __QCOM_PRIV_H__ */
>
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 14/15] mach-snapdragon: fetch serial# from SMEM
2026-05-04 18:57 ` [PATCH v2 14/15] mach-snapdragon: fetch serial# " Casey Connolly
@ 2026-05-18 13:15 ` Sumit Garg
0 siblings, 0 replies; 51+ messages in thread
From: Sumit Garg @ 2026-05-18 13:15 UTC (permalink / raw)
To: Casey Connolly
Cc: u-boot, u-boot-qcom, Tom Rini, Simon Glass, Peng Fan, Marek Vasut,
Alice Guo, Quentin Schulz, Ilias Apalodimas, Neil Armstrong,
Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao, Stefan Roese,
Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
On Mon, May 04, 2026 at 08:57:42PM +0200, Casey Connolly wrote:
> If available, otherwise fall back to cmdline.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> arch/arm/mach-snapdragon/board.c | 11 ++++++++++-
> 1 file changed, 10 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c
> index 7610891191fd..63055a60af44 100644
> --- a/arch/arm/mach-snapdragon/board.c
> +++ b/arch/arm/mach-snapdragon/board.c
> @@ -31,8 +31,9 @@
> #include <fdt_support.h>
> #include <usb.h>
> #include <soc/qcom/smem.h>
> #include <sort.h>
> +#include <soc/qcom/smem.h>
> #include <time.h>
>
> #include "qcom-priv.h"
>
> @@ -251,11 +252,16 @@ static const char *get_cmdline(void)
> }
>
> void qcom_set_serialno(void)
> {
> - const char *cmdline = get_cmdline();
> + const char *cmdline;
> char serial[32];
>
> + if (!qcom_socinfo_init())
> + return;
> +
> + cmdline = get_cmdline();
I would suggest to merge this whole patch into patch#9 when the SMEM
based support is added.
> +
> if (!cmdline) {
> log_debug("Failed to get bootargs\n");
> return;
> }
> @@ -473,8 +479,11 @@ int board_late_init(void)
> else
> memcpy((void *)addr, (void *)gd->fdt_blob,
> fdt32_to_cpu(fdt_blob->totalsize));
>
> + /* Initialise SMEM if it wasn't done already and ensure it's memory is mapped */
> + qcom_smem_init();
It's rather better to move this SMEM init to where it's actually used as
part of qcom_socinfo_init(). That way it's easier to track dependency.
-Sumit
> +
> configure_env();
> qcom_late_init();
>
> qcom_show_boot_context();
>
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 01/15] Revert "dm: SMEM (Shared memory) uclass"
2026-05-04 18:57 ` [PATCH v2 01/15] Revert "dm: SMEM (Shared memory) uclass" Casey Connolly
2026-05-05 10:15 ` Sumit Garg
2026-05-07 15:31 ` Simon Glass
@ 2026-05-18 14:34 ` Neil Armstrong
2 siblings, 0 replies; 51+ messages in thread
From: Neil Armstrong @ 2026-05-18 14:34 UTC (permalink / raw)
To: Casey Connolly, u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Peng Fan, Marek Vasut, Alice Guo,
Quentin Schulz, Ilias Apalodimas, Mattijs Korpershoek,
Kuan-Wei Chiu, Raymond Mao, Stefan Roese, Philip Molloy,
Jerome Forissier, Marek Vasut, Varadarajan Narayanan,
Patrice Chotard, Aswin Murugan, Rasmus Villemoes, Heiko Schocher,
Michal Simek, Sughosh Ganu, Antony Kurniawan Soemardi, Luca Weiss,
Balaji Selvanathan
On 5/4/26 20:57, Casey Connolly wrote:
> SMEM is a highly Qualcomm specific interface, while having a dedicated
> UCLASS for it offers a nice abstraction, for things like memory layout
> parsing we need to use it before the driver model is available.
>
> Therefore, it doesn't make sense to fit SMEM into the driver model.
> Instead let's adopt a model closer to Linux, and parse SMEM really early
> during boot (as soon as we have the FDT).
>
> This reverts commit 7b384eccc785b596f68448b155cbda26df57fb23.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> arch/arm/Kconfig | 1 -
> drivers/Kconfig | 2 --
> drivers/Makefile | 1 -
> drivers/smem/Kconfig | 25 -------------
> drivers/smem/Makefile | 7 ----
> drivers/smem/smem-uclass.c | 46 ------------------------
> include/dm/uclass-id.h | 1 -
> include/smem.h | 90 ----------------------------------------------
> 8 files changed, 173 deletions(-)
>
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index f624675eadf8..dadb18e0b8cd 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -1141,9 +1141,8 @@ config ARCH_SNAPDRAGON
> select POWER_DOMAIN
> select GPIO_EXTRA_HEADER
> select MSM_SMEM
> select OF_CONTROL
> - select SMEM
> select SPMI
> select BOARD_LATE_INIT
> select OF_BOARD
> select SAVE_PREV_BL_FDT_ADDR if !ENABLE_ARM_SOC_BOOT0_HOOK
> diff --git a/drivers/Kconfig b/drivers/Kconfig
> index 47606ddc6c8b..aa293df37c57 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -123,10 +123,8 @@ source "drivers/scsi/Kconfig"
> source "drivers/serial/Kconfig"
>
> source "drivers/sm/Kconfig"
>
> -source "drivers/smem/Kconfig"
> -
> source "drivers/sound/Kconfig"
>
> source "drivers/soc/Kconfig"
>
> diff --git a/drivers/Makefile b/drivers/Makefile
> index 43d0ba332818..ca3e87ffb102 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -118,9 +118,8 @@ obj-y += pwm/
> obj-y += reset/
> obj-y += input/
> obj-y += iommu/
> # SOC specific infrastructure drivers.
> -obj-y += smem/
> obj-y += thermal/
> obj-$(CONFIG_TEE) += tee/
> obj-$(CONFIG_ARM_FFA_TRANSPORT) += firmware/arm-ffa/
> obj-y += axi/
> diff --git a/drivers/smem/Kconfig b/drivers/smem/Kconfig
> deleted file mode 100644
> index e5d7dcc81b17..000000000000
> --- a/drivers/smem/Kconfig
> +++ /dev/null
> @@ -1,25 +0,0 @@
> -menuconfig SMEM
> - bool "SMEM (Shared Memory mamanger) support"
> -
> -if SMEM
> -
> -config SANDBOX_SMEM
> - bool "Sandbox Shared Memory Manager (SMEM)"
> - depends on SANDBOX && DM
> - help
> - enable SMEM support for sandbox. This is an emulation of a real SMEM
> - manager.
> - The sandbox driver allocates a shared memory from the heap and
> - initialzies it on start.
> -
> -config MSM_SMEM
> - bool "Qualcomm Shared Memory Manager (SMEM)"
> - depends on DM
> - depends on ARCH_SNAPDRAGON || ARCH_IPQ40XX
> - select DEVRES
> - help
> - Enable support for the Qualcomm Shared Memory Manager.
> - The driver provides an interface to items in a heap shared among all
> - processors in a Qualcomm platform.
> -
> -endif # menu "SMEM Support"
> diff --git a/drivers/smem/Makefile b/drivers/smem/Makefile
> deleted file mode 100644
> index af3e9b50883c..000000000000
> --- a/drivers/smem/Makefile
> +++ /dev/null
> @@ -1,7 +0,0 @@
> -# SPDX-License-Identifier: GPL-2.0+
> -#
> -# Makefile for the U-Boot SMEM interface drivers
> -
> -obj-$(CONFIG_SANDBOX_SMEM) += sandbox_smem.o
> -obj-$(CONFIG_SMEM) += smem-uclass.o
> -obj-$(CONFIG_MSM_SMEM) += msm_smem.o
> diff --git a/drivers/smem/smem-uclass.c b/drivers/smem/smem-uclass.c
> deleted file mode 100644
> index 4dea5cc4bf1c..000000000000
> --- a/drivers/smem/smem-uclass.c
> +++ /dev/null
> @@ -1,46 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0+
> -/*
> - * Copyright (c) 2018 Ramon Fried <ramon.fried@gmail.com>
> - */
> -
> -#define LOG_CATEGORY UCLASS_SMEM
> -
> -#include <dm.h>
> -#include <smem.h>
> -
> -int smem_alloc(struct udevice *dev, unsigned int host,
> - unsigned int item, size_t size)
> -{
> - struct smem_ops *ops = smem_get_ops(dev);
> -
> - if (!ops->alloc)
> - return -ENOSYS;
> -
> - return ops->alloc(host, item, size);
> -}
> -
> -void *smem_get(struct udevice *dev, unsigned int host,
> - unsigned int item, size_t *size)
> -{
> - struct smem_ops *ops = smem_get_ops(dev);
> -
> - if (!ops->get)
> - return NULL;
> -
> - return ops->get(host, item, size);
> -}
> -
> -int smem_get_free_space(struct udevice *dev, unsigned int host)
> -{
> - struct smem_ops *ops = smem_get_ops(dev);
> -
> - if (!ops->get_free_space)
> - return -ENOSYS;
> -
> - return ops->get_free_space(host);
> -}
> -
> -UCLASS_DRIVER(smem) = {
> - .id = UCLASS_SMEM,
> - .name = "smem",
> -};
> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
> index 36b5d87c304f..fe0aae2720ca 100644
> --- a/include/dm/uclass-id.h
> +++ b/include/dm/uclass-id.h
> @@ -133,9 +133,8 @@ enum uclass_id {
> UCLASS_SCMI_BASE, /* Interface for SCMI Base protocol */
> UCLASS_SCSI, /* SCSI device */
> UCLASS_SERIAL, /* Serial UART */
> UCLASS_SIMPLE_BUS, /* Bus with child devices */
> - UCLASS_SMEM, /* Shared memory interface */
> UCLASS_SOC, /* SOC Device */
> UCLASS_SOUND, /* Playing simple sounds */
> UCLASS_SPI, /* SPI bus */
> UCLASS_SPI_FLASH, /* SPI flash */
> diff --git a/include/smem.h b/include/smem.h
> deleted file mode 100644
> index b19c534ebc43..000000000000
> --- a/include/smem.h
> +++ /dev/null
> @@ -1,90 +0,0 @@
> -/* SPDX-License-Identifier: GPL-2.0+ */
> -/*
> - * The shared memory system is an allocate-only heap structure that
> - * consists of one of more memory areas that can be accessed by the processors
> - * in the SoC.
> - *
> - * Allocation can be done globally for all processors or to an individual processor.
> - * This is controlled by the @host parameter.
> - *
> - * Allocation and management of heap can be implemented in various ways,
> - * The @item parameter should be used as an index/hash to the memory region.
> - *
> - * Copyright (c) 2018 Ramon Fried <ramon.fried@gmail.com>
> - */
> -
> -#ifndef _smemh_
> -#define _smemh_
> -
> -/* struct smem_ops: Operations for the SMEM uclass */
> -struct smem_ops {
> - /**
> - * alloc() - allocate space for a smem item
> - *
> - * @host: remote processor id, or -1 for all processors.
> - * @item: smem item handle
> - * @size: number of bytes to be allocated
> - * @return 0 if OK, -ve on error
> - */
> - int (*alloc)(unsigned int host,
> - unsigned int item, size_t size);
> -
> - /**
> - * get() - Resolve ptr of size of a smem item
> - *
> - * @host: the remote processor, of -1 for all processors.
> - * @item: smem item handle
> - * @size: pointer to be filled out with the size of the item
> - * @return pointer on success, NULL on error
> - */
> - void *(*get)(unsigned int host,
> - unsigned int item, size_t *size);
> -
> - /**
> - * get_free_space() - Get free space in smem in bytes
> - *
> - * @host: the remote processor identifying a partition, or -1
> - * for all processors.
> - * @return free space, -ve on error
> - */
> - int (*get_free_space)(unsigned int host);
> -};
> -
> -#define smem_get_ops(dev) ((struct smem_ops *)(dev)->driver->ops)
> -
> -/**
> - * smem_alloc() - allocate space for a smem item
> - * @host: remote processor id, or -1
> - * @item: smem item handle
> - * @size: number of bytes to be allocated
> - * Return: 0 if OK, -ve on error
> - *
> - * Allocate space for a given smem item of size @size, given that the item is
> - * not yet allocated.
> - */
> -int smem_alloc(struct udevice *dev, unsigned int host, unsigned int item, size_t size);
> -
> -/**
> - * smem_get() - resolve ptr of size of a smem item
> - * @host: the remote processor, or -1 for all processors.
> - * @item: smem item handle
> - * @size: pointer to be filled out with size of the item
> - * Return: pointer on success, NULL on error
> - *
> - * Looks up smem item and returns pointer to it. Size of smem
> - * item is returned in @size.
> - */
> -void *smem_get(struct udevice *dev, unsigned int host, unsigned int item, size_t *size);
> -
> -/**
> - * smem_get_free_space() - retrieve amount of free space in a partition
> - * @host: the remote processor identifying a partition, or -1
> - * for all processors.
> - * Return: size in bytes, -ve on error
> - *
> - * To be used by smem clients as a quick way to determine if any new
> - * allocations has been made.
> - */
> -int smem_get_free_space(struct udevice *dev, unsigned int host);
> -
> -#endif /* _smem_h_ */
>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Thanks,
Neil
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 02/15] smem: drop drivers/smem
2026-05-04 18:57 ` [PATCH v2 02/15] smem: drop drivers/smem Casey Connolly
@ 2026-05-18 14:34 ` Neil Armstrong
0 siblings, 0 replies; 51+ messages in thread
From: Neil Armstrong @ 2026-05-18 14:34 UTC (permalink / raw)
To: Casey Connolly, u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Peng Fan, Marek Vasut, Alice Guo,
Quentin Schulz, Ilias Apalodimas, Mattijs Korpershoek,
Kuan-Wei Chiu, Raymond Mao, Stefan Roese, Philip Molloy,
Jerome Forissier, Marek Vasut, Varadarajan Narayanan,
Patrice Chotard, Aswin Murugan, Rasmus Villemoes, Heiko Schocher,
Michal Simek, Sughosh Ganu, Antony Kurniawan Soemardi, Luca Weiss,
Balaji Selvanathan
On 5/4/26 20:57, Casey Connolly wrote:
> Remove the old qcom SMEM driver port and the sandbox stub
> implementation.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> MAINTAINERS | 1 -
> arch/arm/Kconfig | 1 -
> drivers/smem/msm_smem.c | 958 --------------------------------------------
> drivers/smem/sandbox_smem.c | 44 --
> 4 files changed, 1004 deletions(-)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 0dcc7243124e..4d54a61327c7 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -687,9 +687,8 @@ F: drivers/mmc/msm_sdhci.c
> F: drivers/phy/msm8916-usbh-phy.c
> F: drivers/phy/qcom/
> F: drivers/serial/serial_msm.c
> F: drivers/serial/serial_msm_geni.c
> -F: drivers/smem/msm_smem.c
> F: drivers/spmi/spmi-msm.c
> F: drivers/usb/host/ehci-msm.c
> N: qcom
> N: snapdragon
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index dadb18e0b8cd..a8fcfe76296c 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -1139,9 +1139,8 @@ config ARCH_SNAPDRAGON
> select DM_SERIAL
> select DM_RESET
> select POWER_DOMAIN
> select GPIO_EXTRA_HEADER
> - select MSM_SMEM
> select OF_CONTROL
> select SPMI
> select BOARD_LATE_INIT
> select OF_BOARD
> diff --git a/drivers/smem/msm_smem.c b/drivers/smem/msm_smem.c
> deleted file mode 100644
> index 7a50d5a5792c..000000000000
> --- a/drivers/smem/msm_smem.c
> +++ /dev/null
> @@ -1,958 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0+
> -/*
> - * Copyright (c) 2015, Sony Mobile Communications AB.
> - * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
> - * Copyright (c) 2018, Ramon Fried <ramon.fried@gmail.com>
> - */
> -
> -#include <errno.h>
> -#include <dm.h>
> -#include <dm/device_compat.h>
> -#include <dm/devres.h>
> -#include <dm/of_access.h>
> -#include <dm/of_addr.h>
> -#include <asm/io.h>
> -#include <linux/bug.h>
> -#include <linux/err.h>
> -#include <linux/ioport.h>
> -#include <linux/io.h>
> -#include <linux/sizes.h>
> -#include <smem.h>
> -
> -/*
> - * The Qualcomm shared memory system is an allocate-only heap structure that
> - * consists of one of more memory areas that can be accessed by the processors
> - * in the SoC.
> - *
> - * All systems contains a global heap, accessible by all processors in the SoC,
> - * with a table of contents data structure (@smem_header) at the beginning of
> - * the main shared memory block.
> - *
> - * The global header contains meta data for allocations as well as a fixed list
> - * of 512 entries (@smem_global_entry) that can be initialized to reference
> - * parts of the shared memory space.
> - *
> - *
> - * In addition to this global heap, a set of "private" heaps can be set up at
> - * boot time with access restrictions so that only certain processor pairs can
> - * access the data.
> - *
> - * These partitions are referenced from an optional partition table
> - * (@smem_ptable), that is found 4kB from the end of the main smem region. The
> - * partition table entries (@smem_ptable_entry) lists the involved processors
> - * (or hosts) and their location in the main shared memory region.
> - *
> - * Each partition starts with a header (@smem_partition_header) that identifies
> - * the partition and holds properties for the two internal memory regions. The
> - * two regions are cached and non-cached memory respectively. Each region
> - * contain a link list of allocation headers (@smem_private_entry) followed by
> - * their data.
> - *
> - * Items in the non-cached region are allocated from the start of the partition
> - * while items in the cached region are allocated from the end. The free area
> - * is hence the region between the cached and non-cached offsets. The header of
> - * cached items comes after the data.
> - *
> - * Version 12 (SMEM_GLOBAL_PART_VERSION) changes the item alloc/get procedure
> - * for the global heap. A new global partition is created from the global heap
> - * region with partition type (SMEM_GLOBAL_HOST) and the max smem item count is
> - * set by the bootloader.
> - *
> - */
> -
> -/*
> - * The version member of the smem header contains an array of versions for the
> - * various software components in the SoC. We verify that the boot loader
> - * version is a valid version as a sanity check.
> - */
> -#define SMEM_MASTER_SBL_VERSION_INDEX 7
> -#define SMEM_GLOBAL_HEAP_VERSION 11
> -#define SMEM_GLOBAL_PART_VERSION 12
> -
> -/*
> - * The first 8 items are only to be allocated by the boot loader while
> - * initializing the heap.
> - */
> -#define SMEM_ITEM_LAST_FIXED 8
> -
> -/* Highest accepted item number, for both global and private heaps */
> -#define SMEM_ITEM_COUNT 512
> -
> -/* Processor/host identifier for the application processor */
> -#define SMEM_HOST_APPS 0
> -
> -/* Processor/host identifier for the global partition */
> -#define SMEM_GLOBAL_HOST 0xfffe
> -
> -/* Max number of processors/hosts in a system */
> -#define SMEM_HOST_COUNT 25
> -
> -/**
> - * struct smem_proc_comm - proc_comm communication struct (legacy)
> - * @command: current command to be executed
> - * @status: status of the currently requested command
> - * @params: parameters to the command
> - */
> -struct smem_proc_comm {
> - __le32 command;
> - __le32 status;
> - __le32 params[2];
> -};
> -
> -/**
> - * struct smem_global_entry - entry to reference smem items on the heap
> - * @allocated: boolean to indicate if this entry is used
> - * @offset: offset to the allocated space
> - * @size: size of the allocated space, 8 byte aligned
> - * @aux_base: base address for the memory region used by this unit, or 0 for
> - * the default region. bits 0,1 are reserved
> - */
> -struct smem_global_entry {
> - __le32 allocated;
> - __le32 offset;
> - __le32 size;
> - __le32 aux_base; /* bits 1:0 reserved */
> -};
> -#define AUX_BASE_MASK 0xfffffffc
> -
> -/**
> - * struct smem_header - header found in beginning of primary smem region
> - * @proc_comm: proc_comm communication interface (legacy)
> - * @version: array of versions for the various subsystems
> - * @initialized: boolean to indicate that smem is initialized
> - * @free_offset: index of the first unallocated byte in smem
> - * @available: number of bytes available for allocation
> - * @reserved: reserved field, must be 0
> - * toc: array of references to items
> - */
> -struct smem_header {
> - struct smem_proc_comm proc_comm[4];
> - __le32 version[32];
> - __le32 initialized;
> - __le32 free_offset;
> - __le32 available;
> - __le32 reserved;
> - struct smem_global_entry toc[SMEM_ITEM_COUNT];
> -};
> -
> -/**
> - * struct smem_ptable_entry - one entry in the @smem_ptable list
> - * @offset: offset, within the main shared memory region, of the partition
> - * @size: size of the partition
> - * @flags: flags for the partition (currently unused)
> - * @host0: first processor/host with access to this partition
> - * @host1: second processor/host with access to this partition
> - * @cacheline: alignment for "cached" entries
> - * @reserved: reserved entries for later use
> - */
> -struct smem_ptable_entry {
> - __le32 offset;
> - __le32 size;
> - __le32 flags;
> - __le16 host0;
> - __le16 host1;
> - __le32 cacheline;
> - __le32 reserved[7];
> -};
> -
> -/**
> - * struct smem_ptable - partition table for the private partitions
> - * @magic: magic number, must be SMEM_PTABLE_MAGIC
> - * @version: version of the partition table
> - * @num_entries: number of partitions in the table
> - * @reserved: for now reserved entries
> - * @entry: list of @smem_ptable_entry for the @num_entries partitions
> - */
> -struct smem_ptable {
> - u8 magic[4];
> - __le32 version;
> - __le32 num_entries;
> - __le32 reserved[5];
> - struct smem_ptable_entry entry[];
> -};
> -
> -static const u8 SMEM_PTABLE_MAGIC[] = { 0x24, 0x54, 0x4f, 0x43 }; /* "$TOC" */
> -
> -/**
> - * struct smem_partition_header - header of the partitions
> - * @magic: magic number, must be SMEM_PART_MAGIC
> - * @host0: first processor/host with access to this partition
> - * @host1: second processor/host with access to this partition
> - * @size: size of the partition
> - * @offset_free_uncached: offset to the first free byte of uncached memory in
> - * this partition
> - * @offset_free_cached: offset to the first free byte of cached memory in this
> - * partition
> - * @reserved: for now reserved entries
> - */
> -struct smem_partition_header {
> - u8 magic[4];
> - __le16 host0;
> - __le16 host1;
> - __le32 size;
> - __le32 offset_free_uncached;
> - __le32 offset_free_cached;
> - __le32 reserved[3];
> -};
> -
> -static const u8 SMEM_PART_MAGIC[] = { 0x24, 0x50, 0x52, 0x54 };
> -
> -/**
> - * struct smem_private_entry - header of each item in the private partition
> - * @canary: magic number, must be SMEM_PRIVATE_CANARY
> - * @item: identifying number of the smem item
> - * @size: size of the data, including padding bytes
> - * @padding_data: number of bytes of padding of data
> - * @padding_hdr: number of bytes of padding between the header and the data
> - * @reserved: for now reserved entry
> - */
> -struct smem_private_entry {
> - u16 canary; /* bytes are the same so no swapping needed */
> - __le16 item;
> - __le32 size; /* includes padding bytes */
> - __le16 padding_data;
> - __le16 padding_hdr;
> - __le32 reserved;
> -};
> -#define SMEM_PRIVATE_CANARY 0xa5a5
> -
> -/**
> - * struct smem_info - smem region info located after the table of contents
> - * @magic: magic number, must be SMEM_INFO_MAGIC
> - * @size: size of the smem region
> - * @base_addr: base address of the smem region
> - * @reserved: for now reserved entry
> - * @num_items: highest accepted item number
> - */
> -struct smem_info {
> - u8 magic[4];
> - __le32 size;
> - __le32 base_addr;
> - __le32 reserved;
> - __le16 num_items;
> -};
> -
> -static const u8 SMEM_INFO_MAGIC[] = { 0x53, 0x49, 0x49, 0x49 }; /* SIII */
> -
> -/**
> - * struct smem_region - representation of a chunk of memory used for smem
> - * @aux_base: identifier of aux_mem base
> - * @virt_base: virtual base address of memory with this aux_mem identifier
> - * @size: size of the memory region
> - */
> -struct smem_region {
> - u32 aux_base;
> - void __iomem *virt_base;
> - size_t size;
> -};
> -
> -/**
> - * struct qcom_smem - device data for the smem device
> - * @dev: device pointer
> - * @global_partition: pointer to global partition when in use
> - * @global_cacheline: cacheline size for global partition
> - * @partitions: list of pointers to partitions affecting the current
> - * processor/host
> - * @cacheline: list of cacheline sizes for each host
> - * @item_count: max accepted item number
> - * @num_regions: number of @regions
> - * @regions: list of the memory regions defining the shared memory
> - */
> -struct qcom_smem {
> - struct udevice *dev;
> -
> - struct smem_partition_header *global_partition;
> - size_t global_cacheline;
> - struct smem_partition_header *partitions[SMEM_HOST_COUNT];
> - size_t cacheline[SMEM_HOST_COUNT];
> - u32 item_count;
> -
> - unsigned int num_regions;
> - struct smem_region regions[0];
> -};
> -
> -static struct smem_private_entry *
> -phdr_to_last_uncached_entry(struct smem_partition_header *phdr)
> -{
> - void *p = phdr;
> -
> - return p + le32_to_cpu(phdr->offset_free_uncached);
> -}
> -
> -static void *phdr_to_first_cached_entry(struct smem_partition_header *phdr,
> - size_t cacheline)
> -{
> - void *p = phdr;
> -
> - return p + le32_to_cpu(phdr->size) - ALIGN(sizeof(*phdr), cacheline);
> -}
> -
> -static void *phdr_to_last_cached_entry(struct smem_partition_header *phdr)
> -{
> - void *p = phdr;
> -
> - return p + le32_to_cpu(phdr->offset_free_cached);
> -}
> -
> -static struct smem_private_entry *
> -phdr_to_first_uncached_entry(struct smem_partition_header *phdr)
> -{
> - void *p = phdr;
> -
> - return p + sizeof(*phdr);
> -}
> -
> -static struct smem_private_entry *
> -uncached_entry_next(struct smem_private_entry *e)
> -{
> - void *p = e;
> -
> - return p + sizeof(*e) + le16_to_cpu(e->padding_hdr) +
> - le32_to_cpu(e->size);
> -}
> -
> -static struct smem_private_entry *
> -cached_entry_next(struct smem_private_entry *e, size_t cacheline)
> -{
> - void *p = e;
> -
> - return p - le32_to_cpu(e->size) - ALIGN(sizeof(*e), cacheline);
> -}
> -
> -static void *uncached_entry_to_item(struct smem_private_entry *e)
> -{
> - void *p = e;
> -
> - return p + sizeof(*e) + le16_to_cpu(e->padding_hdr);
> -}
> -
> -static void *cached_entry_to_item(struct smem_private_entry *e)
> -{
> - void *p = e;
> -
> - return p - le32_to_cpu(e->size);
> -}
> -
> -/* Pointer to the one and only smem handle */
> -static struct qcom_smem *__smem;
> -
> -static int qcom_smem_alloc_private(struct qcom_smem *smem,
> - struct smem_partition_header *phdr,
> - unsigned int item,
> - size_t size)
> -{
> - struct smem_private_entry *hdr, *end;
> - size_t alloc_size;
> - void *cached;
> -
> - hdr = phdr_to_first_uncached_entry(phdr);
> - end = phdr_to_last_uncached_entry(phdr);
> - cached = phdr_to_last_cached_entry(phdr);
> -
> - while (hdr < end) {
> - if (hdr->canary != SMEM_PRIVATE_CANARY) {
> - dev_err(smem->dev,
> - "Found invalid canary in hosts %d:%d partition\n",
> - phdr->host0, phdr->host1);
> - return -EINVAL;
> - }
> -
> - if (le16_to_cpu(hdr->item) == item)
> - return -EEXIST;
> -
> - hdr = uncached_entry_next(hdr);
> - }
> -
> - /* Check that we don't grow into the cached region */
> - alloc_size = sizeof(*hdr) + ALIGN(size, 8);
> - if ((void *)hdr + alloc_size >= cached) {
> - dev_err(smem->dev, "Out of memory\n");
> - return -ENOSPC;
> - }
> -
> - hdr->canary = SMEM_PRIVATE_CANARY;
> - hdr->item = cpu_to_le16(item);
> - hdr->size = cpu_to_le32(ALIGN(size, 8));
> - hdr->padding_data = cpu_to_le16(le32_to_cpu(hdr->size) - size);
> - hdr->padding_hdr = 0;
> -
> - /*
> - * Ensure the header is written before we advance the free offset, so
> - * that remote processors that does not take the remote spinlock still
> - * gets a consistent view of the linked list.
> - */
> - dmb();
> - le32_add_cpu(&phdr->offset_free_uncached, alloc_size);
> -
> - return 0;
> -}
> -
> -static int qcom_smem_alloc_global(struct qcom_smem *smem,
> - unsigned int item,
> - size_t size)
> -{
> - struct smem_global_entry *entry;
> - struct smem_header *header;
> -
> - header = smem->regions[0].virt_base;
> - entry = &header->toc[item];
> - if (entry->allocated)
> - return -EEXIST;
> -
> - size = ALIGN(size, 8);
> - if (WARN_ON(size > le32_to_cpu(header->available)))
> - return -ENOMEM;
> -
> - entry->offset = header->free_offset;
> - entry->size = cpu_to_le32(size);
> -
> - /*
> - * Ensure the header is consistent before we mark the item allocated,
> - * so that remote processors will get a consistent view of the item
> - * even though they do not take the spinlock on read.
> - */
> - dmb();
> - entry->allocated = cpu_to_le32(1);
> -
> - le32_add_cpu(&header->free_offset, size);
> - le32_add_cpu(&header->available, -size);
> -
> - return 0;
> -}
> -
> -/**
> - * qcom_smem_alloc() - allocate space for a smem item
> - * @host: remote processor id, or -1
> - * @item: smem item handle
> - * @size: number of bytes to be allocated
> - *
> - * Allocate space for a given smem item of size @size, given that the item is
> - * not yet allocated.
> - */
> -static int qcom_smem_alloc(unsigned int host, unsigned int item, size_t size)
> -{
> - struct smem_partition_header *phdr;
> - int ret;
> -
> - if (!__smem)
> - return -ENOMEM;
> -
> - if (item < SMEM_ITEM_LAST_FIXED) {
> - dev_err(__smem->dev,
> - "Rejecting allocation of static entry %d\n", item);
> - return -EINVAL;
> - }
> -
> - if (WARN_ON(item >= __smem->item_count))
> - return -EINVAL;
> -
> - if (host < SMEM_HOST_COUNT && __smem->partitions[host]) {
> - phdr = __smem->partitions[host];
> - ret = qcom_smem_alloc_private(__smem, phdr, item, size);
> - } else if (__smem->global_partition) {
> - phdr = __smem->global_partition;
> - ret = qcom_smem_alloc_private(__smem, phdr, item, size);
> - } else {
> - ret = qcom_smem_alloc_global(__smem, item, size);
> - }
> -
> - return ret;
> -}
> -
> -static void *qcom_smem_get_global(struct qcom_smem *smem,
> - unsigned int item,
> - size_t *size)
> -{
> - struct smem_header *header;
> - struct smem_region *area;
> - struct smem_global_entry *entry;
> - u32 aux_base;
> - unsigned int i;
> -
> - header = smem->regions[0].virt_base;
> - entry = &header->toc[item];
> - if (!entry->allocated)
> - return ERR_PTR(-ENXIO);
> -
> - aux_base = le32_to_cpu(entry->aux_base) & AUX_BASE_MASK;
> -
> - for (i = 0; i < smem->num_regions; i++) {
> - area = &smem->regions[i];
> -
> - if (area->aux_base == aux_base || !aux_base) {
> - if (size != NULL)
> - *size = le32_to_cpu(entry->size);
> - return area->virt_base + le32_to_cpu(entry->offset);
> - }
> - }
> -
> - return ERR_PTR(-ENOENT);
> -}
> -
> -static void *qcom_smem_get_private(struct qcom_smem *smem,
> - struct smem_partition_header *phdr,
> - size_t cacheline,
> - unsigned int item,
> - size_t *size)
> -{
> - struct smem_private_entry *e, *end;
> -
> - e = phdr_to_first_uncached_entry(phdr);
> - end = phdr_to_last_uncached_entry(phdr);
> -
> - while (e < end) {
> - if (e->canary != SMEM_PRIVATE_CANARY)
> - goto invalid_canary;
> -
> - if (le16_to_cpu(e->item) == item) {
> - if (size != NULL)
> - *size = le32_to_cpu(e->size) -
> - le16_to_cpu(e->padding_data);
> -
> - return uncached_entry_to_item(e);
> - }
> -
> - e = uncached_entry_next(e);
> - }
> -
> - /* Item was not found in the uncached list, search the cached list */
> -
> - e = phdr_to_first_cached_entry(phdr, cacheline);
> - end = phdr_to_last_cached_entry(phdr);
> -
> - while (e > end) {
> - if (e->canary != SMEM_PRIVATE_CANARY)
> - goto invalid_canary;
> -
> - if (le16_to_cpu(e->item) == item) {
> - if (size != NULL)
> - *size = le32_to_cpu(e->size) -
> - le16_to_cpu(e->padding_data);
> -
> - return cached_entry_to_item(e);
> - }
> -
> - e = cached_entry_next(e, cacheline);
> - }
> -
> - return ERR_PTR(-ENOENT);
> -
> -invalid_canary:
> - dev_err(smem->dev, "Found invalid canary in hosts %d:%d partition\n",
> - phdr->host0, phdr->host1);
> -
> - return ERR_PTR(-EINVAL);
> -}
> -
> -/**
> - * qcom_smem_get() - resolve ptr of size of a smem item
> - * @host: the remote processor, or -1
> - * @item: smem item handle
> - * @size: pointer to be filled out with size of the item
> - *
> - * Looks up smem item and returns pointer to it. Size of smem
> - * item is returned in @size.
> - */
> -static void *qcom_smem_get(unsigned int host, unsigned int item, size_t *size)
> -{
> - struct smem_partition_header *phdr;
> - size_t cacheln;
> - void *ptr = ERR_PTR(-ENOMEM);
> -
> - if (!__smem)
> - return ptr;
> -
> - if (WARN_ON(item >= __smem->item_count))
> - return ERR_PTR(-EINVAL);
> -
> - if (host < SMEM_HOST_COUNT && __smem->partitions[host]) {
> - phdr = __smem->partitions[host];
> - cacheln = __smem->cacheline[host];
> - ptr = qcom_smem_get_private(__smem, phdr, cacheln, item, size);
> - } else if (__smem->global_partition) {
> - phdr = __smem->global_partition;
> - cacheln = __smem->global_cacheline;
> - ptr = qcom_smem_get_private(__smem, phdr, cacheln, item, size);
> - } else {
> - ptr = qcom_smem_get_global(__smem, item, size);
> - }
> -
> - return ptr;
> -
> -}
> -
> -/**
> - * qcom_smem_get_free_space() - retrieve amount of free space in a partition
> - * @host: the remote processor identifying a partition, or -1
> - *
> - * To be used by smem clients as a quick way to determine if any new
> - * allocations has been made.
> - */
> -static int qcom_smem_get_free_space(unsigned int host)
> -{
> - struct smem_partition_header *phdr;
> - struct smem_header *header;
> - unsigned int ret;
> -
> - if (!__smem)
> - return -ENOMEM;
> -
> - if (host < SMEM_HOST_COUNT && __smem->partitions[host]) {
> - phdr = __smem->partitions[host];
> - ret = le32_to_cpu(phdr->offset_free_cached) -
> - le32_to_cpu(phdr->offset_free_uncached);
> - } else if (__smem->global_partition) {
> - phdr = __smem->global_partition;
> - ret = le32_to_cpu(phdr->offset_free_cached) -
> - le32_to_cpu(phdr->offset_free_uncached);
> - } else {
> - header = __smem->regions[0].virt_base;
> - ret = le32_to_cpu(header->available);
> - }
> -
> - return ret;
> -}
> -
> -static int qcom_smem_get_sbl_version(struct qcom_smem *smem)
> -{
> - struct smem_header *header;
> - __le32 *versions;
> -
> - header = smem->regions[0].virt_base;
> - versions = header->version;
> -
> - return le32_to_cpu(versions[SMEM_MASTER_SBL_VERSION_INDEX]);
> -}
> -
> -static struct smem_ptable *qcom_smem_get_ptable(struct qcom_smem *smem)
> -{
> - struct smem_ptable *ptable;
> - u32 version;
> -
> - ptable = smem->regions[0].virt_base + smem->regions[0].size - SZ_4K;
> - if (memcmp(ptable->magic, SMEM_PTABLE_MAGIC, sizeof(ptable->magic)))
> - return ERR_PTR(-ENOENT);
> -
> - version = le32_to_cpu(ptable->version);
> - if (version != 1) {
> - dev_err(smem->dev,
> - "Unsupported partition header version %d\n", version);
> - return ERR_PTR(-EINVAL);
> - }
> - return ptable;
> -}
> -
> -static u32 qcom_smem_get_item_count(struct qcom_smem *smem)
> -{
> - struct smem_ptable *ptable;
> - struct smem_info *info;
> -
> - ptable = qcom_smem_get_ptable(smem);
> - if (IS_ERR_OR_NULL(ptable))
> - return SMEM_ITEM_COUNT;
> -
> - info = (struct smem_info *)&ptable->entry[ptable->num_entries];
> - if (memcmp(info->magic, SMEM_INFO_MAGIC, sizeof(info->magic)))
> - return SMEM_ITEM_COUNT;
> -
> - return le16_to_cpu(info->num_items);
> -}
> -
> -static int qcom_smem_set_global_partition(struct qcom_smem *smem)
> -{
> - struct smem_partition_header *header;
> - struct smem_ptable_entry *entry = NULL;
> - struct smem_ptable *ptable;
> - u32 host0, host1, size;
> - int i;
> -
> - ptable = qcom_smem_get_ptable(smem);
> - if (IS_ERR(ptable))
> - return PTR_ERR(ptable);
> -
> - for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
> - entry = &ptable->entry[i];
> - host0 = le16_to_cpu(entry->host0);
> - host1 = le16_to_cpu(entry->host1);
> -
> - if (host0 == SMEM_GLOBAL_HOST && host0 == host1)
> - break;
> - }
> -
> - if (!entry) {
> - dev_err(smem->dev, "Missing entry for global partition\n");
> - return -EINVAL;
> - }
> -
> - if (!le32_to_cpu(entry->offset) || !le32_to_cpu(entry->size)) {
> - dev_err(smem->dev, "Invalid entry for global partition\n");
> - return -EINVAL;
> - }
> -
> - if (smem->global_partition) {
> - dev_err(smem->dev, "Already found the global partition\n");
> - return -EINVAL;
> - }
> -
> - header = smem->regions[0].virt_base + le32_to_cpu(entry->offset);
> - host0 = le16_to_cpu(header->host0);
> - host1 = le16_to_cpu(header->host1);
> -
> - if (memcmp(header->magic, SMEM_PART_MAGIC, sizeof(header->magic))) {
> - dev_err(smem->dev, "Global partition has invalid magic\n");
> - return -EINVAL;
> - }
> -
> - if (host0 != SMEM_GLOBAL_HOST && host1 != SMEM_GLOBAL_HOST) {
> - dev_err(smem->dev, "Global partition hosts are invalid\n");
> - return -EINVAL;
> - }
> -
> - if (le32_to_cpu(header->size) != le32_to_cpu(entry->size)) {
> - dev_err(smem->dev, "Global partition has invalid size\n");
> - return -EINVAL;
> - }
> -
> - size = le32_to_cpu(header->offset_free_uncached);
> - if (size > le32_to_cpu(header->size)) {
> - dev_err(smem->dev,
> - "Global partition has invalid free pointer\n");
> - return -EINVAL;
> - }
> -
> - smem->global_partition = header;
> - smem->global_cacheline = le32_to_cpu(entry->cacheline);
> -
> - return 0;
> -}
> -
> -static int qcom_smem_enumerate_partitions(struct qcom_smem *smem,
> - unsigned int local_host)
> -{
> - struct smem_partition_header *header;
> - struct smem_ptable_entry *entry;
> - struct smem_ptable *ptable;
> - unsigned int remote_host;
> - u32 host0, host1;
> - int i;
> -
> - ptable = qcom_smem_get_ptable(smem);
> - if (IS_ERR(ptable))
> - return PTR_ERR(ptable);
> -
> - for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
> - entry = &ptable->entry[i];
> - host0 = le16_to_cpu(entry->host0);
> - host1 = le16_to_cpu(entry->host1);
> -
> - if (host0 != local_host && host1 != local_host)
> - continue;
> -
> - if (!le32_to_cpu(entry->offset))
> - continue;
> -
> - if (!le32_to_cpu(entry->size))
> - continue;
> -
> - if (host0 == local_host)
> - remote_host = host1;
> - else
> - remote_host = host0;
> -
> - if (remote_host >= SMEM_HOST_COUNT) {
> - dev_err(smem->dev,
> - "Invalid remote host %d\n",
> - remote_host);
> - return -EINVAL;
> - }
> -
> - if (smem->partitions[remote_host]) {
> - dev_err(smem->dev,
> - "Already found a partition for host %d\n",
> - remote_host);
> - return -EINVAL;
> - }
> -
> - header = smem->regions[0].virt_base + le32_to_cpu(entry->offset);
> - host0 = le16_to_cpu(header->host0);
> - host1 = le16_to_cpu(header->host1);
> -
> - if (memcmp(header->magic, SMEM_PART_MAGIC,
> - sizeof(header->magic))) {
> - dev_err(smem->dev,
> - "Partition %d has invalid magic\n", i);
> - return -EINVAL;
> - }
> -
> - if (host0 != local_host && host1 != local_host) {
> - dev_err(smem->dev,
> - "Partition %d hosts are invalid\n", i);
> - return -EINVAL;
> - }
> -
> - if (host0 != remote_host && host1 != remote_host) {
> - dev_err(smem->dev,
> - "Partition %d hosts are invalid\n", i);
> - return -EINVAL;
> - }
> -
> - if (le32_to_cpu(header->size) != le32_to_cpu(entry->size)) {
> - dev_err(smem->dev,
> - "Partition %d has invalid size\n", i);
> - return -EINVAL;
> - }
> -
> - if (le32_to_cpu(header->offset_free_uncached) > le32_to_cpu(header->size)) {
> - dev_err(smem->dev,
> - "Partition %d has invalid free pointer\n", i);
> - return -EINVAL;
> - }
> -
> - smem->partitions[remote_host] = header;
> - smem->cacheline[remote_host] = le32_to_cpu(entry->cacheline);
> - }
> -
> - return 0;
> -}
> -
> -static int qcom_smem_map_memory(struct qcom_smem *smem, struct udevice *dev,
> - const char *name, int i)
> -{
> - int ret;
> - struct ofnode_phandle_args args;
> - struct resource r;
> -
> - if (!dev_read_prop(dev, name, NULL)) {
> - dev_err(dev, "%s prop not found\n", name);
> - return -EINVAL;
> - }
> -
> - ret = dev_read_phandle_with_args(dev, name, NULL, 0, 0, &args);
> - if (ret) {
> - dev_err(dev, "%s phandle read failed\n", name);
> - return -EINVAL;
> - }
> -
> - if (!ofnode_valid(args.node)) {
> - dev_err(dev, "Invalid node from phandle args\n");
> - return -EINVAL;
> - }
> -
> - ret = ofnode_read_resource(args.node, 0, &r);
> - if (ret) {
> - dev_err(dev, "Can't get mmap base address(%d)\n", ret);
> - return ret;
> - }
> - smem->regions[i].aux_base = (u32)r.start;
> - smem->regions[i].size = resource_size(&r);
> - smem->regions[i].virt_base = devm_ioremap(dev, r.start, resource_size(&r));
> - if (!smem->regions[i].virt_base)
> - return -ENOMEM;
> -
> - return 0;
> -}
> -
> -static int qcom_smem_probe(struct udevice *dev)
> -{
> - struct smem_header *header;
> - struct qcom_smem *smem;
> - size_t array_size;
> - int num_regions;
> - u32 version;
> - int ret;
> - fdt_addr_t addr;
> - fdt_size_t size;
> -
> - if (__smem)
> - return 0;
> -
> - num_regions = 1;
> - if (dev_read_prop(dev, "qcom,rpm-msg-ram", NULL))
> - num_regions++;
> -
> - array_size = num_regions * sizeof(struct smem_region);
> - smem = devm_kzalloc(dev, sizeof(*smem) + array_size, GFP_KERNEL);
> - if (!smem)
> - return -ENOMEM;
> -
> - smem->dev = dev;
> - smem->num_regions = num_regions;
> -
> - addr = dev_read_addr_size(dev, &size);
> - if (addr == FDT_ADDR_T_NONE) {
> - ret = qcom_smem_map_memory(smem, dev, "memory-region", 0);
> - if (ret)
> - return ret;
> - } else {
> - smem->regions[0].aux_base = (u32)addr;
> - smem->regions[0].size = size;
> - smem->regions[0].virt_base = devm_ioremap(dev, addr, size);
> - if (!smem->regions[0].virt_base)
> - return -ENOMEM;
> - }
> -
> - if (num_regions > 1) {
> - ret = qcom_smem_map_memory(smem, dev,
> - "qcom,rpm-msg-ram", 1);
> - if (ret)
> - return ret;
> - }
> -
> - header = smem->regions[0].virt_base;
> - if (le32_to_cpu(header->initialized) != 1 ||
> - le32_to_cpu(header->reserved)) {
> - dev_err(dev, "SMEM is not initialized by SBL\n");
> - return -EINVAL;
> - }
> -
> - version = qcom_smem_get_sbl_version(smem);
> - switch (version >> 16) {
> - case SMEM_GLOBAL_PART_VERSION:
> - ret = qcom_smem_set_global_partition(smem);
> - if (ret < 0)
> - return ret;
> - smem->item_count = qcom_smem_get_item_count(smem);
> - break;
> - case SMEM_GLOBAL_HEAP_VERSION:
> - smem->item_count = SMEM_ITEM_COUNT;
> - break;
> - default:
> - dev_err(dev, "Unsupported SMEM version 0x%x\n", version);
> - return -EINVAL;
> - }
> -
> - ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS);
> - if (ret < 0 && ret != -ENOENT)
> - return ret;
> -
> - __smem = smem;
> -
> - return 0;
> -}
> -
> -static int qcom_smem_remove(struct udevice *dev)
> -{
> - __smem = NULL;
> -
> - return 0;
> -}
> -
> -const struct udevice_id qcom_smem_of_match[] = {
> - { .compatible = "qcom,smem" },
> - { }
> -};
> -
> -static const struct smem_ops msm_smem_ops = {
> - .alloc = qcom_smem_alloc,
> - .get = qcom_smem_get,
> - .get_free_space = qcom_smem_get_free_space,
> -};
> -
> -U_BOOT_DRIVER(qcom_smem) = {
> - .name = "qcom_smem",
> - .id = UCLASS_SMEM,
> - .of_match = qcom_smem_of_match,
> - .ops = &msm_smem_ops,
> - .probe = qcom_smem_probe,
> - .remove = qcom_smem_remove,
> -};
> diff --git a/drivers/smem/sandbox_smem.c b/drivers/smem/sandbox_smem.c
> deleted file mode 100644
> index fec98e5611d6..000000000000
> --- a/drivers/smem/sandbox_smem.c
> +++ /dev/null
> @@ -1,44 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0+
> -/*
> - * Copyright (c) 2018 Ramon Fried <ramon.fried@gmail.com>
> - */
> -
> -#include <dm.h>
> -#include <errno.h>
> -#include <smem.h>
> -#include <asm/test.h>
> -
> -static int sandbox_smem_alloc(unsigned int host,
> - unsigned int item, size_t size)
> -{
> - return 0;
> -}
> -
> -static void *sandbox_smem_get(unsigned int host,
> - unsigned int item, size_t *size)
> -{
> - return NULL;
> -}
> -
> -static int sandbox_smem_get_free_space(unsigned int host)
> -{
> - return 0;
> -}
> -
> -static const struct smem_ops sandbox_smem_ops = {
> - .alloc = sandbox_smem_alloc,
> - .get = sandbox_smem_get,
> - .get_free_space = sandbox_smem_get_free_space,
> -};
> -
> -static const struct udevice_id sandbox_smem_ids[] = {
> - { .compatible = "sandbox,smem" },
> - { }
> -};
> -
> -U_BOOT_DRIVER(smem_sandbox) = {
> - .name = "smem_sandbox",
> - .id = UCLASS_SMEM,
> - .of_match = sandbox_smem_ids,
> - .ops = &sandbox_smem_ops,
> -};
>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Thanks,
Neil
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 03/15] Revert "test: smem: add basic smem test"
2026-05-04 18:57 ` [PATCH v2 03/15] Revert "test: smem: add basic smem test" Casey Connolly
2026-05-05 10:26 ` Sumit Garg
@ 2026-05-18 14:34 ` Neil Armstrong
1 sibling, 0 replies; 51+ messages in thread
From: Neil Armstrong @ 2026-05-18 14:34 UTC (permalink / raw)
To: Casey Connolly, u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Peng Fan, Marek Vasut, Alice Guo,
Quentin Schulz, Ilias Apalodimas, Mattijs Korpershoek,
Kuan-Wei Chiu, Raymond Mao, Stefan Roese, Philip Molloy,
Jerome Forissier, Marek Vasut, Varadarajan Narayanan,
Patrice Chotard, Aswin Murugan, Rasmus Villemoes, Heiko Schocher,
Michal Simek, Sughosh Ganu, Antony Kurniawan Soemardi, Luca Weiss,
Balaji Selvanathan
On 5/4/26 20:57, Casey Connolly wrote:
> This reverts commit 20e7705764c4e5ea924f1ea54bb36ebbbeffffe7.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> test/dm/Makefile | 1 -
> test/dm/smem.c | 26 --------------------------
> 2 files changed, 27 deletions(-)
>
> diff --git a/test/dm/Makefile b/test/dm/Makefile
> index 771b703b737d..ace149d4a2b4 100644
> --- a/test/dm/Makefile
> +++ b/test/dm/Makefile
> @@ -111,9 +111,8 @@ obj-$(CONFIG_SCSI) += scsi.o
> obj-$(CONFIG_DM_SERIAL) += serial.o
> obj-$(CONFIG_DM_SPI_FLASH) += sf.o
> obj-$(CONFIG_SIMPLE_BUS) += simple-bus.o
> obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o
> -obj-$(CONFIG_SMEM) += smem.o
> obj-$(CONFIG_SOC_DEVICE) += soc.o
> obj-$(CONFIG_SOUND) += sound.o
> obj-$(CONFIG_DM_SPI) += spi.o
> obj-$(CONFIG_SPMI) += spmi.o
> diff --git a/test/dm/smem.c b/test/dm/smem.c
> deleted file mode 100644
> index 89e74cccc574..000000000000
> --- a/test/dm/smem.c
> +++ /dev/null
> @@ -1,26 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0+
> -/*
> - * Copyright (C) 2018 Ramon Fried <ramon.fried@gmail.com>
> - */
> -
> -#include <dm.h>
> -#include <smem.h>
> -#include <dm/test.h>
> -#include <test/test.h>
> -#include <test/ut.h>
> -
> -/* Basic test of the smem uclass */
> -static int dm_test_smem_base(struct unit_test_state *uts)
> -{
> - struct udevice *dev;
> - size_t size;
> -
> - ut_assertok(uclass_get_device(UCLASS_SMEM, 0, &dev));
> - ut_assertnonnull(dev);
> - ut_assertok(smem_alloc(dev, -1, 0, 16));
> - ut_asserteq(0, smem_get_free_space(dev, -1));
> - ut_assertnull(smem_get(dev, -1, 0, &size));
> -
> - return 0;
> -}
> -DM_TEST(dm_test_smem_base, UTF_SCAN_PDATA | UTF_SCAN_FDT);
>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Thanks,
Neil
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 04/15] Revert "drivers: smem: sandbox"
2026-05-04 18:57 ` [PATCH v2 04/15] Revert "drivers: smem: sandbox" Casey Connolly
2026-05-05 10:26 ` Sumit Garg
@ 2026-05-18 14:34 ` Neil Armstrong
2026-05-18 15:51 ` Fabio Estevam
2 siblings, 0 replies; 51+ messages in thread
From: Neil Armstrong @ 2026-05-18 14:34 UTC (permalink / raw)
To: Casey Connolly, u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Peng Fan, Marek Vasut, Alice Guo,
Quentin Schulz, Ilias Apalodimas, Mattijs Korpershoek,
Kuan-Wei Chiu, Raymond Mao, Stefan Roese, Philip Molloy,
Jerome Forissier, Marek Vasut, Varadarajan Narayanan,
Patrice Chotard, Aswin Murugan, Rasmus Villemoes, Heiko Schocher,
Michal Simek, Sughosh Ganu, Antony Kurniawan Soemardi, Luca Weiss,
Balaji Selvanathan
On 5/4/26 20:57, Casey Connolly wrote:
> This reverts commit 7fd7e2cf339ea2ec570f6dae1cdfaf8e066eb4af.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> arch/sandbox/dts/test.dts | 4 ----
> configs/sandbox64_defconfig | 2 --
> configs/sandbox_defconfig | 2 --
> 3 files changed, 8 deletions(-)
>
> diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
> index ac92ebf1afd7..50201ad61ccd 100644
> --- a/arch/sandbox/dts/test.dts
> +++ b/arch/sandbox/dts/test.dts
> @@ -1521,12 +1521,8 @@
> compatible = "sandbox,scsi";
> sandbox,filepath = "scsi.img";
> };
>
> - smem@0 {
> - compatible = "sandbox,smem";
> - };
> -
> sound {
> compatible = "sandbox,sound";
> cpu {
> sound-dai = <&i2s 0>;
> diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig
> index 60a30d048750..53f7af4270af 100644
> --- a/configs/sandbox64_defconfig
> +++ b/configs/sandbox64_defconfig
> @@ -240,10 +240,8 @@ CONFIG_SANDBOX_RESET=y
> CONFIG_DM_RTC=y
> CONFIG_RTC_RV8803=y
> CONFIG_SCSI=y
> CONFIG_SANDBOX_SERIAL=y
> -CONFIG_SMEM=y
> -CONFIG_SANDBOX_SMEM=y
> CONFIG_SOUND=y
> CONFIG_SOUND_SANDBOX=y
> CONFIG_SOC_DEVICE=y
> CONFIG_SANDBOX_SPI=y
> diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
> index f26295103f1c..7f26405e6ed0 100644
> --- a/configs/sandbox_defconfig
> +++ b/configs/sandbox_defconfig
> @@ -331,10 +331,8 @@ CONFIG_RTC_RV8803=y
> CONFIG_RTC_HT1380=y
> CONFIG_SCSI=y
> CONFIG_SANDBOX_SERIAL=y
> CONFIG_SANDBOX_SM=y
> -CONFIG_SMEM=y
> -CONFIG_SANDBOX_SMEM=y
> CONFIG_SOUND=y
> CONFIG_SOUND_DA7219=y
> CONFIG_SOUND_MAX98357A=y
> CONFIG_SOUND_SANDBOX=y
>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Thanks,
Neil
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 06/15] soc: qcom: import smem from Linux 6.11-rc2
2026-05-04 18:57 ` [PATCH v2 06/15] soc: qcom: import smem from Linux 6.11-rc2 Casey Connolly
2026-05-05 12:40 ` Sumit Garg
@ 2026-05-18 14:36 ` Neil Armstrong
1 sibling, 0 replies; 51+ messages in thread
From: Neil Armstrong @ 2026-05-18 14:36 UTC (permalink / raw)
To: Casey Connolly, u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Peng Fan, Marek Vasut, Alice Guo,
Quentin Schulz, Ilias Apalodimas, Mattijs Korpershoek,
Kuan-Wei Chiu, Raymond Mao, Stefan Roese, Philip Molloy,
Jerome Forissier, Marek Vasut, Varadarajan Narayanan,
Patrice Chotard, Aswin Murugan, Rasmus Villemoes, Heiko Schocher,
Michal Simek, Sughosh Ganu, Antony Kurniawan Soemardi, Luca Weiss,
Balaji Selvanathan
On 5/4/26 20:57, Casey Connolly wrote:
> Import the SMEM driver from Linux.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> drivers/soc/qcom/smem.c | 1279 ++++++++++++++++++++++++++++++++++++++++++++
> include/soc/qcom/smem.h | 20 +
> include/soc/qcom/socinfo.h | 111 ++++
> 3 files changed, 1410 insertions(+)
>
> diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
> new file mode 100644
> index 000000000000..8515b8ae7777
> --- /dev/null
> +++ b/drivers/soc/qcom/smem.c
> @@ -0,0 +1,1279 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2015, Sony Mobile Communications AB.
> + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
> + */
> +
> +#include <linux/hwspinlock.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_reserved_mem.h>
> +#include <linux/platform_device.h>
> +#include <linux/sizes.h>
> +#include <linux/slab.h>
> +#include <linux/soc/qcom/smem.h>
> +#include <linux/soc/qcom/socinfo.h>
> +
> +/*
> + * The Qualcomm shared memory system is a allocate only heap structure that
> + * consists of one of more memory areas that can be accessed by the processors
> + * in the SoC.
> + *
> + * All systems contains a global heap, accessible by all processors in the SoC,
> + * with a table of contents data structure (@smem_header) at the beginning of
> + * the main shared memory block.
> + *
> + * The global header contains meta data for allocations as well as a fixed list
> + * of 512 entries (@smem_global_entry) that can be initialized to reference
> + * parts of the shared memory space.
> + *
> + *
> + * In addition to this global heap a set of "private" heaps can be set up at
> + * boot time with access restrictions so that only certain processor pairs can
> + * access the data.
> + *
> + * These partitions are referenced from an optional partition table
> + * (@smem_ptable), that is found 4kB from the end of the main smem region. The
> + * partition table entries (@smem_ptable_entry) lists the involved processors
> + * (or hosts) and their location in the main shared memory region.
> + *
> + * Each partition starts with a header (@smem_partition_header) that identifies
> + * the partition and holds properties for the two internal memory regions. The
> + * two regions are cached and non-cached memory respectively. Each region
> + * contain a link list of allocation headers (@smem_private_entry) followed by
> + * their data.
> + *
> + * Items in the non-cached region are allocated from the start of the partition
> + * while items in the cached region are allocated from the end. The free area
> + * is hence the region between the cached and non-cached offsets. The header of
> + * cached items comes after the data.
> + *
> + * Version 12 (SMEM_GLOBAL_PART_VERSION) changes the item alloc/get procedure
> + * for the global heap. A new global partition is created from the global heap
> + * region with partition type (SMEM_GLOBAL_HOST) and the max smem item count is
> + * set by the bootloader.
> + *
> + * To synchronize allocations in the shared memory heaps a remote spinlock must
> + * be held - currently lock number 3 of the sfpb or tcsr is used for this on all
> + * platforms.
> + *
> + */
> +
> +/*
> + * The version member of the smem header contains an array of versions for the
> + * various software components in the SoC. We verify that the boot loader
> + * version is a valid version as a sanity check.
> + */
> +#define SMEM_MASTER_SBL_VERSION_INDEX 7
> +#define SMEM_GLOBAL_HEAP_VERSION 11
> +#define SMEM_GLOBAL_PART_VERSION 12
> +
> +/*
> + * The first 8 items are only to be allocated by the boot loader while
> + * initializing the heap.
> + */
> +#define SMEM_ITEM_LAST_FIXED 8
> +
> +/* Highest accepted item number, for both global and private heaps */
> +#define SMEM_ITEM_COUNT 512
> +
> +/* Processor/host identifier for the application processor */
> +#define SMEM_HOST_APPS 0
> +
> +/* Processor/host identifier for the global partition */
> +#define SMEM_GLOBAL_HOST 0xfffe
> +
> +/* Max number of processors/hosts in a system */
> +#define SMEM_HOST_COUNT 20
> +
> +/**
> + * struct smem_proc_comm - proc_comm communication struct (legacy)
> + * @command: current command to be executed
> + * @status: status of the currently requested command
> + * @params: parameters to the command
> + */
> +struct smem_proc_comm {
> + __le32 command;
> + __le32 status;
> + __le32 params[2];
> +};
> +
> +/**
> + * struct smem_global_entry - entry to reference smem items on the heap
> + * @allocated: boolean to indicate if this entry is used
> + * @offset: offset to the allocated space
> + * @size: size of the allocated space, 8 byte aligned
> + * @aux_base: base address for the memory region used by this unit, or 0 for
> + * the default region. bits 0,1 are reserved
> + */
> +struct smem_global_entry {
> + __le32 allocated;
> + __le32 offset;
> + __le32 size;
> + __le32 aux_base; /* bits 1:0 reserved */
> +};
> +#define AUX_BASE_MASK 0xfffffffc
> +
> +/**
> + * struct smem_header - header found in beginning of primary smem region
> + * @proc_comm: proc_comm communication interface (legacy)
> + * @version: array of versions for the various subsystems
> + * @initialized: boolean to indicate that smem is initialized
> + * @free_offset: index of the first unallocated byte in smem
> + * @available: number of bytes available for allocation
> + * @reserved: reserved field, must be 0
> + * @toc: array of references to items
> + */
> +struct smem_header {
> + struct smem_proc_comm proc_comm[4];
> + __le32 version[32];
> + __le32 initialized;
> + __le32 free_offset;
> + __le32 available;
> + __le32 reserved;
> + struct smem_global_entry toc[SMEM_ITEM_COUNT];
> +};
> +
> +/**
> + * struct smem_ptable_entry - one entry in the @smem_ptable list
> + * @offset: offset, within the main shared memory region, of the partition
> + * @size: size of the partition
> + * @flags: flags for the partition (currently unused)
> + * @host0: first processor/host with access to this partition
> + * @host1: second processor/host with access to this partition
> + * @cacheline: alignment for "cached" entries
> + * @reserved: reserved entries for later use
> + */
> +struct smem_ptable_entry {
> + __le32 offset;
> + __le32 size;
> + __le32 flags;
> + __le16 host0;
> + __le16 host1;
> + __le32 cacheline;
> + __le32 reserved[7];
> +};
> +
> +/**
> + * struct smem_ptable - partition table for the private partitions
> + * @magic: magic number, must be SMEM_PTABLE_MAGIC
> + * @version: version of the partition table
> + * @num_entries: number of partitions in the table
> + * @reserved: for now reserved entries
> + * @entry: list of @smem_ptable_entry for the @num_entries partitions
> + */
> +struct smem_ptable {
> + u8 magic[4];
> + __le32 version;
> + __le32 num_entries;
> + __le32 reserved[5];
> + struct smem_ptable_entry entry[];
> +};
> +
> +static const u8 SMEM_PTABLE_MAGIC[] = { 0x24, 0x54, 0x4f, 0x43 }; /* "$TOC" */
> +
> +/**
> + * struct smem_partition_header - header of the partitions
> + * @magic: magic number, must be SMEM_PART_MAGIC
> + * @host0: first processor/host with access to this partition
> + * @host1: second processor/host with access to this partition
> + * @size: size of the partition
> + * @offset_free_uncached: offset to the first free byte of uncached memory in
> + * this partition
> + * @offset_free_cached: offset to the first free byte of cached memory in this
> + * partition
> + * @reserved: for now reserved entries
> + */
> +struct smem_partition_header {
> + u8 magic[4];
> + __le16 host0;
> + __le16 host1;
> + __le32 size;
> + __le32 offset_free_uncached;
> + __le32 offset_free_cached;
> + __le32 reserved[3];
> +};
> +
> +/**
> + * struct smem_partition - describes smem partition
> + * @virt_base: starting virtual address of partition
> + * @phys_base: starting physical address of partition
> + * @cacheline: alignment for "cached" entries
> + * @size: size of partition
> + */
> +struct smem_partition {
> + void __iomem *virt_base;
> + phys_addr_t phys_base;
> + size_t cacheline;
> + size_t size;
> +};
> +
> +static const u8 SMEM_PART_MAGIC[] = { 0x24, 0x50, 0x52, 0x54 };
> +
> +/**
> + * struct smem_private_entry - header of each item in the private partition
> + * @canary: magic number, must be SMEM_PRIVATE_CANARY
> + * @item: identifying number of the smem item
> + * @size: size of the data, including padding bytes
> + * @padding_data: number of bytes of padding of data
> + * @padding_hdr: number of bytes of padding between the header and the data
> + * @reserved: for now reserved entry
> + */
> +struct smem_private_entry {
> + u16 canary; /* bytes are the same so no swapping needed */
> + __le16 item;
> + __le32 size; /* includes padding bytes */
> + __le16 padding_data;
> + __le16 padding_hdr;
> + __le32 reserved;
> +};
> +#define SMEM_PRIVATE_CANARY 0xa5a5
> +
> +/**
> + * struct smem_info - smem region info located after the table of contents
> + * @magic: magic number, must be SMEM_INFO_MAGIC
> + * @size: size of the smem region
> + * @base_addr: base address of the smem region
> + * @reserved: for now reserved entry
> + * @num_items: highest accepted item number
> + */
> +struct smem_info {
> + u8 magic[4];
> + __le32 size;
> + __le32 base_addr;
> + __le32 reserved;
> + __le16 num_items;
> +};
> +
> +static const u8 SMEM_INFO_MAGIC[] = { 0x53, 0x49, 0x49, 0x49 }; /* SIII */
> +
> +/**
> + * struct smem_region - representation of a chunk of memory used for smem
> + * @aux_base: identifier of aux_mem base
> + * @virt_base: virtual base address of memory with this aux_mem identifier
> + * @size: size of the memory region
> + */
> +struct smem_region {
> + phys_addr_t aux_base;
> + void __iomem *virt_base;
> + size_t size;
> +};
> +
> +/**
> + * struct qcom_smem - device data for the smem device
> + * @dev: device pointer
> + * @hwlock: reference to a hwspinlock
> + * @ptable: virtual base of partition table
> + * @global_partition: describes for global partition when in use
> + * @partitions: list of partitions of current processor/host
> + * @item_count: max accepted item number
> + * @socinfo: platform device pointer
> + * @num_regions: number of @regions
> + * @regions: list of the memory regions defining the shared memory
> + */
> +struct qcom_smem {
> + struct device *dev;
> +
> + struct hwspinlock *hwlock;
> +
> + u32 item_count;
> + struct platform_device *socinfo;
> + struct smem_ptable *ptable;
> + struct smem_partition global_partition;
> + struct smem_partition partitions[SMEM_HOST_COUNT];
> +
> + unsigned num_regions;
> + struct smem_region regions[] __counted_by(num_regions);
> +};
> +
> +static void *
> +phdr_to_last_uncached_entry(struct smem_partition_header *phdr)
> +{
> + void *p = phdr;
> +
> + return p + le32_to_cpu(phdr->offset_free_uncached);
> +}
> +
> +static struct smem_private_entry *
> +phdr_to_first_cached_entry(struct smem_partition_header *phdr,
> + size_t cacheline)
> +{
> + void *p = phdr;
> + struct smem_private_entry *e;
> +
> + return p + le32_to_cpu(phdr->size) - ALIGN(sizeof(*e), cacheline);
> +}
> +
> +static void *
> +phdr_to_last_cached_entry(struct smem_partition_header *phdr)
> +{
> + void *p = phdr;
> +
> + return p + le32_to_cpu(phdr->offset_free_cached);
> +}
> +
> +static struct smem_private_entry *
> +phdr_to_first_uncached_entry(struct smem_partition_header *phdr)
> +{
> + void *p = phdr;
> +
> + return p + sizeof(*phdr);
> +}
> +
> +static struct smem_private_entry *
> +uncached_entry_next(struct smem_private_entry *e)
> +{
> + void *p = e;
> +
> + return p + sizeof(*e) + le16_to_cpu(e->padding_hdr) +
> + le32_to_cpu(e->size);
> +}
> +
> +static struct smem_private_entry *
> +cached_entry_next(struct smem_private_entry *e, size_t cacheline)
> +{
> + void *p = e;
> +
> + return p - le32_to_cpu(e->size) - ALIGN(sizeof(*e), cacheline);
> +}
> +
> +static void *uncached_entry_to_item(struct smem_private_entry *e)
> +{
> + void *p = e;
> +
> + return p + sizeof(*e) + le16_to_cpu(e->padding_hdr);
> +}
> +
> +static void *cached_entry_to_item(struct smem_private_entry *e)
> +{
> + void *p = e;
> +
> + return p - le32_to_cpu(e->size);
> +}
> +
> +/* Pointer to the one and only smem handle */
> +static struct qcom_smem *__smem;
> +
> +/* Timeout (ms) for the trylock of remote spinlocks */
> +#define HWSPINLOCK_TIMEOUT 1000
> +
> +/* The qcom hwspinlock id is always plus one from the smem host id */
> +#define SMEM_HOST_ID_TO_HWSPINLOCK_ID(__x) ((__x) + 1)
> +
> +/**
> + * qcom_smem_bust_hwspin_lock_by_host() - bust the smem hwspinlock for a host
> + * @host: remote processor id
> + *
> + * Busts the hwspin_lock for the given smem host id. This helper is intended
> + * for remoteproc drivers that manage remoteprocs with an equivalent smem
> + * driver instance in the remote firmware. Drivers can force a release of the
> + * smem hwspin_lock if the rproc unexpectedly goes into a bad state.
> + *
> + * Context: Process context.
> + *
> + * Returns: 0 on success, otherwise negative errno.
> + */
> +int qcom_smem_bust_hwspin_lock_by_host(unsigned int host)
> +{
> + /* This function is for remote procs, so ignore SMEM_HOST_APPS */
> + if (host == SMEM_HOST_APPS || host >= SMEM_HOST_COUNT)
> + return -EINVAL;
> +
> + return hwspin_lock_bust(__smem->hwlock, SMEM_HOST_ID_TO_HWSPINLOCK_ID(host));
> +}
> +EXPORT_SYMBOL_GPL(qcom_smem_bust_hwspin_lock_by_host);
> +
> +/**
> + * qcom_smem_is_available() - Check if SMEM is available
> + *
> + * Return: true if SMEM is available, false otherwise.
> + */
> +bool qcom_smem_is_available(void)
> +{
> + return !!__smem;
> +}
> +EXPORT_SYMBOL_GPL(qcom_smem_is_available);
> +
> +static int qcom_smem_alloc_private(struct qcom_smem *smem,
> + struct smem_partition *part,
> + unsigned item,
> + size_t size)
> +{
> + struct smem_private_entry *hdr, *end;
> + struct smem_partition_header *phdr;
> + size_t alloc_size;
> + void *cached;
> + void *p_end;
> +
> + phdr = (struct smem_partition_header __force *)part->virt_base;
> + p_end = (void *)phdr + part->size;
> +
> + hdr = phdr_to_first_uncached_entry(phdr);
> + end = phdr_to_last_uncached_entry(phdr);
> + cached = phdr_to_last_cached_entry(phdr);
> +
> + if (WARN_ON((void *)end > p_end || cached > p_end))
> + return -EINVAL;
> +
> + while (hdr < end) {
> + if (hdr->canary != SMEM_PRIVATE_CANARY)
> + goto bad_canary;
> + if (le16_to_cpu(hdr->item) == item)
> + return -EEXIST;
> +
> + hdr = uncached_entry_next(hdr);
> + }
> +
> + if (WARN_ON((void *)hdr > p_end))
> + return -EINVAL;
> +
> + /* Check that we don't grow into the cached region */
> + alloc_size = sizeof(*hdr) + ALIGN(size, 8);
> + if ((void *)hdr + alloc_size > cached) {
> + dev_err(smem->dev, "Out of memory\n");
> + return -ENOSPC;
> + }
> +
> + hdr->canary = SMEM_PRIVATE_CANARY;
> + hdr->item = cpu_to_le16(item);
> + hdr->size = cpu_to_le32(ALIGN(size, 8));
> + hdr->padding_data = cpu_to_le16(le32_to_cpu(hdr->size) - size);
> + hdr->padding_hdr = 0;
> +
> + /*
> + * Ensure the header is written before we advance the free offset, so
> + * that remote processors that does not take the remote spinlock still
> + * gets a consistent view of the linked list.
> + */
> + wmb();
> + le32_add_cpu(&phdr->offset_free_uncached, alloc_size);
> +
> + return 0;
> +bad_canary:
> + dev_err(smem->dev, "Found invalid canary in hosts %hu:%hu partition\n",
> + le16_to_cpu(phdr->host0), le16_to_cpu(phdr->host1));
> +
> + return -EINVAL;
> +}
> +
> +static int qcom_smem_alloc_global(struct qcom_smem *smem,
> + unsigned item,
> + size_t size)
> +{
> + struct smem_global_entry *entry;
> + struct smem_header *header;
> +
> + header = smem->regions[0].virt_base;
> + entry = &header->toc[item];
> + if (entry->allocated)
> + return -EEXIST;
> +
> + size = ALIGN(size, 8);
> + if (WARN_ON(size > le32_to_cpu(header->available)))
> + return -ENOMEM;
> +
> + entry->offset = header->free_offset;
> + entry->size = cpu_to_le32(size);
> +
> + /*
> + * Ensure the header is consistent before we mark the item allocated,
> + * so that remote processors will get a consistent view of the item
> + * even though they do not take the spinlock on read.
> + */
> + wmb();
> + entry->allocated = cpu_to_le32(1);
> +
> + le32_add_cpu(&header->free_offset, size);
> + le32_add_cpu(&header->available, -size);
> +
> + return 0;
> +}
> +
> +/**
> + * qcom_smem_alloc() - allocate space for a smem item
> + * @host: remote processor id, or -1
> + * @item: smem item handle
> + * @size: number of bytes to be allocated
> + *
> + * Allocate space for a given smem item of size @size, given that the item is
> + * not yet allocated.
> + */
> +int qcom_smem_alloc(unsigned host, unsigned item, size_t size)
> +{
> + struct smem_partition *part;
> + unsigned long flags;
> + int ret;
> +
> + if (!__smem)
> + return -EPROBE_DEFER;
> +
> + if (item < SMEM_ITEM_LAST_FIXED) {
> + dev_err(__smem->dev,
> + "Rejecting allocation of static entry %d\n", item);
> + return -EINVAL;
> + }
> +
> + if (WARN_ON(item >= __smem->item_count))
> + return -EINVAL;
> +
> + ret = hwspin_lock_timeout_irqsave(__smem->hwlock,
> + HWSPINLOCK_TIMEOUT,
> + &flags);
> + if (ret)
> + return ret;
> +
> + if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
> + part = &__smem->partitions[host];
> + ret = qcom_smem_alloc_private(__smem, part, item, size);
> + } else if (__smem->global_partition.virt_base) {
> + part = &__smem->global_partition;
> + ret = qcom_smem_alloc_private(__smem, part, item, size);
> + } else {
> + ret = qcom_smem_alloc_global(__smem, item, size);
> + }
> +
> + hwspin_unlock_irqrestore(__smem->hwlock, &flags);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(qcom_smem_alloc);
> +
> +static void *qcom_smem_get_global(struct qcom_smem *smem,
> + unsigned item,
> + size_t *size)
> +{
> + struct smem_header *header;
> + struct smem_region *region;
> + struct smem_global_entry *entry;
> + u64 entry_offset;
> + u32 e_size;
> + u32 aux_base;
> + unsigned i;
> +
> + header = smem->regions[0].virt_base;
> + entry = &header->toc[item];
> + if (!entry->allocated)
> + return ERR_PTR(-ENXIO);
> +
> + aux_base = le32_to_cpu(entry->aux_base) & AUX_BASE_MASK;
> +
> + for (i = 0; i < smem->num_regions; i++) {
> + region = &smem->regions[i];
> +
> + if ((u32)region->aux_base == aux_base || !aux_base) {
> + e_size = le32_to_cpu(entry->size);
> + entry_offset = le32_to_cpu(entry->offset);
> +
> + if (WARN_ON(e_size + entry_offset > region->size))
> + return ERR_PTR(-EINVAL);
> +
> + if (size != NULL)
> + *size = e_size;
> +
> + return region->virt_base + entry_offset;
> + }
> + }
> +
> + return ERR_PTR(-ENOENT);
> +}
> +
> +static void *qcom_smem_get_private(struct qcom_smem *smem,
> + struct smem_partition *part,
> + unsigned item,
> + size_t *size)
> +{
> + struct smem_private_entry *e, *end;
> + struct smem_partition_header *phdr;
> + void *item_ptr, *p_end;
> + u32 padding_data;
> + u32 e_size;
> +
> + phdr = (struct smem_partition_header __force *)part->virt_base;
> + p_end = (void *)phdr + part->size;
> +
> + e = phdr_to_first_uncached_entry(phdr);
> + end = phdr_to_last_uncached_entry(phdr);
> +
> + while (e < end) {
> + if (e->canary != SMEM_PRIVATE_CANARY)
> + goto invalid_canary;
> +
> + if (le16_to_cpu(e->item) == item) {
> + if (size != NULL) {
> + e_size = le32_to_cpu(e->size);
> + padding_data = le16_to_cpu(e->padding_data);
> +
> + if (WARN_ON(e_size > part->size || padding_data > e_size))
> + return ERR_PTR(-EINVAL);
> +
> + *size = e_size - padding_data;
> + }
> +
> + item_ptr = uncached_entry_to_item(e);
> + if (WARN_ON(item_ptr > p_end))
> + return ERR_PTR(-EINVAL);
> +
> + return item_ptr;
> + }
> +
> + e = uncached_entry_next(e);
> + }
> +
> + if (WARN_ON((void *)e > p_end))
> + return ERR_PTR(-EINVAL);
> +
> + /* Item was not found in the uncached list, search the cached list */
> +
> + e = phdr_to_first_cached_entry(phdr, part->cacheline);
> + end = phdr_to_last_cached_entry(phdr);
> +
> + if (WARN_ON((void *)e < (void *)phdr || (void *)end > p_end))
> + return ERR_PTR(-EINVAL);
> +
> + while (e > end) {
> + if (e->canary != SMEM_PRIVATE_CANARY)
> + goto invalid_canary;
> +
> + if (le16_to_cpu(e->item) == item) {
> + if (size != NULL) {
> + e_size = le32_to_cpu(e->size);
> + padding_data = le16_to_cpu(e->padding_data);
> +
> + if (WARN_ON(e_size > part->size || padding_data > e_size))
> + return ERR_PTR(-EINVAL);
> +
> + *size = e_size - padding_data;
> + }
> +
> + item_ptr = cached_entry_to_item(e);
> + if (WARN_ON(item_ptr < (void *)phdr))
> + return ERR_PTR(-EINVAL);
> +
> + return item_ptr;
> + }
> +
> + e = cached_entry_next(e, part->cacheline);
> + }
> +
> + if (WARN_ON((void *)e < (void *)phdr))
> + return ERR_PTR(-EINVAL);
> +
> + return ERR_PTR(-ENOENT);
> +
> +invalid_canary:
> + dev_err(smem->dev, "Found invalid canary in hosts %hu:%hu partition\n",
> + le16_to_cpu(phdr->host0), le16_to_cpu(phdr->host1));
> +
> + return ERR_PTR(-EINVAL);
> +}
> +
> +/**
> + * qcom_smem_get() - resolve ptr of size of a smem item
> + * @host: the remote processor, or -1
> + * @item: smem item handle
> + * @size: pointer to be filled out with size of the item
> + *
> + * Looks up smem item and returns pointer to it. Size of smem
> + * item is returned in @size.
> + */
> +void *qcom_smem_get(unsigned host, unsigned item, size_t *size)
> +{
> + struct smem_partition *part;
> + void *ptr = ERR_PTR(-EPROBE_DEFER);
> +
> + if (!__smem)
> + return ptr;
> +
> + if (WARN_ON(item >= __smem->item_count))
> + return ERR_PTR(-EINVAL);
> +
> + if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
> + part = &__smem->partitions[host];
> + ptr = qcom_smem_get_private(__smem, part, item, size);
> + } else if (__smem->global_partition.virt_base) {
> + part = &__smem->global_partition;
> + ptr = qcom_smem_get_private(__smem, part, item, size);
> + } else {
> + ptr = qcom_smem_get_global(__smem, item, size);
> + }
> +
> + return ptr;
> +}
> +EXPORT_SYMBOL_GPL(qcom_smem_get);
> +
> +/**
> + * qcom_smem_get_free_space() - retrieve amount of free space in a partition
> + * @host: the remote processor identifying a partition, or -1
> + *
> + * To be used by smem clients as a quick way to determine if any new
> + * allocations has been made.
> + */
> +int qcom_smem_get_free_space(unsigned host)
> +{
> + struct smem_partition *part;
> + struct smem_partition_header *phdr;
> + struct smem_header *header;
> + unsigned ret;
> +
> + if (!__smem)
> + return -EPROBE_DEFER;
> +
> + if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
> + part = &__smem->partitions[host];
> + phdr = part->virt_base;
> + ret = le32_to_cpu(phdr->offset_free_cached) -
> + le32_to_cpu(phdr->offset_free_uncached);
> +
> + if (ret > le32_to_cpu(part->size))
> + return -EINVAL;
> + } else if (__smem->global_partition.virt_base) {
> + part = &__smem->global_partition;
> + phdr = part->virt_base;
> + ret = le32_to_cpu(phdr->offset_free_cached) -
> + le32_to_cpu(phdr->offset_free_uncached);
> +
> + if (ret > le32_to_cpu(part->size))
> + return -EINVAL;
> + } else {
> + header = __smem->regions[0].virt_base;
> + ret = le32_to_cpu(header->available);
> +
> + if (ret > __smem->regions[0].size)
> + return -EINVAL;
> + }
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(qcom_smem_get_free_space);
> +
> +static bool addr_in_range(void __iomem *base, size_t size, void *addr)
> +{
> + return base && ((void __iomem *)addr >= base && (void __iomem *)addr < base + size);
> +}
> +
> +/**
> + * qcom_smem_virt_to_phys() - return the physical address associated
> + * with an smem item pointer (previously returned by qcom_smem_get()
> + * @p: the virtual address to convert
> + *
> + * Returns 0 if the pointer provided is not within any smem region.
> + */
> +phys_addr_t qcom_smem_virt_to_phys(void *p)
> +{
> + struct smem_partition *part;
> + struct smem_region *area;
> + u64 offset;
> + u32 i;
> +
> + for (i = 0; i < SMEM_HOST_COUNT; i++) {
> + part = &__smem->partitions[i];
> +
> + if (addr_in_range(part->virt_base, part->size, p)) {
> + offset = p - part->virt_base;
> +
> + return (phys_addr_t)part->phys_base + offset;
> + }
> + }
> +
> + part = &__smem->global_partition;
> +
> + if (addr_in_range(part->virt_base, part->size, p)) {
> + offset = p - part->virt_base;
> +
> + return (phys_addr_t)part->phys_base + offset;
> + }
> +
> + for (i = 0; i < __smem->num_regions; i++) {
> + area = &__smem->regions[i];
> +
> + if (addr_in_range(area->virt_base, area->size, p)) {
> + offset = p - area->virt_base;
> +
> + return (phys_addr_t)area->aux_base + offset;
> + }
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(qcom_smem_virt_to_phys);
> +
> +/**
> + * qcom_smem_get_soc_id() - return the SoC ID
> + * @id: On success, we return the SoC ID here.
> + *
> + * Look up SoC ID from HW/SW build ID and return it.
> + *
> + * Return: 0 on success, negative errno on failure.
> + */
> +int qcom_smem_get_soc_id(u32 *id)
> +{
> + struct socinfo *info;
> +
> + info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, NULL);
> + if (IS_ERR(info))
> + return PTR_ERR(info);
> +
> + *id = __le32_to_cpu(info->id);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(qcom_smem_get_soc_id);
> +
> +/**
> + * qcom_smem_get_feature_code() - return the feature code
> + * @code: On success, return the feature code here.
> + *
> + * Look up the feature code identifier from SMEM and return it.
> + *
> + * Return: 0 on success, negative errno on failure.
> + */
> +int qcom_smem_get_feature_code(u32 *code)
> +{
> + struct socinfo *info;
> + u32 raw_code;
> +
> + info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, NULL);
> + if (IS_ERR(info))
> + return PTR_ERR(info);
> +
> + /* This only makes sense for socinfo >= 16 */
> + if (__le32_to_cpu(info->fmt) < SOCINFO_VERSION(0, 16))
> + return -EOPNOTSUPP;
> +
> + raw_code = __le32_to_cpu(info->feature_code);
> +
> + /* Ensure the value makes sense */
> + if (raw_code > SOCINFO_FC_INT_MAX)
> + raw_code = SOCINFO_FC_UNKNOWN;
> +
> + *code = raw_code;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(qcom_smem_get_feature_code);
> +
> +static int qcom_smem_get_sbl_version(struct qcom_smem *smem)
> +{
> + struct smem_header *header;
> + __le32 *versions;
> +
> + header = smem->regions[0].virt_base;
> + versions = header->version;
> +
> + return le32_to_cpu(versions[SMEM_MASTER_SBL_VERSION_INDEX]);
> +}
> +
> +static struct smem_ptable *qcom_smem_get_ptable(struct qcom_smem *smem)
> +{
> + struct smem_ptable *ptable;
> + u32 version;
> +
> + ptable = smem->ptable;
> + if (memcmp(ptable->magic, SMEM_PTABLE_MAGIC, sizeof(ptable->magic)))
> + return ERR_PTR(-ENOENT);
> +
> + version = le32_to_cpu(ptable->version);
> + if (version != 1) {
> + dev_err(smem->dev,
> + "Unsupported partition header version %d\n", version);
> + return ERR_PTR(-EINVAL);
> + }
> + return ptable;
> +}
> +
> +static u32 qcom_smem_get_item_count(struct qcom_smem *smem)
> +{
> + struct smem_ptable *ptable;
> + struct smem_info *info;
> +
> + ptable = qcom_smem_get_ptable(smem);
> + if (IS_ERR_OR_NULL(ptable))
> + return SMEM_ITEM_COUNT;
> +
> + info = (struct smem_info *)&ptable->entry[ptable->num_entries];
> + if (memcmp(info->magic, SMEM_INFO_MAGIC, sizeof(info->magic)))
> + return SMEM_ITEM_COUNT;
> +
> + return le16_to_cpu(info->num_items);
> +}
> +
> +/*
> + * Validate the partition header for a partition whose partition
> + * table entry is supplied. Returns a pointer to its header if
> + * valid, or a null pointer otherwise.
> + */
> +static struct smem_partition_header *
> +qcom_smem_partition_header(struct qcom_smem *smem,
> + struct smem_ptable_entry *entry, u16 host0, u16 host1)
> +{
> + struct smem_partition_header *header;
> + u32 phys_addr;
> + u32 size;
> +
> + phys_addr = smem->regions[0].aux_base + le32_to_cpu(entry->offset);
> + header = devm_ioremap_wc(smem->dev, phys_addr, le32_to_cpu(entry->size));
> +
> + if (!header)
> + return NULL;
> +
> + if (memcmp(header->magic, SMEM_PART_MAGIC, sizeof(header->magic))) {
> + dev_err(smem->dev, "bad partition magic %4ph\n", header->magic);
> + return NULL;
> + }
> +
> + if (host0 != le16_to_cpu(header->host0)) {
> + dev_err(smem->dev, "bad host0 (%hu != %hu)\n",
> + host0, le16_to_cpu(header->host0));
> + return NULL;
> + }
> + if (host1 != le16_to_cpu(header->host1)) {
> + dev_err(smem->dev, "bad host1 (%hu != %hu)\n",
> + host1, le16_to_cpu(header->host1));
> + return NULL;
> + }
> +
> + size = le32_to_cpu(header->size);
> + if (size != le32_to_cpu(entry->size)) {
> + dev_err(smem->dev, "bad partition size (%u != %u)\n",
> + size, le32_to_cpu(entry->size));
> + return NULL;
> + }
> +
> + if (le32_to_cpu(header->offset_free_uncached) > size) {
> + dev_err(smem->dev, "bad partition free uncached (%u > %u)\n",
> + le32_to_cpu(header->offset_free_uncached), size);
> + return NULL;
> + }
> +
> + return header;
> +}
> +
> +static int qcom_smem_set_global_partition(struct qcom_smem *smem)
> +{
> + struct smem_partition_header *header;
> + struct smem_ptable_entry *entry;
> + struct smem_ptable *ptable;
> + bool found = false;
> + int i;
> +
> + if (smem->global_partition.virt_base) {
> + dev_err(smem->dev, "Already found the global partition\n");
> + return -EINVAL;
> + }
> +
> + ptable = qcom_smem_get_ptable(smem);
> + if (IS_ERR(ptable))
> + return PTR_ERR(ptable);
> +
> + for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
> + entry = &ptable->entry[i];
> + if (!le32_to_cpu(entry->offset))
> + continue;
> + if (!le32_to_cpu(entry->size))
> + continue;
> +
> + if (le16_to_cpu(entry->host0) != SMEM_GLOBAL_HOST)
> + continue;
> +
> + if (le16_to_cpu(entry->host1) == SMEM_GLOBAL_HOST) {
> + found = true;
> + break;
> + }
> + }
> +
> + if (!found) {
> + dev_err(smem->dev, "Missing entry for global partition\n");
> + return -EINVAL;
> + }
> +
> + header = qcom_smem_partition_header(smem, entry,
> + SMEM_GLOBAL_HOST, SMEM_GLOBAL_HOST);
> + if (!header)
> + return -EINVAL;
> +
> + smem->global_partition.virt_base = (void __iomem *)header;
> + smem->global_partition.phys_base = smem->regions[0].aux_base +
> + le32_to_cpu(entry->offset);
> + smem->global_partition.size = le32_to_cpu(entry->size);
> + smem->global_partition.cacheline = le32_to_cpu(entry->cacheline);
> +
> + return 0;
> +}
> +
> +static int
> +qcom_smem_enumerate_partitions(struct qcom_smem *smem, u16 local_host)
> +{
> + struct smem_partition_header *header;
> + struct smem_ptable_entry *entry;
> + struct smem_ptable *ptable;
> + u16 remote_host;
> + u16 host0, host1;
> + int i;
> +
> + ptable = qcom_smem_get_ptable(smem);
> + if (IS_ERR(ptable))
> + return PTR_ERR(ptable);
> +
> + for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
> + entry = &ptable->entry[i];
> + if (!le32_to_cpu(entry->offset))
> + continue;
> + if (!le32_to_cpu(entry->size))
> + continue;
> +
> + host0 = le16_to_cpu(entry->host0);
> + host1 = le16_to_cpu(entry->host1);
> + if (host0 == local_host)
> + remote_host = host1;
> + else if (host1 == local_host)
> + remote_host = host0;
> + else
> + continue;
> +
> + if (remote_host >= SMEM_HOST_COUNT) {
> + dev_err(smem->dev, "bad host %u\n", remote_host);
> + return -EINVAL;
> + }
> +
> + if (smem->partitions[remote_host].virt_base) {
> + dev_err(smem->dev, "duplicate host %u\n", remote_host);
> + return -EINVAL;
> + }
> +
> + header = qcom_smem_partition_header(smem, entry, host0, host1);
> + if (!header)
> + return -EINVAL;
> +
> + smem->partitions[remote_host].virt_base = (void __iomem *)header;
> + smem->partitions[remote_host].phys_base = smem->regions[0].aux_base +
> + le32_to_cpu(entry->offset);
> + smem->partitions[remote_host].size = le32_to_cpu(entry->size);
> + smem->partitions[remote_host].cacheline = le32_to_cpu(entry->cacheline);
> + }
> +
> + return 0;
> +}
> +
> +static int qcom_smem_map_toc(struct qcom_smem *smem, struct smem_region *region)
> +{
> + u32 ptable_start;
> +
> + /* map starting 4K for smem header */
> + region->virt_base = devm_ioremap_wc(smem->dev, region->aux_base, SZ_4K);
> + ptable_start = region->aux_base + region->size - SZ_4K;
> + /* map last 4k for toc */
> + smem->ptable = devm_ioremap_wc(smem->dev, ptable_start, SZ_4K);
> +
> + if (!region->virt_base || !smem->ptable)
> + return -ENOMEM;
> +
> + return 0;
> +}
> +
> +static int qcom_smem_map_global(struct qcom_smem *smem, u32 size)
> +{
> + u32 phys_addr;
> +
> + phys_addr = smem->regions[0].aux_base;
> +
> + smem->regions[0].size = size;
> + smem->regions[0].virt_base = devm_ioremap_wc(smem->dev, phys_addr, size);
> +
> + if (!smem->regions[0].virt_base)
> + return -ENOMEM;
> +
> + return 0;
> +}
> +
> +static int qcom_smem_resolve_mem(struct qcom_smem *smem, const char *name,
> + struct smem_region *region)
> +{
> + struct device *dev = smem->dev;
> + struct device_node *np;
> + struct resource r;
> + int ret;
> +
> + np = of_parse_phandle(dev->of_node, name, 0);
> + if (!np) {
> + dev_err(dev, "No %s specified\n", name);
> + return -EINVAL;
> + }
> +
> + ret = of_address_to_resource(np, 0, &r);
> + of_node_put(np);
> + if (ret)
> + return ret;
> +
> + region->aux_base = r.start;
> + region->size = resource_size(&r);
> +
> + return 0;
> +}
> +
> +static int qcom_smem_probe(struct platform_device *pdev)
> +{
> + struct smem_header *header;
> + struct reserved_mem *rmem;
> + struct qcom_smem *smem;
> + unsigned long flags;
> + int num_regions;
> + int hwlock_id;
> + u32 version;
> + u32 size;
> + int ret;
> + int i;
> +
> + if (__smem)
> + return 0;
> +
> + num_regions = 1;
> + if (of_property_present(pdev->dev.of_node, "qcom,rpm-msg-ram"))
> + num_regions++;
> +
> + smem = devm_kzalloc(&pdev->dev, struct_size(smem, regions, num_regions),
> + GFP_KERNEL);
> + if (!smem)
> + return -ENOMEM;
> +
> + smem->dev = &pdev->dev;
> + smem->num_regions = num_regions;
> +
> + rmem = of_reserved_mem_lookup(pdev->dev.of_node);
> + if (rmem) {
> + smem->regions[0].aux_base = rmem->base;
> + smem->regions[0].size = rmem->size;
> + } else {
> + /*
> + * Fall back to the memory-region reference, if we're not a
> + * reserved-memory node.
> + */
> + ret = qcom_smem_resolve_mem(smem, "memory-region", &smem->regions[0]);
> + if (ret)
> + return ret;
> + }
> +
> + if (num_regions > 1) {
> + ret = qcom_smem_resolve_mem(smem, "qcom,rpm-msg-ram", &smem->regions[1]);
> + if (ret)
> + return ret;
> + }
> +
> +
> + ret = qcom_smem_map_toc(smem, &smem->regions[0]);
> + if (ret)
> + return ret;
> +
> + for (i = 1; i < num_regions; i++) {
> + smem->regions[i].virt_base = devm_ioremap_wc(&pdev->dev,
> + smem->regions[i].aux_base,
> + smem->regions[i].size);
> + if (!smem->regions[i].virt_base) {
> + dev_err(&pdev->dev, "failed to remap %pa\n", &smem->regions[i].aux_base);
> + return -ENOMEM;
> + }
> + }
> +
> + header = smem->regions[0].virt_base;
> + if (le32_to_cpu(header->initialized) != 1 ||
> + le32_to_cpu(header->reserved)) {
> + dev_err(&pdev->dev, "SMEM is not initialized by SBL\n");
> + return -EINVAL;
> + }
> +
> + hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0);
> + if (hwlock_id < 0) {
> + if (hwlock_id != -EPROBE_DEFER)
> + dev_err(&pdev->dev, "failed to retrieve hwlock\n");
> + return hwlock_id;
> + }
> +
> + smem->hwlock = hwspin_lock_request_specific(hwlock_id);
> + if (!smem->hwlock)
> + return -ENXIO;
> +
> + ret = hwspin_lock_timeout_irqsave(smem->hwlock, HWSPINLOCK_TIMEOUT, &flags);
> + if (ret)
> + return ret;
> + size = readl_relaxed(&header->available) + readl_relaxed(&header->free_offset);
> + hwspin_unlock_irqrestore(smem->hwlock, &flags);
> +
> + version = qcom_smem_get_sbl_version(smem);
> + /*
> + * smem header mapping is required only in heap version scheme, so unmap
> + * it here. It will be remapped in qcom_smem_map_global() when whole
> + * partition is mapped again.
> + */
> + devm_iounmap(smem->dev, smem->regions[0].virt_base);
> + switch (version >> 16) {
> + case SMEM_GLOBAL_PART_VERSION:
> + ret = qcom_smem_set_global_partition(smem);
> + if (ret < 0)
> + return ret;
> + smem->item_count = qcom_smem_get_item_count(smem);
> + break;
> + case SMEM_GLOBAL_HEAP_VERSION:
> + qcom_smem_map_global(smem, size);
> + smem->item_count = SMEM_ITEM_COUNT;
> + break;
> + default:
> + dev_err(&pdev->dev, "Unsupported SMEM version 0x%x\n", version);
> + return -EINVAL;
> + }
> +
> + BUILD_BUG_ON(SMEM_HOST_APPS >= SMEM_HOST_COUNT);
> + ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS);
> + if (ret < 0 && ret != -ENOENT)
> + return ret;
> +
> + __smem = smem;
> +
> + smem->socinfo = platform_device_register_data(&pdev->dev, "qcom-socinfo",
> + PLATFORM_DEVID_NONE, NULL,
> + 0);
> + if (IS_ERR(smem->socinfo))
> + dev_dbg(&pdev->dev, "failed to register socinfo device\n");
> +
> + return 0;
> +}
> +
> +static void qcom_smem_remove(struct platform_device *pdev)
> +{
> + platform_device_unregister(__smem->socinfo);
> +
> + hwspin_lock_free(__smem->hwlock);
> + __smem = NULL;
> +}
> +
> +static const struct of_device_id qcom_smem_of_match[] = {
> + { .compatible = "qcom,smem" },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, qcom_smem_of_match);
> +
> +static struct platform_driver qcom_smem_driver = {
> + .probe = qcom_smem_probe,
> + .remove_new = qcom_smem_remove,
> + .driver = {
> + .name = "qcom-smem",
> + .of_match_table = qcom_smem_of_match,
> + .suppress_bind_attrs = true,
> + },
> +};
> +
> +static int __init qcom_smem_init(void)
> +{
> + return platform_driver_register(&qcom_smem_driver);
> +}
> +arch_initcall(qcom_smem_init);
> +
> +static void __exit qcom_smem_exit(void)
> +{
> + platform_driver_unregister(&qcom_smem_driver);
> +}
> +module_exit(qcom_smem_exit)
> +
> +MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
> +MODULE_DESCRIPTION("Qualcomm Shared Memory Manager");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/soc/qcom/smem.h b/include/soc/qcom/smem.h
> new file mode 100644
> index 000000000000..f946e3beca21
> --- /dev/null
> +++ b/include/soc/qcom/smem.h
> @@ -0,0 +1,20 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef __QCOM_SMEM_H__
> +#define __QCOM_SMEM_H__
> +
> +#define QCOM_SMEM_HOST_ANY -1
> +
> +bool qcom_smem_is_available(void);
> +int qcom_smem_alloc(unsigned host, unsigned item, size_t size);
> +void *qcom_smem_get(unsigned host, unsigned item, size_t *size);
> +
> +int qcom_smem_get_free_space(unsigned host);
> +
> +phys_addr_t qcom_smem_virt_to_phys(void *p);
> +
> +int qcom_smem_get_soc_id(u32 *id);
> +int qcom_smem_get_feature_code(u32 *code);
> +
> +int qcom_smem_bust_hwspin_lock_by_host(unsigned int host);
> +
> +#endif
> diff --git a/include/soc/qcom/socinfo.h b/include/soc/qcom/socinfo.h
> new file mode 100644
> index 000000000000..608950443eee
> --- /dev/null
> +++ b/include/soc/qcom/socinfo.h
> @@ -0,0 +1,111 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#ifndef __QCOM_SOCINFO_H__
> +#define __QCOM_SOCINFO_H__
> +
> +#include <linux/types.h>
> +
> +/*
> + * SMEM item id, used to acquire handles to respective
> + * SMEM region.
> + */
> +#define SMEM_HW_SW_BUILD_ID 137
> +
> +#define SMEM_SOCINFO_BUILD_ID_LENGTH 32
> +#define SMEM_SOCINFO_CHIP_ID_LENGTH 32
> +
> +/*
> + * SoC version type with major number in the upper 16 bits and minor
> + * number in the lower 16 bits.
> + */
> +#define SOCINFO_MAJOR(ver) (((ver) >> 16) & 0xffff)
> +#define SOCINFO_MINOR(ver) ((ver) & 0xffff)
> +#define SOCINFO_VERSION(maj, min) ((((maj) & 0xffff) << 16)|((min) & 0xffff))
> +
> +/* Socinfo SMEM item structure */
> +struct socinfo {
> + __le32 fmt;
> + __le32 id;
> + __le32 ver;
> + char build_id[SMEM_SOCINFO_BUILD_ID_LENGTH];
> + /* Version 2 */
> + __le32 raw_id;
> + __le32 raw_ver;
> + /* Version 3 */
> + __le32 hw_plat;
> + /* Version 4 */
> + __le32 plat_ver;
> + /* Version 5 */
> + __le32 accessory_chip;
> + /* Version 6 */
> + __le32 hw_plat_subtype;
> + /* Version 7 */
> + __le32 pmic_model;
> + __le32 pmic_die_rev;
> + /* Version 8 */
> + __le32 pmic_model_1;
> + __le32 pmic_die_rev_1;
> + __le32 pmic_model_2;
> + __le32 pmic_die_rev_2;
> + /* Version 9 */
> + __le32 foundry_id;
> + /* Version 10 */
> + __le32 serial_num;
> + /* Version 11 */
> + __le32 num_pmics;
> + __le32 pmic_array_offset;
> + /* Version 12 */
> + __le32 chip_family;
> + __le32 raw_device_family;
> + __le32 raw_device_num;
> + /* Version 13 */
> + __le32 nproduct_id;
> + char chip_id[SMEM_SOCINFO_CHIP_ID_LENGTH];
> + /* Version 14 */
> + __le32 num_clusters;
> + __le32 ncluster_array_offset;
> + __le32 num_subset_parts;
> + __le32 nsubset_parts_array_offset;
> + /* Version 15 */
> + __le32 nmodem_supported;
> + /* Version 16 */
> + __le32 feature_code;
> + __le32 pcode;
> + __le32 npartnamemap_offset;
> + __le32 nnum_partname_mapping;
> + /* Version 17 */
> + __le32 oem_variant;
> + /* Version 18 */
> + __le32 num_kvps;
> + __le32 kvps_offset;
> + /* Version 19 */
> + __le32 num_func_clusters;
> + __le32 boot_cluster;
> + __le32 boot_core;
> +};
> +
> +/* Internal feature codes */
> +enum qcom_socinfo_feature_code {
> + /* External feature codes */
> + SOCINFO_FC_UNKNOWN = 0x0,
> + SOCINFO_FC_AA,
> + SOCINFO_FC_AB,
> + SOCINFO_FC_AC,
> + SOCINFO_FC_AD,
> + SOCINFO_FC_AE,
> + SOCINFO_FC_AF,
> + SOCINFO_FC_AG,
> + SOCINFO_FC_AH,
> +};
> +
> +/* Internal feature codes */
> +/* Valid values: 0 <= n <= 0xf */
> +#define SOCINFO_FC_Yn(n) (0xf1 + (n))
> +#define SOCINFO_FC_INT_MAX SOCINFO_FC_Yn(0xf)
> +
> +/* Product codes */
> +#define SOCINFO_PC_UNKNOWN 0
> +#define SOCINFO_PCn(n) ((n) + 1)
> +#define SOCINFO_PC_RESERVE (BIT(31) - 1)
> +
> +#endif
>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Thanks,
Neil
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 07/15] soc: qcom: smem: adjust headers for U-Boot
2026-05-04 18:57 ` [PATCH v2 07/15] soc: qcom: smem: adjust headers for U-Boot Casey Connolly
2026-05-05 12:43 ` Sumit Garg
@ 2026-05-18 14:36 ` Neil Armstrong
1 sibling, 0 replies; 51+ messages in thread
From: Neil Armstrong @ 2026-05-18 14:36 UTC (permalink / raw)
To: Casey Connolly, u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Peng Fan, Marek Vasut, Alice Guo,
Quentin Schulz, Ilias Apalodimas, Mattijs Korpershoek,
Kuan-Wei Chiu, Raymond Mao, Stefan Roese, Philip Molloy,
Jerome Forissier, Marek Vasut, Varadarajan Narayanan,
Patrice Chotard, Aswin Murugan, Rasmus Villemoes, Heiko Schocher,
Michal Simek, Sughosh Ganu, Antony Kurniawan Soemardi, Luca Weiss,
Balaji Selvanathan
On 5/4/26 20:57, Casey Connolly wrote:
> Drop Linux headers for the U-Boot ones.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> drivers/soc/qcom/smem.c | 16 +++++++---------
> 1 file changed, 7 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
> index 8515b8ae7777..7143856e85c3 100644
> --- a/drivers/soc/qcom/smem.c
> +++ b/drivers/soc/qcom/smem.c
> @@ -3,19 +3,17 @@
> * Copyright (c) 2015, Sony Mobile Communications AB.
> * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
> */
>
> -#include <linux/hwspinlock.h>
> +#include <dm/device.h>
> +#include <dm/device_compat.h>
> +#include <dm/ofnode.h>
> +#include <linux/bug.h>
> #include <linux/io.h>
> -#include <linux/module.h>
> -#include <linux/of.h>
> -#include <linux/of_address.h>
> -#include <linux/of_reserved_mem.h>
> -#include <linux/platform_device.h>
> +#include <linux/ioport.h>
> #include <linux/sizes.h>
> -#include <linux/slab.h>
> -#include <linux/soc/qcom/smem.h>
> -#include <linux/soc/qcom/socinfo.h>
> +#include <soc/qcom/smem.h>
> +#include <soc/qcom/socinfo.h>
>
> /*
> * The Qualcomm shared memory system is a allocate only heap structure that
> * consists of one of more memory areas that can be accessed by the processors
>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Thanks,
Neil
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 10/15] soc: qcom: smem: stub functions
2026-05-04 18:57 ` [PATCH v2 10/15] soc: qcom: smem: stub functions Casey Connolly
@ 2026-05-18 14:37 ` Neil Armstrong
0 siblings, 0 replies; 51+ messages in thread
From: Neil Armstrong @ 2026-05-18 14:37 UTC (permalink / raw)
To: Casey Connolly, u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Peng Fan, Marek Vasut, Alice Guo,
Quentin Schulz, Ilias Apalodimas, Mattijs Korpershoek,
Kuan-Wei Chiu, Raymond Mao, Stefan Roese, Philip Molloy,
Jerome Forissier, Marek Vasut, Varadarajan Narayanan,
Patrice Chotard, Aswin Murugan, Rasmus Villemoes, Heiko Schocher,
Michal Simek, Sughosh Ganu, Antony Kurniawan Soemardi, Luca Weiss,
Balaji Selvanathan
On 5/4/26 20:57, Casey Connolly wrote:
> Allow smem to be optional for Qualcomm platforms by providing stub
> functions.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> include/soc/qcom/smem.h | 20 ++++++++++++++++++++
> 1 file changed, 20 insertions(+)
>
> diff --git a/include/soc/qcom/smem.h b/include/soc/qcom/smem.h
> index 755003807dba..586432412eb8 100644
> --- a/include/soc/qcom/smem.h
> +++ b/include/soc/qcom/smem.h
> @@ -1,16 +1,36 @@
> /* SPDX-License-Identifier: GPL-2.0 */
> #ifndef __QCOM_SMEM_H__
> #define __QCOM_SMEM_H__
>
> +#include <linux/err.h>
> +#include <stdbool.h>
> +
> #define QCOM_SMEM_HOST_ANY -1
>
> +#if defined(CONFIG_QCOM_SMEM)
> int qcom_smem_init(void);
> int qcom_socinfo_init(void);
>
> bool qcom_smem_is_available(void);
> int qcom_smem_alloc(unsigned host, unsigned item, size_t size);
> void *qcom_smem_get(unsigned host, unsigned item, size_t *size);
>
> int qcom_smem_get_free_space(unsigned host);
> +#else
> +static int qcom_smem_init(void) { return -ENOSYS; }
> +
> +static bool qcom_smem_is_available(void) { return false; }
> +int qcom_smem_alloc(unsigned host, unsigned item, size_t size)
> +{
> + return -ENOSYS;
> +}
> +
> +void *qcom_smem_get(unsigned host, unsigned item, size_t *size)
> +{
> + return ERR_PTR(-ENOSYS);
> +}
> +
> +int qcom_smem_get_free_space(unsigned host);
> +#endif
>
> #endif
>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Thanks,
Neil
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 11/15] soc: qcom: smem: add build infra
2026-05-04 18:57 ` [PATCH v2 11/15] soc: qcom: smem: add build infra Casey Connolly
2026-05-18 12:53 ` Sumit Garg
@ 2026-05-18 14:38 ` Neil Armstrong
1 sibling, 0 replies; 51+ messages in thread
From: Neil Armstrong @ 2026-05-18 14:38 UTC (permalink / raw)
To: Casey Connolly, u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Peng Fan, Marek Vasut, Alice Guo,
Quentin Schulz, Ilias Apalodimas, Mattijs Korpershoek,
Kuan-Wei Chiu, Raymond Mao, Stefan Roese, Philip Molloy,
Jerome Forissier, Marek Vasut, Varadarajan Narayanan,
Patrice Chotard, Aswin Murugan, Rasmus Villemoes, Heiko Schocher,
Michal Simek, Sughosh Ganu, Antony Kurniawan Soemardi, Luca Weiss,
Balaji Selvanathan
On 5/4/26 20:57, Casey Connolly wrote:
> Build the new SMEM driver port, and select it when ARCH_SNAPDRAGON is
> selected, since it will be a hard dependency for Qualcomm platforms.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> drivers/soc/qcom/Kconfig | 8 ++++++++
> drivers/soc/qcom/Makefile | 1 +
> 2 files changed, 9 insertions(+)
>
> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
> index 8243805e46a3..cdd9e30f43e5 100644
> --- a/drivers/soc/qcom/Kconfig
> +++ b/drivers/soc/qcom/Kconfig
> @@ -24,5 +24,13 @@ config QCOM_RPMH
> help
> Say y here to support the Qualcomm RPMh (resource peripheral manager)
> if you need to control regulators on Qualcomm platforms, say y here.
>
> +config QCOM_SMEM
> + bool "Qualcomm SMEM support"
> + help
> + Say y here to support the Qualcomm SMEM (shared memory) client driver.
> + SMEM is a shared memory region that is used to pass information about
> + the hardware (e.g. DRAM layout, PMIC configuration, etc) between bootloader
> + staged and the OS. If in doubt, say y.
> +
> endif # SOC_QCOM
> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
> index 78fae8bbfa16..f4102f9155a8 100644
> --- a/drivers/soc/qcom/Makefile
> +++ b/drivers/soc/qcom/Makefile
> @@ -1,4 +1,5 @@
> # SPDX-License-Identifier: GPL-2.0+
>
> obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o
> obj-$(CONFIG_QCOM_RPMH) += rpmh-rsc.o rpmh.o
> +obj-$(CONFIG_QCOM_SMEM) += smem.o
>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Thanks,
Neil
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 12/15] mach-snapdragon: move memory parsing to its own file
2026-05-04 18:57 ` [PATCH v2 12/15] mach-snapdragon: move memory parsing to its own file Casey Connolly
2026-05-18 12:54 ` Sumit Garg
@ 2026-05-18 14:40 ` Neil Armstrong
1 sibling, 0 replies; 51+ messages in thread
From: Neil Armstrong @ 2026-05-18 14:40 UTC (permalink / raw)
To: Casey Connolly, u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Peng Fan, Marek Vasut, Alice Guo,
Quentin Schulz, Ilias Apalodimas, Mattijs Korpershoek,
Kuan-Wei Chiu, Raymond Mao, Stefan Roese, Philip Molloy,
Jerome Forissier, Marek Vasut, Varadarajan Narayanan,
Patrice Chotard, Aswin Murugan, Rasmus Villemoes, Heiko Schocher,
Michal Simek, Sughosh Ganu, Antony Kurniawan Soemardi, Luca Weiss,
Balaji Selvanathan
On 5/4/26 20:57, Casey Connolly wrote:
> This code is getting a bit complicated, split it out to try and keep
> things a bit better organised as we're going to be supporting populating
> the memory layout from various other sources.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> arch/arm/mach-snapdragon/Makefile | 2 +-
> arch/arm/mach-snapdragon/board.c | 115 -------------------------------
> arch/arm/mach-snapdragon/dram.c | 127 +++++++++++++++++++++++++++++++++++
> arch/arm/mach-snapdragon/qcom-priv.h | 2 +
> 4 files changed, 130 insertions(+), 116 deletions(-)
>
> diff --git a/arch/arm/mach-snapdragon/Makefile b/arch/arm/mach-snapdragon/Makefile
> index 343e825c6fdd..e481e4f26e5c 100644
> --- a/arch/arm/mach-snapdragon/Makefile
> +++ b/arch/arm/mach-snapdragon/Makefile
> @@ -1,7 +1,7 @@
> # SPDX-License-Identifier: GPL-2.0+
> #
> # (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
>
> -obj-y += board.o
> +obj-y += board.o dram.o
> obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += capsule_update.o
> obj-$(CONFIG_OF_LIVE) += of_fixup.o
> diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c
> index e12d3d00caa4..a2d97ad77910 100644
> --- a/arch/arm/mach-snapdragon/board.c
> +++ b/arch/arm/mach-snapdragon/board.c
> @@ -42,123 +42,8 @@ enum qcom_boot_source qcom_boot_source __section(".data") = 0;
> static struct mm_region rbx_mem_map[CONFIG_NR_DRAM_BANKS + 2] = { { 0 } };
>
> struct mm_region *mem_map = rbx_mem_map;
>
> -static struct {
> - phys_addr_t start;
> - phys_size_t size;
> -} prevbl_ddr_banks[CONFIG_NR_DRAM_BANKS] __section(".data") = { 0 };
> -
> -int dram_init(void)
> -{
> - /*
> - * gd->ram_base / ram_size have been setup already
> - * in qcom_parse_memory().
> - */
> - return 0;
> -}
> -
> -static int ddr_bank_cmp(const void *v1, const void *v2)
> -{
> - const struct {
> - phys_addr_t start;
> - phys_size_t size;
> - } *res1 = v1, *res2 = v2;
> -
> - if (!res1->size)
> - return 1;
> - if (!res2->size)
> - return -1;
> -
> - return (res1->start >> 24) - (res2->start >> 24);
> -}
> -
> -/* This has to be done post-relocation since gd->bd isn't preserved */
> -static void qcom_configure_bi_dram(void)
> -{
> - int i;
> -
> - for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
> - gd->bd->bi_dram[i].start = prevbl_ddr_banks[i].start;
> - gd->bd->bi_dram[i].size = prevbl_ddr_banks[i].size;
> - }
> -}
> -
> -int dram_init_banksize(void)
> -{
> - qcom_configure_bi_dram();
> -
> - return 0;
> -}
> -
> -/**
> - * The generic memory parsing code in U-Boot lacks a few things that we
> - * need on Qualcomm:
> - *
> - * 1. It sets gd->ram_size and gd->ram_base to represent a single memory block
> - * 2. setup_dest_addr() later relocates U-Boot to ram_base + ram_size, the end
> - * of that first memory block.
> - *
> - * This results in all memory beyond U-Boot being unusable in Linux when booting
> - * with EFI.
> - *
> - * Since the ranges in the memory node may be out of order, the only way for us
> - * to correctly determine the relocation address for U-Boot is to parse all
> - * memory regions and find the highest valid address.
> - *
> - * We can't use fdtdec_setup_memory_banksize() since it stores the result in
> - * gd->bd, which is not yet allocated.
> - *
> - * @fdt: FDT blob to parse /memory node from
> - *
> - * Return: 0 on success or -ENODATA if /memory node is missing or incomplete
> - */
> -static int qcom_parse_memory(const void *fdt)
> -{
> - int offset;
> - const fdt64_t *memory;
> - int memsize;
> - phys_addr_t ram_end = 0;
> - int i, j, banks;
> -
> - offset = fdt_path_offset(fdt, "/memory");
> - if (offset < 0)
> - return -ENODATA;
> -
> - memory = fdt_getprop(fdt, offset, "reg", &memsize);
> - if (!memory)
> - return -ENODATA;
> -
> - banks = min(memsize / (2 * sizeof(u64)), (ulong)CONFIG_NR_DRAM_BANKS);
> -
> - if (memsize / sizeof(u64) > CONFIG_NR_DRAM_BANKS * 2)
> - log_err("Provided more than the max of %d memory banks\n", CONFIG_NR_DRAM_BANKS);
> -
> - if (banks > CONFIG_NR_DRAM_BANKS)
> - log_err("Provided more memory banks than we can handle\n");
> -
> - for (i = 0, j = 0; i < banks * 2; i += 2, j++) {
> - prevbl_ddr_banks[j].start = get_unaligned_be64(&memory[i]);
> - prevbl_ddr_banks[j].size = get_unaligned_be64(&memory[i + 1]);
> - if (!prevbl_ddr_banks[j].size) {
> - j--;
> - continue;
> - }
> - ram_end = max(ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size);
> - }
> -
> - if (!banks || !prevbl_ddr_banks[0].size)
> - return -ENODATA;
> -
> - /* Sort our RAM banks -_- */
> - qsort(prevbl_ddr_banks, banks, sizeof(prevbl_ddr_banks[0]), ddr_bank_cmp);
> -
> - gd->ram_base = prevbl_ddr_banks[0].start;
> - gd->ram_size = ram_end - gd->ram_base;
> -
> - return 0;
> -}
> -
> static void show_psci_version(void)
> {
> struct arm_smccc_res res;
>
> diff --git a/arch/arm/mach-snapdragon/dram.c b/arch/arm/mach-snapdragon/dram.c
> new file mode 100644
> index 000000000000..ad73b685a935
> --- /dev/null
> +++ b/arch/arm/mach-snapdragon/dram.c
> @@ -0,0 +1,127 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Memory layout parsing for Qualcomm.
> + */
> +
> +#define LOG_CATEGORY LOGC_BOARD
> +#define pr_fmt(fmt) "QCOM-DRAM: " fmt
> +
> +#include <asm-generic/unaligned.h>
> +#include <dm.h>
> +#include <log.h>
> +#include <sort.h>
> +
> +static struct {
> + phys_addr_t start;
> + phys_size_t size;
> +} prevbl_ddr_banks[CONFIG_NR_DRAM_BANKS] __section(".data") = { 0 };
> +
> +int dram_init(void)
> +{
> + /*
> + * gd->ram_base / ram_size have been setup already
> + * in qcom_parse_memory().
> + */
> + return 0;
> +}
> +
> +static int ddr_bank_cmp(const void *v1, const void *v2)
> +{
> + const struct {
> + phys_addr_t start;
> + phys_size_t size;
> + } *res1 = v1, *res2 = v2;
> +
> + if (!res1->size)
> + return 1;
> + if (!res2->size)
> + return -1;
> +
> + return (res1->start >> 24) - (res2->start >> 24);
> +}
> +
> +/* This has to be done post-relocation since gd->bd isn't preserved */
> +static void qcom_configure_bi_dram(void)
> +{
> + int i;
> +
> + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
> + gd->bd->bi_dram[i].start = prevbl_ddr_banks[i].start;
> + gd->bd->bi_dram[i].size = prevbl_ddr_banks[i].size;
> + }
> +}
> +
> +int dram_init_banksize(void)
> +{
> + qcom_configure_bi_dram();
> +
> + return 0;
> +}
> +
> +/**
> + * The generic memory parsing code in U-Boot lacks a few things that we
> + * need on Qualcomm:
> + *
> + * 1. It sets gd->ram_size and gd->ram_base to represent a single memory block
> + * 2. setup_dest_addr() later relocates U-Boot to ram_base + ram_size, the end
> + * of that first memory block.
> + *
> + * This results in all memory beyond U-Boot being unusable in Linux when booting
> + * with EFI.
> + *
> + * Since the ranges in the memory node may be out of order, the only way for us
> + * to correctly determine the relocation address for U-Boot is to parse all
> + * memory regions and find the highest valid address.
> + *
> + * We can't use fdtdec_setup_memory_banksize() since it stores the result in
> + * gd->bd, which is not yet allocated.
> + *
> + * @fdt: FDT blob to parse /memory node from
> + *
> + * Return: 0 on success or -ENODATA if /memory node is missing or incomplete
> + */
> +int qcom_parse_memory(const void *fdt)
> +{
> + int offset;
> + const fdt64_t *memory;
> + int memsize;
> + phys_addr_t ram_end = 0;
> + int i, j, banks;
> +
> + offset = fdt_path_offset(fdt, "/memory");
> + if (offset < 0)
> + return -ENODATA;
> +
> + memory = fdt_getprop(fdt, offset, "reg", &memsize);
> + if (!memory)
> + return -ENODATA;
> +
> + banks = min(memsize / (2 * sizeof(u64)), (ulong)CONFIG_NR_DRAM_BANKS);
> +
> + if (memsize / sizeof(u64) > CONFIG_NR_DRAM_BANKS * 2)
> + log_err("Provided more than the max of %d memory banks\n", CONFIG_NR_DRAM_BANKS);
> +
> + if (banks > CONFIG_NR_DRAM_BANKS)
> + log_err("Provided more memory banks than we can handle\n");
> +
> + for (i = 0, j = 0; i < banks * 2; i += 2, j++) {
> + prevbl_ddr_banks[j].start = get_unaligned_be64(&memory[i]);
> + prevbl_ddr_banks[j].size = get_unaligned_be64(&memory[i + 1]);
> + if (!prevbl_ddr_banks[j].size) {
> + j--;
> + continue;
> + }
> + ram_end = max(ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size);
> + }
> +
> + if (!banks || !prevbl_ddr_banks[0].size)
> + return -ENODATA;
> +
> + /* Sort our RAM banks -_- */
> + qsort(prevbl_ddr_banks, banks, sizeof(prevbl_ddr_banks[0]), ddr_bank_cmp);
> +
> + gd->ram_base = prevbl_ddr_banks[0].start;
> + gd->ram_size = ram_end - gd->ram_base;
> +
> + return 0;
> +}
> diff --git a/arch/arm/mach-snapdragon/qcom-priv.h b/arch/arm/mach-snapdragon/qcom-priv.h
> index b8bf574e8bbb..ce409314a98b 100644
> --- a/arch/arm/mach-snapdragon/qcom-priv.h
> +++ b/arch/arm/mach-snapdragon/qcom-priv.h
> @@ -22,5 +22,7 @@ void qcom_configure_capsule_updates(void);
> #else
> void qcom_configure_capsule_updates(void) {}
> #endif /* EFI_HAVE_CAPSULE_SUPPORT */
>
> +int qcom_parse_memory(const void *fdt);
> +
> #endif /* __QCOM_PRIV_H__ */
>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Thanks,
Neil
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 13/15] mach-snapdragon: support parsing memory map from SMEM
2026-05-04 18:57 ` [PATCH v2 13/15] mach-snapdragon: support parsing memory map from SMEM Casey Connolly
@ 2026-05-18 14:48 ` Neil Armstrong
2026-05-21 13:36 ` Stephan Gerhold
1 sibling, 0 replies; 51+ messages in thread
From: Neil Armstrong @ 2026-05-18 14:48 UTC (permalink / raw)
To: Casey Connolly, u-boot, Sumit Garg, u-boot-qcom
Cc: Tom Rini, Simon Glass, Peng Fan, Marek Vasut, Alice Guo,
Quentin Schulz, Ilias Apalodimas, Mattijs Korpershoek,
Kuan-Wei Chiu, Raymond Mao, Stefan Roese, Philip Molloy,
Jerome Forissier, Marek Vasut, Varadarajan Narayanan,
Patrice Chotard, Aswin Murugan, Rasmus Villemoes, Heiko Schocher,
Michal Simek, Sughosh Ganu, Antony Kurniawan Soemardi, Luca Weiss,
Balaji Selvanathan
On 5/4/26 20:57, Casey Connolly wrote:
> It is possible to derive the memory map for a Qualcomm platform from the
> SMEM shared memory region. The memory map is populated by the preloader.
>
> Introduce support for parsing this data and using it to populate
> U-Boot's memory map. Since we aren't yet sure if this will work for
> every platform, it is not yet used in all cases, if U-Boot is booted
> with an internal FDT which has the memory map defined, this will
> be used instead.
>
> If the FDT is internal FDT with no memory map defined, then U-Boot will
> try to use SMEM. This should remove the need to define the memory map
> statically in a U-Boot overlay for boards that don't chainload.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> ---
> arch/arm/Kconfig | 1 +
> arch/arm/mach-snapdragon/board.c | 45 +++++---
> arch/arm/mach-snapdragon/dram.c | 172 +++++++++++++++++++++++++------
> arch/arm/mach-snapdragon/qcom-priv.h | 17 ++-
> arch/arm/mach-snapdragon/rampart.h | 194 +++++++++++++++++++++++++++++++++++
> 5 files changed, 386 insertions(+), 43 deletions(-)
>
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index a8fcfe76296c..1da0677d85b3 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -1140,8 +1140,9 @@ config ARCH_SNAPDRAGON
> select DM_RESET
> select POWER_DOMAIN
> select GPIO_EXTRA_HEADER
> select OF_CONTROL
> + select QCOM_SMEM
> select SPMI
> select BOARD_LATE_INIT
> select OF_BOARD
> select SAVE_PREV_BL_FDT_ADDR if !ENABLE_ARM_SOC_BOOT0_HOOK
> diff --git a/arch/arm/mach-snapdragon/board.c b/arch/arm/mach-snapdragon/board.c
> index a2d97ad77910..7610891191fd 100644
> --- a/arch/arm/mach-snapdragon/board.c
> +++ b/arch/arm/mach-snapdragon/board.c
> @@ -29,16 +29,18 @@
> #include <lmb.h>
> #include <malloc.h>
> #include <fdt_support.h>
> #include <usb.h>
> +#include <soc/qcom/smem.h>
> #include <sort.h>
> #include <time.h>
>
> #include "qcom-priv.h"
>
> DECLARE_GLOBAL_DATA_PTR;
>
> enum qcom_boot_source qcom_boot_source __section(".data") = 0;
> +enum qcom_memmap_source qcom_memmap_source __section(".data") = 0;
>
> static struct mm_region rbx_mem_map[CONFIG_NR_DRAM_BANKS + 2] = { { 0 } };
>
> struct mm_region *mem_map = rbx_mem_map;
> @@ -109,18 +111,18 @@ int board_fdt_blob_setup(void **fdtp)
> (phys_addr_t)external_fdt);
>
> /* Prefer memory information from internal DT if it's present */
> if (internal_valid)
> - ret = qcom_parse_memory(internal_fdt);
> + ret = qcom_parse_memory(internal_fdt, true);
>
> if (ret < 0 && external_valid) {
> /* No internal FDT or it lacks a proper /memory node.
> * The previous bootloader handed us something, let's try that.
> */
> if (internal_valid)
> debug("No memory info in internal FDT, falling back to external\n");
>
> - ret = qcom_parse_memory(external_fdt);
> + ret = qcom_parse_memory(external_fdt, false);
> }
>
> if (ret < 0)
> panic("No valid memory ranges found!\n");
> @@ -373,23 +375,39 @@ static void configure_env(void)
>
> qcom_set_serialno();
> }
>
> -void qcom_show_boot_source(void)
> +static void qcom_show_boot_context(void)
> {
> - const char *name = "UNKNOWN";
> + const char *boot_source = "UNKNOWN";
> + const char *memmap_source = "UNKNOWN";
>
> switch (qcom_boot_source) {
> case QCOM_BOOT_SOURCE_ANDROID:
> - name = "ABL";
> + boot_source = "ABL";
> break;
> case QCOM_BOOT_SOURCE_XBL:
> - name = "XBL";
> + boot_source = "XBL";
> break;
> }
>
> - log_info("U-Boot loaded from %s\n", name);
> - env_set("boot_source", name);
> + log_info("U-Boot loaded from %s\n", boot_source);
> + env_set("boot_source", boot_source);
> +
> + switch (qcom_memmap_source) {
> + case QCOM_MEMMAP_SOURCE_INTERNAL_FDT:
> + memmap_source = "INTERNAL_FDT";
> + break;
> + case QCOM_MEMMAP_SOURCE_EXTERNAL_FDT:
> + memmap_source = "EXTERNAL_FDT";
> + break;
> + case QCOM_MEMMAP_SOURCE_SMEM:
> + memmap_source = "SMEM";
> + break;
> + }
> +
> + log_info("Memory map loaded from %s\n", memmap_source);
> + env_set("memmap_source", memmap_source);
> }
>
> void __weak qcom_late_init(void)
> {
> @@ -458,9 +476,9 @@ int board_late_init(void)
>
> configure_env();
> qcom_late_init();
>
> - qcom_show_boot_source();
> + qcom_show_boot_context();
> /* Configure the dfu_string for capsule updates */
> qcom_configure_capsule_updates();
>
> return 0;
> @@ -648,11 +666,14 @@ void enable_caches(void)
> gd->arch.tlb_emerg = gd->arch.tlb_addr;
> gd->arch.tlb_addr = tlb_addr;
> gd->arch.tlb_size = tlb_size;
>
> - /* On some boards speculative access may trigger a NOC or XPU violation so explicitly mark reserved
> - * regions as inacessible (PTE_TYPE_FAULT) */
> - if (fdt_node_check_compatible(gd->fdt_blob, 0, "qcom,qcs404") == 0) {
> + /*
> + * On some boards speculative access may trigger a NOC or XPU violation so explicitly mark
> + * reserved regions as inacessible (PTE_TYPE_FAULT)
> + */
> + if (qcom_memmap_source == QCOM_MEMMAP_SOURCE_SMEM ||
> + fdt_node_check_compatible(gd->fdt_blob, 0, "qcom,qcs404") == 0) {
> carveout_start = get_timer(0);
> /* Takes ~20-50ms on SDM845 */
> configure_reserved_memory();
> debug("carveout time: %lums\n", get_timer(carveout_start));
> diff --git a/arch/arm/mach-snapdragon/dram.c b/arch/arm/mach-snapdragon/dram.c
> index ad73b685a935..af5f3c84882c 100644
> --- a/arch/arm/mach-snapdragon/dram.c
> +++ b/arch/arm/mach-snapdragon/dram.c
> @@ -9,8 +9,12 @@
> #include <asm-generic/unaligned.h>
> #include <dm.h>
> #include <log.h>
> #include <sort.h>
> +#include <soc/qcom/smem.h>
> +
> +#include "qcom-priv.h"
> +#include "rampart.h"
>
> static struct {
> phys_addr_t start;
> phys_size_t size;
> @@ -47,8 +51,12 @@ static void qcom_configure_bi_dram(void)
>
> for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
> gd->bd->bi_dram[i].start = prevbl_ddr_banks[i].start;
> gd->bd->bi_dram[i].size = prevbl_ddr_banks[i].size;
> + debug("Bank[%d]: start = %#011llx, size = %#011llx\n",
> + i, gd->bd->bi_dram[i].start, gd->bd->bi_dram[i].size);
> + if (!prevbl_ddr_banks[i].size)
> + break;
> }
> }
>
> int dram_init_banksize(void)
> @@ -57,8 +65,124 @@ int dram_init_banksize(void)
>
> return 0;
> }
>
> +#define entry_field(v, e, field) (v == 0 ? ((struct ram_partition_entry_v0 *)(e))->field : \
> + (v == 1 ? ((struct ram_partition_entry_v1 *)(e))->field : \
> + ((struct ram_partition_entry_v3 *)(e))->field))
> +#define entry_start(v, e) entry_field(v, e, start_address)
> +#define entry_length(v, e) entry_field(v, e, length)
> +#define entry_category(v, e) entry_field(v, e, partition_category)
> +#define entry_domain(v, e) entry_field(v, e, partition_domain)
> +#define entry_type(v, e) entry_field(v, e, partition_type)
> +
> +/* Parse memory map from SMEM, return the number of entries */
> +static int qcom_parse_memory_smem(phys_addr_t *ram_end)
> +{
> + size_t size;
> + int i, j = 0, ret;
> + struct usable_ram_partition_table_header *header;
> + u32 ver;
> + void *entry;
> + u32 entry_size; // Size of each RAM partition entry (version dependent)
Re-order the variables since you'll send a v3
> +
> + ret = qcom_smem_init();
> + if (ret) {
> + debug("Failed to initialize SMEM: %d.\n", ret);
> + return ret;
> + }
> +
> + header = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_USABLE_RAM_PARTITION_TABLE, &size);
> + if (!header) {
> + debug("Failed to find SMEM partition.\n");
> + return -ENODEV;
> + }
> +
> + ver = header->version;
> + debug("SMEM RAM partition table version %u. %u entries\n", ver, header->num_partitions);
> +
> + switch (ver) {
> + case 0:
> + entry_size = sizeof(struct ram_partition_entry_v0);
> + entry = ((struct usable_ram_partition_table_v0 *)header)->entries;
> + break;
> + case 1:
> + entry_size = sizeof(struct ram_partition_entry_v1);
> + entry = ((struct usable_ram_partition_table_v1 *)header)->entries;
> + break;
> + default:
> + pr_warn("Unknown SMEM ram partition table version!\n");
> + case 2:
> + case 3:
> + entry_size = sizeof(struct ram_partition_entry_v3);
> + entry = ((struct usable_ram_partition_table_v3 *)header)->entries;
> + break;
> + }
> +
> + debug("SMEM RAM partition entry size: %u bytes\n", entry_size);
> +
> + /* Check validy of RAM */
> + for (i = 0; i < header->num_partitions && j < CONFIG_NR_DRAM_BANKS; i++, entry += entry_size) {
> + debug("Entry %d: [%#010llx - %#010llx]\n", i, entry_start(ver, entry),
> + (u64)entry_start(ver, entry) + entry_length(ver, entry));
> + debug(" cat: %#04x type: %#04x domain: %#04x\n", entry_category(ver, entry),
> + entry_type(ver, entry), entry_domain(ver, entry));
> +
> +
Drop empty line
> + if (entry_category(ver, entry) != RAM_PARTITION_SDRAM ||
> + entry_type(ver, entry) != RAM_PARTITION_SYS_MEMORY)
> + continue;
> + if (!entry_length(ver, entry) && !entry_start(ver, entry))
> + break;
> +
> + prevbl_ddr_banks[j].start = entry_start(ver, entry);
> + prevbl_ddr_banks[j].size = entry_length(ver, entry);
> + *ram_end = max(*ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size);
> + j++;
> + }
> +
> + if (j == CONFIG_NR_DRAM_BANKS)
> + pr_err("SMEM: More than CONFIG_NR_DRAM_BANKS (%u) entries!", CONFIG_NR_DRAM_BANKS);
> +
> + return j;
> +}
Undef the entry_* macros after ?
> +
> +static void qcom_parse_memory_dt(const fdt64_t *fdt, int *banks, phys_addr_t *ram_end)
> +{
> + int offset;
> + const fdt64_t *memory;
move this one first
> + int memsize;
> + int i, j;
> +
> + *ram_end = 0;
> +
> + offset = fdt_path_offset(fdt, "/memory");
> + if (offset < 0)
> + return;
> +
> + memory = fdt_getprop(fdt, offset, "reg", &memsize);
> + if (!memory)
> + return;
> +
> + *banks = min(memsize / (2 * sizeof(u64)), (ulong)CONFIG_NR_DRAM_BANKS);
> +
> + if (memsize / sizeof(u64) > CONFIG_NR_DRAM_BANKS * 2)
> + log_err("Provided more than the max of %d memory banks\n", CONFIG_NR_DRAM_BANKS);
> +
> + if (*banks > CONFIG_NR_DRAM_BANKS)
> + log_err("Provided more memory banks than we can handle\n");
> +
> + for (i = 0, j = 0; i < *banks * 2; i += 2, j++) {
> + prevbl_ddr_banks[j].start = get_unaligned_be64(&memory[i]);
> + prevbl_ddr_banks[j].size = get_unaligned_be64(&memory[i + 1]);
> + if (!prevbl_ddr_banks[j].size) {
> + j--;
> + continue;
> + }
> + *ram_end = max(*ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size);
> + }
> +}
> +
> /**
> * The generic memory parsing code in U-Boot lacks a few things that we
> * need on Qualcomm:
> *
> @@ -76,48 +200,36 @@ int dram_init_banksize(void)
> * We can't use fdtdec_setup_memory_banksize() since it stores the result in
> * gd->bd, which is not yet allocated.
> *
> * @fdt: FDT blob to parse /memory node from
> + * @fdt_is_internal: is the FDT one embedded into U-Boot or was it provided by a prior
> + * bootloader stage? This determined if we should try to rely on SMEM
> + * (internal FDT) or error. We currently assume that if we are passed
> + * an external FDT then it already has the memory map populated.
> *
> * Return: 0 on success or -ENODATA if /memory node is missing or incomplete
> */
> -int qcom_parse_memory(const void *fdt)
> +int qcom_parse_memory(const void *fdt, bool fdt_is_internal)
> {
> - int offset;
> - const fdt64_t *memory;
> - int memsize;
> phys_addr_t ram_end = 0;
> - int i, j, banks;
> + int banks;
>
> - offset = fdt_path_offset(fdt, "/memory");
> - if (offset < 0)
> - return -ENODATA;
> + qcom_memmap_source = fdt_is_internal ? QCOM_MEMMAP_SOURCE_INTERNAL_FDT
> + : QCOM_MEMMAP_SOURCE_EXTERNAL_FDT;
>
> - memory = fdt_getprop(fdt, offset, "reg", &memsize);
> - if (!memory)
> - return -ENODATA;
> + qcom_parse_memory_dt(fdt, &banks, &ram_end);
>
> - banks = min(memsize / (2 * sizeof(u64)), (ulong)CONFIG_NR_DRAM_BANKS);
> -
> - if (memsize / sizeof(u64) > CONFIG_NR_DRAM_BANKS * 2)
> - log_err("Provided more than the max of %d memory banks\n", CONFIG_NR_DRAM_BANKS);
> -
> - if (banks > CONFIG_NR_DRAM_BANKS)
> - log_err("Provided more memory banks than we can handle\n");
> -
> - for (i = 0, j = 0; i < banks * 2; i += 2, j++) {
> - prevbl_ddr_banks[j].start = get_unaligned_be64(&memory[i]);
> - prevbl_ddr_banks[j].size = get_unaligned_be64(&memory[i + 1]);
> - if (!prevbl_ddr_banks[j].size) {
> - j--;
> - continue;
> - }
> - ram_end = max(ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size);
> + /*
> + * If using an internal FDT but the memory node is empty
> + * then fall back to SMEM.
> + */
> + if (!prevbl_ddr_banks[0].size && fdt_is_internal) {
> + banks = qcom_parse_memory_smem(&ram_end);
> + if (banks < 0)
> + panic("Couldn't find a valid memory map!\n");
> + qcom_memmap_source = QCOM_MEMMAP_SOURCE_SMEM;
> }
>
> - if (!banks || !prevbl_ddr_banks[0].size)
> - return -ENODATA;
> -
> /* Sort our RAM banks -_- */
> qsort(prevbl_ddr_banks, banks, sizeof(prevbl_ddr_banks[0]), ddr_bank_cmp);
>
> gd->ram_base = prevbl_ddr_banks[0].start;
> diff --git a/arch/arm/mach-snapdragon/qcom-priv.h b/arch/arm/mach-snapdragon/qcom-priv.h
> index ce409314a98b..39dc8fcc76ad 100644
> --- a/arch/arm/mach-snapdragon/qcom-priv.h
> +++ b/arch/arm/mach-snapdragon/qcom-priv.h
> @@ -2,8 +2,10 @@
>
> #ifndef __QCOM_PRIV_H__
> #define __QCOM_PRIV_H__
>
> +#include <stdbool.h>
> +
> /**
> * enum qcom_boot_source - Track where we got loaded from.
> * Used for capsule update logic.
> *
> @@ -16,13 +18,26 @@ enum qcom_boot_source {
> };
>
> extern enum qcom_boot_source qcom_boot_source;
>
> +/*
> + * enum qcom_memmap_source - Track where we got the memory map from.
> + * used for debugging and validation.
> + */
> +enum qcom_memmap_source {
> + QCOM_MEMMAP_SOURCE_INTERNAL_FDT = 1,
> + QCOM_MEMMAP_SOURCE_EXTERNAL_FDT,
> + QCOM_MEMMAP_SOURCE_SMEM,
> +};
> +
> +/* Set by qcom_parse_memory() */
> +extern enum qcom_memmap_source qcom_memmap_source;
> +
> #if IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_SUPPORT)
> void qcom_configure_capsule_updates(void);
> #else
> void qcom_configure_capsule_updates(void) {}
> #endif /* EFI_HAVE_CAPSULE_SUPPORT */
>
> -int qcom_parse_memory(const void *fdt);
> +int qcom_parse_memory(const void *fdt, bool fdt_is_internal);
>
> #endif /* __QCOM_PRIV_H__ */
> diff --git a/arch/arm/mach-snapdragon/rampart.h b/arch/arm/mach-snapdragon/rampart.h
> new file mode 100644
> index 000000000000..57f3dbb7eec0
> --- /dev/null
> +++ b/arch/arm/mach-snapdragon/rampart.h
> @@ -0,0 +1,194 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * RAM partition table definitions
> + *
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + *
> + */
> +
> +#ifndef __QCOM_SMEM_RAMPART_H__
> +#define __QCOM_SMEM_RAMPART_H__
> +
> +#include <linux/types.h>
> +
> +#define SMEM_USABLE_RAM_PARTITION_TABLE 402
> +
> +#define RAM_PARTITION_H_MAJOR 03
> +#define RAM_PARTITION_H_MINOR 00
> +
> +/**
> + * Total length of zero filled name string. This is not a C
> + * string, as it can occupy the total number of bytes, and if
> + * it does, it does not require a zero terminator. It cannot
> + * be manipulated with standard string handling library functions.
> + */
> +#define RAM_PART_NAME_LENGTH 16
> +
> +/**
> + * Number of RAM partition entries which are usable by APPS.
> + */
> +#define RAM_NUM_PART_ENTRIES 32
> +
> +/**
> + * @name: Magic numbers
> + * Used in identifying valid RAM partition table.
> + */
> +#define RAM_PART_MAGIC1 0x9da5e0a8
> +#define RAM_PART_MAGIC2 0xaf9ec4e2
> +
> +/**
> + * RAM partition attributes.
> + */
> +enum ram_partition_attribute_t {
> + RAM_PARTITION_READ_ONLY = 0, /* Read-only RAM partition */
> + RAM_PARTITION_READWRITE, /* Read/write RAM partition */
> +};
> +
> +/**
> + * RAM partition categories.
> + */
> +enum ram_partition_category_t {
> + RAM_PARTITION_IRAM = 4, /* IRAM RAM partition */
> + RAM_PARTITION_IMEM = 5, /* IMEM RAM partition */
> + RAM_PARTITION_SDRAM = 14, /* SDRAM type without specific bus information**/
> +};
> +
> +/**
> + * RAM Partition domains.
> + * @note: For shared RAM partition, domain value would be 0b11:\n
> + * RAM_PARTITION_APPS_DOMAIN | RAM_PARTITION_MODEM_DOMAIN.
> + */
> +enum ram_partition_domain_t {
> + RAM_PARTITION_DEFAULT_DOMAIN = 0, /* 0b00: No specific domain definition */
> + RAM_PARTITION_APPS_DOMAIN = 1, /* 0b01: APPS RAM partition */
> + RAM_PARTITION_MODEM_DOMAIN = 2, /* 0b10: MODEM RAM partition */
> +};
> +
> +/**
> + * RAM Partition types.
> + * @note: The RAM_PARTITION_SYS_MEMORY type represents DDR rams that are attached
> + * to the current system.
> + */
> +enum ram_partition_type_t {
> + RAM_PARTITION_SYS_MEMORY = 1, /* system memory */
> + RAM_PARTITION_BOOT_REGION_MEMORY1, /* boot loader memory 1 */
> + RAM_PARTITION_BOOT_REGION_MEMORY2, /* boot loader memory 2, reserved */
> + RAM_PARTITION_APPSBL_MEMORY, /* apps boot loader memory */
> + RAM_PARTITION_APPS_MEMORY, /* apps usage memory */
> + RAM_PARTITION_TOOLS_FV_MEMORY, /* tools usage memory */
> + RAM_PARTITION_QUANTUM_FV_MEMORY, /* quantum usage memory */
> + RAM_PARTITION_QUEST_FV_MEMORY, /* quest usage memory */
> +};
> +
> +/* Common table header between versions */
> +struct usable_ram_partition_table_header {
> + u32 magic1; /* Magic number to identify valid RAM partition table */
> + u32 magic2; /* Magic number to identify valid RAM partition table */
> + u32 version; /* Version number to track structure definition changes */
> + u32 reserved1; /* Reserved for future use */
> +
> + u32 num_partitions; /* Number of RAM partition table entries */
> +};
> +
> +/* Holds information for an entry in the RAM partition table */
> +struct ram_partition_entry_v3 {
> + char name[RAM_PART_NAME_LENGTH]; /* Partition name, unused for now */
> + u64 start_address; /* Partition start address in RAM */
> + u64 length; /* Partition length in RAM in Bytes */
> + u32 partition_attribute; /* Partition attribute */
> + u32 partition_category; /* Partition category */
> + u32 partition_domain; /* Partition domain */
> + u32 partition_type; /* Partition type */
> + u32 num_partitions; /* Number of partitions on device */
> + u32 hw_info; /* hw information such as type and frequency */
> + u8 highest_bank_bit; /* Highest bit corresponding to a bank */
> + u8 reserve0; /* Reserved for future use */
> + u8 reserve1; /* Reserved for future use */
> + u8 reserve2; /* Reserved for future use */
> + u32 min_pasr_size; /* Minimum PASR size in MB */
> + u64 available_length; /* Available Partition length in RAM in Bytes */
> +};
> +
> +/*
> + * Defines the RAM partition table structure
> + *
> + * Do not change the placement of the first four elements so that future
> + * compatibility will always be guaranteed at least for the identifiers.
> + *
> + * The other portion of the structure may be changed as necessary to accommodate
> + * new features. Be sure to increment version number if you change it.
> + */
> +struct usable_ram_partition_table_v3 {
> + struct usable_ram_partition_table_header header;
> +
> + u32 reserved2; /* Added for 8 bytes alignment of header */
> +
> + /* RAM partition table entries */
> + struct ram_partition_entry_v3 entries[RAM_NUM_PART_ENTRIES];
> +};
> +
> +/* Version 1 structure 32 Bit - Holds information for an entry in the RAM partition table */
> +struct ram_partition_entry_v1 {
> + char name[RAM_PART_NAME_LENGTH]; /* Partition name, unused for now */
> + u64 start_address; /* Partition start address in RAM */
> + u64 length; /* Partition length in RAM in Bytes */
> + u32 partition_attribute; /* Partition attribute */
> + u32 partition_category; /* Partition category */
> + u32 partition_domain; /* Partition domain */
> + u32 partition_type; /* Partition type */
> + u32 num_partitions; /* Number of partitions on device */
> + u32 hw_info; /* hw information such as type and frequency */
> + u32 reserved4; /* Reserved for future use */
> + u32 reserved5; /* Reserved for future use */
> +};
> +
> +/*
> + * Defines the RAM partition table structure (Version 1)
> + *
> + * Do not change the placement of the first four elements so that future
> + * compatibility will always be guaranteed at least for the identifiers.
> + *
> + * The other portion of the structure may be changed as necessary to accommodate
> + * new features. Be sure to increment version number if you change it.
> + */
> +struct usable_ram_partition_table_v1 {
> + struct usable_ram_partition_table_header header;
> +
> + u32 reserved2; /* Added for 8 bytes alignment of header */
> +
> + /* RAM partition table entries */
> + struct ram_partition_entry_v1 entries[RAM_NUM_PART_ENTRIES];
> +};
> +
> +/* Version 0 structure 32 Bit - Holds information for an entry in the RAM partition table */
> +struct ram_partition_entry_v0 {
> + char name[RAM_PART_NAME_LENGTH]; /* Partition name, unused for now */
> + u32 start_address; /* Partition start address in RAM */
> + u32 length; /* Partition length in RAM in Bytes */
> + u32 partition_attribute; /* Partition attribute */
> + u32 partition_category; /* Partition category */
> + u32 partition_domain; /* Partition domain */
> + u32 partition_type; /* Partition type */
> + u32 num_partitions; /* Number of partitions on device */
> + u32 reserved3; /* Reserved for future use */
> + u32 reserved4; /* Reserved for future use */
> + u32 reserved5; /* Reserved for future use */
> +};
> +
> +/*
> + * Defines the RAM partition table structure (Version 0)
> + *
> + * Do not change the placement of the first four elements so that future
> + * compatibility will always be guaranteed at least for the identifiers.
> + *
> + * The other portion of the structure may be changed as necessary to accommodate
> + * new features. Be sure to increment version number if you change it.
> + */
> +struct usable_ram_partition_table_v0 {
> + struct usable_ram_partition_table_header header;
> +
> + /* RAM partition table entries */
> + struct ram_partition_entry_v0 entries[RAM_NUM_PART_ENTRIES];
> +};
> +
> +#endif // __QCOM_SMEM_RAMPART_H__
>
Thanks,
Neil
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 04/15] Revert "drivers: smem: sandbox"
2026-05-04 18:57 ` [PATCH v2 04/15] Revert "drivers: smem: sandbox" Casey Connolly
2026-05-05 10:26 ` Sumit Garg
2026-05-18 14:34 ` Neil Armstrong
@ 2026-05-18 15:51 ` Fabio Estevam
2 siblings, 0 replies; 51+ messages in thread
From: Fabio Estevam @ 2026-05-18 15:51 UTC (permalink / raw)
To: Casey Connolly
Cc: u-boot, Sumit Garg, u-boot-qcom, Tom Rini, Simon Glass, Peng Fan,
Marek Vasut, Alice Guo, Quentin Schulz, Ilias Apalodimas,
Neil Armstrong, Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao,
Stefan Roese, Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
On Mon, May 4, 2026 at 4:07 PM Casey Connolly <casey.connolly@linaro.org> wrote:
>
> This reverts commit 7fd7e2cf339ea2ec570f6dae1cdfaf8e066eb4af.
It would be helpful to have an explanation here for why the revert is
being made.
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v2 13/15] mach-snapdragon: support parsing memory map from SMEM
2026-05-04 18:57 ` [PATCH v2 13/15] mach-snapdragon: support parsing memory map from SMEM Casey Connolly
2026-05-18 14:48 ` Neil Armstrong
@ 2026-05-21 13:36 ` Stephan Gerhold
1 sibling, 0 replies; 51+ messages in thread
From: Stephan Gerhold @ 2026-05-21 13:36 UTC (permalink / raw)
To: Casey Connolly
Cc: u-boot, Sumit Garg, u-boot-qcom, Tom Rini, Simon Glass, Peng Fan,
Marek Vasut, Alice Guo, Quentin Schulz, Ilias Apalodimas,
Neil Armstrong, Mattijs Korpershoek, Kuan-Wei Chiu, Raymond Mao,
Stefan Roese, Philip Molloy, Jerome Forissier, Marek Vasut,
Varadarajan Narayanan, Patrice Chotard, Aswin Murugan,
Rasmus Villemoes, Heiko Schocher, Michal Simek, Sughosh Ganu,
Antony Kurniawan Soemardi, Luca Weiss, Balaji Selvanathan
On Mon, May 04, 2026 at 08:57:41PM +0200, Casey Connolly wrote:
> It is possible to derive the memory map for a Qualcomm platform from the
> SMEM shared memory region. The memory map is populated by the preloader.
>
> Introduce support for parsing this data and using it to populate
> U-Boot's memory map. Since we aren't yet sure if this will work for
> every platform, it is not yet used in all cases, if U-Boot is booted
> with an internal FDT which has the memory map defined, this will
> be used instead.
>
> If the FDT is internal FDT with no memory map defined, then U-Boot will
> try to use SMEM. This should remove the need to define the memory map
> statically in a U-Boot overlay for boards that don't chainload.
>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
Thanks a lot for working on this!
On DragonBoard 410c with (qcom_)dragonboard410c_defconfig:
$ make qcom_dragonboard410c_defconfig
HOSTCC scripts/basic/fixdep
GEN Makefile
HOSTCC scripts/kconfig/conf.o
YACC scripts/kconfig/zconf.tab.[ch]
LEX scripts/kconfig/zconf.lex.c
HOSTCC scripts/kconfig/zconf.tab.o
HOSTLD scripts/kconfig/conf
WARNING: unmet direct dependencies detected for QCOM_SMEM
Depends on [n]: SOC_QCOM [=n]
Selected by [y]:
- ARCH_SNAPDRAGON [=y] && <choice>
WARNING: unmet direct dependencies detected for QCOM_SMEM
Depends on [n]: SOC_QCOM [=n]
Selected by [y]:
- ARCH_SNAPDRAGON [=y] && <choice>
I added CONFIG_SOC_QCOM=y in (qcom_)dragonboard410c_defconfig to
workaround this, but we probably should select SOC_QCOM for
ARCH_SNAPDRAGON to fix this properly.
Then:
$ make
aarch64-linux-gnu-ld: arch/arm/mach-snapdragon/dram.o: in function `qcom_configure_capsule_updates':
arch/arm/mach-snapdragon/qcom-priv.h:38: multiple definition of `qcom_configure_capsule_updates';
arch/arm/mach-snapdragon/board.o:arch/arm/mach-snapdragon/qcom-priv.h:38: first defined here
I added:
diff --git a/arch/arm/mach-snapdragon/qcom-priv.h b/arch/arm/mach-snapdragon/qcom-priv.h
index 39dc8fcc76a..27e5c54e512 100644
--- a/arch/arm/mach-snapdragon/qcom-priv.h
+++ b/arch/arm/mach-snapdragon/qcom-priv.h
@@ -35,7 +35,7 @@ extern enum qcom_memmap_source qcom_memmap_source;
#if IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_SUPPORT)
void qcom_configure_capsule_updates(void);
#else
-void qcom_configure_capsule_updates(void) {}
+static inline void qcom_configure_capsule_updates(void) {}
#endif /* EFI_HAVE_CAPSULE_SUPPORT */
With that fixed:
U-Boot 2026.07-rc2-...
Qualcomm-DragonBoard 410C
DRAM: 1 GiB
...
U-Boot loaded from XBL
Memory map loaded from INTERNAL_FDT
Then I removed the /memory map in the internal FDT (delete
arch/arm/dts/apq8016-sbc-u-boot.dtsi), and it detects the correct 2 GiB
RAM on this board!
U-Boot 2026.07-rc2-...
Qualcomm-DragonBoard 410C
DRAM: 2 GiB
...
U-Boot loaded from XBL
Memory map loaded from SMEM
So feel free to add my Tested-by: # db410c if you fix the errors
mentioned above (also feel free to include a patch to delete
arch/arm/dts/apq8016-sbc-u-boot.dtsi, or I can do this myself later once
your series lands).
Thanks!
Stephan
^ permalink raw reply related [flat|nested] 51+ messages in thread
end of thread, other threads:[~2026-05-21 13:36 UTC | newest]
Thread overview: 51+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-04 18:57 [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot Casey Connolly
2026-05-04 18:57 ` [PATCH v2 01/15] Revert "dm: SMEM (Shared memory) uclass" Casey Connolly
2026-05-05 10:15 ` Sumit Garg
2026-05-07 15:31 ` Simon Glass
2026-05-07 20:05 ` Casey Connolly
2026-05-18 14:34 ` Neil Armstrong
2026-05-04 18:57 ` [PATCH v2 02/15] smem: drop drivers/smem Casey Connolly
2026-05-18 14:34 ` Neil Armstrong
2026-05-04 18:57 ` [PATCH v2 03/15] Revert "test: smem: add basic smem test" Casey Connolly
2026-05-05 10:26 ` Sumit Garg
2026-05-18 14:34 ` Neil Armstrong
2026-05-04 18:57 ` [PATCH v2 04/15] Revert "drivers: smem: sandbox" Casey Connolly
2026-05-05 10:26 ` Sumit Garg
2026-05-18 14:34 ` Neil Armstrong
2026-05-18 15:51 ` Fabio Estevam
2026-05-04 18:57 ` [PATCH v2 05/15] mach-snapdragon: fix reserved memory carveout Casey Connolly
2026-05-05 12:25 ` Sumit Garg
2026-05-05 12:39 ` Casey Connolly
2026-05-07 7:45 ` Sumit Garg
2026-05-07 20:29 ` Casey Connolly
2026-05-04 18:57 ` [PATCH v2 06/15] soc: qcom: import smem from Linux 6.11-rc2 Casey Connolly
2026-05-05 12:40 ` Sumit Garg
2026-05-05 12:45 ` Casey Connolly
2026-05-18 14:36 ` Neil Armstrong
2026-05-04 18:57 ` [PATCH v2 07/15] soc: qcom: smem: adjust headers for U-Boot Casey Connolly
2026-05-05 12:43 ` Sumit Garg
2026-05-18 14:36 ` Neil Armstrong
2026-05-04 18:57 ` [PATCH v2 08/15] soc: qcom: smem: adjust " Casey Connolly
2026-05-08 10:43 ` Aswin Murugan
2026-05-11 12:22 ` Casey Connolly
2026-05-18 11:03 ` Sumit Garg
2026-05-04 18:57 ` [PATCH v2 09/15] soc: qcom: smem: get serial number from socinfo Casey Connolly
2026-05-18 12:49 ` Sumit Garg
2026-05-04 18:57 ` [PATCH v2 10/15] soc: qcom: smem: stub functions Casey Connolly
2026-05-18 14:37 ` Neil Armstrong
2026-05-04 18:57 ` [PATCH v2 11/15] soc: qcom: smem: add build infra Casey Connolly
2026-05-18 12:53 ` Sumit Garg
2026-05-18 14:38 ` Neil Armstrong
2026-05-04 18:57 ` [PATCH v2 12/15] mach-snapdragon: move memory parsing to its own file Casey Connolly
2026-05-18 12:54 ` Sumit Garg
2026-05-18 14:40 ` Neil Armstrong
2026-05-04 18:57 ` [PATCH v2 13/15] mach-snapdragon: support parsing memory map from SMEM Casey Connolly
2026-05-18 14:48 ` Neil Armstrong
2026-05-21 13:36 ` Stephan Gerhold
2026-05-04 18:57 ` [PATCH v2 14/15] mach-snapdragon: fetch serial# " Casey Connolly
2026-05-18 13:15 ` Sumit Garg
2026-05-04 18:57 ` [PATCH v2 15/15] configs: add qcom_sm8650_defconfig and debug fragment Casey Connolly
2026-05-05 6:45 ` Luca Weiss
2026-05-05 10:11 ` [PATCH v2 00/15] qcom: smem: modernize SMEM in U-Boot Sumit Garg
2026-05-05 12:25 ` Casey Connolly
2026-05-05 12:35 ` Sumit Garg
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.