From patchwork Thu Aug 13 02:37:42 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mathieu Olivari X-Patchwork-Id: 506822 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from arrakis.dune.hu (arrakis.dune.hu [78.24.191.176]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 47EB91401F6 for ; Thu, 13 Aug 2015 12:41:39 +1000 (AEST) Received: from arrakis.dune.hu (localhost [127.0.0.1]) by arrakis.dune.hu (Postfix) with ESMTP id 217B628C0CA; Thu, 13 Aug 2015 04:39:10 +0200 (CEST) Received: from arrakis.dune.hu (localhost [127.0.0.1]) by arrakis.dune.hu (Postfix) with ESMTP id ECA5B28BF3F for ; Thu, 13 Aug 2015 04:37:27 +0200 (CEST) X-policyd-weight: using cached result; rate:hard: -7.6 Received: from smtp.codeaurora.org (smtp.codeaurora.org [198.145.29.96]) by arrakis.dune.hu (Postfix) with ESMTPS for ; Thu, 13 Aug 2015 04:37:15 +0200 (CEST) Received: from smtp.codeaurora.org (localhost [127.0.0.1]) by smtp.codeaurora.org (Postfix) with ESMTP id 6F567140D62; Thu, 13 Aug 2015 02:37:57 +0000 (UTC) Received: by smtp.codeaurora.org (Postfix, from userid 486) id 60744140D6E; Thu, 13 Aug 2015 02:37:57 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-caf-smtp.dmz.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=ALL_TRUSTED,BAYES_00 autolearn=ham version=3.3.1 Received: from mathieu-linux.qualcomm.com (qf-scl1nat.qualcomm.com [207.114.132.30]) (using TLSv1.2 with cipher AES128-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: mathieu@smtp.codeaurora.org) by smtp.codeaurora.org (Postfix) with ESMTPSA id 18D48140D62; Thu, 13 Aug 2015 02:37:51 +0000 (UTC) From: Mathieu Olivari To: nbd@openwrt.org, jogo@openwrt.org, blogic@openwrt.org Date: Wed, 12 Aug 2015 19:37:42 -0700 Message-Id: <1439433464-1526-4-git-send-email-mathieu@codeaurora.org> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1439433464-1526-1-git-send-email-mathieu@codeaurora.org> References: <1439433464-1526-1-git-send-email-mathieu@codeaurora.org> X-Virus-Scanned: ClamAV using ClamSMTP Cc: openwrt-devel@lists.openwrt.org Subject: [OpenWrt-Devel] [PATCH 3/5] ipq806x: add SMEM support X-BeenThere: openwrt-devel@lists.openwrt.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: OpenWrt Development List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: openwrt-devel-bounces@lists.openwrt.org Sender: "openwrt-devel" 2 patches are cherry-picked from the following LKML thread: *https://lkml.org/lkml/2015/4/11/208 The last patch (036-soc-qcom-add-smem-to-IPQ806x-platforms.patch) is adding the corresponding DT nodes required for IPQ806x. Signed-off-by: Mathieu Olivari --- ...soc-qcom-Add-device-tree-binding-for-SMEM.patch | 82 ++ ...soc-qcom-Add-Shared-Memory-Manager-driver.patch | 841 +++++++++++++++++++++ ...36-soc-qcom-add-smem-to-IPQ806x-platforms.patch | 26 + ...1-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch | 4 +- ...-qcom-add-pcie-nodes-to-ipq806x-platforms.patch | 4 +- .../patches-3.18/115-add-pcie-aux-clk-dts.patch | 6 +- .../157-ARM-DT-ipq8064-Add-ADM-device-node.patch | 2 +- ...-dts-Add-NAND-controller-node-for-ipq806x.patch | 2 +- ...-qcom-add-gmac-nodes-to-ipq806x-platforms.patch | 2 +- ...soc-qcom-Add-device-tree-binding-for-SMEM.patch | 82 ++ ...soc-qcom-Add-Shared-Memory-Manager-driver.patch | 841 +++++++++++++++++++++ ...36-soc-qcom-add-smem-to-IPQ806x-platforms.patch | 27 + ...-qcom-add-pcie-nodes-to-ipq806x-platforms.patch | 2 +- .../patches-4.1/115-add-pcie-aux-clk-dts.patch | 6 +- .../patches-4.1/126-add-rpm-to-ipq8064-dts.patch | 4 +- ...m-Add-necessary-DT-data-for-Krait-cpufreq.patch | 4 +- .../157-ARM-DT-ipq8064-Add-ADM-device-node.patch | 2 +- ...-dts-Add-NAND-controller-node-for-ipq806x.patch | 2 +- ...-qcom-add-gmac-nodes-to-ipq806x-platforms.patch | 2 +- 19 files changed, 1920 insertions(+), 21 deletions(-) create mode 100644 target/linux/ipq806x/patches-3.18/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch create mode 100644 target/linux/ipq806x/patches-3.18/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch create mode 100644 target/linux/ipq806x/patches-3.18/036-soc-qcom-add-smem-to-IPQ806x-platforms.patch create mode 100644 target/linux/ipq806x/patches-4.1/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch create mode 100644 target/linux/ipq806x/patches-4.1/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch create mode 100644 target/linux/ipq806x/patches-4.1/036-soc-qcom-add-smem-to-IPQ806x-platforms.patch diff --git a/target/linux/ipq806x/patches-3.18/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch b/target/linux/ipq806x/patches-3.18/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch new file mode 100644 index 0000000..d22db22 --- /dev/null +++ b/target/linux/ipq806x/patches-3.18/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch @@ -0,0 +1,82 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2,1/2] soc: qcom: Add device tree binding for SMEM +From: Bjorn Andersson +X-Patchwork-Id: 6202201 +Message-Id: <1428795178-24312-1-git-send-email-bjorn.andersson@sonymobile.com> +To: Rob Herring , Pawel Moll , + Mark Rutland , + Ian Campbell , + Kumar Gala , Jeffrey Hugo , + Andry Gross +Cc: , + linux-arm-msm , + +Date: Sat, 11 Apr 2015 16:32:57 -0700 + +Add device tree binding documentation for the Qualcom Shared Memory +manager. + +Signed-off-by: Bjorn Andersson + +--- +Changes since v1: +- None + + .../devicetree/bindings/soc/qcom/qcom,smem.txt | 49 ++++++++++++++++++++++ + 1 file changed, 49 insertions(+) + create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,smem.txt + +--- /dev/null ++++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smem.txt +@@ -0,0 +1,49 @@ ++Qualcomm Shared Memory binding ++ ++This binding describes the Qualcomm Shared Memory, used to share data between ++various subsystems and OSes in Qualcomm platforms. ++ ++- compatible: ++ Usage: required ++ Value type: ++ Definition: must be: ++ "qcom,smem" ++ ++- memory-region: ++ Usage: required ++ Value type: ++ Definition: handle to memory reservation for main smem memory region. ++ ++- reg: ++ Usage: optional ++ Value type: ++ Definition: base address and size pair for any additional memory areas ++ of the shared memory. ++ ++- hwspinlocks: ++ Usage: required ++ Value type: ++ Definition: reference to a hwspinlock used to protect allocations from ++ the shared memory ++ ++= EXAMPLE ++ ++ reserved-memory { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ ++ smem_region: smem@fa00000 { ++ reg = <0xfa00000 0x200000>; ++ no-map; ++ }; ++ }; ++ ++ smem@fa00000 { ++ compatible = "qcom,smem"; ++ ++ memory-region = <&smem_region>; ++ reg = <0xfc428000 0x4000>; ++ ++ hwlocks = <&tcsr_mutex 3>; ++ }; diff --git a/target/linux/ipq806x/patches-3.18/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch b/target/linux/ipq806x/patches-3.18/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch new file mode 100644 index 0000000..c8cff1a --- /dev/null +++ b/target/linux/ipq806x/patches-3.18/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch @@ -0,0 +1,841 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2,2/2] soc: qcom: Add Shared Memory Manager driver +From: Bjorn Andersson +X-Patchwork-Id: 6202211 +Message-Id: <1428795178-24312-2-git-send-email-bjorn.andersson@sonymobile.com> +To: Kumar Gala , Andy Gross , + David Brown , Jeffrey Hugo +Cc: , , + +Date: Sat, 11 Apr 2015 16:32:58 -0700 + +The Shared Memory Manager driver implements an interface for allocating +and accessing items in the memory area shared among all of the +processors in a Qualcomm platform. + +Signed-off-by: Bjorn Andersson +Reviewed-by: Andy Gross +Tested-by: Andy Gross + +--- +Changes since v1: +- ioremapping the regions nocache +- improved documentation of the two regions of partitions +- corrected free space check in private allocator + + drivers/soc/qcom/Kconfig | 7 + + drivers/soc/qcom/Makefile | 1 + + drivers/soc/qcom/smem.c | 768 ++++++++++++++++++++++++++++++++++++++++++ + include/linux/soc/qcom/smem.h | 14 + + 4 files changed, 790 insertions(+) + create mode 100644 drivers/soc/qcom/smem.c + create mode 100644 include/linux/soc/qcom/smem.h + +--- a/drivers/soc/qcom/Kconfig ++++ b/drivers/soc/qcom/Kconfig +@@ -9,3 +9,10 @@ config QCOM_GSBI + functions for connecting the underlying serial UART, SPI, and I2C + devices to the output pins. + ++config QCOM_SMEM ++ tristate "Qualcomm Shared Memory Manager (SMEM)" ++ depends on ARCH_QCOM ++ help ++ Say y here to 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. +--- a/drivers/soc/qcom/Makefile ++++ b/drivers/soc/qcom/Makefile +@@ -1 +1,2 @@ + obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o ++obj-$(CONFIG_QCOM_SMEM) += smem.o +--- /dev/null ++++ b/drivers/soc/qcom/smem.c +@@ -0,0 +1,768 @@ ++/* ++ * Copyright (c) 2015, Sony Mobile Communications AB. ++ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * 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 metadata 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. ++ * ++ * ++ * 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. ++ * ++ */ ++ ++/** ++ * 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 { ++ u32 command; ++ u32 status; ++ u32 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 { ++ u32 allocated; ++ u32 offset; ++ u32 size; ++ u32 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]; ++ u32 version[32]; ++ u32 initialized; ++ u32 free_offset; ++ u32 available; ++ u32 reserved; ++ struct smem_global_entry toc[]; ++}; ++ ++/** ++ * 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 ++ * @reserved: reserved entries for later use ++ */ ++struct smem_ptable_entry { ++ u32 offset; ++ u32 size; ++ u32 flags; ++ u16 host0; ++ u16 host1; ++ u32 reserved[8]; ++}; ++ ++/** ++ * 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 { ++ u32 magic; ++ u32 version; ++ u32 num_entries; ++ u32 reserved[5]; ++ struct smem_ptable_entry entry[]; ++}; ++#define SMEM_PTABLE_MAGIC 0x434f5424 /* "$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 { ++ u32 magic; ++ u16 host0; ++ u16 host1; ++ u32 size; ++ u32 offset_free_uncached; ++ u32 offset_free_cached; ++ u32 reserved[3]; ++}; ++#define SMEM_PART_MAGIC 0x54525024 /* "$PRT" */ ++ ++/** ++ * 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; ++ u16 item; ++ u32 size; /* includes padding bytes */ ++ u16 padding_data; ++ u16 padding_hdr; ++ u32 reserved; ++}; ++#define SMEM_PRIVATE_CANARY 0xa5a5 ++ ++/* ++ * Item 3 of the global heap contains an array of versions for the various ++ * software components in the SoC. We verify that the boot loader version is ++ * what the expected version (SMEM_EXPECTED_VERSION) as a sanity check. ++ */ ++#define SMEM_ITEM_VERSION 3 ++#define SMEM_MASTER_SBL_VERSION_INDEX 7 ++#define SMEM_EXPECTED_VERSION 11 ++ ++/* ++ * 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_LAST 512 ++ ++/* Processor/host identifier for the application processor */ ++#define SMEM_HOST_APPS 0 ++ ++/* Max number of processors/hosts in a system */ ++#define SMEM_HOST_COUNT 7 ++ ++/** ++ * 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 ++ * @hwlock: reference to a hwspinlock ++ * @partitions: list of pointers to partitions affecting the current ++ * processor/host ++ * @num_regions: number of @regions ++ * @regions: list of the memory regions defining the shared memory ++ */ ++struct qcom_smem { ++ struct device *dev; ++ ++ struct hwspinlock *hwlock; ++ ++ struct smem_partition_header *partitions[SMEM_HOST_COUNT]; ++ ++ unsigned num_regions; ++ struct smem_region regions[0]; ++}; ++ ++/* 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 ++ ++static int qcom_smem_alloc_private(struct qcom_smem *smem, ++ unsigned host, ++ unsigned item, ++ size_t size) ++{ ++ struct smem_partition_header *phdr; ++ struct smem_private_entry *hdr; ++ size_t alloc_size; ++ void *p; ++ ++ /* We're not going to find it if there's no matching partition */ ++ if (host >= SMEM_HOST_COUNT || !smem->partitions[host]) ++ return -ENOENT; ++ ++ phdr = smem->partitions[host]; ++ ++ p = (void *)phdr + sizeof(*phdr); ++ while (p < (void *)phdr + phdr->offset_free_uncached) { ++ hdr = p; ++ ++ if (hdr->canary != SMEM_PRIVATE_CANARY) { ++ dev_err(smem->dev, ++ "Found invalid canary in host %d partition\n", ++ host); ++ return -EINVAL; ++ } ++ ++ if (hdr->item == item) ++ return -EEXIST; ++ ++ p += sizeof(*hdr) + hdr->padding_hdr + hdr->size; ++ } ++ ++ /* Check that we don't grow into the cached region */ ++ alloc_size = sizeof(*hdr) + ALIGN(size, 8); ++ if (p + alloc_size >= (void *)phdr + phdr->offset_free_cached) { ++ dev_err(smem->dev, "Out of memory\n"); ++ return -ENOSPC; ++ } ++ ++ hdr = p; ++ hdr->canary = SMEM_PRIVATE_CANARY; ++ hdr->item = item; ++ hdr->size = ALIGN(size, 8); ++ hdr->padding_data = 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(); ++ phdr->offset_free_uncached += alloc_size; ++ ++ return 0; ++} ++ ++static int qcom_smem_alloc_global(struct qcom_smem *smem, ++ unsigned item, ++ size_t size) ++{ ++ struct smem_header *header; ++ struct smem_global_entry *entry; ++ ++ if (WARN_ON(item >= SMEM_ITEM_LAST)) ++ return -EINVAL; ++ ++ header = smem->regions[0].virt_base; ++ entry = &header->toc[item]; ++ if (entry->allocated) ++ return -EEXIST; ++ ++ size = ALIGN(size, 8); ++ if (WARN_ON(size > header->available)) ++ return -ENOMEM; ++ ++ entry->offset = header->free_offset; ++ entry->size = 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 = 1; ++ ++ header->free_offset += size; ++ 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) ++{ ++ 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; ++ } ++ ++ ret = hwspin_lock_timeout_irqsave(__smem->hwlock, ++ HWSPINLOCK_TIMEOUT, ++ &flags); ++ if (ret) ++ return ret; ++ ++ ret = qcom_smem_alloc_private(__smem, host, item, size); ++ if (ret == -ENOENT) ++ ret = qcom_smem_alloc_global(__smem, item, size); ++ ++ hwspin_unlock_irqrestore(__smem->hwlock, &flags); ++ ++ return ret; ++} ++EXPORT_SYMBOL(qcom_smem_alloc); ++ ++static int qcom_smem_get_global(struct qcom_smem *smem, ++ unsigned item, ++ void **ptr, ++ size_t *size) ++{ ++ struct smem_header *header; ++ struct smem_region *area; ++ struct smem_global_entry *entry; ++ u32 aux_base; ++ unsigned i; ++ ++ if (WARN_ON(item >= SMEM_ITEM_LAST)) ++ return -EINVAL; ++ ++ header = smem->regions[0].virt_base; ++ entry = &header->toc[item]; ++ if (!entry->allocated) ++ return -ENXIO; ++ ++ if (ptr != NULL) { ++ aux_base = 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) { ++ *ptr = area->virt_base + entry->offset; ++ break; ++ } ++ } ++ } ++ if (size != NULL) ++ *size = entry->size; ++ ++ return 0; ++} ++ ++static int qcom_smem_get_private(struct qcom_smem *smem, ++ unsigned host, ++ unsigned item, ++ void **ptr, ++ size_t *size) ++{ ++ struct smem_partition_header *phdr; ++ struct smem_private_entry *hdr; ++ void *p; ++ ++ /* We're not going to find it if there's no matching partition */ ++ if (host >= SMEM_HOST_COUNT || !smem->partitions[host]) ++ return -ENOENT; ++ ++ phdr = smem->partitions[host]; ++ ++ p = (void *)phdr + sizeof(*phdr); ++ while (p < (void *)phdr + phdr->offset_free_uncached) { ++ hdr = p; ++ ++ if (hdr->canary != SMEM_PRIVATE_CANARY) { ++ dev_err(smem->dev, ++ "Found invalid canary in host %d partition\n", ++ host); ++ return -EINVAL; ++ } ++ ++ if (hdr->item == item) { ++ if (ptr != NULL) ++ *ptr = p + sizeof(*hdr) + hdr->padding_hdr; ++ ++ if (size != NULL) ++ *size = hdr->size - hdr->padding_data; ++ ++ return 0; ++ } ++ ++ p += sizeof(*hdr) + hdr->padding_hdr + hdr->size; ++ } ++ ++ return -ENOENT; ++} ++ ++/** ++ * qcom_smem_get - resolve ptr of size of a smem item ++ * @host: the remote processor, or -1 ++ * @item: smem item handle ++ * @ptr: pointer to be filled out with address of the item ++ * @size: pointer to be filled out with size of the item ++ * ++ * Looks up pointer and size of a smem item. ++ */ ++int qcom_smem_get(unsigned host, unsigned item, void **ptr, size_t *size) ++{ ++ unsigned long flags; ++ int ret; ++ ++ if (!__smem) ++ return -EPROBE_DEFER; ++ ++ ret = hwspin_lock_timeout_irqsave(__smem->hwlock, ++ HWSPINLOCK_TIMEOUT, ++ &flags); ++ if (ret) ++ return ret; ++ ++ ret = qcom_smem_get_private(__smem, host, item, ptr, size); ++ if (ret == -ENOENT) ++ ret = qcom_smem_get_global(__smem, item, ptr, size); ++ ++ hwspin_unlock_irqrestore(__smem->hwlock, &flags); ++ return ret; ++ ++} ++EXPORT_SYMBOL(qcom_smem_get); ++ ++/** ++ * qcom_smem_get_free_space - retrieve amont of free space in a partition ++ * @host: the remote processor identifing 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_header *phdr; ++ struct smem_header *header; ++ unsigned ret; ++ ++ if (!__smem) ++ return -EPROBE_DEFER; ++ ++ if (host < SMEM_HOST_COUNT && __smem->partitions[host]) { ++ phdr = __smem->partitions[host]; ++ ret = phdr->offset_free_uncached; ++ } else { ++ header = __smem->regions[0].virt_base; ++ ret = header->available; ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL(qcom_smem_get_free_space); ++ ++static int qcom_smem_get_sbl_version(struct qcom_smem *smem) ++{ ++ unsigned *versions; ++ size_t size; ++ int ret; ++ ++ ret = qcom_smem_get_global(smem, SMEM_ITEM_VERSION, ++ (void **)&versions, &size); ++ if (ret < 0) { ++ dev_err(smem->dev, "Unable to read the version item\n"); ++ return -ENOENT; ++ } ++ ++ if (size < sizeof(unsigned) * SMEM_MASTER_SBL_VERSION_INDEX) { ++ dev_err(smem->dev, "Version item is too small\n"); ++ return -EINVAL; ++ } ++ ++ return versions[SMEM_MASTER_SBL_VERSION_INDEX]; ++} ++ ++static int qcom_smem_enumerate_partitions(struct qcom_smem *smem, ++ unsigned local_host) ++{ ++ struct smem_partition_header *header; ++ struct smem_ptable_entry *entry; ++ struct smem_ptable *ptable; ++ unsigned remote_host; ++ int i; ++ ++ ptable = smem->regions[0].virt_base + smem->regions[0].size - 4 * 1024; ++ if (ptable->magic != SMEM_PTABLE_MAGIC) ++ return 0; ++ ++ if (ptable->version != 1) { ++ dev_err(smem->dev, ++ "Unsupported partition header version %d\n", ++ ptable->version); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < ptable->num_entries; i++) { ++ entry = &ptable->entry[i]; ++ ++ if (entry->host0 != local_host && entry->host1 != local_host) ++ continue; ++ ++ if (!entry->offset) ++ continue; ++ ++ if (!entry->size) ++ continue; ++ ++ if (entry->host0 == local_host) ++ remote_host = entry->host1; ++ else ++ remote_host = entry->host0; ++ ++ 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 + entry->offset; ++ ++ if (header->magic != SMEM_PART_MAGIC) { ++ dev_err(smem->dev, ++ "Partition %d has invalid magic\n", i); ++ return -EINVAL; ++ } ++ ++ if (header->host0 != local_host && header->host1 != local_host) { ++ dev_err(smem->dev, ++ "Partition %d hosts are invalid\n", i); ++ return -EINVAL; ++ } ++ ++ if (header->host0 != remote_host && header->host1 != remote_host) { ++ dev_err(smem->dev, ++ "Partition %d hosts are invalid\n", i); ++ return -EINVAL; ++ } ++ ++ if (header->size != entry->size) { ++ dev_err(smem->dev, ++ "Partition %d has invalid size\n", i); ++ return -EINVAL; ++ } ++ ++ if (header->offset_free_uncached > header->size) { ++ dev_err(smem->dev, ++ "Partition %d has invalid free pointer\n", i); ++ return -EINVAL; ++ } ++ ++ smem->partitions[remote_host] = header; ++ } ++ ++ return 0; ++} ++ ++static int qcom_smem_count_mem_regions(struct platform_device *pdev) ++{ ++ struct resource *res; ++ int num_regions = 0; ++ int i; ++ ++ for (i = 0; i < pdev->num_resources; i++) { ++ res = &pdev->resource[i]; ++ ++ if (resource_type(res) == IORESOURCE_MEM) ++ num_regions++; ++ } ++ ++ return num_regions; ++} ++ ++static int qcom_smem_probe(struct platform_device *pdev) ++{ ++ struct smem_header *header; ++ struct device_node *np; ++ struct qcom_smem *smem; ++ struct resource *res; ++ struct resource r; ++ size_t array_size; ++ int num_regions = 0; ++ int hwlock_id; ++ u32 version; ++ int ret; ++ int i; ++ ++ num_regions = qcom_smem_count_mem_regions(pdev) + 1; ++ ++ array_size = num_regions * sizeof(struct smem_region); ++ smem = devm_kzalloc(&pdev->dev, sizeof(*smem) + array_size, GFP_KERNEL); ++ if (!smem) ++ return -ENOMEM; ++ ++ smem->dev = &pdev->dev; ++ smem->num_regions = num_regions; ++ ++ np = of_parse_phandle(pdev->dev.of_node, "memory-region", 0); ++ if (!np) { ++ dev_err(&pdev->dev, "No memory-region specified\n"); ++ return -EINVAL; ++ } ++ ++ ret = of_address_to_resource(np, 0, &r); ++ of_node_put(np); ++ if (ret) ++ return ret; ++ ++ smem->regions[0].aux_base = (u32)r.start; ++ smem->regions[0].size = resource_size(&r); ++ smem->regions[0].virt_base = devm_ioremap_nocache(&pdev->dev, ++ r.start, ++ resource_size(&r)); ++ if (!smem->regions[0].virt_base) ++ return -ENOMEM; ++ ++ for (i = 1; i < num_regions; i++) { ++ res = platform_get_resource(pdev, IORESOURCE_MEM, i - 1); ++ ++ smem->regions[i].aux_base = (u32)res->start; ++ smem->regions[i].size = resource_size(res); ++ smem->regions[i].virt_base = devm_ioremap_nocache(&pdev->dev, ++ res->start, ++ resource_size(res)); ++ if (!smem->regions[i].virt_base) ++ return -ENOMEM; ++ } ++ ++ header = smem->regions[0].virt_base; ++ if (header->initialized != 1 || header->reserved) { ++ dev_err(&pdev->dev, "SMEM is not initilized by SBL\n"); ++ return -EINVAL; ++ } ++ ++ version = qcom_smem_get_sbl_version(smem); ++ if (version >> 16 != SMEM_EXPECTED_VERSION) { ++ dev_err(&pdev->dev, "Unsupported smem version 0x%x\n", version); ++ return -EINVAL; ++ } ++ ++ ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS); ++ if (ret < 0) ++ return ret; ++ ++ hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0); ++ if (hwlock_id < 0) { ++ 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; ++ ++ __smem = smem; ++ ++ return 0; ++} ++ ++static int qcom_smem_remove(struct platform_device *pdev) ++{ ++ hwspin_lock_free(__smem->hwlock); ++ __smem = NULL; ++ ++ return 0; ++} ++ ++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 = 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 "); ++MODULE_DESCRIPTION("Qualcomm Shared Memory Manager"); ++MODULE_LICENSE("GPLv2"); +--- /dev/null ++++ b/include/linux/soc/qcom/smem.h +@@ -0,0 +1,14 @@ ++#ifndef __QCOM_SMEM_H__ ++#define __QCOM_SMEM_H__ ++ ++struct device_node; ++struct qcom_smem; ++ ++#define QCOM_SMEM_HOST_ANY -1 ++ ++int qcom_smem_alloc(unsigned host, unsigned item, size_t size); ++int qcom_smem_get(unsigned host, unsigned item, void **ptr, size_t *size); ++ ++int qcom_smem_get_free_space(unsigned host); ++ ++#endif diff --git a/target/linux/ipq806x/patches-3.18/036-soc-qcom-add-smem-to-IPQ806x-platforms.patch b/target/linux/ipq806x/patches-3.18/036-soc-qcom-add-smem-to-IPQ806x-platforms.patch new file mode 100644 index 0000000..9ba69dd --- /dev/null +++ b/target/linux/ipq806x/patches-3.18/036-soc-qcom-add-smem-to-IPQ806x-platforms.patch @@ -0,0 +1,26 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -54,7 +54,7 @@ + no-map; + }; + +- smem@41000000 { ++ smem: smem@41000000 { + reg = <0x41000000 0x200000>; + no-map; + }; +@@ -303,5 +303,14 @@ + + #hwlock-cells = <1>; + }; ++ ++ smem { ++ compatible = "qcom,smem"; ++ ++ memory-region = <&smem>; ++ reg = <0x41000000 0x4000>; ++ ++ hwlocks = <&sfpb_mutex 3>; ++ }; + }; + }; diff --git a/target/linux/ipq806x/patches-3.18/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch b/target/linux/ipq806x/patches-3.18/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch index d430572..755d0b0 100644 --- a/target/linux/ipq806x/patches-3.18/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch +++ b/target/linux/ipq806x/patches-3.18/101-ARM-qcom-add-USB-nodes-to-ipq806x-ap148.patch @@ -32,9 +32,9 @@ }; --- a/arch/arm/boot/dts/qcom-ipq8064.dtsi +++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -303,5 +303,90 @@ +@@ -312,5 +312,90 @@ - #hwlock-cells = <1>; + hwlocks = <&sfpb_mutex 3>; }; + + hs_phy_1: phy@100f8800 { diff --git a/target/linux/ipq806x/patches-3.18/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-3.18/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch index 66f305e..d5b8b6f 100644 --- a/target/linux/ipq806x/patches-3.18/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch +++ b/target/linux/ipq806x/patches-3.18/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch @@ -134,8 +134,8 @@ Signed-off-by: Mathieu Olivari / { model = "Qualcomm IPQ8064"; -@@ -318,6 +320,129 @@ - #hwlock-cells = <1>; +@@ -327,6 +329,129 @@ + hwlocks = <&sfpb_mutex 3>; }; + pcie0: pci@1b500000 { diff --git a/target/linux/ipq806x/patches-3.18/115-add-pcie-aux-clk-dts.patch b/target/linux/ipq806x/patches-3.18/115-add-pcie-aux-clk-dts.patch index 99bd360..bf28446 100644 --- a/target/linux/ipq806x/patches-3.18/115-add-pcie-aux-clk-dts.patch +++ b/target/linux/ipq806x/patches-3.18/115-add-pcie-aux-clk-dts.patch @@ -1,6 +1,6 @@ --- a/arch/arm/boot/dts/qcom-ipq8064.dtsi +++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -348,15 +348,21 @@ +@@ -357,15 +357,21 @@ clocks = <&gcc PCIE_A_CLK>, <&gcc PCIE_H_CLK>, @@ -26,7 +26,7 @@ status = "disabled"; }; -@@ -389,15 +395,21 @@ +@@ -398,15 +404,21 @@ clocks = <&gcc PCIE_1_A_CLK>, <&gcc PCIE_1_H_CLK>, @@ -52,7 +52,7 @@ status = "disabled"; }; -@@ -430,15 +442,21 @@ +@@ -439,15 +451,21 @@ clocks = <&gcc PCIE_2_A_CLK>, <&gcc PCIE_2_H_CLK>, diff --git a/target/linux/ipq806x/patches-3.18/157-ARM-DT-ipq8064-Add-ADM-device-node.patch b/target/linux/ipq806x/patches-3.18/157-ARM-DT-ipq8064-Add-ADM-device-node.patch index 8b137d7..34507de 100644 --- a/target/linux/ipq806x/patches-3.18/157-ARM-DT-ipq8064-Add-ADM-device-node.patch +++ b/target/linux/ipq806x/patches-3.18/157-ARM-DT-ipq8064-Add-ADM-device-node.patch @@ -13,7 +13,7 @@ Signed-off-by: Andy Gross --- a/arch/arm/boot/dts/qcom-ipq8064.dtsi +++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -669,5 +669,25 @@ +@@ -678,5 +678,25 @@ }; }; diff --git a/target/linux/ipq806x/patches-3.18/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch b/target/linux/ipq806x/patches-3.18/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch index fe8e77c..e837bea 100644 --- a/target/linux/ipq806x/patches-3.18/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch +++ b/target/linux/ipq806x/patches-3.18/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch @@ -26,7 +26,7 @@ arch/arm/boot/dts/qcom-ipq8064.dtsi | 15 +++++++++++++++ --- a/arch/arm/boot/dts/qcom-ipq8064.dtsi +++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -689,5 +689,21 @@ +@@ -698,5 +698,21 @@ status = "disabled"; }; diff --git a/target/linux/ipq806x/patches-3.18/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-3.18/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch index 7e7cd47..52ffd6c 100644 --- a/target/linux/ipq806x/patches-3.18/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch +++ b/target/linux/ipq806x/patches-3.18/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch @@ -117,7 +117,7 @@ Signed-off-by: Mathieu Olivari }; --- a/arch/arm/boot/dts/qcom-ipq8064.dtsi +++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -705,5 +705,91 @@ +@@ -714,5 +714,91 @@ status = "disabled"; }; diff --git a/target/linux/ipq806x/patches-4.1/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch b/target/linux/ipq806x/patches-4.1/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch new file mode 100644 index 0000000..d22db22 --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/034-soc-qcom-Add-device-tree-binding-for-SMEM.patch @@ -0,0 +1,82 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2,1/2] soc: qcom: Add device tree binding for SMEM +From: Bjorn Andersson +X-Patchwork-Id: 6202201 +Message-Id: <1428795178-24312-1-git-send-email-bjorn.andersson@sonymobile.com> +To: Rob Herring , Pawel Moll , + Mark Rutland , + Ian Campbell , + Kumar Gala , Jeffrey Hugo , + Andry Gross +Cc: , + linux-arm-msm , + +Date: Sat, 11 Apr 2015 16:32:57 -0700 + +Add device tree binding documentation for the Qualcom Shared Memory +manager. + +Signed-off-by: Bjorn Andersson + +--- +Changes since v1: +- None + + .../devicetree/bindings/soc/qcom/qcom,smem.txt | 49 ++++++++++++++++++++++ + 1 file changed, 49 insertions(+) + create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,smem.txt + +--- /dev/null ++++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smem.txt +@@ -0,0 +1,49 @@ ++Qualcomm Shared Memory binding ++ ++This binding describes the Qualcomm Shared Memory, used to share data between ++various subsystems and OSes in Qualcomm platforms. ++ ++- compatible: ++ Usage: required ++ Value type: ++ Definition: must be: ++ "qcom,smem" ++ ++- memory-region: ++ Usage: required ++ Value type: ++ Definition: handle to memory reservation for main smem memory region. ++ ++- reg: ++ Usage: optional ++ Value type: ++ Definition: base address and size pair for any additional memory areas ++ of the shared memory. ++ ++- hwspinlocks: ++ Usage: required ++ Value type: ++ Definition: reference to a hwspinlock used to protect allocations from ++ the shared memory ++ ++= EXAMPLE ++ ++ reserved-memory { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ ++ smem_region: smem@fa00000 { ++ reg = <0xfa00000 0x200000>; ++ no-map; ++ }; ++ }; ++ ++ smem@fa00000 { ++ compatible = "qcom,smem"; ++ ++ memory-region = <&smem_region>; ++ reg = <0xfc428000 0x4000>; ++ ++ hwlocks = <&tcsr_mutex 3>; ++ }; diff --git a/target/linux/ipq806x/patches-4.1/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch b/target/linux/ipq806x/patches-4.1/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch new file mode 100644 index 0000000..5598da2 --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/035-soc-qcom-Add-Shared-Memory-Manager-driver.patch @@ -0,0 +1,841 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2,2/2] soc: qcom: Add Shared Memory Manager driver +From: Bjorn Andersson +X-Patchwork-Id: 6202211 +Message-Id: <1428795178-24312-2-git-send-email-bjorn.andersson@sonymobile.com> +To: Kumar Gala , Andy Gross , + David Brown , Jeffrey Hugo +Cc: , , + +Date: Sat, 11 Apr 2015 16:32:58 -0700 + +The Shared Memory Manager driver implements an interface for allocating +and accessing items in the memory area shared among all of the +processors in a Qualcomm platform. + +Signed-off-by: Bjorn Andersson +Reviewed-by: Andy Gross +Tested-by: Andy Gross + +--- +Changes since v1: +- ioremapping the regions nocache +- improved documentation of the two regions of partitions +- corrected free space check in private allocator + + drivers/soc/qcom/Kconfig | 7 + + drivers/soc/qcom/Makefile | 1 + + drivers/soc/qcom/smem.c | 768 ++++++++++++++++++++++++++++++++++++++++++ + include/linux/soc/qcom/smem.h | 14 + + 4 files changed, 790 insertions(+) + create mode 100644 drivers/soc/qcom/smem.c + create mode 100644 include/linux/soc/qcom/smem.h + +--- a/drivers/soc/qcom/Kconfig ++++ b/drivers/soc/qcom/Kconfig +@@ -10,3 +10,10 @@ config QCOM_GSBI + functions for connecting the underlying serial UART, SPI, and I2C + devices to the output pins. + ++config QCOM_SMEM ++ tristate "Qualcomm Shared Memory Manager (SMEM)" ++ depends on ARCH_QCOM ++ help ++ Say y here to 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. +--- a/drivers/soc/qcom/Makefile ++++ b/drivers/soc/qcom/Makefile +@@ -1 +1,2 @@ + obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o ++obj-$(CONFIG_QCOM_SMEM) += smem.o +--- /dev/null ++++ b/drivers/soc/qcom/smem.c +@@ -0,0 +1,768 @@ ++/* ++ * Copyright (c) 2015, Sony Mobile Communications AB. ++ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * 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 metadata 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. ++ * ++ * ++ * 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. ++ * ++ */ ++ ++/** ++ * 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 { ++ u32 command; ++ u32 status; ++ u32 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 { ++ u32 allocated; ++ u32 offset; ++ u32 size; ++ u32 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]; ++ u32 version[32]; ++ u32 initialized; ++ u32 free_offset; ++ u32 available; ++ u32 reserved; ++ struct smem_global_entry toc[]; ++}; ++ ++/** ++ * 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 ++ * @reserved: reserved entries for later use ++ */ ++struct smem_ptable_entry { ++ u32 offset; ++ u32 size; ++ u32 flags; ++ u16 host0; ++ u16 host1; ++ u32 reserved[8]; ++}; ++ ++/** ++ * 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 { ++ u32 magic; ++ u32 version; ++ u32 num_entries; ++ u32 reserved[5]; ++ struct smem_ptable_entry entry[]; ++}; ++#define SMEM_PTABLE_MAGIC 0x434f5424 /* "$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 { ++ u32 magic; ++ u16 host0; ++ u16 host1; ++ u32 size; ++ u32 offset_free_uncached; ++ u32 offset_free_cached; ++ u32 reserved[3]; ++}; ++#define SMEM_PART_MAGIC 0x54525024 /* "$PRT" */ ++ ++/** ++ * 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; ++ u16 item; ++ u32 size; /* includes padding bytes */ ++ u16 padding_data; ++ u16 padding_hdr; ++ u32 reserved; ++}; ++#define SMEM_PRIVATE_CANARY 0xa5a5 ++ ++/* ++ * Item 3 of the global heap contains an array of versions for the various ++ * software components in the SoC. We verify that the boot loader version is ++ * what the expected version (SMEM_EXPECTED_VERSION) as a sanity check. ++ */ ++#define SMEM_ITEM_VERSION 3 ++#define SMEM_MASTER_SBL_VERSION_INDEX 7 ++#define SMEM_EXPECTED_VERSION 11 ++ ++/* ++ * 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_LAST 512 ++ ++/* Processor/host identifier for the application processor */ ++#define SMEM_HOST_APPS 0 ++ ++/* Max number of processors/hosts in a system */ ++#define SMEM_HOST_COUNT 7 ++ ++/** ++ * 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 ++ * @hwlock: reference to a hwspinlock ++ * @partitions: list of pointers to partitions affecting the current ++ * processor/host ++ * @num_regions: number of @regions ++ * @regions: list of the memory regions defining the shared memory ++ */ ++struct qcom_smem { ++ struct device *dev; ++ ++ struct hwspinlock *hwlock; ++ ++ struct smem_partition_header *partitions[SMEM_HOST_COUNT]; ++ ++ unsigned num_regions; ++ struct smem_region regions[0]; ++}; ++ ++/* 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 ++ ++static int qcom_smem_alloc_private(struct qcom_smem *smem, ++ unsigned host, ++ unsigned item, ++ size_t size) ++{ ++ struct smem_partition_header *phdr; ++ struct smem_private_entry *hdr; ++ size_t alloc_size; ++ void *p; ++ ++ /* We're not going to find it if there's no matching partition */ ++ if (host >= SMEM_HOST_COUNT || !smem->partitions[host]) ++ return -ENOENT; ++ ++ phdr = smem->partitions[host]; ++ ++ p = (void *)phdr + sizeof(*phdr); ++ while (p < (void *)phdr + phdr->offset_free_uncached) { ++ hdr = p; ++ ++ if (hdr->canary != SMEM_PRIVATE_CANARY) { ++ dev_err(smem->dev, ++ "Found invalid canary in host %d partition\n", ++ host); ++ return -EINVAL; ++ } ++ ++ if (hdr->item == item) ++ return -EEXIST; ++ ++ p += sizeof(*hdr) + hdr->padding_hdr + hdr->size; ++ } ++ ++ /* Check that we don't grow into the cached region */ ++ alloc_size = sizeof(*hdr) + ALIGN(size, 8); ++ if (p + alloc_size >= (void *)phdr + phdr->offset_free_cached) { ++ dev_err(smem->dev, "Out of memory\n"); ++ return -ENOSPC; ++ } ++ ++ hdr = p; ++ hdr->canary = SMEM_PRIVATE_CANARY; ++ hdr->item = item; ++ hdr->size = ALIGN(size, 8); ++ hdr->padding_data = 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(); ++ phdr->offset_free_uncached += alloc_size; ++ ++ return 0; ++} ++ ++static int qcom_smem_alloc_global(struct qcom_smem *smem, ++ unsigned item, ++ size_t size) ++{ ++ struct smem_header *header; ++ struct smem_global_entry *entry; ++ ++ if (WARN_ON(item >= SMEM_ITEM_LAST)) ++ return -EINVAL; ++ ++ header = smem->regions[0].virt_base; ++ entry = &header->toc[item]; ++ if (entry->allocated) ++ return -EEXIST; ++ ++ size = ALIGN(size, 8); ++ if (WARN_ON(size > header->available)) ++ return -ENOMEM; ++ ++ entry->offset = header->free_offset; ++ entry->size = 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 = 1; ++ ++ header->free_offset += size; ++ 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) ++{ ++ 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; ++ } ++ ++ ret = hwspin_lock_timeout_irqsave(__smem->hwlock, ++ HWSPINLOCK_TIMEOUT, ++ &flags); ++ if (ret) ++ return ret; ++ ++ ret = qcom_smem_alloc_private(__smem, host, item, size); ++ if (ret == -ENOENT) ++ ret = qcom_smem_alloc_global(__smem, item, size); ++ ++ hwspin_unlock_irqrestore(__smem->hwlock, &flags); ++ ++ return ret; ++} ++EXPORT_SYMBOL(qcom_smem_alloc); ++ ++static int qcom_smem_get_global(struct qcom_smem *smem, ++ unsigned item, ++ void **ptr, ++ size_t *size) ++{ ++ struct smem_header *header; ++ struct smem_region *area; ++ struct smem_global_entry *entry; ++ u32 aux_base; ++ unsigned i; ++ ++ if (WARN_ON(item >= SMEM_ITEM_LAST)) ++ return -EINVAL; ++ ++ header = smem->regions[0].virt_base; ++ entry = &header->toc[item]; ++ if (!entry->allocated) ++ return -ENXIO; ++ ++ if (ptr != NULL) { ++ aux_base = 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) { ++ *ptr = area->virt_base + entry->offset; ++ break; ++ } ++ } ++ } ++ if (size != NULL) ++ *size = entry->size; ++ ++ return 0; ++} ++ ++static int qcom_smem_get_private(struct qcom_smem *smem, ++ unsigned host, ++ unsigned item, ++ void **ptr, ++ size_t *size) ++{ ++ struct smem_partition_header *phdr; ++ struct smem_private_entry *hdr; ++ void *p; ++ ++ /* We're not going to find it if there's no matching partition */ ++ if (host >= SMEM_HOST_COUNT || !smem->partitions[host]) ++ return -ENOENT; ++ ++ phdr = smem->partitions[host]; ++ ++ p = (void *)phdr + sizeof(*phdr); ++ while (p < (void *)phdr + phdr->offset_free_uncached) { ++ hdr = p; ++ ++ if (hdr->canary != SMEM_PRIVATE_CANARY) { ++ dev_err(smem->dev, ++ "Found invalid canary in host %d partition\n", ++ host); ++ return -EINVAL; ++ } ++ ++ if (hdr->item == item) { ++ if (ptr != NULL) ++ *ptr = p + sizeof(*hdr) + hdr->padding_hdr; ++ ++ if (size != NULL) ++ *size = hdr->size - hdr->padding_data; ++ ++ return 0; ++ } ++ ++ p += sizeof(*hdr) + hdr->padding_hdr + hdr->size; ++ } ++ ++ return -ENOENT; ++} ++ ++/** ++ * qcom_smem_get - resolve ptr of size of a smem item ++ * @host: the remote processor, or -1 ++ * @item: smem item handle ++ * @ptr: pointer to be filled out with address of the item ++ * @size: pointer to be filled out with size of the item ++ * ++ * Looks up pointer and size of a smem item. ++ */ ++int qcom_smem_get(unsigned host, unsigned item, void **ptr, size_t *size) ++{ ++ unsigned long flags; ++ int ret; ++ ++ if (!__smem) ++ return -EPROBE_DEFER; ++ ++ ret = hwspin_lock_timeout_irqsave(__smem->hwlock, ++ HWSPINLOCK_TIMEOUT, ++ &flags); ++ if (ret) ++ return ret; ++ ++ ret = qcom_smem_get_private(__smem, host, item, ptr, size); ++ if (ret == -ENOENT) ++ ret = qcom_smem_get_global(__smem, item, ptr, size); ++ ++ hwspin_unlock_irqrestore(__smem->hwlock, &flags); ++ return ret; ++ ++} ++EXPORT_SYMBOL(qcom_smem_get); ++ ++/** ++ * qcom_smem_get_free_space - retrieve amont of free space in a partition ++ * @host: the remote processor identifing 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_header *phdr; ++ struct smem_header *header; ++ unsigned ret; ++ ++ if (!__smem) ++ return -EPROBE_DEFER; ++ ++ if (host < SMEM_HOST_COUNT && __smem->partitions[host]) { ++ phdr = __smem->partitions[host]; ++ ret = phdr->offset_free_uncached; ++ } else { ++ header = __smem->regions[0].virt_base; ++ ret = header->available; ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL(qcom_smem_get_free_space); ++ ++static int qcom_smem_get_sbl_version(struct qcom_smem *smem) ++{ ++ unsigned *versions; ++ size_t size; ++ int ret; ++ ++ ret = qcom_smem_get_global(smem, SMEM_ITEM_VERSION, ++ (void **)&versions, &size); ++ if (ret < 0) { ++ dev_err(smem->dev, "Unable to read the version item\n"); ++ return -ENOENT; ++ } ++ ++ if (size < sizeof(unsigned) * SMEM_MASTER_SBL_VERSION_INDEX) { ++ dev_err(smem->dev, "Version item is too small\n"); ++ return -EINVAL; ++ } ++ ++ return versions[SMEM_MASTER_SBL_VERSION_INDEX]; ++} ++ ++static int qcom_smem_enumerate_partitions(struct qcom_smem *smem, ++ unsigned local_host) ++{ ++ struct smem_partition_header *header; ++ struct smem_ptable_entry *entry; ++ struct smem_ptable *ptable; ++ unsigned remote_host; ++ int i; ++ ++ ptable = smem->regions[0].virt_base + smem->regions[0].size - 4 * 1024; ++ if (ptable->magic != SMEM_PTABLE_MAGIC) ++ return 0; ++ ++ if (ptable->version != 1) { ++ dev_err(smem->dev, ++ "Unsupported partition header version %d\n", ++ ptable->version); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < ptable->num_entries; i++) { ++ entry = &ptable->entry[i]; ++ ++ if (entry->host0 != local_host && entry->host1 != local_host) ++ continue; ++ ++ if (!entry->offset) ++ continue; ++ ++ if (!entry->size) ++ continue; ++ ++ if (entry->host0 == local_host) ++ remote_host = entry->host1; ++ else ++ remote_host = entry->host0; ++ ++ 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 + entry->offset; ++ ++ if (header->magic != SMEM_PART_MAGIC) { ++ dev_err(smem->dev, ++ "Partition %d has invalid magic\n", i); ++ return -EINVAL; ++ } ++ ++ if (header->host0 != local_host && header->host1 != local_host) { ++ dev_err(smem->dev, ++ "Partition %d hosts are invalid\n", i); ++ return -EINVAL; ++ } ++ ++ if (header->host0 != remote_host && header->host1 != remote_host) { ++ dev_err(smem->dev, ++ "Partition %d hosts are invalid\n", i); ++ return -EINVAL; ++ } ++ ++ if (header->size != entry->size) { ++ dev_err(smem->dev, ++ "Partition %d has invalid size\n", i); ++ return -EINVAL; ++ } ++ ++ if (header->offset_free_uncached > header->size) { ++ dev_err(smem->dev, ++ "Partition %d has invalid free pointer\n", i); ++ return -EINVAL; ++ } ++ ++ smem->partitions[remote_host] = header; ++ } ++ ++ return 0; ++} ++ ++static int qcom_smem_count_mem_regions(struct platform_device *pdev) ++{ ++ struct resource *res; ++ int num_regions = 0; ++ int i; ++ ++ for (i = 0; i < pdev->num_resources; i++) { ++ res = &pdev->resource[i]; ++ ++ if (resource_type(res) == IORESOURCE_MEM) ++ num_regions++; ++ } ++ ++ return num_regions; ++} ++ ++static int qcom_smem_probe(struct platform_device *pdev) ++{ ++ struct smem_header *header; ++ struct device_node *np; ++ struct qcom_smem *smem; ++ struct resource *res; ++ struct resource r; ++ size_t array_size; ++ int num_regions = 0; ++ int hwlock_id; ++ u32 version; ++ int ret; ++ int i; ++ ++ num_regions = qcom_smem_count_mem_regions(pdev) + 1; ++ ++ array_size = num_regions * sizeof(struct smem_region); ++ smem = devm_kzalloc(&pdev->dev, sizeof(*smem) + array_size, GFP_KERNEL); ++ if (!smem) ++ return -ENOMEM; ++ ++ smem->dev = &pdev->dev; ++ smem->num_regions = num_regions; ++ ++ np = of_parse_phandle(pdev->dev.of_node, "memory-region", 0); ++ if (!np) { ++ dev_err(&pdev->dev, "No memory-region specified\n"); ++ return -EINVAL; ++ } ++ ++ ret = of_address_to_resource(np, 0, &r); ++ of_node_put(np); ++ if (ret) ++ return ret; ++ ++ smem->regions[0].aux_base = (u32)r.start; ++ smem->regions[0].size = resource_size(&r); ++ smem->regions[0].virt_base = devm_ioremap_nocache(&pdev->dev, ++ r.start, ++ resource_size(&r)); ++ if (!smem->regions[0].virt_base) ++ return -ENOMEM; ++ ++ for (i = 1; i < num_regions; i++) { ++ res = platform_get_resource(pdev, IORESOURCE_MEM, i - 1); ++ ++ smem->regions[i].aux_base = (u32)res->start; ++ smem->regions[i].size = resource_size(res); ++ smem->regions[i].virt_base = devm_ioremap_nocache(&pdev->dev, ++ res->start, ++ resource_size(res)); ++ if (!smem->regions[i].virt_base) ++ return -ENOMEM; ++ } ++ ++ header = smem->regions[0].virt_base; ++ if (header->initialized != 1 || header->reserved) { ++ dev_err(&pdev->dev, "SMEM is not initilized by SBL\n"); ++ return -EINVAL; ++ } ++ ++ version = qcom_smem_get_sbl_version(smem); ++ if (version >> 16 != SMEM_EXPECTED_VERSION) { ++ dev_err(&pdev->dev, "Unsupported smem version 0x%x\n", version); ++ return -EINVAL; ++ } ++ ++ ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS); ++ if (ret < 0) ++ return ret; ++ ++ hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0); ++ if (hwlock_id < 0) { ++ 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; ++ ++ __smem = smem; ++ ++ return 0; ++} ++ ++static int qcom_smem_remove(struct platform_device *pdev) ++{ ++ hwspin_lock_free(__smem->hwlock); ++ __smem = NULL; ++ ++ return 0; ++} ++ ++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 = 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 "); ++MODULE_DESCRIPTION("Qualcomm Shared Memory Manager"); ++MODULE_LICENSE("GPLv2"); +--- /dev/null ++++ b/include/linux/soc/qcom/smem.h +@@ -0,0 +1,14 @@ ++#ifndef __QCOM_SMEM_H__ ++#define __QCOM_SMEM_H__ ++ ++struct device_node; ++struct qcom_smem; ++ ++#define QCOM_SMEM_HOST_ANY -1 ++ ++int qcom_smem_alloc(unsigned host, unsigned item, size_t size); ++int qcom_smem_get(unsigned host, unsigned item, void **ptr, size_t *size); ++ ++int qcom_smem_get_free_space(unsigned host); ++ ++#endif diff --git a/target/linux/ipq806x/patches-4.1/036-soc-qcom-add-smem-to-IPQ806x-platforms.patch b/target/linux/ipq806x/patches-4.1/036-soc-qcom-add-smem-to-IPQ806x-platforms.patch new file mode 100644 index 0000000..d30cf7b --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/036-soc-qcom-add-smem-to-IPQ806x-platforms.patch @@ -0,0 +1,27 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -55,7 +55,7 @@ + no-map; + }; + +- smem@41000000 { ++ smem: smem@41000000 { + reg = <0x41000000 0x200000>; + no-map; + }; +@@ -79,6 +79,15 @@ + + #hwlock-cells = <1>; + }; ++ ++ smem { ++ compatible = "qcom,smem"; ++ ++ memory-region = <&smem>; ++ reg = <0x41000000 0x4000>; ++ ++ hwlocks = <&sfpb_mutex 3>; ++ }; + }; + + soc: soc { diff --git a/target/linux/ipq806x/patches-4.1/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-4.1/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch index 51a08fb..1974bc7 100644 --- a/target/linux/ipq806x/patches-4.1/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch +++ b/target/linux/ipq806x/patches-4.1/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch @@ -134,7 +134,7 @@ Signed-off-by: Mathieu Olivari / { model = "Qualcomm IPQ8064"; -@@ -341,5 +343,127 @@ +@@ -350,5 +352,127 @@ #reset-cells = <1>; }; diff --git a/target/linux/ipq806x/patches-4.1/115-add-pcie-aux-clk-dts.patch b/target/linux/ipq806x/patches-4.1/115-add-pcie-aux-clk-dts.patch index 40115a4..7615356 100644 --- a/target/linux/ipq806x/patches-4.1/115-add-pcie-aux-clk-dts.patch +++ b/target/linux/ipq806x/patches-4.1/115-add-pcie-aux-clk-dts.patch @@ -1,6 +1,6 @@ --- a/arch/arm/boot/dts/qcom-ipq8064.dtsi +++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -371,15 +371,21 @@ +@@ -380,15 +380,21 @@ clocks = <&gcc PCIE_A_CLK>, <&gcc PCIE_H_CLK>, @@ -26,7 +26,7 @@ status = "disabled"; }; -@@ -412,15 +418,21 @@ +@@ -421,15 +427,21 @@ clocks = <&gcc PCIE_1_A_CLK>, <&gcc PCIE_1_H_CLK>, @@ -52,7 +52,7 @@ status = "disabled"; }; -@@ -453,15 +465,21 @@ +@@ -462,15 +474,21 @@ clocks = <&gcc PCIE_2_A_CLK>, <&gcc PCIE_2_H_CLK>, diff --git a/target/linux/ipq806x/patches-4.1/126-add-rpm-to-ipq8064-dts.patch b/target/linux/ipq806x/patches-4.1/126-add-rpm-to-ipq8064-dts.patch index 458dcd5..8e88b79 100644 --- a/target/linux/ipq806x/patches-4.1/126-add-rpm-to-ipq8064-dts.patch +++ b/target/linux/ipq806x/patches-4.1/126-add-rpm-to-ipq8064-dts.patch @@ -8,7 +8,7 @@ #include #include #include -@@ -104,6 +105,63 @@ +@@ -113,6 +114,63 @@ reg-names = "lpass-lpaif"; }; @@ -72,7 +72,7 @@ qcom_pinmux: pinmux@800000 { compatible = "qcom,ipq8064-pinctrl"; reg = <0x800000 0x4000>; -@@ -148,6 +206,12 @@ +@@ -157,6 +215,12 @@ reg = <0x02098000 0x1000>, <0x02008000 0x1000>; }; diff --git a/target/linux/ipq806x/patches-4.1/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch b/target/linux/ipq806x/patches-4.1/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch index b25d955..bcab7b2 100644 --- a/target/linux/ipq806x/patches-4.1/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch +++ b/target/linux/ipq806x/patches-4.1/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch @@ -37,7 +37,7 @@ }; }; -@@ -84,6 +102,46 @@ +@@ -93,6 +111,46 @@ }; }; @@ -84,7 +84,7 @@ soc: soc { #address-cells = <1>; #size-cells = <1>; -@@ -199,11 +257,13 @@ +@@ -208,11 +266,13 @@ acc0: clock-controller@2088000 { compatible = "qcom,kpss-acc-v1"; reg = <0x02088000 0x1000>, <0x02008000 0x1000>; diff --git a/target/linux/ipq806x/patches-4.1/157-ARM-DT-ipq8064-Add-ADM-device-node.patch b/target/linux/ipq806x/patches-4.1/157-ARM-DT-ipq8064-Add-ADM-device-node.patch index e5c57a9..9129336 100644 --- a/target/linux/ipq806x/patches-4.1/157-ARM-DT-ipq8064-Add-ADM-device-node.patch +++ b/target/linux/ipq806x/patches-4.1/157-ARM-DT-ipq8064-Add-ADM-device-node.patch @@ -26,7 +26,7 @@ Signed-off-by: Andy Gross #address-cells = <1>; --- a/arch/arm/boot/dts/qcom-ipq8064.dtsi +++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -607,5 +607,25 @@ +@@ -616,5 +616,25 @@ status = "disabled"; }; diff --git a/target/linux/ipq806x/patches-4.1/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch b/target/linux/ipq806x/patches-4.1/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch index bd9bf47..e04deb8 100644 --- a/target/linux/ipq806x/patches-4.1/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch +++ b/target/linux/ipq806x/patches-4.1/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch @@ -26,7 +26,7 @@ arch/arm/boot/dts/qcom-ipq8064.dtsi | 15 +++++++++++++++ --- a/arch/arm/boot/dts/qcom-ipq8064.dtsi +++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -627,5 +627,21 @@ +@@ -636,5 +636,21 @@ status = "disabled"; }; diff --git a/target/linux/ipq806x/patches-4.1/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-4.1/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch index c21ebcd..f4f56f2 100644 --- a/target/linux/ipq806x/patches-4.1/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch +++ b/target/linux/ipq806x/patches-4.1/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch @@ -117,7 +117,7 @@ Signed-off-by: Mathieu Olivari }; --- a/arch/arm/boot/dts/qcom-ipq8064.dtsi +++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi -@@ -643,5 +643,91 @@ +@@ -652,5 +652,91 @@ status = "disabled"; };